mirror of https://github.com/YGGverse/nexd.git
yggverse
7 months ago
commit
a3ffecb1db
4 changed files with 186 additions and 0 deletions
@ -0,0 +1,22 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2023 m15o |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||||
|
this software and associated documentation files (the "Software"), to deal in |
||||||
|
the Software without restriction, including without limitation the rights to |
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do |
||||||
|
so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next |
||||||
|
paragraph) shall be included in all copies or substantial portions of the |
||||||
|
Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,19 @@ |
|||||||
|
# nexd - Server for Nex Protocol written in Go |
||||||
|
|
||||||
|
Unofficial GIT build of [nexd](https://hg.sr.ht/~m15o/nexd) Mercurial repository. |
||||||
|
|
||||||
|
Includes [nex-pfm](https://hg.sr.ht/~m15o/nex-pfm) + modified for Linux / amd64 makefile version. |
||||||
|
|
||||||
|
## Install |
||||||
|
|
||||||
|
* `git clone https://github.com/YGGverse/nexd.git` |
||||||
|
* `cd nexd` |
||||||
|
* `make` |
||||||
|
|
||||||
|
## Launch |
||||||
|
|
||||||
|
``` |
||||||
|
nexd path/to/destination/folder |
||||||
|
``` |
||||||
|
|
||||||
|
* server open `index` file by default, directory listing instead |
@ -0,0 +1,143 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"io" |
||||||
|
"io/fs" |
||||||
|
"log" |
||||||
|
"net" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
"sort" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
func write(w io.Writer, s string) (int, error) { |
||||||
|
return w.Write([]byte(s + "\n")) |
||||||
|
} |
||||||
|
|
||||||
|
type Handler struct { |
||||||
|
FS fs.FS |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Handler) handleDir(p string, w io.Writer) error { |
||||||
|
if header, err := fs.ReadFile(h.FS, path.Join(p, ".header")); err == nil { |
||||||
|
write(w, string(header)) |
||||||
|
} |
||||||
|
modified := true |
||||||
|
if _, err := fs.Stat(h.FS, path.Join(p, ".modified")); err != nil { |
||||||
|
modified = false |
||||||
|
} |
||||||
|
asc := false |
||||||
|
if _, err := fs.Stat(h.FS, path.Join(p, ".desc")); err != nil { |
||||||
|
asc = true |
||||||
|
} |
||||||
|
dirents, err := fs.ReadDir(h.FS, p) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
sort.Slice(dirents, func(i, j int) bool { |
||||||
|
if modified { |
||||||
|
st1, err := dirents[i].Info() |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
st2, err := dirents[j].Info() |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
return st1.ModTime().After(st2.ModTime()) |
||||||
|
} else if asc { |
||||||
|
return dirents[i].Name() < dirents[j].Name() |
||||||
|
} else { |
||||||
|
return dirents[i].Name() > dirents[j].Name() |
||||||
|
} |
||||||
|
}) |
||||||
|
for _, entry := range dirents { |
||||||
|
name := entry.Name() |
||||||
|
if strings.HasPrefix(name, ".") { |
||||||
|
continue |
||||||
|
} |
||||||
|
i, err := entry.Info() |
||||||
|
if err != nil { |
||||||
|
continue |
||||||
|
} |
||||||
|
if i.Mode()&(1<<2) == 0 { |
||||||
|
continue |
||||||
|
} |
||||||
|
if entry.IsDir() { |
||||||
|
name += "/" |
||||||
|
} |
||||||
|
if _, err := write(w, "=> "+name); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Handler) Handle(p string, w io.Writer) error { |
||||||
|
if p == "/" || p == "" { |
||||||
|
p = "." |
||||||
|
} else { |
||||||
|
p = strings.Trim(p, "/") |
||||||
|
} |
||||||
|
|
||||||
|
f, err := h.FS.Open(p) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer f.Close() |
||||||
|
|
||||||
|
stat, err := f.Stat() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if stat.IsDir() { |
||||||
|
index, err := h.FS.Open(path.Join(p, "index")) |
||||||
|
if err != nil { |
||||||
|
return h.handleDir(p, w) |
||||||
|
} |
||||||
|
defer index.Close() |
||||||
|
f = index |
||||||
|
} |
||||||
|
|
||||||
|
_, err = io.Copy(w, f) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func serve(h *Handler, rw io.ReadWriteCloser) { |
||||||
|
defer rw.Close() |
||||||
|
scanner := bufio.NewScanner(rw) |
||||||
|
scanner.Scan() |
||||||
|
sel := scanner.Text() |
||||||
|
if err := h.Handle(sel, rw); err != nil { |
||||||
|
rw.Write([]byte("document not found")) |
||||||
|
log.Println(err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func listenAndServe(h *Handler) error { |
||||||
|
l, err := net.Listen("tcp", ":1900") |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer l.Close() |
||||||
|
|
||||||
|
for { |
||||||
|
rw, err := l.Accept() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
go serve(h, rw) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func main() { |
||||||
|
if len(os.Args) < 2 { |
||||||
|
log.Fatal("usage: nexd path") |
||||||
|
} |
||||||
|
|
||||||
|
h := Handler{FS: os.DirFS(os.Args[1])} |
||||||
|
log.Fatal(listenAndServe(&h)) |
||||||
|
} |
Loading…
Reference in new issue