X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcontrol%2Fhttp.c;h=a923d1140f1a22637f897eceb381fe0787f1e1e4;hb=9fad47dcd802710f621133f4440163d0a345e2d1;hp=16f6c8ec9454893d151a070e6b932ea022ede556;hpb=a362f5c383ee7033ef62f8376d7179d2d756c1e9;p=vlc diff --git a/modules/control/http.c b/modules/control/http.c index 16f6c8ec94..a923d1140f 100644 --- a/modules/control/http.c +++ b/modules/control/http.c @@ -1,8 +1,8 @@ /***************************************************************************** * http.c : http mini-server ;) ***************************************************************************** - * Copyright (C) 2001 VideoLAN - * $Id: http.c,v 1.11 2003/07/10 22:24:09 fenrir Exp $ + * Copyright (C) 2001-2005 the VideoLAN team + * $Id$ * * Authors: Gildas Bazin * Laurent Aimar @@ -25,11 +25,22 @@ /***************************************************************************** * Preamble *****************************************************************************/ +/* TODO: + * - clean up ? + * - doc ! (mouarf ;) + */ + #include #include #include -#include "httpd.h" +#include +#include /* for fullscreen */ + +#include "vlc_httpd.h" +#include "vlc_vlm.h" +#include "vlc_tls.h" +#include "charset.h" #ifdef HAVE_SYS_STAT_H # include @@ -47,29 +58,98 @@ # include #endif -#if (!defined( WIN32 ) || defined(__MINGW32__)) -/* Mingw has its own version of dirent */ +#ifdef HAVE_DIRENT_H # include #endif +/* stat() support for large files on win32 */ +#if defined( WIN32 ) && !defined( UNDER_CE ) +# define stat _stati64 +#endif + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +static int Open ( vlc_object_t * ); +static void Close( vlc_object_t * ); + +#define HOST_TEXT N_( "Host address" ) +#define HOST_LONGTEXT N_( \ + "You can set the address and port the http interface will bind to." ) +#define SRC_TEXT N_( "Source directory" ) +#define SRC_LONGTEXT N_( "Source directory" ) +#define CERT_TEXT N_( "Certificate file" ) +#define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \ + "(enables SSL)" ) +#define KEY_TEXT N_( "Private key file" ) +#define KEY_LONGTEXT N_( "HTTP interface x509 PEM private key file" ) +#define CA_TEXT N_( "Root CA file" ) +#define CA_LONGTEXT N_( "HTTP interface x509 PEM trusted root CA " \ + "certificates file" ) +#define CRL_TEXT N_( "CRL file" ) +#define CRL_LONGTEXT N_( "HTTP interace Certificates Revocation List file" ) + +vlc_module_begin(); + set_shortname( _("HTTP")); + set_description( _("HTTP remote control interface") ); + set_category( CAT_INTERFACE ); + set_subcategory( SUBCAT_INTERFACE_GENERAL ); + add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE ); + add_string ( "http-src", NULL, NULL, SRC_TEXT, SRC_LONGTEXT, VLC_TRUE ); + set_section( N_("HTTP SSL" ), 0 ); + add_string ( "http-intf-cert", NULL, NULL, CERT_TEXT, CERT_LONGTEXT, VLC_TRUE ); + add_string ( "http-intf-key", NULL, NULL, KEY_TEXT, KEY_LONGTEXT, VLC_TRUE ); + add_string ( "http-intf-ca", NULL, NULL, CA_TEXT, CA_LONGTEXT, VLC_TRUE ); + add_string ( "http-intf-crl", NULL, NULL, CRL_TEXT, CRL_LONGTEXT, VLC_TRUE ); + set_capability( "interface", 0 ); + set_callbacks( Open, Close ); +vlc_module_end(); + /***************************************************************************** * Local prototypes *****************************************************************************/ -static int Activate ( vlc_object_t * ); -static void Close ( vlc_object_t * ); static void Run ( intf_thread_t *p_intf ); -static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir ); +static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, + char *psz_dir ); + +static int DirectoryCheck( char *psz_dir ) +{ + DIR *p_dir; + +#ifdef HAVE_SYS_STAT_H + struct stat stat_info; + + if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) ) + { + return VLC_EGENERIC; + } +#endif + + if( ( p_dir = opendir( psz_dir ) ) == NULL ) + { + return VLC_EGENERIC; + } + closedir( p_dir ); -static int http_get( httpd_file_callback_args_t *p_args, - uint8_t *p_request, int i_request, - uint8_t **pp_data, int *pi_data ); + return VLC_SUCCESS; +} + +static int HttpCallback( httpd_file_sys_t *p_args, + httpd_file_t *, + uint8_t *p_request, + uint8_t **pp_data, int *pi_data ); + +static char *uri_extract_value( char *psz_uri, const char *psz_name, + char *psz_value, int i_value_max ); +static int uri_test_param( char *psz_uri, const char *psz_name ); -static void uri_extract_value( char *psz_uri, char *psz_name, - char *psz_value, int i_value_max ); static void uri_decode_url_encoded( char *psz ); +static char *Find_end_MRL( char *psz ); +static playlist_item_t *parse_MRL( intf_thread_t * , char *psz ); + /***************************************************************************** * *****************************************************************************/ @@ -87,67 +167,51 @@ typedef struct { char *stack[STACK_MAX]; int i_stack; -} stack_t; +} rpn_stack_t; -struct httpd_file_callback_args_t +struct httpd_file_sys_t { - intf_thread_t *p_intf; - httpd_file_t *p_file; + intf_thread_t *p_intf; + httpd_file_t *p_file; + httpd_redirect_t *p_redir; + httpd_redirect_t *p_redir2; char *file; char *name; - char *mime; + + vlc_bool_t b_html; /* inited for each access */ - stack_t stack; + rpn_stack_t stack; mvar_t *vars; }; struct intf_sys_t { - httpd_t *p_httpd; httpd_host_t *p_httpd_host; - int i_files; - httpd_file_callback_args_t **pp_files; + int i_files; + httpd_file_sys_t **pp_files; playlist_t *p_playlist; input_thread_t *p_input; + vlm_t *p_vlm; + char *psz_html_type; }; -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -#define HOST_TEXT N_( "Host address" ) -#define HOST_LONGTEXT N_( \ - "You can set the address and port on which the http interface will bind" ) -#define SRC_TEXT N_( "Source directory" ) -#define SRC_LONGTEXT N_( "Source directory" ) - -vlc_module_begin(); - set_description( _("HTTP remote control interface") ); - add_category_hint( N_("HTTP remote control"), NULL, VLC_TRUE ); - add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE ); -#if defined(SYS_DARWIN) || defined(SYS_BEOS) \ - || (defined(WIN32) && !defined(UNDER_CE)) - add_string ( "http-src", NULL, NULL, SRC_TEXT, SRC_LONGTEXT, VLC_TRUE ); -#else - add_string ( "http-src", "share/http", NULL, SRC_TEXT, SRC_LONGTEXT, VLC_TRUE ); -#endif - set_capability( "interface", 0 ); - set_callbacks( Activate, Close ); -vlc_module_end(); /***************************************************************************** * Activate: initialize and create stuff *****************************************************************************/ -static int Activate( vlc_object_t *p_this ) +static int Open( vlc_object_t *p_this ) { intf_thread_t *p_intf = (intf_thread_t*)p_this; intf_sys_t *p_sys; char *psz_host; char *psz_address = ""; + const char *psz_cert = NULL, *psz_key = NULL, *psz_ca = NULL, + *psz_crl = NULL; int i_port = 0; char *psz_src; @@ -164,29 +228,61 @@ static int Activate( vlc_object_t *p_this ) i_port = atoi( psz_parser ); } } - if( i_port <= 0 ) - { - i_port= 8080; - } - msg_Dbg( p_intf, "base %s:%d", psz_address, i_port ); p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) ); + if( !p_intf->p_sys ) + { + return( VLC_ENOMEM ); + } p_sys->p_playlist = NULL; p_sys->p_input = NULL; + p_sys->p_vlm = NULL; - if( ( p_sys->p_httpd = httpd_Find( VLC_OBJECT(p_intf), VLC_TRUE ) ) == NULL ) + /* determine Content-Type value for HTML pages */ + vlc_current_charset(&psz_src); + if( psz_src == NULL ) { - msg_Err( p_intf, "cannot create/find httpd" ); free( p_sys ); - return VLC_EGENERIC; + return VLC_ENOMEM; } + p_sys->psz_html_type = malloc( 20 + strlen( psz_src ) ); + if( p_sys->psz_html_type == NULL ) + { + free( p_sys ); + free( psz_src ); + return VLC_ENOMEM ; + } + sprintf( p_sys->psz_html_type, "text/html; charset=%s", psz_src ); + free( psz_src ); - if( ( p_sys->p_httpd_host = - p_sys->p_httpd->pf_register_host( p_sys->p_httpd, - psz_address, i_port ) ) == NULL ) + /* determine SSL configuration */ + psz_cert = config_GetPsz( p_intf, "http-intf-cert" ); + if ( psz_cert != NULL ) + { + msg_Dbg( p_intf, "enablind TLS for HTTP interface (cert file: %s)", + psz_cert ); + psz_key = config_GetPsz( p_intf, "http-intf-key" ); + psz_ca = config_GetPsz( p_intf, "http-intf-ca" ); + psz_crl = config_GetPsz( p_intf, "http-intf-crl" ); + + if( i_port <= 0 ) + i_port = 8443; + } + else + { + if( i_port <= 0 ) + i_port= 8080; + } + + msg_Dbg( p_intf, "base %s:%d", psz_address, i_port ); + + p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_intf), psz_address, + i_port, psz_cert, psz_key, psz_ca, + psz_crl ); + if( p_sys->p_httpd_host == NULL ) { msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port ); - httpd_Release( p_sys->p_httpd ); + free( p_sys->psz_html_type ); free( p_sys ); return VLC_EGENERIC; } @@ -196,26 +292,37 @@ static int Activate( vlc_object_t *p_this ) free( psz_host ); } - p_sys->i_files = 0; - p_sys->pp_files = malloc( sizeof( httpd_file_callback_args_t *) ); + p_sys->i_files = 0; + p_sys->pp_files = NULL; -#if defined(SYS_DARWIN) || defined(SYS_BEOS) +#if defined(SYS_DARWIN) || defined(SYS_BEOS) || defined(WIN32) if ( ( psz_src = config_GetPsz( p_intf, "http-src" )) == NULL ) { char * psz_vlcpath = p_intf->p_libvlc->psz_vlcpath; psz_src = malloc( strlen(psz_vlcpath) + strlen("/share/http" ) + 1 ); + if( !psz_src ) return VLC_ENOMEM; +#if defined(WIN32) + sprintf( psz_src, "%s/http", psz_vlcpath); +#else sprintf( psz_src, "%s/share/http", psz_vlcpath); - } -#elif defined(WIN32) && !defined(UNDER_CE) - if ( (psz_src = config_GetPsz( p_intf, "http-src" )) == NULL ) - { - char * psz_vlcpath = p_intf->p_libvlc->psz_vlcpath; - psz_src = malloc( strlen(psz_vlcpath) + strlen("\\share\\http") + 1 ); - sprintf( psz_src, "%s\\share\\", psz_vlcpath); +#endif } #else psz_src = config_GetPsz( p_intf, "http-src" ); + + if( !psz_src || *psz_src == '\0' ) + { + if( !DirectoryCheck( "share/http" ) ) + { + psz_src = strdup( "share/http" ); + } + else if( !DirectoryCheck( DATA_PATH "/http" ) ) + { + psz_src = strdup( DATA_PATH "/http" ); + } + } #endif + if( !psz_src || *psz_src == '\0' ) { msg_Err( p_intf, "invalid src dir" ); @@ -234,18 +341,22 @@ static int Activate( vlc_object_t *p_this ) if( p_sys->i_files <= 0 ) { - msg_Err( p_intf, "cannot find any files" ); + msg_Err( p_intf, "cannot find any files (%s)", psz_src ); goto failed; } p_intf->pf_run = Run; + free( psz_src ); return VLC_SUCCESS; failed: - free( p_sys->pp_files ); - p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd, - p_sys->p_httpd_host ); - httpd_Release( p_sys->p_httpd ); + if( psz_src ) free( psz_src ); + if( p_sys->pp_files ) + { + free( p_sys->pp_files ); + } + httpd_HostDelete( p_sys->p_httpd_host ); + free( p_sys->psz_html_type ); free( p_sys ); return VLC_EGENERIC; } @@ -260,19 +371,29 @@ void Close ( vlc_object_t *p_this ) int i; + if( p_sys->p_vlm ) + { + vlm_Delete( p_sys->p_vlm ); + } for( i = 0; i < p_sys->i_files; i++ ) { - p_sys->p_httpd->pf_unregister_file( p_sys->p_httpd, - p_sys->pp_files[i]->p_file ); - /* do not free mime */ + httpd_FileDelete( p_sys->pp_files[i]->p_file ); + if( p_sys->pp_files[i]->p_redir ) + httpd_RedirectDelete( p_sys->pp_files[i]->p_redir ); + if( p_sys->pp_files[i]->p_redir2 ) + httpd_RedirectDelete( p_sys->pp_files[i]->p_redir2 ); + free( p_sys->pp_files[i]->file ); free( p_sys->pp_files[i]->name ); free( p_sys->pp_files[i] ); } - free( p_sys->pp_files ); - p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd, - p_sys->p_httpd_host ); - httpd_Release( p_sys->p_httpd ); + if( p_sys->pp_files ) + { + free( p_sys->pp_files ); + } + httpd_HostDelete( p_sys->p_httpd_host ); + + free( p_sys->psz_html_type ); free( p_sys ); } @@ -335,39 +456,41 @@ static void Run( intf_thread_t *p_intf ) /**************************************************************************** * FileToUrl: create a good name for an url from filename ****************************************************************************/ -static char *FileToUrl( char *name ) +static char *FileToUrl( char *name, vlc_bool_t *pb_index ) { char *url, *p; url = p = malloc( strlen( name ) + 1 ); + *pb_index = VLC_FALSE; + if( !url || !p ) + { + return NULL; + } + #ifdef WIN32 - while( *name == '\\' ) + while( *name == '\\' || *name == '/' ) +#else + while( *name == '/' ) +#endif { name++; } *p++ = '/'; + strcpy( p, name ); + +#ifdef WIN32 + /* convert '\\' into '/' */ + name = p; while( *name ) { if( *name == '\\' ) { *p++ = '/'; } - else - { - *p++ = *name; - } name++; } - *p++ = '\0'; -#else - while( name[0] == '/' ) - { - name++; - } - *p++ = '/'; - strcpy( p, name ); #endif /* index.* -> / */ @@ -376,70 +499,17 @@ static char *FileToUrl( char *name ) if( !strncmp( p, "/index.", 7 ) ) { p[1] = '\0'; + *pb_index = VLC_TRUE; } } return url; } -/**************************************************************************** - * FileToMime: XXX duplicated with modules/access_out/http.c - ****************************************************************************/ -static struct -{ - char *psz_ext; - char *psz_mime; -} http_mime[] = -{ - { ".htm", "text/html" }, - { ".html", "text/html" }, - - /* media mime */ - { ".avi", "video/avi" }, - { ".asf", "video/x-ms-asf" }, - { ".m1a", "audio/mpeg" }, - { ".m2a", "audio/mpeg" }, - { ".m1v", "video/mpeg" }, - { ".m2v", "video/mpeg" }, - { ".mp2", "audio/mpeg" }, - { ".mp3", "audio/mpeg" }, - { ".mpa", "audio/mpeg" }, - { ".mpg", "video/mpeg" }, - { ".mpeg", "video/mpeg" }, - { ".mpe", "video/mpeg" }, - { ".mov", "video/quicktime" }, - { ".moov", "video/quicktime" }, - { ".ogg", "application/ogg" }, - { ".ogm", "application/ogg" }, - { ".wav", "audio/wav" }, - - /* end */ - { NULL, NULL } -}; - -static char *FileToMime( char *psz_name ) -{ - char *psz_ext; - - psz_ext = strrchr( psz_name, '.' ); - if( psz_ext ) - { - int i; - - for( i = 0; http_mime[i].psz_ext != NULL ; i++ ) - { - if( !strcmp( http_mime[i].psz_ext, psz_ext ) ) - { - return( http_mime[i].psz_mime ); - } - } - } - return( "application/octet-stream" ); -} - /**************************************************************************** * ParseDirectory: parse recursively a directory, adding each file ****************************************************************************/ -static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir ) +static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, + char *psz_dir ) { intf_sys_t *p_sys = p_intf->p_sys; char dir[MAX_DIR_SIZE]; @@ -452,9 +522,13 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir char *user = NULL; char *password = NULL; + char **ppsz_hosts = NULL; + int i_hosts = 0; + + int i, i_dirlen; #ifdef HAVE_SYS_STAT_H - if( ( stat( psz_dir, &stat_info ) == -1 ) || !S_ISDIR( stat_info.st_mode ) ) + if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) ) { return VLC_EGENERIC; } @@ -466,6 +540,13 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir return VLC_EGENERIC; } + i_dirlen = strlen( psz_dir ); + if( i_dirlen + 10 > MAX_DIR_SIZE ) + { + msg_Warn( p_intf, "skipping too deep dir (%s)", psz_dir ); + return 0; + } + msg_Dbg( p_intf, "dir=%s", psz_dir ); sprintf( dir, "%s/.access", psz_dir ); @@ -480,7 +561,8 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir if( i_size > 0 ) { char *p; - while( i_size > 0 && ( line[i_size-1] == '\n' || line[i_size-1] == '\r' ) ) + while( i_size > 0 && ( line[i_size-1] == '\n' || + line[i_size-1] == '\r' ) ) { i_size--; } @@ -495,9 +577,47 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir password = strdup( p ); } } - msg_Dbg( p_intf, "using user=%s password=%s (read=%d)", user, password, i_size ); + msg_Dbg( p_intf, "using user=%s password=%s (read=%d)", + user, password, i_size ); + + fclose( file ); + } + + sprintf( dir, "%s/.hosts", psz_dir ); + if( ( file = fopen( dir, "r" ) ) != NULL ) + { + char line[1024]; + int i_size; + + msg_Dbg( p_intf, "find .hosts in dir=%s", psz_dir ); + + while( !feof( file ) ) + { + fgets( line, 1023, file ); + i_size = strlen(line); + if( i_size > 0 && line[0] != '#' ) + { + while( i_size > 0 && ( line[i_size-1] == '\n' || + line[i_size-1] == '\r' ) ) + { + i_size--; + } + if( !i_size ) continue; + + line[i_size] = '\0'; + + msg_Dbg( p_intf, "restricted to %s (read=%d)", + line, i_size ); + TAB_APPEND( i_hosts, ppsz_hosts, strdup( line ) ); + } + } fclose( file ); + + if( net_CheckIP( p_intf, "0.0.0.0", ppsz_hosts, i_hosts ) < 0 ) + { + msg_Err( p_intf, ".hosts file is invalid in dir=%s", psz_dir ); + } } for( ;; ) @@ -508,60 +628,68 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir break; } - if( p_dir_content->d_name[0] == '.' ) - { + if( ( p_dir_content->d_name[0] == '.' ) + || ( i_dirlen + strlen( p_dir_content->d_name ) > MAX_DIR_SIZE ) ) continue; - } + sprintf( dir, "%s/%s", psz_dir, p_dir_content->d_name ); if( ParseDirectory( p_intf, psz_root, dir ) ) { -#define f p_sys->pp_files[p_sys->i_files] - f = malloc( sizeof( httpd_file_callback_args_t ) ); + httpd_file_sys_t *f = malloc( sizeof( httpd_file_sys_t ) ); + vlc_bool_t b_index; + f->p_intf = p_intf; + f->p_file = NULL; + f->p_redir = NULL; + f->p_redir2 = NULL; f->file = strdup( dir ); - f->name = FileToUrl( &dir[strlen( psz_root )] ); - f->mime = FileToMime( &dir[strlen( psz_root )] ); + f->name = FileToUrl( &dir[strlen( psz_root )], &b_index ); + f->b_html = strstr( &dir[strlen( psz_root )], ".htm" ) ? VLC_TRUE : VLC_FALSE; - msg_Dbg( p_intf, "file=%s (url=%s mime=%s)", f->file, f->name, f->mime ); + if( !f->name ) + { + msg_Err( p_intf , "unable to parse directory" ); + closedir( p_dir ); + free( f ); + return( VLC_ENOMEM ); + } + msg_Dbg( p_intf, "file=%s (url=%s)", + f->file, f->name ); + + f->p_file = httpd_FileNew( p_sys->p_httpd_host, + f->name, + f->b_html ? p_sys->psz_html_type : NULL, + user, password, ppsz_hosts, i_hosts, + HttpCallback, f ); - f->p_file = - p_sys->p_httpd->pf_register_file( p_sys->p_httpd, - f->name, f->mime, - user, password, - http_get, http_get, - f ); if( f->p_file ) { - p_sys->i_files++; - p_sys->pp_files = realloc( p_sys->pp_files, (p_sys->i_files+1) * sizeof( httpd_file_callback_args_t ) ); + TAB_APPEND( p_sys->i_files, p_sys->pp_files, f ); } -#define fold p_sys->pp_files[p_sys->i_files-1] - - /* FIXME for rep/ add rep (it would be better to do a redirection) */ - if( strlen(fold->name) > 1 && fold->name[strlen(fold->name) - 1] == '/' ) + /* for url that ends by / add + * - a redirect from rep to rep/ + * - in case of index.* rep/index.html to rep/ */ + if( f && f->name[strlen(f->name) - 1] == '/' ) { - f = malloc( sizeof( httpd_file_callback_args_t ) ); - f->p_intf = p_intf; - f->file = strdup( fold->file ); - f->name = strdup( fold->name ); - f->mime = fold->mime; - - f->name[strlen(f->name) - 1] = '\0'; - msg_Dbg( p_intf, "file=%s (url=%s mime=%s)", f->file, f->name, f->mime ); - f->p_file = - p_sys->p_httpd->pf_register_file( p_sys->p_httpd, - f->name, f->mime, - user, password, - http_get, http_get, - f ); - if( f->p_file ) + char *psz_redir = strdup( f->name ); + char *p; + psz_redir[strlen( psz_redir ) - 1] = '\0'; + + msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name ); + f->p_redir = httpd_RedirectNew( p_sys->p_httpd_host, f->name, psz_redir ); + free( psz_redir ); + + if( b_index && ( p = strstr( f->file, "index." ) ) ) { - p_sys->i_files++; - p_sys->pp_files = realloc( p_sys->pp_files, (p_sys->i_files+1) * sizeof( httpd_file_callback_args_t ) ); + asprintf( &psz_redir, "%s%s", f->name, p ); + + msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name ); + f->p_redir2 = httpd_RedirectNew( p_sys->p_httpd_host, + f->name, psz_redir ); + + free( psz_redir ); } } -#undef fold -#undef f } } @@ -573,6 +701,13 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir { free( password ); } + for( i = 0; i < i_hosts; i++ ) + { + TAB_REMOVE( i_hosts, ppsz_hosts, ppsz_hosts[0] ); + } + + closedir( p_dir ); + return VLC_SUCCESS; } @@ -584,6 +719,7 @@ static mvar_t *mvar_New( char *name, char *value ) { mvar_t *v = malloc( sizeof( mvar_t ) ); + if( !v ) return NULL; v->name = strdup( name ); v->value = strdup( value ? value : "" ); @@ -740,7 +876,7 @@ static char *mvar_GetValue( mvar_t *v, char *field ) } else { - return ""; + return field; } } } @@ -765,7 +901,6 @@ static mvar_t *mvar_IntegerSetNew( char *name, char *arg ) char *str = dup; mvar_t *s = mvar_New( name, "set" ); - fprintf( stderr," mvar_IntegerSetNew: name=`%s' arg=`%s'\n", name, str ); while( str ) { @@ -781,7 +916,6 @@ static mvar_t *mvar_IntegerSetNew( char *name, char *arg ) i_step = 0; i_match = sscanf( str, "%d:%d:%d", &i_start, &i_stop, &i_step ); - fprintf( stderr," mvar_IntegerSetNew: m=%d start=%d stop=%d step=%d\n", i_match, i_start, i_stop, i_step ); if( i_match == 1 ) { @@ -797,12 +931,12 @@ static mvar_t *mvar_IntegerSetNew( char *name, char *arg ) { int i; - if( ( i_start < i_stop && i_step > 0 ) || - ( i_start > i_stop && i_step < 0 ) ) + if( ( i_start <= i_stop && i_step > 0 ) || + ( i_start >= i_stop && i_step < 0 ) ) { for( i = i_start; ; i += i_step ) { - char value[512]; + char value[79]; if( ( i_step > 0 && i > i_stop ) || ( i_step < 0 && i < i_stop ) ) @@ -810,7 +944,6 @@ static mvar_t *mvar_IntegerSetNew( char *name, char *arg ) break; } - fprintf( stderr," mvar_IntegerSetNew: adding %d\n", i ); sprintf( value, "%d", i ); mvar_PushNewVar( s, name, value ); @@ -824,29 +957,77 @@ static mvar_t *mvar_IntegerSetNew( char *name, char *arg ) return s; } +void PlaylistListNode( playlist_t *p_pl, playlist_item_t *p_node, + char *name, mvar_t *s, int i_depth ) +{ + if( p_node != NULL ) + { + if (p_node->i_children == -1) + { + char value[512]; + mvar_t *itm = mvar_New( name, "set" ); + + sprintf( value, "%d", ( p_pl->status.p_item == p_node )? 1 : 0 ); + mvar_AppendNewVar( itm, "current", value ); + + sprintf( value, "%d", p_node->input.i_id ); + mvar_AppendNewVar( itm, "index", value ); + + mvar_AppendNewVar( itm, "name", p_node->input.psz_name ); + + mvar_AppendNewVar( itm, "uri", p_node->input.psz_uri ); + + sprintf( value, "Item"); + mvar_AppendNewVar( itm, "type", value ); + + sprintf( value, "%d", i_depth ); + mvar_AppendNewVar( itm, "depth", value ); + + mvar_AppendVar( s, itm ); + } + else + { + char value[512]; + int i_child; + mvar_t *itm = mvar_New( name, "set" ); + + mvar_AppendNewVar( itm, "name", p_node->input.psz_name ); + mvar_AppendNewVar( itm, "uri", p_node->input.psz_name ); + + sprintf( value, "Node" ); + mvar_AppendNewVar( itm, "type", value ); + + sprintf( value, "%d", p_node->input.i_id ); + mvar_AppendNewVar( itm, "index", value ); + + sprintf( value, "%d", p_node->i_children); + mvar_AppendNewVar( itm, "i_children", value ); + + sprintf( value, "%d", i_depth ); + mvar_AppendNewVar( itm, "depth", value ); + + mvar_AppendVar( s, itm ); + + for (i_child = 0 ; i_child < p_node->i_children ; i_child++) + PlaylistListNode( p_pl, p_node->pp_children[i_child], name, s, i_depth + 1); + + } + } +} + static mvar_t *mvar_PlaylistSetNew( char *name, playlist_t *p_pl ) { + playlist_view_t *p_view; mvar_t *s = mvar_New( name, "set" ); - int i; - fprintf( stderr," mvar_PlaylistSetNew: name=`%s'\n", name ); vlc_mutex_lock( &p_pl->object_lock ); - for( i = 0; i < p_pl->i_size; i++ ) - { - mvar_t *itm = mvar_New( name, "set" ); - char value[512]; - - sprintf( value, "%d", i == p_pl->i_index ? 1 : 0 ); - mvar_AppendNewVar( itm, "current", value ); - sprintf( value, "%d", i ); - mvar_AppendNewVar( itm, "index", value ); + p_view = playlist_ViewFind( p_pl, VIEW_CATEGORY ); /* FIXME */ - mvar_AppendNewVar( itm, "name", p_pl->pp_items[i]->psz_name ); + if( p_view != NULL ) + PlaylistListNode( p_pl, p_view->p_root, name, s, 0 ); - mvar_AppendVar( s, itm ); - } vlc_mutex_unlock( &p_pl->object_lock ); return s; @@ -855,52 +1036,47 @@ static mvar_t *mvar_PlaylistSetNew( char *name, playlist_t *p_pl ) static mvar_t *mvar_InfoSetNew( char *name, input_thread_t *p_input ) { mvar_t *s = mvar_New( name, "set" ); + int i, j; - input_info_category_t * p_category; - input_info_t * p_info; - - fprintf( stderr," mvar_InfoSetNew: name=`%s'\n", name ); if( p_input == NULL ) { return s; } - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_category = p_input->stream.p_info; - while ( p_category ) + vlc_mutex_lock( &p_input->input.p_item->lock ); + for ( i = 0; i < p_input->input.p_item->i_categories; i++ ) { + info_category_t *p_category = p_input->input.p_item->pp_categories[i]; mvar_t *cat = mvar_New( name, "set" ); mvar_t *iset = mvar_New( "info", "set" ); mvar_AppendNewVar( cat, "name", p_category->psz_name ); mvar_AppendVar( cat, iset ); - p_info = p_category->p_info; - while ( p_info ) + for ( j = 0; j < p_category->i_infos; j++ ) { + info_t *p_info = p_category->pp_infos[j]; mvar_t *info = mvar_New( "info", "" ); - msg_Dbg( p_input, "adding info name=%s value=%s", p_info->psz_name, p_info->psz_value ); + msg_Dbg( p_input, "adding info name=%s value=%s", + p_info->psz_name, p_info->psz_value ); mvar_AppendNewVar( info, "name", p_info->psz_name ); mvar_AppendNewVar( info, "value", p_info->psz_value ); mvar_AppendVar( iset, info ); - p_info = p_info->p_next; } mvar_AppendVar( s, cat ); - p_category = p_category->p_next; } - vlc_mutex_unlock( &p_input->stream.stream_lock ); + vlc_mutex_unlock( &p_input->input.p_item->lock ); return s; } - +#if 0 static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type ) { mvar_t *s = mvar_New( name, "set" ); httpd_info_t info; int i; - fprintf( stderr," mvar_HttpdInfoSetNew: name=`%s'\n", name ); if( !p_httpd->pf_control( p_httpd, i_type, &info, NULL ) ) { for( i= 0; i < info.i_count; ) @@ -927,18 +1103,267 @@ static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type ) free( info.info[i].psz_name ); free( info.info[i].psz_value ); } + if( info.i_count > 0 ) + { + free( info.info ); + } return s; } -static void SSInit( stack_t * ); -static void SSClean( stack_t * ); -static void EvaluateRPN( mvar_t *, stack_t *, char * ); +#endif -static void SSPush ( stack_t *, char * ); -static char *SSPop ( stack_t * ); +static mvar_t *mvar_FileSetNew( char *name, char *psz_dir ) +{ + mvar_t *s = mvar_New( name, "set" ); + char tmp[MAX_DIR_SIZE], *p, *src; +#ifdef HAVE_SYS_STAT_H + struct stat stat_info; +#endif + DIR *p_dir; + struct dirent *p_dir_content; + char sep; -static void SSPushN ( stack_t *, int ); -static int SSPopN ( stack_t *, mvar_t * ); + /* convert all / to native separator */ +#if defined( WIN32 ) + while( (p = strchr( psz_dir, '/' )) ) + { + *p = '\\'; + } + sep = '\\'; +#else + sep = '/'; +#endif + + /* remove trailling separator */ + while( strlen( psz_dir ) > 1 && +#if defined( WIN32 ) + !( strlen(psz_dir)==3 && psz_dir[1]==':' && psz_dir[2]==sep ) && +#endif + psz_dir[strlen( psz_dir ) -1 ] == sep ) + { + psz_dir[strlen( psz_dir ) -1 ] ='\0'; + } + /* remove double separator */ + for( p = src = psz_dir; *src != '\0'; src++, p++ ) + { + if( src[0] == sep && src[1] == sep ) + { + src++; + } + *p = *src; + } + *p = '\0'; + + if( *psz_dir == '\0' ) + { + return s; + } + /* first fix all .. dir */ + p = src = psz_dir; + while( *src ) + { + if( src[0] == '.' && src[1] == '.' ) + { + src += 2; + if( p <= &psz_dir[1] ) + { + continue; + } + + p -= 2; + + while( p > &psz_dir[1] && *p != sep ) + { + p--; + } + } + else if( *src == sep ) + { + if( p > psz_dir && p[-1] == sep ) + { + src++; + } + else + { + *p++ = *src++; + } + } + else + { + do + { + *p++ = *src++; + } while( *src && *src != sep ); + } + } + *p = '\0'; + + +#ifdef HAVE_SYS_STAT_H + if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) ) + { + return s; + } +#endif + + if( ( p_dir = opendir( psz_dir ) ) == NULL ) + { + fprintf( stderr, "cannot open dir (%s)", psz_dir ); + return s; + } + + /* remove traling / or \ */ + for( p = &psz_dir[strlen( psz_dir) - 1]; + p >= psz_dir && ( *p =='/' || *p =='\\' ); p-- ) + { + *p = '\0'; + } + + for( ;; ) + { + mvar_t *f; + + /* parse psz_src dir */ + if( ( p_dir_content = readdir( p_dir ) ) == NULL ) + { + break; + } + if( !strcmp( p_dir_content->d_name, "." ) ) + { + continue; + } + + sprintf( tmp, "%s/%s", psz_dir, p_dir_content->d_name ); + +#ifdef HAVE_SYS_STAT_H + if( stat( tmp, &stat_info ) == -1 ) + { + continue; + } +#endif + f = mvar_New( name, "set" ); + mvar_AppendNewVar( f, "name", tmp ); + +#ifdef HAVE_SYS_STAT_H + if( S_ISDIR( stat_info.st_mode ) ) + { + mvar_AppendNewVar( f, "type", "directory" ); + } + else if( S_ISREG( stat_info.st_mode ) ) + { + mvar_AppendNewVar( f, "type", "file" ); + } + else + { + mvar_AppendNewVar( f, "type", "unknown" ); + } + + sprintf( tmp, I64Fd, (int64_t)stat_info.st_size ); + mvar_AppendNewVar( f, "size", tmp ); + + /* FIXME memory leak FIXME */ +#ifdef HAVE_CTIME_R + ctime_r( &stat_info.st_mtime, tmp ); + mvar_AppendNewVar( f, "date", tmp ); +#else + mvar_AppendNewVar( f, "date", ctime( &stat_info.st_mtime ) ); +#endif + +#else + mvar_AppendNewVar( f, "type", "unknown" ); + mvar_AppendNewVar( f, "size", "unknown" ); + mvar_AppendNewVar( f, "date", "unknown" ); +#endif + mvar_AppendVar( s, f ); + } + + return s; +} + +static mvar_t *mvar_VlmSetNew( char *name, vlm_t *vlm ) +{ + mvar_t *s = mvar_New( name, "set" ); + vlm_message_t *msg; + int i; + + if( vlm == NULL ) return s; + + if( vlm_ExecuteCommand( vlm, "show", &msg ) ) + { + return s; + } + + for( i = 0; i < msg->i_child; i++ ) + { + /* Over media, schedule */ + vlm_message_t *ch = msg->child[i]; + int j; + + for( j = 0; j < ch->i_child; j++ ) + { + /* Over name */ + vlm_message_t *el = ch->child[j]; + vlm_message_t *inf, *desc; + mvar_t *set; + char psz[500]; + int k; + + sprintf( psz, "show %s", el->psz_name ); + if( vlm_ExecuteCommand( vlm, psz, &inf ) ) + continue; + desc = inf->child[0]; + + /* Add a node with name and info */ + set = mvar_New( name, "set" ); + mvar_AppendNewVar( set, "name", el->psz_name ); + + for( k = 0; k < desc->i_child; k++ ) + { + vlm_message_t *ch = desc->child[k]; + if( ch->i_child > 0 ) + { + int c; + mvar_t *n = mvar_New( ch->psz_name, "set" ); + + for( c = 0; c < ch->i_child; c++ ) + { + if( ch->child[c]->psz_value ) + { + mvar_AppendNewVar( n, ch->child[c]->psz_name, ch->child[c]->psz_value ); + } + else + { + mvar_t *in = mvar_New( ch->psz_name, ch->child[c]->psz_name ); + mvar_AppendVar( n, in ); + } + } + mvar_AppendVar( set, n ); + } + else + { + mvar_AppendNewVar( set, ch->psz_name, ch->psz_value ); + } + } + vlm_MessageDelete( inf ); + + mvar_AppendVar( s, set ); + } + } + vlm_MessageDelete( msg ); + + return s; +} + + +static void SSInit( rpn_stack_t * ); +static void SSClean( rpn_stack_t * ); +static void EvaluateRPN( mvar_t *, rpn_stack_t *, char * ); + +static void SSPush ( rpn_stack_t *, char * ); +static char *SSPop ( rpn_stack_t * ); + +static void SSPushN ( rpn_stack_t *, int ); +static int SSPopN ( rpn_stack_t *, mvar_t * ); /**************************************************************************** @@ -951,7 +1376,7 @@ typedef struct char *param2; } macro_t; -static int FileLoad( FILE *f, uint8_t **pp_data, int *pi_data ) +static int FileLoad( FILE *f, char **pp_data, int *pi_data ) { int i_read; @@ -972,11 +1397,11 @@ static int FileLoad( FILE *f, uint8_t **pp_data, int *pi_data ) return VLC_SUCCESS; } -static int MacroParse( macro_t *m, uint8_t *psz_src ) +static int MacroParse( macro_t *m, char *psz_src ) { - uint8_t *dup = strdup( psz_src ); - uint8_t *src = dup; - uint8_t *p; + char *dup = strdup( (char *)psz_src ); + char *src = dup; + char *p; int i_skip; #define EXTRACT( name, l ) \ @@ -1069,12 +1494,32 @@ enum macroType MVLC_NEXT, MVLC_PREVIOUS, MVLC_ADD, + MVLC_DEL, + MVLC_EMPTY, + MVLC_SEEK, + MVLC_KEEP, + MVLC_SORT, + MVLC_MOVE, + MVLC_VOLUME, + MVLC_FULLSCREEN, MVLC_CLOSE, MVLC_SHUTDOWN, + + MVLC_VLM_NEW, + MVLC_VLM_SETUP, + MVLC_VLM_DEL, + MVLC_VLM_PLAY, + MVLC_VLM_PAUSE, + MVLC_VLM_STOP, + MVLC_VLM_SEEK, + MVLC_VLM_LOAD, + MVLC_VLM_SAVE, + MVLC_FOREACH, MVLC_IF, MVLC_RPN, + MVLC_STACK, MVLC_ELSE, MVLC_END, MVLC_GET, @@ -1099,14 +1544,36 @@ StrToMacroTypeTab [] = { "stop", MVLC_STOP }, { "pause", MVLC_PAUSE }, { "next", MVLC_NEXT }, - { "prevous", MVLC_PREVIOUS }, + { "previous", MVLC_PREVIOUS }, + { "seek", MVLC_SEEK }, + { "keep", MVLC_KEEP }, + { "fullscreen", MVLC_FULLSCREEN }, + { "volume", MVLC_VOLUME }, + + /* playlist management */ { "add", MVLC_ADD }, + { "delete", MVLC_DEL }, + { "empty", MVLC_EMPTY }, + { "sort", MVLC_SORT }, + { "move", MVLC_MOVE }, /* admin control */ { "close", MVLC_CLOSE }, { "shutdown", MVLC_SHUTDOWN }, + /* vlm control */ + { "vlm_new", MVLC_VLM_NEW }, + { "vlm_setup", MVLC_VLM_SETUP }, + { "vlm_del", MVLC_VLM_DEL }, + { "vlm_play", MVLC_VLM_PLAY }, + { "vlm_pause", MVLC_VLM_PAUSE }, + { "vlm_stop", MVLC_VLM_STOP }, + { "vlm_seek", MVLC_VLM_SEEK }, + { "vlm_load", MVLC_VLM_LOAD }, + { "vlm_save", MVLC_VLM_SAVE }, + { "rpn", MVLC_RPN }, + { "stack", MVLC_STACK }, { "foreach", MVLC_FOREACH }, { "value", MVLC_VALUE }, @@ -1142,11 +1609,11 @@ static int StrToMacroType( char *name ) return MVLC_UNKNOWN; } -static void MacroDo( httpd_file_callback_args_t *p_args, +static void MacroDo( httpd_file_sys_t *p_args, macro_t *m, - uint8_t *p_request, int i_request, - uint8_t **pp_data, int *pi_data, - uint8_t **pp_dst ) + char *p_request, int i_request, + char **pp_data, int *pi_data, + char **pp_dst ) { intf_thread_t *p_intf = p_args->p_intf; intf_sys_t *p_sys = p_args->p_intf->p_sys; @@ -1165,7 +1632,17 @@ static void MacroDo( httpd_file_callback_args_t *p_args, #define PRINTS( str, s ) \ ALLOC( strlen( str ) + strlen( s ) + 1 ); \ - *pp_dst += sprintf( *pp_dst, str, s ); + { \ + char * psz_cur = *pp_dst; \ + *pp_dst += sprintf( *pp_dst, str, s ); \ + while( psz_cur && *psz_cur ) \ + { \ + /* Prevent script injection */ \ + if( *psz_cur == '<' ) *psz_cur = '*'; \ + if( *psz_cur == '>' ) *psz_cur = '*'; \ + psz_cur++ ; \ + } \ + } switch( StrToMacroType( m->id ) ) { @@ -1189,48 +1666,455 @@ static void MacroDo( httpd_file_callback_args_t *p_args, uri_extract_value( p_request, "item", item, 512 ); i_item = atoi( item ); - playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO, i_item ); + playlist_Control( p_sys->p_playlist, PLAYLIST_ITEMPLAY, + playlist_ItemGetById( p_sys->p_playlist, + i_item ) ); msg_Dbg( p_intf, "requested playlist item: %i", i_item ); break; } case MVLC_STOP: - playlist_Command( p_sys->p_playlist, PLAYLIST_STOP, 0 ); + playlist_Control( p_sys->p_playlist, PLAYLIST_STOP); msg_Dbg( p_intf, "requested playlist stop" ); break; case MVLC_PAUSE: - playlist_Command( p_sys->p_playlist, PLAYLIST_PAUSE, 0 ); + playlist_Control( p_sys->p_playlist, PLAYLIST_PAUSE ); msg_Dbg( p_intf, "requested playlist pause" ); break; case MVLC_NEXT: - playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO, - p_sys->p_playlist->i_index + 1 ); + playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, 1 ); msg_Dbg( p_intf, "requested playlist next" ); break; case MVLC_PREVIOUS: - playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO, - p_sys->p_playlist->i_index - 1 ); + playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1); msg_Dbg( p_intf, "requested playlist next" ); break; - case MVLC_ADD: + case MVLC_FULLSCREEN: + { + if( p_sys->p_input ) + { + vout_thread_t *p_vout; + p_vout = vlc_object_find( p_sys->p_input, + VLC_OBJECT_VOUT, FIND_CHILD ); + + if( p_vout ) + { + p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE; + vlc_object_release( p_vout ); + msg_Dbg( p_intf, "requested fullscreen toggle" ); + } + } + } + break; + case MVLC_SEEK: + { + vlc_value_t val; + char value[30]; + char * p_value; + int i_stock = 0; + uint64_t i_length; + int i_value = 0; + int i_relative = 0; +#define POSITION_ABSOLUTE 12 +#define POSITION_REL_FOR 13 +#define POSITION_REL_BACK 11 +#define VL_TIME_ABSOLUTE 0 +#define VL_TIME_REL_FOR 1 +#define VL_TIME_REL_BACK -1 + if( p_sys->p_input ) + { + uri_extract_value( p_request, "seek_value", value, 20 ); + uri_decode_url_encoded( value ); + p_value = value; + var_Get( p_sys->p_input, "length", &val); + i_length = val.i_time; + + while( p_value[0] != '\0' ) + { + switch(p_value[0]) + { + case '+': + { + i_relative = VL_TIME_REL_FOR; + p_value++; + break; + } + case '-': + { + i_relative = VL_TIME_REL_BACK; + p_value++; + break; + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + i_stock = strtol( p_value , &p_value , 10 ); + break; + } + case '%': /* for percentage ie position */ + { + i_relative += POSITION_ABSOLUTE; + i_value = i_stock; + i_stock = 0; + p_value[0] = '\0'; + break; + } + case ':': + { + i_value = 60 * (i_value + i_stock) ; + i_stock = 0; + p_value++; + break; + } + case 'h': case 'H': /* hours */ + { + i_value += 3600 * i_stock; + i_stock = 0; + /* other characters which are not numbers are not important */ + while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') ) + { + p_value++; + } + break; + } + case 'm': case 'M': case '\'': /* minutes */ + { + i_value += 60 * i_stock; + i_stock = 0; + p_value++; + while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') ) + { + p_value++; + } + break; + } + case 's': case 'S': case '"': /* seconds */ + { + i_value += i_stock; + i_stock = 0; + while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') ) + { + p_value++; + } + break; + } + default: + { + p_value++; + break; + } + } + } + + /* if there is no known symbol, I consider it as seconds. Otherwise, i_stock = 0 */ + i_value += i_stock; + + switch(i_relative) + { + case VL_TIME_ABSOLUTE: + { + if( (uint64_t)( i_value ) * 1000000 <= i_length ) + val.i_time = (uint64_t)( i_value ) * 1000000; + else + val.i_time = i_length; + + var_Set( p_sys->p_input, "time", val ); + msg_Dbg( p_intf, "requested seek position: %dsec", i_value ); + break; + } + case VL_TIME_REL_FOR: + { + var_Get( p_sys->p_input, "time", &val ); + if( (uint64_t)( i_value ) * 1000000 + val.i_time <= i_length ) + { + val.i_time = ((uint64_t)( i_value ) * 1000000) + val.i_time; + } else + { + val.i_time = i_length; + } + var_Set( p_sys->p_input, "time", val ); + msg_Dbg( p_intf, "requested seek position forward: %dsec", i_value ); + break; + } + case VL_TIME_REL_BACK: + { + var_Get( p_sys->p_input, "time", &val ); + if( (int64_t)( i_value ) * 1000000 > val.i_time ) + { + val.i_time = 0; + } else + { + val.i_time = val.i_time - ((uint64_t)( i_value ) * 1000000); + } + var_Set( p_sys->p_input, "time", val ); + msg_Dbg( p_intf, "requested seek position backward: %dsec", i_value ); + break; + } + case POSITION_ABSOLUTE: + { + val.f_float = __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 ); + var_Set( p_sys->p_input, "position", val ); + msg_Dbg( p_intf, "requested seek percent: %d", i_value ); + break; + } + case POSITION_REL_FOR: + { + var_Get( p_sys->p_input, "position", &val ); + val.f_float += __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 ); + var_Set( p_sys->p_input, "position", val ); + msg_Dbg( p_intf, "requested seek percent forward: %d", i_value ); + break; + } + case POSITION_REL_BACK: + { + var_Get( p_sys->p_input, "position", &val ); + val.f_float -= __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 ); + var_Set( p_sys->p_input, "position", val ); + msg_Dbg( p_intf, "requested seek percent backward: %d", i_value ); + break; + } + default: + { + msg_Dbg( p_intf, "requested seek: what the f*** is going on here ?" ); + break; + } + } + } +#undef POSITION_ABSOLUTE +#undef POSITION_REL_FOR +#undef POSITION_REL_BACK +#undef VL_TIME_ABSOLUTE +#undef VL_TIME_REL_FOR +#undef VL_TIME_REL_BACK + break; + } + case MVLC_VOLUME: + { + char vol[8]; + audio_volume_t i_volume; + int i_value; + + uri_extract_value( p_request, "value", vol, 8 ); + aout_VolumeGet( p_intf, &i_volume ); + uri_decode_url_encoded( vol ); + + if( vol[0] == '+' ) + { + i_value = atoi( vol + 1 ); + if( (i_volume + i_value) > AOUT_VOLUME_MAX ) + { + aout_VolumeSet( p_intf , AOUT_VOLUME_MAX ); + msg_Dbg( p_intf, "requested volume set: max" ); + } else + { + aout_VolumeSet( p_intf , (i_volume + i_value) ); + msg_Dbg( p_intf, "requested volume set: +%i", (i_volume + i_value) ); + } + } else + if( vol[0] == '-' ) + { + i_value = atoi( vol + 1 ); + if( (i_volume - i_value) < AOUT_VOLUME_MIN ) + { + aout_VolumeSet( p_intf , AOUT_VOLUME_MIN ); + msg_Dbg( p_intf, "requested volume set: min" ); + } else + { + aout_VolumeSet( p_intf , (i_volume - i_value) ); + msg_Dbg( p_intf, "requested volume set: -%i", (i_volume - i_value) ); + } + } else + if( strstr(vol, "%") != NULL ) + { + i_value = atoi( vol ); + if( (i_value <= 100) && (i_value>=0) ){ + aout_VolumeSet( p_intf, (i_value * (AOUT_VOLUME_MAX - AOUT_VOLUME_MIN))/100+AOUT_VOLUME_MIN); + msg_Dbg( p_intf, "requested volume set: %i%%", atoi( vol )); + } + } else + { + i_value = atoi( vol ); + if( ( i_value <= AOUT_VOLUME_MAX ) && ( i_value >= AOUT_VOLUME_MIN ) ) + { + aout_VolumeSet( p_intf , atoi( vol ) ); + msg_Dbg( p_intf, "requested volume set: %i", atoi( vol ) ); + } + } + break; + } + + /* playlist management */ + case MVLC_ADD: + { + char mrl[512]; + playlist_item_t * p_item; + + uri_extract_value( p_request, "mrl", mrl, 512 ); + uri_decode_url_encoded( mrl ); + p_item = parse_MRL( p_intf, mrl ); + + if( !p_item || !p_item->input.psz_uri || + !*p_item->input.psz_uri ) + { + msg_Dbg( p_intf, "invalid requested mrl: %s", mrl ); + } else + { + playlist_AddItem( p_sys->p_playlist , p_item , + PLAYLIST_APPEND, PLAYLIST_END ); + msg_Dbg( p_intf, "requested mrl add: %s", mrl ); + } + + break; + } + case MVLC_DEL: + { + int i_item, *p_items = NULL, i_nb_items = 0; + char item[512], *p_parser = p_request; + + /* Get the list of items to delete */ + while( (p_parser = + uri_extract_value( p_parser, "item", item, 512 )) ) + { + if( !*item ) continue; + + i_item = atoi( item ); + p_items = realloc( p_items, (i_nb_items + 1) * + sizeof(int) ); + p_items[i_nb_items] = i_item; + i_nb_items++; + } + + if( i_nb_items ) + { + int i; + for( i = 0; i < i_nb_items; i++ ) + { + playlist_LockDelete( p_sys->p_playlist, p_items[i] ); + msg_Dbg( p_intf, "requested playlist delete: %d", + p_items[i] ); + p_items[i] = -1; + } + } + + if( p_items ) free( p_items ); + break; + } + case MVLC_KEEP: + { + int i_item, *p_items = NULL, i_nb_items = 0; + char item[512], *p_parser = p_request; + int i,j; + + /* Get the list of items to keep */ + while( (p_parser = + uri_extract_value( p_parser, "item", item, 512 )) ) + { + if( !*item ) continue; + + i_item = atoi( item ); + p_items = realloc( p_items, (i_nb_items + 1) * + sizeof(int) ); + p_items[i_nb_items] = i_item; + i_nb_items++; + } + + for( i = p_sys->p_playlist->i_size - 1 ; i >= 0; i-- ) + { + /* Check if the item is in the keep list */ + for( j = 0 ; j < i_nb_items ; j++ ) + { + if( p_items[j] == + p_sys->p_playlist->pp_items[i]->input.i_id ) break; + } + if( j == i_nb_items ) + { + playlist_LockDelete( p_sys->p_playlist, p_sys->p_playlist->pp_items[i]->input.i_id ); + msg_Dbg( p_intf, "requested playlist delete: %d", + i ); + } + } + + if( p_items ) free( p_items ); + break; + } + case MVLC_EMPTY: + { + playlist_LockClear( p_sys->p_playlist ); + msg_Dbg( p_intf, "requested playlist empty" ); + break; + } + case MVLC_SORT: + { + char type[12]; + char order[2]; + char item[512]; + int i_order; + int i_item; + + uri_extract_value( p_request, "type", type, 12 ); + uri_extract_value( p_request, "order", order, 2 ); + uri_extract_value( p_request, "item", item, 512 ); + i_item = atoi( item ); + + if( order[0] == '0' ) i_order = ORDER_NORMAL; + else i_order = ORDER_REVERSE; + + if( !strcmp( type , "title" ) ) + { + playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/ + p_sys->p_playlist->pp_views[0]->p_root, + SORT_TITLE_NODES_FIRST, + ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE ); + msg_Dbg( p_intf, "requested playlist sort by title (%d)" , i_order ); + } else if( !strcmp( type , "author" ) ) + { + playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/ + p_sys->p_playlist->pp_views[0]->p_root, + SORT_AUTHOR, + ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE ); + msg_Dbg( p_intf, "requested playlist sort by author (%d)" , i_order ); + } else if( !strcmp( type , "shuffle" ) ) + { + playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/ + p_sys->p_playlist->pp_views[0]->p_root, + SORT_RANDOM, + ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE ); + msg_Dbg( p_intf, "requested playlist shuffle"); + } + + break; + } + case MVLC_MOVE: { - char mrl[512]; - uri_extract_value( p_request, "mrl", mrl, 512 ); - uri_decode_url_encoded( mrl ); - playlist_Add( p_sys->p_playlist, mrl, - PLAYLIST_APPEND, PLAYLIST_END ); - msg_Dbg( p_intf, "requested playlist add: %s", mrl ); + char psz_pos[6]; + char psz_newpos[6]; + int i_pos; + int i_newpos; + uri_extract_value( p_request, "psz_pos", psz_pos, 6 ); + uri_extract_value( p_request, "psz_newpos", psz_newpos, 6 ); + i_pos = atoi( psz_pos ); + i_newpos = atoi( psz_newpos ); + if ( i_pos < i_newpos ) + { + playlist_Move( p_sys->p_playlist, i_pos, i_newpos + 1 ); + } else { + playlist_Move( p_sys->p_playlist, i_pos, i_newpos ); + } + msg_Dbg( p_intf, "requested move playlist item %d to %d", i_pos, i_newpos); break; } + /* admin function */ case MVLC_CLOSE: { char id[512]; uri_extract_value( p_request, "id", id, 512 ); msg_Dbg( p_intf, "requested close id=%s", id ); +#if 0 if( p_sys->p_httpd->pf_control( p_sys->p_httpd, HTTPD_SET_CLOSE, id, NULL ) ) { msg_Warn( p_intf, "close failed for id=%s", id ); } +#endif break; } case MVLC_SHUTDOWN: @@ -1239,8 +2123,159 @@ static void MacroDo( httpd_file_callback_args_t *p_args, p_intf->p_vlc->b_die = VLC_TRUE; break; } + /* vlm */ + case MVLC_VLM_NEW: + case MVLC_VLM_SETUP: + { + static const char *vlm_properties[11] = + { + /* no args */ + "enabled", "disabled", "loop", "unloop", + /* args required */ + "input", "output", "option", "date", "period", "repeat", "append", + }; + vlm_message_t *vlm_answer; + char name[512]; + char *psz = malloc( strlen( p_request ) + 1000 ); + char *p = psz; + char *vlm_error; + int i; + + if( p_intf->p_sys->p_vlm == NULL ) + p_intf->p_sys->p_vlm = vlm_New( p_intf ); + + if( p_intf->p_sys->p_vlm == NULL ) break; + + uri_extract_value( p_request, "name", name, 512 ); + if( StrToMacroType( control ) == MVLC_VLM_NEW ) + { + char type[20]; + uri_extract_value( p_request, "type", type, 20 ); + p += sprintf( psz, "new %s %s", name, type ); + } + else + { + p += sprintf( psz, "setup %s", name ); + } + /* Parse the request */ + for( i = 0; i < 11; i++ ) + { + char val[512]; + uri_extract_value( p_request, vlm_properties[i], val, 512 ); + uri_decode_url_encoded( val ); + if( strlen( val ) > 0 && i >= 4 ) + { + p += sprintf( p, " %s %s", vlm_properties[i], val ); + } + else if( uri_test_param( p_request, vlm_properties[i] ) && i < 4 ) + { + p += sprintf( p, " %s", vlm_properties[i] ); + } + } + vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer ); + if( vlm_answer->psz_value == NULL ) /* there is no error */ + { + vlm_error = strdup( "" ); + } + else + { + vlm_error = malloc( strlen(vlm_answer->psz_name) + + strlen(vlm_answer->psz_value) + + strlen( " : ") + 1 ); + sprintf( vlm_error , "%s : %s" , vlm_answer->psz_name, + vlm_answer->psz_value ); + } + + mvar_AppendNewVar( p_args->vars, "vlm_error", vlm_error ); + + vlm_MessageDelete( vlm_answer ); + free( vlm_error ); + free( psz ); + break; + } + + case MVLC_VLM_DEL: + { + vlm_message_t *vlm_answer; + char name[512]; + char psz[512+10]; + if( p_intf->p_sys->p_vlm == NULL ) + p_intf->p_sys->p_vlm = vlm_New( p_intf ); + + if( p_intf->p_sys->p_vlm == NULL ) break; + + uri_extract_value( p_request, "name", name, 512 ); + sprintf( psz, "del %s", name ); + + vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer ); + /* FIXME do a vlm_answer -> var stack conversion */ + vlm_MessageDelete( vlm_answer ); + break; + } + + case MVLC_VLM_PLAY: + case MVLC_VLM_PAUSE: + case MVLC_VLM_STOP: + case MVLC_VLM_SEEK: + { + vlm_message_t *vlm_answer; + char name[512]; + char psz[512+10]; + if( p_intf->p_sys->p_vlm == NULL ) + p_intf->p_sys->p_vlm = vlm_New( p_intf ); + + if( p_intf->p_sys->p_vlm == NULL ) break; + + uri_extract_value( p_request, "name", name, 512 ); + if( StrToMacroType( control ) == MVLC_VLM_PLAY ) + sprintf( psz, "control %s play", name ); + else if( StrToMacroType( control ) == MVLC_VLM_PAUSE ) + sprintf( psz, "control %s pause", name ); + else if( StrToMacroType( control ) == MVLC_VLM_STOP ) + sprintf( psz, "control %s stop", name ); + else if( StrToMacroType( control ) == MVLC_VLM_SEEK ) + { + char percent[20]; + uri_extract_value( p_request, "percent", percent, 512 ); + sprintf( psz, "control %s seek %s", name, percent ); + } + + vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer ); + /* FIXME do a vlm_answer -> var stack conversion */ + vlm_MessageDelete( vlm_answer ); + break; + } + case MVLC_VLM_LOAD: + case MVLC_VLM_SAVE: + { + vlm_message_t *vlm_answer; + char file[512]; + char psz[512]; + + if( p_intf->p_sys->p_vlm == NULL ) + p_intf->p_sys->p_vlm = vlm_New( p_intf ); + + if( p_intf->p_sys->p_vlm == NULL ) break; + + uri_extract_value( p_request, "file", file, 512 ); + uri_decode_url_encoded( file ); + + if( StrToMacroType( control ) == MVLC_VLM_LOAD ) + sprintf( psz, "load %s", file ); + else + sprintf( psz, "save %s", file ); + + vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer ); + /* FIXME do a vlm_answer -> var stack conversion */ + vlm_MessageDelete( vlm_answer ); + break; + } + default: - PRINTS( "", control ); + if( *control ) + { + PRINTS( "", control ); + } break; } break; @@ -1294,7 +2329,7 @@ static void MacroDo( httpd_file_callback_args_t *p_args, { case MVLC_INT: i = config_GetInt( p_intf, m->param1 ); - sprintf( value, "%i", i ); + sprintf( value, "%d", i ); break; case MVLC_FLOAT: f = config_GetFloat( p_intf, m->param1 ); @@ -1302,12 +2337,15 @@ static void MacroDo( httpd_file_callback_args_t *p_args, break; case MVLC_STRING: psz = config_GetPsz( p_intf, m->param1 ); - sprintf( value, "%s", psz ? psz : "" ); + snprintf( value, sizeof( value ), "%s", psz ? psz : "" ); + if( psz ) free( psz ); break; default: - sprintf( value, "invalid type(%s) in set", m->param2 ); + snprintf( value, sizeof( value ), + "invalid type(%s) in set", m->param2 ); break; } + value[sizeof( value ) - 1] = '\0'; msg_Dbg( p_intf, "get name=%s value=%s type=%s", m->param1, value, m->param2 ); PRINTS( "%s", value ); break; @@ -1316,9 +2354,16 @@ static void MacroDo( httpd_file_callback_args_t *p_args, { char *s, *v; - EvaluateRPN( p_args->vars, &p_args->stack, m->param1 ); - s = SSPop( &p_args->stack ); - v = mvar_GetValue( p_args->vars, s ); + if( m->param1 ) + { + EvaluateRPN( p_args->vars, &p_args->stack, m->param1 ); + s = SSPop( &p_args->stack ); + v = mvar_GetValue( p_args->vars, s ); + } + else + { + v = s = SSPop( &p_args->stack ); + } PRINTS( "%s", v ); free( s ); @@ -1327,6 +2372,17 @@ static void MacroDo( httpd_file_callback_args_t *p_args, case MVLC_RPN: EvaluateRPN( p_args->vars, &p_args->stack, m->param1 ); break; + +/* Usefull for learning stack management */ + case MVLC_STACK: + { + int i; + msg_Dbg( p_intf, "stack" ); + for (i=0;i<(&p_args->stack)->i_stack;i++) + msg_Dbg( p_intf, "%d -> %s", i, (&p_args->stack)->stack[i] ); + break; + } + case MVLC_UNKNOWN: default: PRINTS( "", m->id ); @@ -1338,14 +2394,14 @@ static void MacroDo( httpd_file_callback_args_t *p_args, #undef ALLOC } -static uint8_t *MacroSearch( uint8_t *src, uint8_t *end, int i_mvlc, vlc_bool_t b_after ) +static char *MacroSearch( char *src, char *end, int i_mvlc, vlc_bool_t b_after ) { int i_id; int i_level = 0; while( src < end ) { - if( src + 4 < end && !strncmp( src, "p_intf; - uint8_t *src, *dup, *end; - uint8_t *dst = *pp_dst; + char *src, *dup, *end; + char *dst = *pp_dst; src = dup = malloc( _end - _src + 1 ); end = src +( _end - _src ); @@ -1408,10 +2466,10 @@ static void Execute( httpd_file_callback_args_t *p_args, /* we parse searching vars, &p_args->stack, m.param1 ); if( SSPopN( &p_args->stack, p_args->vars ) ) @@ -1440,11 +2498,11 @@ static void Execute( httpd_file_callback_args_t *p_args, if( i_test == 0 ) { - uint8_t *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE ); + char *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE ); if( start ) { - uint8_t *stop = MacroSearch( start, endif, MVLC_END, VLC_FALSE ); + char *stop = MacroSearch( start, endif, MVLC_END, VLC_FALSE ); if( stop ) { Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop ); @@ -1453,7 +2511,7 @@ static void Execute( httpd_file_callback_args_t *p_args, } else if( i_test == 1 ) { - uint8_t *stop; + char *stop; if( ( stop = MacroSearch( src, endif, MVLC_ELSE, VLC_FALSE ) ) == NULL ) { stop = MacroSearch( src, endif, MVLC_END, VLC_FALSE ); @@ -1469,27 +2527,42 @@ static void Execute( httpd_file_callback_args_t *p_args, } case MVLC_FOREACH: { - uint8_t *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE ); - uint8_t *start = src; - uint8_t *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE ); + char *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE ); + char *start = src; + char *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE ); if( stop ) { mvar_t *index; int i_idx; mvar_t *v; - if( !strncmp( m.param2, "integer=", 8 ) ) + if( !strcmp( m.param2, "integer" ) ) + { + char *arg = SSPop( &p_args->stack ); + index = mvar_IntegerSetNew( m.param1, arg ); + free( arg ); + } + else if( !strcmp( m.param2, "directory" ) ) { - index = mvar_IntegerSetNew( m.param1, &m.param2[8] ); + char *arg = SSPop( &p_args->stack ); + index = mvar_FileSetNew( m.param1, arg ); + free( arg ); } else if( !strcmp( m.param2, "playlist" ) ) { index = mvar_PlaylistSetNew( m.param1, p_intf->p_sys->p_playlist ); } - else if( !strcmp( m.param2, "informations" ) ) + else if( !strcmp( m.param2, "information" ) ) { index = mvar_InfoSetNew( m.param1, p_intf->p_sys->p_input ); } + else if( !strcmp( m.param2, "vlm" ) ) + { + if( p_intf->p_sys->p_vlm == NULL ) + p_intf->p_sys->p_vlm = vlm_New( p_intf ); + index = mvar_VlmSetNew( m.param1, p_intf->p_sys->p_vlm ); + } +#if 0 else if( !strcmp( m.param2, "hosts" ) ) { index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_HOSTS ); @@ -1502,6 +2575,7 @@ static void Execute( httpd_file_callback_args_t *p_args, { index = mvar_HttpdInfoSetNew(m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_CONNECTIONS); } +#endif else if( ( v = mvar_GetVar( p_args->vars, m.param2 ) ) ) { index = mvar_Duplicate( v ); @@ -1564,24 +2638,31 @@ static void Execute( httpd_file_callback_args_t *p_args, } /**************************************************************************** - * http_get: + * HttpCallback: **************************************************************************** - * a file with mime == text/html is parsed and all "macro" replaced + * a file with b_html is parsed and all "macro" replaced * * valid id are * ****************************************************************************/ -static int http_get( httpd_file_callback_args_t *p_args, - uint8_t *p_request, int i_request, - uint8_t **pp_data, int *pi_data ) +static int HttpCallback( httpd_file_sys_t *p_args, + httpd_file_t *p_file, + uint8_t *_p_request, + uint8_t **_pp_data, int *pi_data ) { + char *p_request = (char *)_p_request; + char **pp_data = (char **)_pp_data; + int i_request = p_request ? strlen( p_request ) : 0; char *p; FILE *f; if( ( f = fopen( p_args->file, "r" ) ) == NULL ) { p = *pp_data = malloc( 10240 ); - + if( !p ) + { + return VLC_EGENERIC; + } p += sprintf( p, "\n" ); p += sprintf( p, "\n" ); p += sprintf( p, "Error loading %s\n", p_args->file ); @@ -1589,29 +2670,75 @@ static int http_get( httpd_file_callback_args_t *p_args, p += sprintf( p, "\n" ); p += sprintf( p, "

