[vlc-commits] [Git][videolan/vlc][master] 2 commits: access/ftp: add LIST fallback if MLST is not supported

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Fri May 1 13:03:52 UTC 2026



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
8190144e by Felix Paul Kühne at 2026-05-01T14:30:56+02:00
access/ftp: add LIST fallback if MLST is not supported

This parses the unix style LIST answer provided by most servers. Refs #26798

- - - - -
1e7e99a8 by Felix Paul Kühne at 2026-05-01T14:30:56+02:00
access/ftp: extend LIST fallback with a DOS-style parser

Refs #26798

- - - - -


1 changed file:

- modules/access/ftp.c


Changes:

=====================================
modules/access/ftp.c
=====================================
@@ -128,6 +128,7 @@ typedef struct ftp_features_t
     bool b_unicode;
     bool b_authtls;
     bool b_mlst;
+    bool b_list;
 } ftp_features_t;
 
 enum tls_mode_e
@@ -1036,6 +1037,118 @@ static time_t ftp_mktime(const char *const timeval)
     return (time_t) -1;
 }
 
+/* Skip leading whitespace and the next whitespace-separated token. */
+static char *SkipToken( char *p )
+{
+    while( *p == ' ' || *p == '\t' ) p++;
+    while( *p && *p != ' ' && *p != '\t' ) p++;
+    return p;
+}
+
+/**
+ * Parse a Unix-style LIST entry (as emitted by `ls -l`)
+ *
+ * Handles the de-facto standard format used by vsftpd, proftpd,
+ * pure-ftpd and similar servers when MLST is unavailable. Example:
+ *   drwxr-xr-x  2 user group  4096 Apr 22 10:00 dirname
+ *   -rw-r--r--  1 user group  1234 Apr 22 10:00 filename
+ *   lrwxrwxrwx  1 user group    11 Apr 22 10:00 link -> target
+*/
+static int ParseListUnixEntry( char *line, int *type, long long *size,
+                               char **filename )
+{
+    if( strlen( line ) < 11 )
+        return -1;
+
+    bool is_link = false;
+    switch( line[0] )
+    {
+        case 'd': *type = ITEM_TYPE_DIRECTORY; break;
+        case '-': *type = ITEM_TYPE_FILE;      break;
+        case 'l': *type = ITEM_TYPE_UNKNOWN; is_link = true; break;
+        default:
+            return -1;
+    }
+
+    if( line[10] != ' ' && line[10] != '\t' )
+        return -1;
+
+    char *p = line + 10;
+
+    p = SkipToken( p ); /* link count */
+    p = SkipToken( p ); /* owner */
+    p = SkipToken( p ); /* group */
+
+    while( *p == ' ' || *p == '\t' ) p++;
+    if( !isdigit( (unsigned char)*p ) || sscanf( p, "%lld", size ) != 1 )
+        return -1;
+    p = SkipToken( p );
+
+    p = SkipToken( p ); /* month */
+    p = SkipToken( p ); /* day */
+    p = SkipToken( p ); /* time or year */
+
+    while( *p == ' ' || *p == '\t' ) p++;
+    if( !*p ) return -1;
+
+    *filename = p;
+
+    if( is_link )
+    {
+        char *arrow = NULL;
+        for( char *q = strstr( p, " -> " ); q != NULL;
+             q = strstr( q + 1, " -> " ) )
+            arrow = q;
+        if( arrow )
+            *arrow = '\0';
+    }
+
+    return 0;
+}
+
+/**
+ * Parse a DOS-style LIST entry (as emitted by Microsoft IIS in DOS mode)
+ *
+ * Example:
+ *   04-22-26  10:00AM       <DIR>          dirname
+ *   04-22-26  10:00AM                 1234 filename.ext
+ *
+ * The year may be 2 or 4 digits, and the AM/PM suffix is optional
+ * (some servers emit 24-hour time).
+ */
+static int ParseListDosEntry( char *line, int *type, long long *size,
+                              char **filename )
+{
+    unsigned mm, dd, yy, hh, mi;
+    int n = 0;
+    if( sscanf( line, "%2u-%2u-%4u %2u:%2u%n",
+                &mm, &dd, &yy, &hh, &mi, &n ) != 5 )
+        return -1;
+
+    char *p = line + n;
+    if( *p == 'A' || *p == 'P' )
+    {
+        if( p[1] != 'M' ) return -1;
+        p += 2;
+    }
+
+    while( *p == ' ' || *p == '\t' ) p++;
+
+    if( !strncmp( p, "<DIR>", 5 ) )
+        *type = ITEM_TYPE_DIRECTORY;
+    else if( isdigit( (unsigned char)*p ) && sscanf( p, "%lld", size ) == 1 )
+        *type = ITEM_TYPE_FILE;
+    else
+        return -1;
+
+    p = SkipToken( p );
+    while( *p == ' ' || *p == '\t' ) p++;
+    if( !*p ) return -1;
+
+    *filename = p;
+    return 0;
+}
+
 /*****************************************************************************
  * DirRead:
  *****************************************************************************/
@@ -1093,6 +1206,20 @@ static int DirRead (stream_t *p_access, input_item_node_t *p_current_node)
                 }
             }
         }
+        else if( p_sys->features.b_list )
+        {
+            long long parsed_size = 0;
+            if( ParseListUnixEntry( psz_line, &type, &parsed_size,
+                                    &psz_file ) != 0 &&
+                ParseListDosEntry( psz_line, &type, &parsed_size,
+                                   &psz_file ) != 0 )
+            {
+                /* Unrecognized LIST format (or "total N" header): skip. */
+                free( psz_line );
+                continue;
+            }
+            size = parsed_size;
+        }
         else
             psz_file = psz_line;
 
@@ -1332,12 +1459,26 @@ static int ftp_StartStream( vlc_object_t *p_access, access_sys_t *p_sys,
             msg_Dbg( p_access, "Using MLST extension to list" );
         }
         else
+        if( ftp_SendCommand( p_access, p_sys, "LIST" ) >= 0 &&
+            ftp_RecvCommandInit( p_access, p_sys ) == 1 )
+        {
+            p_sys->features.b_mlst = false;
+            p_sys->features.b_list = true;
+            msg_Dbg( p_access, "Using LIST command to list directory" );
+        }
+        else
         if( ftp_SendCommand( p_access, p_sys, "NLST" ) < 0 ||
             ftp_RecvCommandInit( p_access, p_sys ) != 1 )
         {
             msg_Err( p_access, "cannot list directory contents" );
             return VLC_EGENERIC;
         }
+        else
+        {
+            p_sys->features.b_mlst = false;
+            p_sys->features.b_list = false;
+            msg_Dbg( p_access, "Falling back to NLST to list directory" );
+        }
     }
     else
     {



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2481b45cf0e6e48722ef7650cfb6b4e92fa8aa2b...1e7e99a838e91a483430aac9a749d75fd601871a

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2481b45cf0e6e48722ef7650cfb6b4e92fa8aa2b...1e7e99a838e91a483430aac9a749d75fd601871a
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list