aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-02-03 23:09:40 +0100
committerRuben Beltran del Rio <git@r.bdr.sh>2025-02-03 23:09:40 +0100
commit7bd6f5adda126bbe7caf94330e08d9adf0e66466 (patch)
treeb2f36525740ba351cd3a7498b974eb00c360aedc /internal
parentfe1cce153b572ba215318c6ccd349cde3d7b7619 (diff)
Allow for personal ~ folder
Diffstat (limited to 'internal')
-rw-r--r--internal/mobius/friendship_quest_file_extensions.go158
-rw-r--r--internal/mobius/transaction_handlers.go6
2 files changed, 161 insertions, 3 deletions
diff --git a/internal/mobius/friendship_quest_file_extensions.go b/internal/mobius/friendship_quest_file_extensions.go
new file mode 100644
index 0000000..fb23b79
--- /dev/null
+++ b/internal/mobius/friendship_quest_file_extensions.go
@@ -0,0 +1,158 @@
+package mobius
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/jhalter/mobius/hotline"
+)
+
+// ResolveUserPath processes paths for the special `~` directory.
+// If the requested path starts with `~`, it returns the user's corresponding folder.
+func ResolveUserPath(cc *hotline.ClientConn, requestedPath string) (string, error) {
+ if strings.HasPrefix(requestedPath, "~") {
+ userFolder := filepath.Join("~", cc.Account.Login)
+ actualUserFolder := filepath.Join(cc.FileRoot(), userFolder)
+ if stat, err := os.Stat(actualUserFolder); err == nil && stat.IsDir() {
+ return strings.Replace(requestedPath, "~", userFolder, 1), nil
+ } else {
+ return "", fmt.Errorf("user folder does not exist")
+ }
+ }
+ return requestedPath, nil
+}
+
+// updateTransactionPath updates the FieldFilePath in a transaction in-place.
+func updateTransactionPath(t *hotline.Transaction, newPath string) {
+ for i, field := range t.Fields {
+ if field.Type == hotline.FieldFilePath {
+
+ // Convert newPath to the correct binary format
+ encodedPath, err := txtEncoder.String(newPath)
+ if err != nil {
+ return // Encoding failure, don't update
+ }
+
+ // Convert the new path into the correct binary format for FilePath
+ fpBytes, err := encodeFilePath(encodedPath)
+ if err != nil {
+ return
+ }
+
+ // Assign correctly formatted binary path
+ t.Fields[i].Data = fpBytes
+ return
+ }
+ }
+}
+
+// Encode a string path into hotline.FilePath binary format
+func encodeFilePath(path string) ([]byte, error) {
+ components := strings.Split(path, string(filepath.Separator))
+ var fp hotline.FilePath
+
+ // Set the item count
+ binary.BigEndian.PutUint16(fp.ItemCount[:], uint16(len(components)))
+
+ var buffer bytes.Buffer
+ err := binary.Write(&buffer, binary.BigEndian, fp.ItemCount)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, component := range components {
+ if component == "" {
+ continue
+ }
+
+ if len(component) > 255 { // Ensure component size is within range
+ return nil, fmt.Errorf("file path component too long")
+ }
+
+ // Convert to MacRoman encoding
+ encodedComponent, err := txtEncoder.String(component)
+ if err != nil {
+ return nil, err
+ }
+
+ // Write length and name
+ buffer.Write([]byte{0x00, 0x00, byte(len(encodedComponent))}) // Leading bytes
+ buffer.Write([]byte(encodedComponent))
+ }
+
+ return buffer.Bytes(), nil
+}
+
+// HandleGetFileNameListWithUserFolders modifies the file listing behavior for `~`
+func HandleGetFileNameListWithUserFolders(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
+ requestedPath, err := hotline.ReadPath(cc.FileRoot(), t.GetField(hotline.FieldFilePath).Data, nil)
+ if err != nil {
+ return res
+ }
+
+ if requestedPath == cc.FileRoot() {
+ fileNames, err := hotline.GetFileNameList(cc.FileRoot(), cc.Server.Config.IgnoreFiles)
+ if err != nil {
+ return res
+ }
+
+ userFolder := filepath.Join(cc.FileRoot(), "~", cc.Account.Login)
+ if stat, err := os.Stat(userFolder); err != nil || !stat.IsDir() {
+ filteredFiles := []hotline.Field{}
+ for _, file := range fileNames {
+ if !strings.Contains(string(file.Data), "~") {
+ filteredFiles = append(filteredFiles, file)
+ }
+ }
+ return append(res, cc.NewReply(t, filteredFiles...))
+ }
+ return append(res, cc.NewReply(t, fileNames...))
+ }
+
+ if strings.HasPrefix(requestedPath, filepath.Join(cc.FileRoot(), "~")) {
+ resolvedPath, err := ResolveUserPath(cc, requestedPath[len(cc.FileRoot())+1:])
+ if err != nil {
+ return res
+ }
+ updateTransactionPath(t, resolvedPath)
+ return HandleGetFileNameList(cc, t)
+ }
+
+ return HandleGetFileNameList(cc, t)
+}
+
+// HandleUploadFileWithUserFolders ensures uploads go to the correct user folder when using `~`.
+func HandleUploadFileWithUserFolders(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
+ requestedPath, err := hotline.ReadPath(cc.FileRoot(), t.GetField(hotline.FieldFilePath).Data, nil)
+ if err != nil {
+ return res
+ }
+
+ resolvedPath, err := ResolveUserPath(cc, requestedPath[len(cc.FileRoot())+1:])
+ if err != nil {
+ return cc.NewErrReply(t, "Cannot upload to non-existent user folder.")
+ }
+
+ updateTransactionPath(t, resolvedPath)
+ return HandleUploadFile(cc, t)
+}
+
+// HandleUploadFolderWithUserFolders ensures directory uploads go to the correct user folder when using `~`.
+func HandleUploadFolderWithUserFolders(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
+ requestedPath, err := hotline.ReadPath(cc.FileRoot(), t.GetField(hotline.FieldFilePath).Data, nil)
+ if err != nil {
+ return res
+ }
+
+ resolvedPath, err := ResolveUserPath(cc, requestedPath[len(cc.FileRoot())+1:])
+ if err != nil {
+ return cc.NewErrReply(t, "Cannot upload to non-existent user folder.")
+ }
+
+ updateTransactionPath(t, resolvedPath)
+ return HandleUploadFolder(cc, t)
+}
diff --git a/internal/mobius/transaction_handlers.go b/internal/mobius/transaction_handlers.go
index cf2357c..0cba65a 100644
--- a/internal/mobius/transaction_handlers.go
+++ b/internal/mobius/transaction_handlers.go
@@ -35,7 +35,7 @@ func RegisterHandlers(srv *hotline.Server) {
srv.HandleFunc(hotline.TranDownloadFldr, HandleDownloadFolder)
srv.HandleFunc(hotline.TranGetClientInfoText, HandleGetClientInfoText)
srv.HandleFunc(hotline.TranGetFileInfo, HandleGetFileInfo)
- srv.HandleFunc(hotline.TranGetFileNameList, HandleGetFileNameList)
+ srv.HandleFunc(hotline.TranGetFileNameList, HandleGetFileNameListWithUserFolders)
srv.HandleFunc(hotline.TranGetMsgs, HandleGetMsgs)
srv.HandleFunc(hotline.TranGetNewsArtData, HandleGetNewsArtData)
srv.HandleFunc(hotline.TranGetNewsArtNameList, HandleGetNewsArtNameList)
@@ -63,8 +63,8 @@ func RegisterHandlers(srv *hotline.Server) {
srv.HandleFunc(hotline.TranSetClientUserInfo, HandleSetClientUserInfo)
srv.HandleFunc(hotline.TranSetFileInfo, HandleSetFileInfo)
srv.HandleFunc(hotline.TranSetUser, HandleSetUser)
- srv.HandleFunc(hotline.TranUploadFile, HandleUploadFile)
- srv.HandleFunc(hotline.TranUploadFldr, HandleUploadFolder)
+ srv.HandleFunc(hotline.TranUploadFile, HandleUploadFileWithUserFolders)
+ srv.HandleFunc(hotline.TranUploadFldr, HandleUploadFolderWithUserFolders)
srv.HandleFunc(hotline.TranUserBroadcast, HandleUserBroadcast)
srv.HandleFunc(hotline.TranDownloadBanner, HandleDownloadBanner)
}