sshserver: add scp support

fixes https://github.com/boldsoftware/sketch/issues/53
diff --git a/loop/server/sshserver.go b/loop/server/sshserver.go
index f1af330..cfef14b 100644
--- a/loop/server/sshserver.go
+++ b/loop/server/sshserver.go
@@ -5,6 +5,7 @@
 	"context"
 	"fmt"
 	"io"
+	"log"
 	"log/slog"
 	"os"
 	"os/exec"
@@ -13,6 +14,7 @@
 
 	"github.com/creack/pty"
 	"github.com/gliderlabs/ssh"
+	"github.com/pkg/sftp"
 	gossh "golang.org/x/crypto/ssh"
 )
 
@@ -72,6 +74,9 @@
 			"tcpip-forward":        forwardHandler.HandleSSHRequest,
 			"cancel-tcpip-forward": forwardHandler.HandleSSHRequest,
 		},
+		SubsystemHandlers: map[string]ssh.SubsystemHandler{
+			"sftp": handleSftp,
+		},
 		HostSigners: []ssh.Signer{signer},
 		PublicKeyHandler: func(ctx ssh.Context, key ssh.PublicKey) bool {
 			// Check if the provided key matches any of our allowed keys
@@ -93,6 +98,27 @@
 	return server.ListenAndServe()
 }
 
+func handleSftp(sess ssh.Session) {
+	debugStream := io.Discard
+	serverOptions := []sftp.ServerOption{
+		sftp.WithDebug(debugStream),
+	}
+	server, err := sftp.NewServer(
+		sess,
+		serverOptions...,
+	)
+	if err != nil {
+		log.Printf("sftp server init error: %s\n", err)
+		return
+	}
+	if err := server.Serve(); err == io.EOF {
+		server.Close()
+		fmt.Println("sftp client exited session.")
+	} else if err != nil {
+		fmt.Println("sftp server completed with error:", err)
+	}
+}
+
 func handlePTYSession(ctx context.Context, s ssh.Session, ptyReq ssh.Pty, winCh <-chan ssh.Window) {
 	cmd := exec.CommandContext(ctx, "/bin/bash")
 	slog.DebugContext(ctx, "handlePTYSession", slog.Any("ptyReq", ptyReq))