[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