- commit
- bbc9409
- parent
- 97adfeb
- author
- Antonio Mika
- date
- 2022-08-25 05:20:26 +0000 UTC
Added rsync and sftp download support
+2,
-0
1@@ -14,6 +14,7 @@ import (
2 "git.sr.ht/~erock/pico/imgs/storage"
3 "git.sr.ht/~erock/pico/shared"
4 "git.sr.ht/~erock/pico/wish/cms"
5+ "git.sr.ht/~erock/pico/wish/list"
6 "git.sr.ht/~erock/pico/wish/pipe"
7 "git.sr.ht/~erock/pico/wish/proxy"
8 wishrsync "git.sr.ht/~erock/pico/wish/send/rsync"
9@@ -37,6 +38,7 @@ func createRouter(cfg *shared.ConfigSite, handler utils.CopyFromClientHandler) p
10 return func(sh ssh.Handler, s ssh.Session) []wish.Middleware {
11 return []wish.Middleware{
12 pipe.Middleware(handler, ""),
13+ list.Middleware(handler),
14 scp.Middleware(handler),
15 wishrsync.Middleware(handler),
16 bm.Middleware(cms.Middleware(&cfg.ConfigCms, cfg)),
+2,
-0
1@@ -13,6 +13,7 @@ import (
2 "git.sr.ht/~erock/pico/lists"
3 "git.sr.ht/~erock/pico/shared"
4 "git.sr.ht/~erock/pico/wish/cms"
5+ "git.sr.ht/~erock/pico/wish/list"
6 "git.sr.ht/~erock/pico/wish/pipe"
7 "git.sr.ht/~erock/pico/wish/proxy"
8 wishrsync "git.sr.ht/~erock/pico/wish/send/rsync"
9@@ -35,6 +36,7 @@ func createRouter(handler *filehandlers.ScpUploadHandler) proxy.Router {
10 return func(sh ssh.Handler, s ssh.Session) []wish.Middleware {
11 return []wish.Middleware{
12 pipe.Middleware(handler, ".txt"),
13+ list.Middleware(handler),
14 scp.Middleware(handler),
15 wishrsync.Middleware(handler),
16 bm.Middleware(cms.Middleware(&handler.Cfg.ConfigCms, handler.Cfg)),
+2,
-0
1@@ -13,6 +13,7 @@ import (
2 "git.sr.ht/~erock/pico/pastes"
3 "git.sr.ht/~erock/pico/shared"
4 "git.sr.ht/~erock/pico/wish/cms"
5+ "git.sr.ht/~erock/pico/wish/list"
6 "git.sr.ht/~erock/pico/wish/pipe"
7 "git.sr.ht/~erock/pico/wish/proxy"
8 wishrsync "git.sr.ht/~erock/pico/wish/send/rsync"
9@@ -35,6 +36,7 @@ func createRouter(handler *filehandlers.ScpUploadHandler) proxy.Router {
10 return func(sh ssh.Handler, s ssh.Session) []wish.Middleware {
11 return []wish.Middleware{
12 pipe.Middleware(handler, ""),
13+ list.Middleware(handler),
14 scp.Middleware(handler),
15 wishrsync.Middleware(handler),
16 bm.Middleware(cms.Middleware(&handler.Cfg.ConfigCms, handler.Cfg)),
+2,
-0
1@@ -13,6 +13,7 @@ import (
2 "git.sr.ht/~erock/pico/prose"
3 "git.sr.ht/~erock/pico/shared"
4 "git.sr.ht/~erock/pico/wish/cms"
5+ "git.sr.ht/~erock/pico/wish/list"
6 "git.sr.ht/~erock/pico/wish/pipe"
7 "git.sr.ht/~erock/pico/wish/proxy"
8 wishrsync "git.sr.ht/~erock/pico/wish/send/rsync"
9@@ -35,6 +36,7 @@ func createRouter(handler *filehandlers.ScpUploadHandler) proxy.Router {
10 return func(sh ssh.Handler, s ssh.Session) []wish.Middleware {
11 return []wish.Middleware{
12 pipe.Middleware(handler, ".md"),
13+ list.Middleware(handler),
14 scp.Middleware(handler),
15 wishrsync.Middleware(handler),
16 bm.Middleware(cms.Middleware(&handler.Cfg.ConfigCms, handler.Cfg)),
+9,
-0
1@@ -5,6 +5,7 @@ import (
2 "fmt"
3 "io"
4 "net/http"
5+ "os"
6 "path"
7 "time"
8
9@@ -60,6 +61,14 @@ func (h *UploadImgHandler) removePost(data *PostMetaData) error {
10 return nil
11 }
12
13+func (h *UploadImgHandler) Read(s ssh.Session, filename string) (os.FileInfo, io.ReaderAt, error) {
14+ return nil, nil, nil
15+}
16+
17+func (h *UploadImgHandler) List(s ssh.Session, filename string) ([]os.FileInfo, error) {
18+ return nil, nil
19+}
20+
21 func (h *UploadImgHandler) Validate(s ssh.Session) error {
22 var err error
23 key, err := util.KeyText(s)
+65,
-0
1@@ -5,6 +5,7 @@ import (
2 "fmt"
3 "io"
4 "net/http"
5+ "os"
6 "path"
7 "strings"
8 "time"
9@@ -45,6 +46,70 @@ func NewScpPostHandler(dbpool db.DB, cfg *shared.ConfigSite, hooks ScpFileHooks)
10 }
11 }
12
13+func (h *ScpUploadHandler) Read(s ssh.Session, filename string) (os.FileInfo, io.ReaderAt, error) {
14+ cleanFilename := strings.ReplaceAll(filename, "/", "")
15+
16+ if cleanFilename == "" || cleanFilename == "." {
17+ return nil, nil, os.ErrNotExist
18+ }
19+
20+ post, err := h.DBPool.FindPostWithFilename(cleanFilename, h.User.ID, h.Cfg.Space)
21+ if err != nil {
22+ return nil, nil, err
23+ }
24+
25+ fileInfo := &utils.VirtualFile{
26+ FName: post.Filename,
27+ FIsDir: false,
28+ FSize: int64(post.FileSize),
29+ FModTime: *post.UpdatedAt,
30+ }
31+
32+ return fileInfo, strings.NewReader(post.Text), nil
33+}
34+
35+func (h *ScpUploadHandler) List(s ssh.Session, filename string) ([]os.FileInfo, error) {
36+ var fileList []os.FileInfo
37+ cleanFilename := strings.ReplaceAll(filename, "/", "")
38+
39+ var err error
40+ var post *db.Post
41+ var posts []*db.Post
42+
43+ if cleanFilename == "" || cleanFilename == "." {
44+ name := cleanFilename
45+ if name == "" {
46+ name = "/"
47+ }
48+
49+ fileList = append(fileList, &utils.VirtualFile{
50+ FName: name,
51+ FIsDir: true,
52+ })
53+
54+ posts, err = h.DBPool.FindAllPostsForUser(h.User.ID, h.Cfg.Space)
55+ } else {
56+ post, err = h.DBPool.FindPostWithFilename(cleanFilename, h.User.ID, h.Cfg.Space)
57+
58+ posts = append(posts, post)
59+ }
60+
61+ if err != nil {
62+ return nil, err
63+ }
64+
65+ for _, post := range posts {
66+ fileList = append(fileList, &utils.VirtualFile{
67+ FName: post.Filename,
68+ FIsDir: false,
69+ FSize: int64(post.FileSize),
70+ FModTime: *post.UpdatedAt,
71+ })
72+ }
73+
74+ return fileList, nil
75+}
76+
77 func (h *ScpUploadHandler) Validate(s ssh.Session) error {
78 var err error
79 key, err := util.KeyText(s)
M
go.mod
+2,
-2
1@@ -3,11 +3,11 @@ module git.sr.ht/~erock/pico
2 go 1.19
3
4 // replace git.sr.ht/~erock/pico/wish => /home/erock/pico/wish
5-// replace github.com/antoniomika/go-rsync-receiver => /home/erock/pico/go-rsync-receiver
6+// replace github.com/antoniomika/go-rsync-receiver => /Users/.../go-rsync-receiver
7
8 require (
9 github.com/alecthomas/chroma v0.10.0
10- github.com/antoniomika/go-rsync-receiver v0.0.0-20220817204644-b0fcbb706c46
11+ github.com/antoniomika/go-rsync-receiver v0.0.0-20220825041817-0387edc9afb7
12 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
13 github.com/charmbracelet/bubbles v0.13.0
14 github.com/charmbracelet/bubbletea v0.22.1
M
go.sum
+2,
-0
1@@ -46,6 +46,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
2 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
3 github.com/antoniomika/go-rsync-receiver v0.0.0-20220817204644-b0fcbb706c46 h1:xf3+67PUvBatjQk3mO2umUrPT6FqAArKru1DBDaZNaY=
4 github.com/antoniomika/go-rsync-receiver v0.0.0-20220817204644-b0fcbb706c46/go.mod h1:zmqePVIo1hp+WEKxERLLGHJBDOr8/z/T4eFqXgWIw1w=
5+github.com/antoniomika/go-rsync-receiver v0.0.0-20220825041817-0387edc9afb7 h1:CECWxPqxYAwjlUQxEeuaqnjlTsOsaAMfu6f27xHswm0=
6+github.com/antoniomika/go-rsync-receiver v0.0.0-20220825041817-0387edc9afb7/go.mod h1:zmqePVIo1hp+WEKxERLLGHJBDOr8/z/T4eFqXgWIw1w=
7 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
8 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
9 github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+1,
-1
1@@ -2,7 +2,7 @@ package imgs
2
3 import (
4 "git.sr.ht/~erock/pico/db"
5- "git.sr.ht/~erock/pico/filehandlers/imgs"
6+ uploadimgs "git.sr.ht/~erock/pico/filehandlers/imgs"
7 "git.sr.ht/~erock/pico/imgs/storage"
8 "git.sr.ht/~erock/pico/shared"
9 "git.sr.ht/~erock/pico/wish/send/utils"
+13,
-4
1@@ -4,7 +4,9 @@ import (
2 "fmt"
3 "io"
4 "log"
5+ "os"
6 "strings"
7+ "time"
8
9 "git.sr.ht/~erock/pico/wish/send"
10 "git.sr.ht/~erock/pico/wish/send/utils"
11@@ -27,13 +29,20 @@ func (h *handler) Validate(session ssh.Session) error {
12 return nil
13 }
14
15-func (h *handler) Read(session ssh.Session, file *utils.FileEntry) (io.Reader, error) {
16+func (h *handler) Read(session ssh.Session, filename string) (os.FileInfo, io.ReaderAt, error) {
17 log.Printf("Received validate from session: %+v", session)
18
19- return strings.NewReader("lorem ipsum dolor"), nil
20+ data := strings.NewReader("lorem ipsum dolor")
21+
22+ return &utils.VirtualFile{
23+ FName: "test",
24+ FIsDir: false,
25+ FSize: data.Size(),
26+ FModTime: time.Now(),
27+ }, data, nil
28 }
29
30-func (h *handler) List(session ssh.Session) ([]string, error) {
31+func (h *handler) List(session ssh.Session, filename string) ([]os.FileInfo, error) {
32 return nil, nil
33 }
34
35@@ -41,7 +50,7 @@ func main() {
36 h := &handler{}
37
38 s, err := wish.NewServer(
39- wish.WithAddress(":9000"),
40+ wish.WithAddress("localhost:9000"),
41 wish.WithHostKeyPath("ssh_data/term_info_ed25519"),
42 send.Middleware(h),
43 )
+51,
-0
1@@ -0,0 +1,51 @@
2+package list
3+
4+import (
5+ "sort"
6+ "strings"
7+
8+ "git.sr.ht/~erock/pico/wish/send/utils"
9+ "github.com/charmbracelet/wish"
10+ "github.com/gliderlabs/ssh"
11+)
12+
13+func Middleware(writeHandler utils.CopyFromClientHandler) wish.Middleware {
14+ return func(sshHandler ssh.Handler) ssh.Handler {
15+ return func(session ssh.Session) {
16+ cmd := session.Command()
17+ if len(cmd) == 0 || (len(cmd) > 1 && cmd[1] != "ls") {
18+ sshHandler(session)
19+ return
20+ }
21+
22+ err := writeHandler.Validate(session)
23+ if err != nil {
24+ utils.ErrorHandler(session, err)
25+ return
26+ }
27+
28+ fileList, err := writeHandler.List(session, "/")
29+ if err != nil {
30+ utils.ErrorHandler(session, err)
31+ return
32+ }
33+
34+ var data []string
35+ for _, file := range fileList {
36+ name := strings.ReplaceAll(file.Name(), "/", "")
37+ if file.IsDir() {
38+ name += "/"
39+ }
40+
41+ data = append(data, name)
42+ }
43+
44+ sort.Strings(data)
45+
46+ _, err = session.Write([]byte(strings.Join(data, "\n")))
47+ if err != nil {
48+ utils.ErrorHandler(session, err)
49+ }
50+ }
51+ }
52+}
+35,
-1
1@@ -5,10 +5,13 @@ import (
2 "io"
3 "io/fs"
4 "log"
5+ "os"
6 "path"
7
8 "git.sr.ht/~erock/pico/wish/send/utils"
9 "github.com/antoniomika/go-rsync-receiver/rsyncreceiver"
10+ "github.com/antoniomika/go-rsync-receiver/rsyncsender"
11+ rsyncutils "github.com/antoniomika/go-rsync-receiver/utils"
12 "github.com/charmbracelet/wish"
13 "github.com/gliderlabs/ssh"
14 )
15@@ -18,7 +21,29 @@ type handler struct {
16 writeHandler utils.CopyFromClientHandler
17 }
18
19-func (h *handler) Put(fileName string, content io.Reader, fileSize int64, mTime int64, aTime int64) (written int64, err error) {
20+func (h *handler) Skip(file *rsyncutils.ReceiverFile) bool {
21+ return false
22+}
23+
24+func (h *handler) List(path string) ([]os.FileInfo, error) {
25+ list, err := h.writeHandler.List(h.session, path)
26+ if err != nil {
27+ return nil, err
28+ }
29+
30+ newList := list
31+ if list[0].IsDir() {
32+ newList = list[1:]
33+ }
34+
35+ return newList, nil
36+}
37+
38+func (h *handler) Read(path string) (os.FileInfo, io.ReaderAt, error) {
39+ return h.writeHandler.Read(h.session, path)
40+}
41+
42+func (h *handler) Put(fileName string, content io.Reader, fileSize int64, mTime int64, aTime int64) (int64, error) {
43 cleanName := path.Base(fileName)
44 fileEntry := &utils.FileEntry{
45 Name: cleanName,
46@@ -63,6 +88,15 @@ func Middleware(writeHandler utils.CopyFromClientHandler) wish.Middleware {
47 writeHandler: writeHandler,
48 }
49
50+ for _, arg := range cmd {
51+ if arg == "--sender" {
52+ if err := rsyncsender.ClientRun(nil, session, fileHandler, cmd[len(cmd)-1], true); err != nil {
53+ log.Println("error running rsync:", err)
54+ }
55+ return
56+ }
57+ }
58+
59 if _, err := rsyncreceiver.ClientRun(nil, session, fileHandler, true); err != nil {
60 log.Println("error running rsync:", err)
61 }
+12,
-0
1@@ -0,0 +1,12 @@
2+package scp
3+
4+import (
5+ "errors"
6+
7+ "git.sr.ht/~erock/pico/wish/send/utils"
8+ "github.com/gliderlabs/ssh"
9+)
10+
11+func copyToClient(session ssh.Session, info Info, handler utils.CopyFromClientHandler) error {
12+ return errors.New("unsupported, use rsync or sftp")
13+}
+5,
-4
1@@ -37,7 +37,11 @@ func Middleware(writeHandler utils.CopyFromClientHandler) wish.Middleware {
2
3 switch info.Op {
4 case OpCopyToClient:
5- err = fmt.Errorf("copying from server to client not supported")
6+ if writeHandler == nil {
7+ err = fmt.Errorf("no handler provided for scp -t")
8+ break
9+ }
10+ err = copyToClient(session, info, writeHandler)
11 case OpCopyFromClient:
12 if writeHandler == nil {
13 err = fmt.Errorf("no handler provided for scp -t")
14@@ -47,10 +51,7 @@ func Middleware(writeHandler utils.CopyFromClientHandler) wish.Middleware {
15 }
16 if err != nil {
17 utils.ErrorHandler(session, err)
18- return
19 }
20-
21- sshHandler(session)
22 }
23 }
24 }
+0,
-31
1@@ -1,31 +0,0 @@
2-package sftp
3-
4-import (
5- "os"
6- "time"
7-)
8-
9-type tempfile struct {
10- name string
11- isDir bool
12- size int64
13- modTime time.Time
14- sys any
15-}
16-
17-func (f *tempfile) Name() string { return f.name }
18-func (f *tempfile) Size() int64 { return f.size }
19-func (f *tempfile) Mode() os.FileMode {
20- if f.isDir {
21- return os.FileMode(0755) | os.ModeDir
22- }
23- return os.FileMode(0644)
24-}
25-func (f *tempfile) ModTime() time.Time {
26- if f.modTime.IsZero() {
27- return time.Now()
28- }
29- return f.modTime
30-}
31-func (f *tempfile) IsDir() bool { return f.isDir }
32-func (f *tempfile) Sys() any { return f.sys }
+12,
-4
1@@ -29,7 +29,6 @@ func (f listerat) ListAt(ls []os.FileInfo, offset int64) (int, error) {
2 type handler struct {
3 session ssh.Session
4 writeHandler utils.CopyFromClientHandler
5- rootFile *tempfile
6 }
7
8 func (f *handler) Filecmd(r *sftp.Request) error {
9@@ -41,9 +40,16 @@ func (f *handler) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
10 case "List":
11 fallthrough
12 case "Stat":
13- if r.Filepath == "/" {
14- return listerat{f.rootFile}, nil
15+ listData, err := f.writeHandler.List(f.session, r.Filepath)
16+ if err != nil {
17+ return nil, err
18 }
19+
20+ if r.Method == "List" {
21+ listData = listData[1:]
22+ }
23+
24+ return listerat(listData), nil
25 }
26
27 return nil, errors.New("unsupported")
28@@ -70,5 +76,7 @@ func (f *handler) Fileread(r *sftp.Request) (io.ReaderAt, error) {
29 return nil, os.ErrInvalid
30 }
31
32- return nil, errors.New("unsupported")
33+ _, reader, err := f.writeHandler.Read(f.session, r.Filepath)
34+
35+ return reader, err
36 }
+0,
-6
1@@ -29,15 +29,9 @@ func SubsystemHandler(writeHandler utils.CopyFromClientHandler) ssh.SubsystemHan
2 return
3 }
4
5- rootFile := &tempfile{
6- name: "/",
7- isDir: true,
8- }
9-
10 handler := &handler{
11 session: session,
12 writeHandler: writeHandler,
13- rootFile: rootFile,
14 }
15
16 handlers := sftp.Handlers{
+31,
-0
1@@ -0,0 +1,31 @@
2+package utils
3+
4+import (
5+ "os"
6+ "time"
7+)
8+
9+type VirtualFile struct {
10+ FName string
11+ FIsDir bool
12+ FSize int64
13+ FModTime time.Time
14+ FSys any
15+}
16+
17+func (f *VirtualFile) Name() string { return f.FName }
18+func (f *VirtualFile) Size() int64 { return f.FSize }
19+func (f *VirtualFile) Mode() os.FileMode {
20+ if f.FIsDir {
21+ return os.FileMode(0755) | os.ModeDir
22+ }
23+ return os.FileMode(0644)
24+}
25+func (f *VirtualFile) ModTime() time.Time {
26+ if f.FModTime.IsZero() {
27+ return time.Now()
28+ }
29+ return f.FModTime
30+}
31+func (f *VirtualFile) IsDir() bool { return f.FIsDir }
32+func (f *VirtualFile) Sys() any { return f.FSys }
+4,
-1
1@@ -5,6 +5,7 @@ import (
2 "fmt"
3 "io"
4 "io/fs"
5+ "os"
6 "strconv"
7
8 "github.com/gliderlabs/ssh"
9@@ -55,12 +56,14 @@ func octalPerms(info fs.FileMode) string {
10 type CopyFromClientHandler interface {
11 // Write should write the given file.
12 Write(ssh.Session, *FileEntry) (string, error)
13+ Read(ssh.Session, string) (os.FileInfo, io.ReaderAt, error)
14+ List(ssh.Session, string) ([]os.FileInfo, error)
15 Validate(ssh.Session) error
16 }
17
18 func KeyText(session ssh.Session) (string, error) {
19 if session.PublicKey() == nil {
20- return "", fmt.Errorf("Session doesn't have public key")
21+ return "", fmt.Errorf("session doesn't have public key")
22 }
23 kb := base64.StdEncoding.EncodeToString(session.PublicKey().Marshal())
24 return fmt.Sprintf("%s %s", session.PublicKey().Type(), kb), nil