repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

commit
bbc9409
parent
97adfeb
author
Antonio Mika
date
2022-08-25 05:20:26 +0000 UTC
Added rsync and sftp download support
19 files changed,  +250, -54
M go.mod
M go.sum
M cmd/imgs/ssh/main.go
+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)),
M cmd/lists/ssh/main.go
+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)),
M cmd/pastes/ssh/main.go
+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)),
M cmd/prose/ssh/main.go
+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)),
M filehandlers/imgs/handler.go
+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)
M filehandlers/post_handler.go
+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=
M imgs/client.go
+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"
M wish/cmd/server/main.go
+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 	)
A wish/list/list.go
+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+}
M wish/send/rsync/rsync.go
+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 			}
A wish/send/scp/copy_to_client.go
+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+}
M wish/send/scp/scp.go
+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 }
D wish/send/sftp/file.go
+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 }
M wish/send/sftp/handler.go
+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 }
M wish/send/sftp/sftp.go
+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{
A wish/send/utils/file.go
+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 }
M wish/send/utils/utils.go
+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