Browse Source

Merge pull request #31 from digineo/listings

Add support for more listing formats
Julien Laffaye 10 years ago
parent
commit
e6587b1638
2 changed files with 114 additions and 31 deletions
  1. 104 31
      ftp.go
  2. 10 0
      parse_test.go

+ 104 - 31
ftp.go

@@ -292,45 +292,118 @@ func (c *ServerConn) cmdDataConnFrom(offset uint64, format string, args ...inter
 // parseListLine parses the various non-standard format returned by the LIST
 // FTP command.
 func parseListLine(line string) (*Entry, error) {
-	fields := strings.Fields(line)
-	if len(fields) < 9 {
-		return nil, errors.New("Unsupported LIST line")
-	}
-
-	e := &Entry{}
-	switch fields[0][0] {
-	case '-':
-		e.Type = EntryTypeFile
-	case 'd':
-		e.Type = EntryTypeFolder
-	case 'l':
-		e.Type = EntryTypeLink
-	default:
-		return nil, errors.New("Unknown entry type")
-	}
+	var err error
 
-	if e.Type == EntryTypeFile {
-		size, err := strconv.ParseUint(fields[4], 10, 0)
-		if err != nil {
+	if strings.HasPrefix(line, "modify=") {
+		e := &Entry{}
+		arr := strings.Split(line, "; ")
+		e.Name = arr[1]
+
+		for _, field := range strings.Split(arr[0], ";") {
+			i := strings.Index(field, "=")
+			if i < 1 {
+				return nil, errors.New("Unsupported LIST line")
+			}
+
+			key := field[:i]
+			value := field[i+1:]
+
+			switch key {
+			case "modify":
+				e.Time, _ = time.Parse("20060102150405", value)
+			case "type":
+				switch value {
+				case "dir", "cdir", "pdir":
+					e.Type = EntryTypeFolder
+				case "file":
+					e.Type = EntryTypeFile
+				}
+			case "size":
+				e.Size, _ = strconv.ParseUint(value, 0, 64)
+			}
+		}
+		return e, nil
+
+	} else {
+		fields := strings.Fields(line)
+		if len(fields) >= 7 && fields[1] == "folder" {
+			e := &Entry{
+				Type: EntryTypeFolder,
+				Name: strings.Join(fields[6:], " "),
+			}
+			if err = e.setTime(fields[3:6]); err != nil {
+				return nil, err
+			}
+
+			return e, nil
+		}
+
+		if fields[1] == "0" {
+			e := &Entry{
+				Type: EntryTypeFile,
+				Name: strings.Join(fields[7:], " "),
+			}
+
+			if err = e.setSize(fields[2]); err != nil {
+				return nil, err
+			}
+			if err = e.setTime(fields[4:7]); err != nil {
+				return nil, err
+			}
+
+			return e, nil
+		}
+
+		if len(fields) < 9 {
+			//panic(fmt.Sprintf("%d %v", len(fields), fields[6]))
+			return nil, errors.New("Unsupported LIST line")
+		}
+
+		e := &Entry{}
+		switch fields[0][0] {
+		case '-':
+			e.Type = EntryTypeFile
+		case 'd':
+			e.Type = EntryTypeFolder
+		case 'l':
+			e.Type = EntryTypeLink
+		default:
+			return nil, errors.New("Unknown entry type")
+		}
+
+		if e.Type == EntryTypeFile {
+			if err = e.setSize(fields[4]); err != nil {
+				return nil, err
+			}
+		}
+
+		if err = e.setTime(fields[5:8]); err != nil {
 			return nil, err
 		}
-		e.Size = size
+
+		e.Name = strings.Join(fields[8:], " ")
+		return e, nil
 	}
+}
+
+func (e *Entry) setSize(str string) (err error) {
+	e.Size, err = strconv.ParseUint(str, 10, 0)
+	return
+}
+
+func (e *Entry) setTime(fields []string) (err error) {
 	var timeStr string
-	if strings.Contains(fields[7], ":") { // this year
+	if strings.Contains(fields[2], ":") { // this year
 		thisYear, _, _ := time.Now().Date()
-		timeStr = fields[6] + " " + fields[5] + " " + strconv.Itoa(thisYear)[2:4] + " " + fields[7] + " GMT"
+		timeStr = fields[1] + " " + fields[0] + " " + strconv.Itoa(thisYear)[2:4] + " " + fields[2] + " GMT"
 	} else { // not this year
-		timeStr = fields[6] + " " + fields[5] + " " + fields[7][2:4] + " " + "00:00" + " GMT"
-	}
-	t, err := time.Parse("_2 Jan 06 15:04 MST", timeStr)
-	if err != nil {
-		return nil, err
+		if len(fields[2]) != 4 {
+			return errors.New("Invalid year format in time string")
+		}
+		timeStr = fields[1] + " " + fields[0] + " " + fields[2][2:4] + " " + "00:00" + " GMT"
 	}
-	e.Time = t
-
-	e.Name = strings.Join(fields[8:], " ")
-	return e, nil
+	e.Time, err = time.Parse("_2 Jan 06 15:04 MST", timeStr)
+	return
 }
 
 // NameList issues an NLST FTP command.

+ 10 - 0
parse_test.go

@@ -21,11 +21,21 @@ var listTests = []line{
 	{"drwxr-xr-x    3 110      1002            3 Dec 02  2009 p u b", "p u b", 0, EntryTypeFolder, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
 	{"-rwxr-xr-x    3 110      1002            1234567 Dec 02  2009 fileName", "fileName", 1234567, EntryTypeFile, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
 	{"lrwxrwxrwx   1 root     other          7 Jan 25 00:17 bin -> usr/bin", "bin -> usr/bin", 0, EntryTypeLink, time.Date(thisYear, time.January, 25, 0, 17, 0, 0, time.UTC)},
+	// Another ls style
+	{"drwxr-xr-x               folder        0 Aug 15 05:49 !!!-Tipp des Haus!", "!!!-Tipp des Haus!", 0, EntryTypeFolder, time.Date(thisYear, time.August, 15, 5, 49, 0, 0, time.UTC)},
+	{"drwxrwxrwx               folder        0 Aug 11 20:32 P0RN", "P0RN", 0, EntryTypeFolder, time.Date(thisYear, time.August, 11, 20, 32, 0, 0, time.UTC)},
+	{"-rw-r--r--        0   195291136 195291136 Nov 16  2006 VIDEO_TS.VOB", "VIDEO_TS.VOB", 195291136, EntryTypeFile, time.Date(2006, time.November, 16, 0, 0, 0, 0, time.UTC)},
 	// Microsoft's FTP servers for Windows
 	{"----------   1 owner    group         1803128 Jul 10 10:18 ls-lR.Z", "ls-lR.Z", 1803128, EntryTypeFile, time.Date(thisYear, time.July, 10, 10, 18, 0, 0, time.UTC)},
 	{"d---------   1 owner    group               0 May  9 19:45 Softlib", "Softlib", 0, EntryTypeFolder, time.Date(thisYear, time.May, 9, 19, 45, 0, 0, time.UTC)},
 	// WFTPD for MSDOS
 	{"-rwxrwxrwx   1 noone    nogroup      322 Aug 19  1996 message.ftp", "message.ftp", 322, EntryTypeFile, time.Date(1996, time.August, 19, 0, 0, 0, 0, time.UTC)},
+	// Some other
+	{"modify=20150813224845;perm=fle;type=cdir;unique=119FBB87U4;UNIX.group=0;UNIX.mode=0755;UNIX.owner=0; .", ".", 0, EntryTypeFolder, time.Date(2015, time.August, 13, 22, 48, 45, 0, time.UTC)},
+	{"modify=20150813224845;perm=fle;type=pdir;unique=119FBB87U4;UNIX.group=0;UNIX.mode=0755;UNIX.owner=0; ..", "..", 0, EntryTypeFolder, time.Date(2015, time.August, 13, 22, 48, 45, 0, time.UTC)},
+	{"modify=20150806235817;perm=fle;type=dir;unique=1B20F360U4;UNIX.group=0;UNIX.mode=0755;UNIX.owner=0; movies", "movies", 0, EntryTypeFolder, time.Date(2015, time.August, 6, 23, 58, 17, 0, time.UTC)},
+	{"modify=20150814172949;perm=flcdmpe;type=dir;unique=85A0C168U4;UNIX.group=0;UNIX.mode=0777;UNIX.owner=0; _upload", "_upload", 0, EntryTypeFolder, time.Date(2015, time.August, 14, 17, 29, 49, 0, time.UTC)},
+	{"modify=20150813175250;perm=adfr;size=951;type=file;unique=119FBB87UE;UNIX.group=0;UNIX.mode=0644;UNIX.owner=0; welcome.msg", "welcome.msg", 951, EntryTypeFile, time.Date(2015, time.August, 13, 17, 52, 50, 0, time.UTC)},
 }
 
 // Not supported, at least we should properly return failure