Error loading %s for %s

\n", p_args->file, p_args->name ); p += sprintf( p, "
\n" ); - p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "VideoLAN\n" ); p += sprintf( p, "\n" ); p += sprintf( p, "\n" ); - *pi_data = strlen( *pp_data ) + 1; + *pi_data = strlen( *pp_data ); return VLC_SUCCESS; } - if( strcmp( p_args->mime, "text/html" ) ) + if( !p_args->b_html ) { FileLoad( f, pp_data, pi_data ); } else { int i_buffer; - uint8_t *p_buffer; - uint8_t *dst; + char *p_buffer; + char *dst; + vlc_value_t val; + char position[4]; /* percentage */ + char time[12]; /* in seconds */ + char length[12]; /* in seconds */ + audio_volume_t i_volume; + char volume[5]; + char state[8]; + +#define p_sys p_args->p_intf->p_sys + if( p_sys->p_input ) + { + var_Get( p_sys->p_input, "position", &val); + sprintf( position, "%d" , (int)((val.f_float) * 100.0)); + var_Get( p_sys->p_input, "time", &val); + sprintf( time, "%d" , (int)(val.i_time / 1000000) ); + var_Get( p_sys->p_input, "length", &val); + sprintf( length, "%d" , (int)(val.i_time / 1000000) ); + + var_Get( p_sys->p_input, "state", &val ); + if( val.i_int == PLAYING_S ) + { + sprintf( state, "playing" ); + } else if( val.i_int == PAUSE_S ) + { + sprintf( state, "paused" ); + } else + { + sprintf( state, "stop" ); + } + } else + { + sprintf( position, "%d", 0 ); + sprintf( time, "%d", 0 ); + sprintf( length, "%d", 0 ); + sprintf( state, "stop" ); + } +#undef p_sys + + aout_VolumeGet( p_args->p_intf , &i_volume ); + sprintf( volume , "%d" , (int)i_volume ); p_args->vars = mvar_New( "variables", "" ); mvar_AppendNewVar( p_args->vars, "url_param", i_request > 0 ? "1" : "0" ); + mvar_AppendNewVar( p_args->vars, "url_value", p_request ); mvar_AppendNewVar( p_args->vars, "version", VERSION_MESSAGE ); mvar_AppendNewVar( p_args->vars, "copyright", COPYRIGHT_MESSAGE ); + mvar_AppendNewVar( p_args->vars, "stream_position", position ); + mvar_AppendNewVar( p_args->vars, "stream_time", time ); + mvar_AppendNewVar( p_args->vars, "stream_length", length ); + mvar_AppendNewVar( p_args->vars, "volume", volume ); + mvar_AppendNewVar( p_args->vars, "stream_state", state ); SSInit( &p_args->stack ); @@ -1625,11 +2752,12 @@ static int http_get( httpd_file_callback_args_t *p_args, /* we parse executing all macros */ Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, &p_buffer[0], &p_buffer[i_buffer] ); - *dst++ = '\0'; + *dst = '\0'; *pi_data = dst - *pp_data; SSClean( &p_args->stack ); mvar_Delete( p_args->vars ); + free( p_buffer ); } fclose( f ); @@ -1640,12 +2768,35 @@ static int http_get( httpd_file_callback_args_t *p_args, /**************************************************************************** * uri parser ****************************************************************************/ -static void uri_extract_value( char *psz_uri, char *psz_name, - char *psz_value, int i_value_max ) +static int uri_test_param( char *psz_uri, const char *psz_name ) { - char *p; + char *p = psz_uri; + + while( (p = strstr( p, psz_name )) ) + { + /* Verify that we are dealing with a post/get argument */ + if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' ) + { + return VLC_TRUE; + } + p++; + } + + return VLC_FALSE; +} +static char *uri_extract_value( char *psz_uri, const char *psz_name, + char *psz_value, int i_value_max ) +{ + char *p = psz_uri; + + while( (p = strstr( p, psz_name )) ) + { + /* Verify that we are dealing with a post/get argument */ + if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' ) + break; + p++; + } - p = strstr( psz_uri, psz_name ); if( p ) { int i_len; @@ -1680,11 +2831,14 @@ static void uri_extract_value( char *psz_uri, char *psz_name, { strncpy( psz_value, "", i_value_max ); } + p += i_len; } else { strncpy( psz_value, "", i_value_max ); } + + return p; } static void uri_decode_url_encoded( char *psz ) @@ -1726,12 +2880,12 @@ static void uri_decode_url_encoded( char *psz ) /**************************************************************************** * Light RPN evaluator ****************************************************************************/ -static void SSInit( stack_t *st ) +static void SSInit( rpn_stack_t *st ) { st->i_stack = 0; } -static void SSClean( stack_t *st ) +static void SSClean( rpn_stack_t *st ) { while( st->i_stack > 0 ) { @@ -1739,7 +2893,7 @@ static void SSClean( stack_t *st ) } } -static void SSPush( stack_t *st, char *s ) +static void SSPush( rpn_stack_t *st, char *s ) { if( st->i_stack < STACK_MAX ) { @@ -1747,7 +2901,7 @@ static void SSPush( stack_t *st, char *s ) } } -static char * SSPop( stack_t *st ) +static char * SSPop( rpn_stack_t *st ) { if( st->i_stack <= 0 ) { @@ -1759,7 +2913,7 @@ static char * SSPop( stack_t *st ) } } -static int SSPopN( stack_t *st, mvar_t *vars ) +static int SSPopN( rpn_stack_t *st, mvar_t *vars ) { char *name; char *value; @@ -1779,7 +2933,7 @@ static int SSPopN( stack_t *st, mvar_t *vars ) return( i ); } -static void SSPushN( stack_t *st, int i ) +static void SSPushN( rpn_stack_t *st, int i ) { char v[512]; @@ -1787,7 +2941,7 @@ static void SSPushN( stack_t *st, int i ) SSPush( st, v ); } -static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) +static void EvaluateRPN( mvar_t *vars, rpn_stack_t *st, char *exp ) { for( ;; ) { @@ -1803,11 +2957,13 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) { /* extract string */ p = &s[0]; + exp++; while( *exp && *exp != '\'' ) { *p++ = *exp++; } *p = '\0'; + exp++; SSPush( st, s ); continue; } @@ -1857,8 +3013,8 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) } else if( !strcmp( s, "-" ) ) { - int i = SSPopN( st, vars ); int j = SSPopN( st, vars ); + int i = SSPopN( st, vars ); SSPushN( st, i - j ); } else if( !strcmp( s, "*" ) ) @@ -1869,8 +3025,8 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) { int i, j; - i = SSPopN( st, vars ); j = SSPopN( st, vars ); + i = SSPopN( st, vars ); SSPushN( st, j != 0 ? i / j : 0 ); } @@ -1878,8 +3034,8 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) { int i, j; - i = SSPopN( st, vars ); j = SSPopN( st, vars ); + i = SSPopN( st, vars ); SSPushN( st, j != 0 ? i % j : 0 ); } @@ -1888,39 +3044,43 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) { SSPushN( st, SSPopN( st, vars ) == SSPopN( st, vars ) ? -1 : 0 ); } + else if( !strcmp( s, "!=" ) ) + { + SSPushN( st, SSPopN( st, vars ) != SSPopN( st, vars ) ? -1 : 0 ); + } else if( !strcmp( s, "<" ) ) { - int i = SSPopN( st, vars ); int j = SSPopN( st, vars ); + int i = SSPopN( st, vars ); SSPushN( st, i < j ? -1 : 0 ); } else if( !strcmp( s, ">" ) ) { - int i = SSPopN( st, vars ); int j = SSPopN( st, vars ); + int i = SSPopN( st, vars ); SSPushN( st, i > j ? -1 : 0 ); } else if( !strcmp( s, "<=" ) ) { - int i = SSPopN( st, vars ); int j = SSPopN( st, vars ); + int i = SSPopN( st, vars ); SSPushN( st, i <= j ? -1 : 0 ); } else if( !strcmp( s, ">=" ) ) { - int i = SSPopN( st, vars ); int j = SSPopN( st, vars ); + int i = SSPopN( st, vars ); SSPushN( st, i >= j ? -1 : 0 ); } /* 3. string functions */ else if( !strcmp( s, "strcat" ) ) { - char *s1 = SSPop( st ); char *s2 = SSPop( st ); + char *s1 = SSPop( st ); char *str = malloc( strlen( s1 ) + strlen( s2 ) + 1 ); strcpy( str, s1 ); @@ -1933,14 +3093,50 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) } else if( !strcmp( s, "strcmp" ) ) { - char *s1 = SSPop( st ); char *s2 = SSPop( st ); + char *s1 = SSPop( st ); SSPushN( st, strcmp( s1, s2 ) ); free( s1 ); free( s2 ); } - else if( !strcmp( s, "strlen" ) ) + else if( !strcmp( s, "strncmp" ) ) + { + int n = SSPopN( st, vars ); + char *s2 = SSPop( st ); + char *s1 = SSPop( st ); + + SSPushN( st, strncmp( s1, s2 , n ) ); + free( s1 ); + free( s2 ); + } + else if( !strcmp( s, "strsub" ) ) + { + int n = SSPopN( st, vars ); + int m = SSPopN( st, vars ); + int i_len; + char *s = SSPop( st ); + char *str; + + if( n >= m ) + { + i_len = n - m + 1; + } + else + { + i_len = 0; + } + + str = malloc( i_len + 1 ); + + memcpy( str, s + m - 1, i_len ); + str[ i_len ] = '\0'; + + SSPush( st, str ); + free( s ); + free( str ); + } + else if( !strcmp( s, "strlen" ) ) { char *str = SSPop( st ); @@ -1977,8 +3173,8 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) } else if( !strcmp( s, "store" ) ) { - char *name = SSPop( st ); char *value = SSPop( st ); + char *name = SSPop( st ); mvar_PushNewVar( vars, name, value ); free( name ); @@ -1993,6 +3189,16 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) free( name ); } + else if( !strcmp( s, "url_extract" ) ) + { + char *url = mvar_GetValue( vars, "url_value" ); + char *name = SSPop( st ); + char value[512]; + + uri_extract_value( url, name, value, 512 ); + uri_decode_url_encoded( value ); + SSPush( st, value ); + } else { SSPush( st, s ); @@ -2000,5 +3206,207 @@ static void EvaluateRPN( mvar_t *vars, stack_t *st, char *exp ) } } +/********************************************************************** + * Find_end_MRL: Find the end of the sentence : + * this function parses the string psz and find the end of the item + * and/or option with detecting the " and ' problems. + * returns NULL if an error is detected, otherwise, returns a pointer + * of the end of the sentence (after the last character) + **********************************************************************/ +static char *Find_end_MRL( char *psz ) +{ + char *s_sent = psz; + + switch( *s_sent ) + { + case '\"': + { + s_sent++; + + while( ( *s_sent != '\"' ) && ( *s_sent != '\0' ) ) + { + if( *s_sent == '\'' ) + { + s_sent = Find_end_MRL( s_sent ); + + if( s_sent == NULL ) + { + return NULL; + } + } else + { + s_sent++; + } + } + + if( *s_sent == '\"' ) + { + s_sent++; + return s_sent; + } else /* *s_sent == '\0' , which means the number of " is incorrect */ + { + return NULL; + } + break; + } + case '\'': + { + s_sent++; + + while( ( *s_sent != '\'' ) && ( *s_sent != '\0' ) ) + { + if( *s_sent == '\"' ) + { + s_sent = Find_end_MRL( s_sent ); + + if( s_sent == NULL ) + { + return NULL; + } + } else + { + s_sent++; + } + } + + if( *s_sent == '\'' ) + { + s_sent++; + return s_sent; + } else /* *s_sent == '\0' , which means the number of ' is incorrect */ + { + return NULL; + } + break; + } + default: /* now we can look for spaces */ + { + while( ( *s_sent != ' ' ) && ( *s_sent != '\0' ) ) + { + if( ( *s_sent == '\'' ) || ( *s_sent == '\"' ) ) + { + s_sent = Find_end_MRL( s_sent ); + } else + { + s_sent++; + } + } + return s_sent; + } + } +} + +/********************************************************************** + * parse_MRL: parse the MRL, find the mrl string and the options, + * create an item with all information in it, and return the item. + * return NULL if there is an error. + **********************************************************************/ +static playlist_item_t *parse_MRL( intf_thread_t *p_intf, char *psz ) +{ + char **ppsz_options = NULL; + char *mrl; + char *s_mrl = psz; + int i_error = 0; + char *s_temp; + int i = 0; + int i_options = 0; + playlist_item_t * p_item = NULL; + + /* In case there is spaces before the mrl */ + while( ( *s_mrl == ' ' ) && ( *s_mrl != '\0' ) ) + { + s_mrl++; + } + + /* extract the mrl */ + s_temp = strstr( s_mrl , " :" ); + if( s_temp == NULL ) + { + s_temp = s_mrl + strlen( s_mrl ); + } else + { + while( (*s_temp == ' ') && (s_temp != s_mrl ) ) + { + s_temp--; + } + s_temp++; + } + + /* if the mrl is between " or ', we must remove them */ + if( (*s_mrl == '\'') || (*s_mrl == '\"') ) + { + mrl = (char *)malloc( (s_temp - s_mrl - 1) * sizeof( char ) ); + strncpy( mrl , (s_mrl + 1) , s_temp - s_mrl - 2 ); + mrl[ s_temp - s_mrl - 2 ] = '\0'; + } else + { + mrl = (char *)malloc( (s_temp - s_mrl + 1) * sizeof( char ) ); + strncpy( mrl , s_mrl , s_temp - s_mrl ); + mrl[ s_temp - s_mrl ] = '\0'; + } + + s_mrl = s_temp; + /* now we can take care of the options */ + while( (*s_mrl != '\0') && (i_error == 0) ) + { + switch( *s_mrl ) + { + case ' ': + { + s_mrl++; + break; + } + case ':': /* an option */ + { + s_temp = Find_end_MRL( s_mrl ); + + if( s_temp == NULL ) + { + i_error = 1; + } + else + { + i_options++; + ppsz_options = realloc( ppsz_options , i_options * + sizeof(char *) ); + ppsz_options[ i_options - 1 ] = + malloc( (s_temp - s_mrl + 1) * sizeof(char) ); + + strncpy( ppsz_options[ i_options - 1 ] , s_mrl , + s_temp - s_mrl ); + + /* don't forget to finish the string with a '\0' */ + (ppsz_options[ i_options - 1 ])[ s_temp - s_mrl ] = '\0'; + + s_mrl = s_temp; + } + break; + } + default: + { + i_error = 1; + break; + } + } + } + + if( i_error != 0 ) + { + free( mrl ); + } + else + { + /* now create an item */ + p_item = playlist_ItemNew( p_intf, mrl, mrl); + for( i = 0 ; i< i_options ; i++ ) + { + playlist_ItemAddOption( p_item, ppsz_options[i] ); + } + } + for( i = 0; i < i_options; i++ ) free( ppsz_options[i] ); + if( i_options ) free( ppsz_options ); + + return p_item; +}