11 "github.com/jhalter/mobius/hotline"
14 // ResolveUserPath processes paths for the special `~` directory.
15 // If the requested path starts with `~`, it returns the user's corresponding folder.
16 func ResolveUserPath(cc *hotline.ClientConn, requestedPath string) (string, error) {
17 if strings.HasPrefix(requestedPath, "~") {
18 userFolder := filepath.Join("~", cc.Account.Login)
19 actualUserFolder := filepath.Join(cc.FileRoot(), userFolder)
20 if stat, err := os.Stat(actualUserFolder); err == nil && stat.IsDir() {
21 return strings.Replace(requestedPath, "~", userFolder, 1), nil
23 return "", fmt.Errorf("user folder does not exist")
26 return requestedPath, nil
29 // updateTransactionPath updates the FieldFilePath in a transaction in-place.
30 func updateTransactionPath(t *hotline.Transaction, newPath string) {
31 for i, field := range t.Fields {
32 if field.Type == hotline.FieldFilePath {
34 // Convert newPath to the correct binary format
35 encodedPath, err := txtEncoder.String(newPath)
37 return // Encoding failure, don't update
40 // Convert the new path into the correct binary format for FilePath
41 fpBytes, err := encodeFilePath(encodedPath)
46 // Assign correctly formatted binary path
47 t.Fields[i].Data = fpBytes
53 // Encode a string path into hotline.FilePath binary format
54 func encodeFilePath(path string) ([]byte, error) {
55 components := strings.Split(path, string(filepath.Separator))
56 var fp hotline.FilePath
59 binary.BigEndian.PutUint16(fp.ItemCount[:], uint16(len(components)))
61 var buffer bytes.Buffer
62 err := binary.Write(&buffer, binary.BigEndian, fp.ItemCount)
67 for _, component := range components {
72 if len(component) > 255 { // Ensure component size is within range
73 return nil, fmt.Errorf("file path component too long")
76 // Convert to MacRoman encoding
77 encodedComponent, err := txtEncoder.String(component)
82 // Write length and name
83 buffer.Write([]byte{0x00, 0x00, byte(len(encodedComponent))}) // Leading bytes
84 buffer.Write([]byte(encodedComponent))
87 return buffer.Bytes(), nil
90 // HandleGetFileNameListWithUserFolders modifies the file listing behavior for `~`
91 func HandleGetFileNameListWithUserFolders(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
92 requestedPath, err := hotline.ReadPath(cc.FileRoot(), t.GetField(hotline.FieldFilePath).Data, nil)
97 if requestedPath == cc.FileRoot() {
98 fileNames, err := hotline.GetFileNameList(cc.FileRoot(), cc.Server.Config.IgnoreFiles)
103 userFolder := filepath.Join(cc.FileRoot(), "~", cc.Account.Login)
104 if stat, err := os.Stat(userFolder); err != nil || !stat.IsDir() {
105 filteredFiles := []hotline.Field{}
106 for _, file := range fileNames {
107 if !strings.Contains(string(file.Data), "~") {
108 filteredFiles = append(filteredFiles, file)
111 return append(res, cc.NewReply(t, filteredFiles...))
113 return append(res, cc.NewReply(t, fileNames...))
116 if strings.HasPrefix(requestedPath, filepath.Join(cc.FileRoot(), "~")) {
117 resolvedPath, err := ResolveUserPath(cc, requestedPath[len(cc.FileRoot())+1:])
121 updateTransactionPath(t, resolvedPath)
122 return HandleGetFileNameList(cc, t)
125 return HandleGetFileNameList(cc, t)
128 // HandleUploadFileWithUserFolders ensures uploads go to the correct user folder when using `~`.
129 func HandleUploadFileWithUserFolders(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
130 requestedPath, err := hotline.ReadPath(cc.FileRoot(), t.GetField(hotline.FieldFilePath).Data, nil)
135 resolvedPath, err := ResolveUserPath(cc, requestedPath[len(cc.FileRoot())+1:])
137 return cc.NewErrReply(t, "Cannot upload to non-existent user folder.")
140 updateTransactionPath(t, resolvedPath)
141 return HandleUploadFile(cc, t)
144 // HandleUploadFolderWithUserFolders ensures directory uploads go to the correct user folder when using `~`.
145 func HandleUploadFolderWithUserFolders(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
146 requestedPath, err := hotline.ReadPath(cc.FileRoot(), t.GetField(hotline.FieldFilePath).Data, nil)
151 resolvedPath, err := ResolveUserPath(cc, requestedPath[len(cc.FileRoot())+1:])
153 return cc.NewErrReply(t, "Cannot upload to non-existent user folder.")
156 updateTransactionPath(t, resolvedPath)
157 return HandleUploadFolder(cc, t)
160 func HandleDeleteFileWithUserFolders(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
161 requestedPath, err := hotline.ReadPath(cc.FileRoot(), t.GetField(hotline.FieldFilePath).Data, nil)
166 resolvedPath, err := ResolveUserPath(cc, requestedPath[len(cc.FileRoot())+1:])
168 return cc.NewErrReply(t, "Cannot upload to non-existent user folder.")
171 updateTransactionPath(t, resolvedPath)
172 return HandleUploadFolder(cc, t)