44 "errors"
55 "fmt"
66 "io"
7+ "io/fs"
78 "os"
89 "path"
910 "strings"
@@ -19,11 +20,42 @@ var errFileList = errors.New("listing a file isn't allowed")
1920var supportedlistArgs = []string {"-al" , "-la" , "-a" , "-l" }
2021
2122func (c * clientHandler ) absPath (p string ) string {
22- if strings . HasPrefix ( p , "/" ) {
23+ if path . IsAbs ( p ) {
2324 return path .Clean (p )
2425 }
2526
26- return path .Clean (c .Path () + "/" + p )
27+ return path .Join (c .Path (), p )
28+ }
29+
30+ // getRelativePath returns the specified path as relative to the
31+ // current working directory. The specified path must be cleaned
32+ func (c * clientHandler ) getRelativePath (p string ) string {
33+ var sb strings.Builder
34+ base := c .Path ()
35+
36+ for {
37+ if base == p {
38+ return sb .String ()
39+ }
40+
41+ if ! strings .HasSuffix (base , "/" ) {
42+ base += "/"
43+ }
44+
45+ if strings .HasPrefix (p , base ) {
46+ sb .WriteString (strings .TrimPrefix (p , base ))
47+
48+ return sb .String ()
49+ }
50+
51+ if base == "/" || base == "./" {
52+ return p
53+ }
54+
55+ sb .WriteString ("../" )
56+
57+ base = path .Dir (path .Clean (base ))
58+ }
2759}
2860
2961func (c * clientHandler ) handleCWD (param string ) error {
@@ -156,7 +188,7 @@ func (c *clientHandler) checkLISTArgs(args string) string {
156188func (c * clientHandler ) handleLIST (param string ) error {
157189 info := fmt .Sprintf ("LIST %v" , param )
158190
159- if files , err := c .getFileList (param , true ); err == nil || err == io .EOF {
191+ if files , _ , err := c .getFileList (param , true ); err == nil || err == io .EOF {
160192 if tr , errTr := c .TransferOpen (info ); errTr == nil {
161193 err = c .dirTransferLIST (tr , files )
162194 c .TransferClose (err )
@@ -175,9 +207,9 @@ func (c *clientHandler) handleLIST(param string) error {
175207func (c * clientHandler ) handleNLST (param string ) error {
176208 info := fmt .Sprintf ("NLST %v" , param )
177209
178- if files , err := c .getFileList (param , true ); err == nil || err == io .EOF {
210+ if files , parentDir , err := c .getFileList (param , true ); err == nil || err == io .EOF {
179211 if tr , errTrOpen := c .TransferOpen (info ); errTrOpen == nil {
180- err = c .dirTransferNLST (tr , files )
212+ err = c .dirTransferNLST (tr , files , parentDir )
181213 c .TransferClose (err )
182214
183215 return nil
@@ -191,15 +223,18 @@ func (c *clientHandler) handleNLST(param string) error {
191223 return nil
192224}
193225
194- func (c * clientHandler ) dirTransferNLST (w io.Writer , files []os.FileInfo ) error {
226+ func (c * clientHandler ) dirTransferNLST (w io.Writer , files []os.FileInfo , parentDir string ) error {
195227 if len (files ) == 0 {
196228 _ , err := w .Write ([]byte ("" ))
197229
198230 return err
199231 }
200232
201233 for _ , file := range files {
202- if _ , err := fmt .Fprintf (w , "%s\r \n " , file .Name ()); err != nil {
234+ // Based on RFC 959 NLST is intended to return information that can be used
235+ // by a program to further process the files automatically.
236+ // So we return paths relative to the current working directory
237+ if _ , err := fmt .Fprintf (w , "%s\r \n " , path .Join (c .getRelativePath (parentDir ), file .Name ())); err != nil {
203238 return err
204239 }
205240 }
@@ -216,7 +251,7 @@ func (c *clientHandler) handleMLSD(param string) error {
216251
217252 info := fmt .Sprintf ("MLSD %v" , param )
218253
219- if files , err := c .getFileList (param , false ); err == nil || err == io .EOF {
254+ if files , _ , err := c .getFileList (param , false ); err == nil || err == io .EOF {
220255 if tr , errTr := c .TransferOpen (info ); errTr == nil {
221256 err = c .dirTransferMLSD (tr , files )
222257 c .TransferClose (err )
@@ -312,39 +347,46 @@ func (c *clientHandler) writeMLSxEntry(w io.Writer, file os.FileInfo) error {
312347 return err
313348}
314349
315- func (c * clientHandler ) getFileList (param string , filePathAllowed bool ) ([]os.FileInfo , error ) {
350+ func (c * clientHandler ) getFileList (param string , filePathAllowed bool ) ([]os.FileInfo , string , error ) {
316351 if ! c .server .settings .DisableLISTArgs {
317352 param = c .checkLISTArgs (param )
318353 }
319354 // directory or filePath
320355 listPath := c .absPath (param )
356+ c .SetListPath (listPath )
321357
322358 // return list of single file if directoryPath points to file and filePathAllowed
323359 info , err := c .driver .Stat (listPath )
324360 if err != nil {
325- return nil , err
361+ return nil , "" , err
326362 }
327363
328364 if ! info .IsDir () {
329365 if filePathAllowed {
330- return []os.FileInfo {info }, nil
366+ return []os.FileInfo {info }, path . Dir ( c . getListPath ()), nil
331367 }
332368
333- return nil , errFileList
369+ return nil , "" , errFileList
334370 }
335371
372+ var files []fs.FileInfo
373+
336374 if fileList , ok := c .driver .(ClientDriverExtensionFileList ); ok {
337- return fileList .ReadDir (listPath )
375+ files , err = fileList .ReadDir (listPath )
376+
377+ return files , c .getListPath (), err
338378 }
339379
340380 directory , errOpenFile := c .driver .Open (listPath )
341381 if errOpenFile != nil {
342- return nil , errOpenFile
382+ return nil , "" , errOpenFile
343383 }
344384
345385 defer c .closeDirectory (listPath , directory )
346386
347- return directory .Readdir (- 1 )
387+ files , err = directory .Readdir (- 1 )
388+
389+ return files , c .getListPath (), err
348390}
349391
350392func (c * clientHandler ) closeDirectory (directoryPath string , directory afero.File ) {
0 commit comments