X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcontrol%2Fhttp.c;h=82842c646339eef2a4a77b574199acf74fdd1f8f;hb=bb137a2792b665978c3b836778fbd4ec1c61c975;hp=619addbcc98719e57a7d5119d2dcc554340db375;hpb=58e25ba45e0d05ca9fd88606953814fb23d3b0eb;p=vlc diff --git a/modules/control/http.c b/modules/control/http.c index 619addbcc9..82842c6463 100644 --- a/modules/control/http.c +++ b/modules/control/http.c @@ -6,6 +6,7 @@ * * Authors: Gildas Bazin * Laurent Aimar + * Christophe Massiot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,12 +26,8 @@ /***************************************************************************** * Preamble *****************************************************************************/ -/* TODO: - * - clean up ? - * - doc ! (mouarf ;) - */ - #include +#include #include #include @@ -79,6 +76,9 @@ static void Close( vlc_object_t * ); "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 CHARSET_TEXT N_( "Charset" ) +#define CHARSET_LONGTEXT N_( \ + "Charset declared in Content-Type header (default UTF-8)." ) #define CERT_TEXT N_( "Certificate file" ) #define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \ "(enables SSL)" ) @@ -97,6 +97,7 @@ vlc_module_begin(); 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 ); + add_string ( "http-charset", "UTF-8", NULL, CHARSET_TEXT, CHARSET_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 ); @@ -115,6 +116,7 @@ static void Run ( intf_thread_t *p_intf ); static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir ); +#if !defined(SYS_DARWIN) && !defined(SYS_BEOS) && !defined(WIN32) static int DirectoryCheck( char *psz_dir ) { DIR *p_dir; @@ -136,6 +138,7 @@ static int DirectoryCheck( char *psz_dir ) return VLC_SUCCESS; } +#endif static int HttpCallback( httpd_file_sys_t *p_args, httpd_file_t *, @@ -148,8 +151,7 @@ static int uri_test_param( char *psz_uri, const char *psz_name ); 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 ); +static playlist_item_t *parse_MRL( intf_thread_t *, char *psz, char *psz_name ); /***************************************************************************** * @@ -198,6 +200,7 @@ struct intf_sys_t input_thread_t *p_input; vlm_t *p_vlm; char *psz_html_type; + vlc_iconv_t iconv_from_utf8, iconv_to_utf8; }; @@ -240,12 +243,13 @@ static int Open( vlc_object_t *p_this ) p_sys->p_vlm = NULL; /* determine Content-Type value for HTML pages */ - vlc_current_charset(&psz_src); - if( psz_src == NULL ) + psz_src = config_GetPsz( p_intf, "http-charset" ); + if( psz_src == NULL || !*psz_src ) { - free( p_sys ); - return VLC_ENOMEM; + if( psz_src != NULL ) free( psz_src ); + psz_src = strdup("UTF-8"); } + p_sys->psz_html_type = malloc( 20 + strlen( psz_src ) ); if( p_sys->psz_html_type == NULL ) { @@ -254,13 +258,35 @@ static int Open( vlc_object_t *p_this ) return VLC_ENOMEM ; } sprintf( p_sys->psz_html_type, "text/html; charset=%s", psz_src ); + msg_Dbg( p_intf, "using charset=%s", psz_src ); + + if( strcmp( psz_src, "UTF-8" ) ) + { + p_sys->iconv_from_utf8 = vlc_iconv_open( psz_src, "UTF-8" ); + if( p_sys->iconv_from_utf8 == (vlc_iconv_t)-1 ) + msg_Warn( p_intf, "unable to perform charset conversion to %s", + psz_src ); + else + { + p_sys->iconv_to_utf8 = vlc_iconv_open( "UTF-8", psz_src ); + if( p_sys->iconv_to_utf8 == (vlc_iconv_t)-1 ) + msg_Warn( p_intf, + "unable to perform charset conversion from %s", + psz_src ); + } + } + else + { + p_sys->iconv_from_utf8 = p_sys->iconv_to_utf8 = (vlc_iconv_t)-1; + } + free( psz_src ); /* 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)", + msg_Dbg( p_intf, "enabling 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" ); @@ -357,15 +383,19 @@ failed: free( p_sys->pp_files ); } httpd_HostDelete( p_sys->p_httpd_host ); - free( p_sys->psz_html_type ); + free( p_sys->psz_html_type ); + if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 ) + vlc_iconv_close( p_sys->iconv_from_utf8 ); + if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 ) + vlc_iconv_close( p_sys->iconv_to_utf8 ); free( p_sys ); return VLC_EGENERIC; } /***************************************************************************** - * CloseIntf: destroy interface + * Close: destroy interface *****************************************************************************/ -void Close ( vlc_object_t *p_this ) +static void Close ( vlc_object_t *p_this ) { intf_thread_t *p_intf = (intf_thread_t *)p_this; intf_sys_t *p_sys = p_intf->p_sys; @@ -393,8 +423,12 @@ void Close ( vlc_object_t *p_this ) free( p_sys->pp_files ); } httpd_HostDelete( p_sys->p_httpd_host ); - free( p_sys->psz_html_type ); + + if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 ) + vlc_iconv_close( p_sys->iconv_from_utf8 ); + if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 ) + vlc_iconv_close( p_sys->iconv_to_utf8 ); free( p_sys ); } @@ -452,7 +486,7 @@ static void Run( intf_thread_t *p_intf ) /***************************************************************************** * Local functions *****************************************************************************/ -#define MAX_DIR_SIZE 10240 +#define MAX_DIR_SIZE 2560 /**************************************************************************** * FileToUrl: create a good name for an url from filename @@ -506,6 +540,66 @@ static char *FileToUrl( char *name, vlc_bool_t *pb_index ) return url; } +static char *FromUTF8( intf_thread_t *p_intf, char *psz_utf8 ) +{ + intf_sys_t *p_sys = p_intf->p_sys; + + if ( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 ) + { + char *psz_in = psz_utf8; + size_t i_in = strlen(psz_in); + size_t i_out = i_in * 2; + char *psz_local = malloc(i_out + 1); + char *psz_out = psz_local; + + size_t i_ret = vlc_iconv( p_sys->iconv_from_utf8, &psz_in, &i_in, + &psz_out, &i_out ); + if( i_ret == (size_t)-1 || i_in ) + { + msg_Warn( p_intf, + "failed to convert \"%s\" to desired charset (%s)", + psz_utf8, strerror(errno) ); + free( psz_local ); + return strdup( psz_utf8 ); + } + + *psz_out = '\0'; + return psz_local; + } + else + return strdup( psz_utf8 ); +} + +static char *ToUTF8( intf_thread_t *p_intf, char *psz_local ) +{ + intf_sys_t *p_sys = p_intf->p_sys; + + if ( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 ) + { + char *psz_in = psz_local; + size_t i_in = strlen(psz_in); + size_t i_out = i_in * 6; + char *psz_utf8 = malloc(i_out + 1); + char *psz_out = psz_utf8; + + size_t i_ret = vlc_iconv( p_sys->iconv_to_utf8, &psz_in, &i_in, + &psz_out, &i_out ); + if( i_ret == (size_t)-1 || i_in ) + { + msg_Warn( p_intf, + "failed to convert \"%s\" to desired charset (%s)", + psz_local, strerror(errno) ); + free( psz_utf8 ); + return strdup( psz_local ); + } + + *psz_out = '\0'; + return psz_utf8; + } + else + return strdup( psz_local ); +} + /**************************************************************************** * ParseDirectory: parse recursively a directory, adding each file ****************************************************************************/ @@ -602,19 +696,25 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, 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 ) ) { httpd_file_sys_t *f = malloc( sizeof( httpd_file_sys_t ) ); vlc_bool_t b_index; + char *psz_tmp; 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 )], &b_index ); + psz_tmp = vlc_fix_readdir_charset( p_intf, dir ); + f->file = FromUTF8( p_intf, psz_tmp ); + free( psz_tmp ); + psz_tmp = vlc_fix_readdir_charset( p_intf, + &dir[strlen( psz_root )] ); + f->name = FileToUrl( psz_tmp, &b_index ); + free( psz_tmp ); f->b_html = strstr( &dir[strlen( psz_root )], ".htm" ) ? VLC_TRUE : VLC_FALSE; if( !f->name ) @@ -683,7 +783,7 @@ static int ParseDirectory( intf_thread_t *p_intf, char *psz_root, * var and set handling ****************************************************************************/ -static mvar_t *mvar_New( char *name, char *value ) +static mvar_t *mvar_New( const char *name, const char *value ) { mvar_t *v = malloc( sizeof( mvar_t ) ); @@ -720,7 +820,7 @@ static void mvar_AppendVar( mvar_t *v, mvar_t *f ) v->i_field++; } -static mvar_t *mvar_Duplicate( mvar_t *v ) +static mvar_t *mvar_Duplicate( const mvar_t *v ) { int i; mvar_t *n; @@ -768,7 +868,7 @@ static void mvar_RemoveVar( mvar_t *v, mvar_t *f ) /* FIXME should do a realloc */ } -static mvar_t *mvar_GetVar( mvar_t *s, char *name ) +static mvar_t *mvar_GetVar( mvar_t *s, const char *name ) { int i; char base[512], *field, *p; @@ -849,13 +949,15 @@ static char *mvar_GetValue( mvar_t *v, char *field ) } } -static void mvar_PushNewVar( mvar_t *vars, char *name, char *value ) +static void mvar_PushNewVar( mvar_t *vars, const char *name, + const char *value ) { mvar_t *f = mvar_New( name, value ); mvar_PushVar( vars, f ); } -static void mvar_AppendNewVar( mvar_t *vars, char *name, char *value ) +static void mvar_AppendNewVar( mvar_t *vars, const char *name, + const char *value ) { mvar_t *f = mvar_New( name, value ); mvar_AppendVar( vars, f ); @@ -863,7 +965,7 @@ static void mvar_AppendNewVar( mvar_t *vars, char *name, char *value ) /* arg= start[:stop[:step]],.. */ -static mvar_t *mvar_IntegerSetNew( char *name, char *arg ) +static mvar_t *mvar_IntegerSetNew( const char *name, const char *arg ) { char *dup = strdup( arg ); char *str = dup; @@ -925,14 +1027,16 @@ 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 ) +static void PlaylistListNode( intf_thread_t *p_intf, 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) + if( p_node->i_children == -1 ) { char value[512]; + char *psz; mvar_t *itm = mvar_New( name, "set" ); sprintf( value, "%d", ( p_pl->status.p_item == p_node )? 1 : 0 ); @@ -941,9 +1045,13 @@ void PlaylistListNode( playlist_t *p_pl, playlist_item_t *p_node, sprintf( value, "%d", p_node->input.i_id ); mvar_AppendNewVar( itm, "index", value ); - mvar_AppendNewVar( itm, "name", p_node->input.psz_name ); + psz = FromUTF8( p_intf, p_node->input.psz_name ); + mvar_AppendNewVar( itm, "name", psz ); + free( psz ); - mvar_AppendNewVar( itm, "uri", p_node->input.psz_uri ); + psz = FromUTF8( p_intf, p_node->input.psz_uri ); + mvar_AppendNewVar( itm, "uri", psz ); + free( psz ); sprintf( value, "Item"); mvar_AppendNewVar( itm, "type", value ); @@ -956,11 +1064,14 @@ void PlaylistListNode( playlist_t *p_pl, playlist_item_t *p_node, else { char value[512]; + char *psz; 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 ); + psz = FromUTF8( p_intf, p_node->input.psz_name ); + mvar_AppendNewVar( itm, "name", psz ); + mvar_AppendNewVar( itm, "uri", psz ); + free( psz ); sprintf( value, "Node" ); mvar_AppendNewVar( itm, "type", value ); @@ -977,13 +1088,15 @@ void PlaylistListNode( playlist_t *p_pl, playlist_item_t *p_node, 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); + PlaylistListNode( p_intf, p_pl, p_node->pp_children[i_child], + name, s, i_depth + 1); } } } -static mvar_t *mvar_PlaylistSetNew( char *name, playlist_t *p_pl ) +static mvar_t *mvar_PlaylistSetNew( intf_thread_t *p_intf, char *name, + playlist_t *p_pl ) { playlist_view_t *p_view; mvar_t *s = mvar_New( name, "set" ); @@ -994,14 +1107,15 @@ static mvar_t *mvar_PlaylistSetNew( char *name, playlist_t *p_pl ) p_view = playlist_ViewFind( p_pl, VIEW_CATEGORY ); /* FIXME */ if( p_view != NULL ) - PlaylistListNode( p_pl, p_view->p_root, name, s, 0 ); + PlaylistListNode( p_intf, p_pl, p_view->p_root, name, s, 0 ); vlc_mutex_unlock( &p_pl->object_lock ); return s; } -static mvar_t *mvar_InfoSetNew( char *name, input_thread_t *p_input ) +static mvar_t *mvar_InfoSetNew( intf_thread_t *p_intf, char *name, + input_thread_t *p_input ) { mvar_t *s = mvar_New( name, "set" ); int i, j; @@ -1015,21 +1129,29 @@ static mvar_t *mvar_InfoSetNew( char *name, input_thread_t *p_input ) 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]; + char *psz; + mvar_t *cat = mvar_New( name, "set" ); mvar_t *iset = mvar_New( "info", "set" ); - mvar_AppendNewVar( cat, "name", p_category->psz_name ); + psz = FromUTF8( p_intf, p_category->psz_name ); + mvar_AppendNewVar( cat, "name", psz ); + free( psz ); mvar_AppendVar( cat, iset ); for ( j = 0; j < p_category->i_infos; j++ ) { info_t *p_info = p_category->pp_infos[j]; mvar_t *info = mvar_New( "info", "" ); + char *psz_name = FromUTF8( p_intf, p_info->psz_name ); + char *psz_value = FromUTF8( p_intf, 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 ); + psz_name, psz_value ); + mvar_AppendNewVar( info, "name", psz_name ); + mvar_AppendNewVar( info, "value", psz_value ); + free( psz_name ); + free( psz_value ); mvar_AppendVar( iset, info ); } mvar_AppendVar( s, cat ); @@ -1038,6 +1160,110 @@ static mvar_t *mvar_InfoSetNew( char *name, input_thread_t *p_input ) return s; } + +static mvar_t *mvar_InputVarSetNew( intf_thread_t *p_intf, char *name, + input_thread_t *p_input, + const char *psz_variable ) +{ + intf_sys_t *p_sys = p_intf->p_sys; + mvar_t *s = mvar_New( name, "set" ); + vlc_value_t val, val_list, text_list; + int i_type, i; + + if( p_input == NULL ) + { + return s; + } + + /* Check the type of the object variable */ + i_type = var_Type( p_sys->p_input, psz_variable ); + + /* Make sure we want to display the variable */ + if( i_type & VLC_VAR_HASCHOICE ) + { + var_Change( p_sys->p_input, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL ); + if( val.i_int == 0 ) return s; + if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 ) + return s; + } + else + { + return s; + } + + switch( i_type & VLC_VAR_TYPE ) + { + case VLC_VAR_VOID: + case VLC_VAR_BOOL: + case VLC_VAR_VARIABLE: + case VLC_VAR_STRING: + case VLC_VAR_INTEGER: + break; + default: + /* Variable doesn't exist or isn't handled */ + return s; + } + + if( var_Get( p_sys->p_input, psz_variable, &val ) < 0 ) + { + return s; + } + + if( var_Change( p_sys->p_input, psz_variable, VLC_VAR_GETLIST, + &val_list, &text_list ) < 0 ) + { + if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string ); + return s; + } + + for( i = 0; i < val_list.p_list->i_count; i++ ) + { + char *psz, psz_int[16]; + mvar_t *itm; + + switch( i_type & VLC_VAR_TYPE ) + { + case VLC_VAR_STRING: + itm = mvar_New( name, "set" ); + psz = FromUTF8( p_intf, text_list.p_list->p_values[i].psz_string ); + mvar_AppendNewVar( itm, "name", psz ); + psz = FromUTF8( p_intf, val_list.p_list->p_values[i].psz_string ); + mvar_AppendNewVar( itm, "id", psz ); + free( psz ); + snprintf( psz_int, sizeof(psz_int), "%d", + ( !strcmp( val.psz_string, + val_list.p_list->p_values[i].psz_string ) + && !( i_type & VLC_VAR_ISCOMMAND ) ) ); + mvar_AppendNewVar( itm, "selected", psz_int ); + mvar_AppendVar( s, itm ); + break; + + case VLC_VAR_INTEGER: + itm = mvar_New( name, "set" ); + psz = FromUTF8( p_intf, text_list.p_list->p_values[i].psz_string ); + mvar_AppendNewVar( itm, "name", psz ); + snprintf( psz_int, sizeof(psz_int), "%d", + val_list.p_list->p_values[i].i_int ); + mvar_AppendNewVar( itm, "id", psz_int ); + snprintf( psz_int, sizeof(psz_int), "%d", + ( val.i_int == val_list.p_list->p_values[i].i_int ) + && !( i_type & VLC_VAR_ISCOMMAND ) ); + mvar_AppendNewVar( itm, "selected", psz_int ); + mvar_AppendVar( s, itm ); + break; + + default: + break; + } + } + + /* clean up everything */ + if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string ); + var_Change( p_sys->p_input, psz_variable, VLC_VAR_FREELIST, &val_list, + &text_list ); + return s; +} + #if 0 static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type ) { @@ -1080,15 +1306,28 @@ static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type ) } #endif -static mvar_t *mvar_FileSetNew( char *name, char *psz_dir ) +/* Utility function for scandir */ +static int Filter( const struct dirent *foo ) +{ + return VLC_TRUE; +} + +static int InsensitiveAlphasort( const struct dirent **foo1, + const struct dirent **foo2 ) +{ + return strcasecmp( (*foo1)->d_name, (*foo2)->d_name ); +} + +static mvar_t *mvar_FileSetNew( intf_thread_t *p_intf, char *name, + char *psz_dir ) { mvar_t *s = mvar_New( name, "set" ); - char tmp[MAX_DIR_SIZE], *p, *src; + char tmp[MAX_DIR_SIZE], dir[MAX_DIR_SIZE], *p, *src; #ifdef HAVE_SYS_STAT_H struct stat stat_info; #endif - DIR *p_dir; - struct dirent *p_dir_content; + struct dirent **pp_dir_content; + int i_dir_content, i; char sep; /* convert all / to native separator */ @@ -1126,6 +1365,15 @@ static mvar_t *mvar_FileSetNew( char *name, char *psz_dir ) { return s; } + + if( *psz_dir == '~' ) + { + /* This is incomplete : we should also support the ~cmassiot/ syntax. */ + snprintf( dir, sizeof(dir), "%s/%s", p_intf->p_vlc->psz_homedir, + psz_dir + 1 ); + psz_dir = dir; + } + /* first fix all .. dir */ p = src = psz_dir; while( *src ) @@ -1166,7 +1414,6 @@ static mvar_t *mvar_FileSetNew( char *name, char *psz_dir ) } *p = '\0'; - #ifdef HAVE_SYS_STAT_H if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) ) { @@ -1174,34 +1421,35 @@ static mvar_t *mvar_FileSetNew( char *name, char *psz_dir ) } #endif - if( ( p_dir = opendir( psz_dir ) ) == NULL ) + /* parse psz_src dir */ + if( ( i_dir_content = scandir( psz_dir, &pp_dir_content, Filter, + InsensitiveAlphasort ) ) == -1 ) { - fprintf( stderr, "cannot open dir (%s)", psz_dir ); + msg_Warn( p_intf, "scandir error on %s (%s)", psz_dir, + strerror(errno) ); return s; } - /* remove traling / or \ */ + /* remove trailing / or \ */ for( p = &psz_dir[strlen( psz_dir) - 1]; p >= psz_dir && ( *p =='/' || *p =='\\' ); p-- ) { *p = '\0'; } - for( ;; ) + for( i = 0; i < i_dir_content; i++ ) { + struct dirent *p_dir_content = pp_dir_content[i]; mvar_t *f; + const char *psz_ext; + char *psz_name, *psz_tmp; - /* 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 ); + snprintf( tmp, sizeof(tmp), "%s/%s", psz_dir, p_dir_content->d_name ); #ifdef HAVE_SYS_STAT_H if( stat( tmp, &stat_info ) == -1 ) @@ -1210,7 +1458,19 @@ static mvar_t *mvar_FileSetNew( char *name, char *psz_dir ) } #endif f = mvar_New( name, "set" ); + + psz_tmp = vlc_fix_readdir_charset( p_intf, p_dir_content->d_name ); + psz_name = FromUTF8( p_intf, psz_tmp ); + free( psz_tmp ); + snprintf( tmp, sizeof(tmp), "%s/%s", psz_dir, psz_name ); mvar_AppendNewVar( f, "name", tmp ); + mvar_AppendNewVar( f, "basename", psz_name ); + + /* put file extension in 'ext' */ + psz_ext = strrchr( psz_name, '.' ); + mvar_AppendNewVar( f, "ext", psz_ext != NULL ? psz_ext + 1 : "" ); + + free( psz_name ); #ifdef HAVE_SYS_STAT_H if( S_ISDIR( stat_info.st_mode ) ) @@ -1325,7 +1585,8 @@ static mvar_t *mvar_VlmSetNew( char *name, vlm_t *vlm ) static void SSInit( rpn_stack_t * ); static void SSClean( rpn_stack_t * ); -static void EvaluateRPN( mvar_t *, rpn_stack_t *, char * ); +static void EvaluateRPN( intf_thread_t *p_intf, mvar_t *, rpn_stack_t *, + char * ); static void SSPush ( rpn_stack_t *, char * ); static char *SSPop ( rpn_stack_t * ); @@ -1344,6 +1605,184 @@ typedef struct char *param2; } macro_t; +static void Seek( intf_thread_t *p_intf, char *p_value ) +{ + intf_sys_t *p_sys = p_intf->p_sys; + vlc_value_t val; + 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 ) + { + 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 +} + static int FileLoad( FILE *f, char **pp_data, int *pi_data ) { int i_read; @@ -1484,6 +1923,7 @@ enum macroType MVLC_VLM_LOAD, MVLC_VLM_SAVE, + MVLC_INCLUDE, MVLC_FOREACH, MVLC_IF, MVLC_RPN, @@ -1541,16 +1981,17 @@ StrToMacroTypeTab [] = { "vlm_save", MVLC_VLM_SAVE }, { "rpn", MVLC_RPN }, - { "stack", MVLC_STACK }, + { "stack", MVLC_STACK }, + { "include", MVLC_INCLUDE }, { "foreach", MVLC_FOREACH }, { "value", MVLC_VALUE }, { "if", MVLC_IF }, { "else", MVLC_ELSE }, { "end", MVLC_END }, - { "get", MVLC_GET }, - { "set", MVLC_SET }, + { "get", MVLC_GET }, + { "set", MVLC_SET }, { "int", MVLC_INT }, { "float", MVLC_FLOAT }, { "string", MVLC_STRING }, @@ -1641,7 +2082,7 @@ static void MacroDo( httpd_file_sys_t *p_args, break; } case MVLC_STOP: - playlist_Control( p_sys->p_playlist, PLAYLIST_STOP); + playlist_Control( p_sys->p_playlist, PLAYLIST_STOP ); msg_Dbg( p_intf, "requested playlist stop" ); break; case MVLC_PAUSE: @@ -1653,11 +2094,10 @@ static void MacroDo( httpd_file_sys_t *p_args, msg_Dbg( p_intf, "requested playlist next" ); break; case MVLC_PREVIOUS: - playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1); - msg_Dbg( p_intf, "requested playlist next" ); + playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1 ); + msg_Dbg( p_intf, "requested playlist previous" ); break; case MVLC_FULLSCREEN: - { if( p_sys->p_input ) { vout_thread_t *p_vout; @@ -1671,188 +2111,13 @@ static void MacroDo( httpd_file_sys_t *p_args, msg_Dbg( p_intf, "requested fullscreen toggle" ); } } - } - break; + 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 + uri_extract_value( p_request, "seek_value", value, 30 ); + uri_decode_url_encoded( value ); + Seek( p_intf, value ); break; } case MVLC_VOLUME: @@ -1872,34 +2137,37 @@ static void MacroDo( httpd_file_sys_t *p_args, { aout_VolumeSet( p_intf , AOUT_VOLUME_MAX ); msg_Dbg( p_intf, "requested volume set: max" ); - } else + } + 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] == '-' ) + } + 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 + } + 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 ) + } + 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); + if( (i_value <= 400) && (i_value>=0) ){ + aout_VolumeSet( p_intf, (i_value * (AOUT_VOLUME_MAX - AOUT_VOLUME_MIN))/400+AOUT_VOLUME_MIN); msg_Dbg( p_intf, "requested volume set: %i%%", atoi( vol )); } - } else - { + } + else + { i_value = atoi( vol ); if( ( i_value <= AOUT_VOLUME_MAX ) && ( i_value >= AOUT_VOLUME_MIN ) ) { @@ -1913,20 +2181,27 @@ static void MacroDo( httpd_file_sys_t *p_args, /* playlist management */ case MVLC_ADD: { - char mrl[512]; - playlist_item_t * p_item; + char mrl[1024], psz_name[1024]; + playlist_item_t *p_item; - uri_extract_value( p_request, "mrl", mrl, 512 ); + uri_extract_value( p_request, "mrl", mrl, 1024 ); uri_decode_url_encoded( mrl ); - p_item = parse_MRL( p_intf, mrl ); + uri_extract_value( p_request, "name", psz_name, 1024 ); + uri_decode_url_encoded( psz_name ); + if( !*psz_name ) + { + memcpy( psz_name, mrl, 1024 ); + } + p_item = parse_MRL( p_intf, mrl, psz_name ); if( !p_item || !p_item->input.psz_uri || !*p_item->input.psz_uri ) { msg_Dbg( p_intf, "invalid requested mrl: %s", mrl ); - } else + } + else { - playlist_AddItem( p_sys->p_playlist , p_item , + playlist_AddItem( p_sys->p_playlist, p_item, PLAYLIST_APPEND, PLAYLIST_END ); msg_Dbg( p_intf, "requested mrl add: %s", mrl ); } @@ -2033,14 +2308,16 @@ static void MacroDo( httpd_file_sys_t *p_args, 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" ) ) + } + 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" ) ) + } + 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, @@ -2064,7 +2341,9 @@ static void MacroDo( httpd_file_sys_t *p_args, if ( i_pos < i_newpos ) { playlist_Move( p_sys->p_playlist, i_pos, i_newpos + 1 ); - } else { + } + 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); @@ -2242,7 +2521,7 @@ static void MacroDo( httpd_file_sys_t *p_args, default: if( *control ) { - PRINTS( "", control ); + PRINTS( "", control ); } break; } @@ -2305,16 +2584,22 @@ static void MacroDo( httpd_file_sys_t *p_args, break; case MVLC_STRING: psz = config_GetPsz( p_intf, m->param1 ); - snprintf( value, sizeof( value ), "%s", psz ? psz : "" ); - if( psz ) free( psz ); + if( psz != NULL ) + { + strncpy( value, psz,sizeof( value ) ); + free( psz ); + value[sizeof( value ) - 1] = '\0'; + } + else + *value = '\0'; + msg_Dbg( p_intf, "%d: value = \"%s\"", __LINE__, value ); break; default: snprintf( value, sizeof( value ), "invalid type(%s) in set", m->param2 ); + value[sizeof( value ) - 1] = '\0'; 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; } @@ -2324,7 +2609,7 @@ static void MacroDo( httpd_file_sys_t *p_args, if( m->param1 ) { - EvaluateRPN( p_args->vars, &p_args->stack, m->param1 ); + EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m->param1 ); s = SSPop( &p_args->stack ); v = mvar_GetValue( p_args->vars, s ); } @@ -2338,10 +2623,10 @@ static void MacroDo( httpd_file_sys_t *p_args, break; } case MVLC_RPN: - EvaluateRPN( p_args->vars, &p_args->stack, m->param1 ); + EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m->param1 ); break; -/* Usefull for learning stack management */ + /* Useful to learn stack management */ case MVLC_STACK: { int i; @@ -2448,12 +2733,52 @@ static void Execute( httpd_file_sys_t *p_args, switch( StrToMacroType( m.id ) ) { + case MVLC_INCLUDE: + { + FILE *f; + int i_buffer; + char *p_buffer; + char psz_file[MAX_DIR_SIZE]; + char *p; + + if( m.param1[0] != '/' ) + { + strcpy( psz_file, p_args->file ); + p = strrchr( psz_file, '/' ); + if( p != NULL ) + strcpy( p + 1, m.param1 ); + else + strcpy( psz_file, m.param1 ); + } + else + { + strcpy( psz_file, m.param1 ); + } + + if( ( f = fopen( psz_file, "r" ) ) == NULL ) + { + msg_Warn( p_args->p_intf, + "unable to include file %s (%s)", + psz_file, strerror(errno) ); + break; + } + + /* first we load in a temporary buffer */ + FileLoad( f, &p_buffer, &i_buffer ); + + /* we parse executing all macros */ + Execute( p_args, p_request, i_request, pp_data, pi_data, + &dst, &p_buffer[0], &p_buffer[i_buffer] ); + free( p_buffer ); + fclose(f); + break; + } case MVLC_IF: { vlc_bool_t i_test; char *endif; - EvaluateRPN( p_args->vars, &p_args->stack, m.param1 ); + EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m.param1 ); if( SSPopN( &p_args->stack, p_args->vars ) ) { i_test = 1; @@ -2473,7 +2798,8 @@ static void Execute( httpd_file_sys_t *p_args, 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 ); + Execute( p_args, p_request, i_request, + pp_data, pi_data, &dst, start, stop ); } } } @@ -2486,7 +2812,8 @@ static void Execute( httpd_file_sys_t *p_args, } if( stop ) { - Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, src, stop ); + Execute( p_args, p_request, i_request, + pp_data, pi_data, &dst, src, stop ); } } @@ -2513,16 +2840,29 @@ static void Execute( httpd_file_sys_t *p_args, else if( !strcmp( m.param2, "directory" ) ) { char *arg = SSPop( &p_args->stack ); - index = mvar_FileSetNew( m.param1, arg ); + index = mvar_FileSetNew( p_intf, m.param1, arg ); free( arg ); } else if( !strcmp( m.param2, "playlist" ) ) { - index = mvar_PlaylistSetNew( m.param1, p_intf->p_sys->p_playlist ); + index = mvar_PlaylistSetNew( p_intf, m.param1, + p_intf->p_sys->p_playlist ); } else if( !strcmp( m.param2, "information" ) ) { - index = mvar_InfoSetNew( m.param1, p_intf->p_sys->p_input ); + index = mvar_InfoSetNew( p_intf, m.param1, + p_intf->p_sys->p_input ); + } + else if( !strcmp( m.param2, "program" ) + || !strcmp( m.param2, "title" ) + || !strcmp( m.param2, "chapter" ) + || !strcmp( m.param2, "audio-es" ) + || !strcmp( m.param2, "video-es" ) + || !strcmp( m.param2, "spu-es" ) ) + { + index = mvar_InputVarSetNew( p_intf, m.param1, + p_intf->p_sys->p_input, + m.param2 ); } else if( !strcmp( m.param2, "vlm" ) ) { @@ -2566,7 +2906,8 @@ static void Execute( httpd_file_sys_t *p_args, mvar_PushVar( p_args->vars, f ); - Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop ); + Execute( p_args, p_request, i_request, + pp_data, pi_data, &dst, start, stop ); mvar_RemoveVar( p_args->vars, f ); mvar_Delete( f ); @@ -2578,7 +2919,8 @@ static void Execute( httpd_file_sys_t *p_args, break; } default: - MacroDo( p_args, &m, p_request, i_request, pp_data, pi_data, &dst ); + MacroDo( p_args, &m, p_request, i_request, + pp_data, pi_data, &dst ); break; } @@ -2678,14 +3020,17 @@ static int HttpCallback( httpd_file_sys_t *p_args, if( val.i_int == PLAYING_S ) { sprintf( state, "playing" ); - } else if( val.i_int == PAUSE_S ) + } + else if( val.i_int == PAUSE_S ) { sprintf( state, "paused" ); - } else + } + else { sprintf( state, "stop" ); } - } else + } + else { sprintf( position, "%d", 0 ); sprintf( time, "%d", 0 ); @@ -2718,7 +3063,8 @@ static int HttpCallback( httpd_file_sys_t *p_args, dst = *pp_data = malloc( *pi_data ); /* we parse executing all macros */ - Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, &p_buffer[0], &p_buffer[i_buffer] ); + Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, + &p_buffer[0], &p_buffer[i_buffer] ); *dst = '\0'; *pi_data = dst - *pp_data; @@ -2743,7 +3089,8 @@ static int uri_test_param( char *psz_uri, const char *psz_name ) 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' ) + if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n') + && p[strlen(psz_name)] == '=' ) { return VLC_TRUE; } @@ -2760,7 +3107,8 @@ static char *uri_extract_value( char *psz_uri, const char *psz_name, 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' ) + if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n') + && p[strlen(psz_name)] == '=' ) break; p++; } @@ -2841,10 +3189,50 @@ static void uri_decode_url_encoded( char *psz ) *psz++ = *p++; } } - *psz++ ='\0'; + *psz++ = '\0'; free( dup ); } +/* Since the resulting string is smaller we can work in place, so it is + * permitted to have psz == new. new points to the first word of the + * string, the function returns the remaining string. */ +static char *FirstWord( char *psz, char *new ) +{ + vlc_bool_t b_end; + + while( *psz == ' ' ) + psz++; + + while( *psz != '\0' && *psz != ' ' ) + { + if( *psz == '\'' ) + { + char c = *psz++; + while( *psz != '\0' && *psz != c ) + { + if( *psz == '\\' && psz[1] != '\0' ) + psz++; + *new++ = *psz++; + } + if( *psz == c ) + psz++; + } + else + { + if( *psz == '\\' && psz[1] != '\0' ) + psz++; + *new++ = *psz++; + } + } + b_end = !*psz; + + *new++ = '\0'; + if( !b_end ) + return psz + 1; + else + return NULL; +} + /**************************************************************************** * Light RPN evaluator ****************************************************************************/ @@ -2909,13 +3297,16 @@ static void SSPushN( rpn_stack_t *st, int i ) SSPush( st, v ); } -static void EvaluateRPN( mvar_t *vars, rpn_stack_t *st, char *exp ) +static void EvaluateRPN( intf_thread_t *p_intf, mvar_t *vars, + rpn_stack_t *st, char *exp ) { - for( ;; ) + intf_sys_t *p_sys = p_intf->p_sys; + + while( exp != NULL && *exp != '\0' ) { - char s[100], *p; + char *p, *s; - /* skip spcae */ + /* skip space */ while( *exp == ' ' ) { exp++; @@ -2924,33 +3315,22 @@ static void EvaluateRPN( mvar_t *vars, rpn_stack_t *st, char *exp ) if( *exp == '\'' ) { /* extract string */ - p = &s[0]; - exp++; - while( *exp && *exp != '\'' ) - { - *p++ = *exp++; - } - *p = '\0'; - exp++; - SSPush( st, s ); + p = FirstWord( exp, exp ); + SSPush( st, exp ); + exp = p; continue; } /* extract token */ - p = strchr( exp, ' ' ); - if( !p ) + p = FirstWord( exp, exp ); + s = exp; + if( p == NULL ) { - strcpy( s, exp ); - exp += strlen( exp ); } else { - int i = p -exp; - strncpy( s, exp, i ); - s[i] = '\0'; - - exp = p + 1; + exp = p; } if( *s == '\0' ) @@ -3104,13 +3484,229 @@ static void EvaluateRPN( mvar_t *vars, rpn_stack_t *st, char *exp ) free( s ); free( str ); } - else if( !strcmp( s, "strlen" ) ) + else if( !strcmp( s, "strlen" ) ) { char *str = SSPop( st ); SSPushN( st, strlen( str ) ); free( str ); } + else if( !strcmp( s, "str_replace" ) ) + { + char *psz_to = SSPop( st ); + char *psz_from = SSPop( st ); + char *psz_in = SSPop( st ); + char *psz_in_current = psz_in; + char *psz_out = malloc( strlen(psz_in) * strlen(psz_to) + 1 ); + char *psz_out_current = psz_out; + + while( (p = strstr( psz_in_current, psz_from )) != NULL ) + { + memcpy( psz_out_current, psz_in_current, p - psz_in_current ); + psz_out_current += p - psz_in_current; + strcpy( psz_out_current, psz_to ); + psz_out_current += strlen(psz_to); + psz_in_current = p + strlen(psz_from); + } + strcpy( psz_out_current, psz_in_current ); + psz_out_current += strlen(psz_in_current); + *psz_out_current = '\0'; + + SSPush( st, psz_out ); + free( psz_to ); + free( psz_from ); + free( psz_in ); + free( psz_out ); + } + else if( !strcmp( s, "url_extract" ) ) + { + char *url = mvar_GetValue( vars, "url_value" ); + char *name = SSPop( st ); + char value[512]; + char *tmp; + + uri_extract_value( url, name, value, 512 ); + uri_decode_url_encoded( value ); + tmp = FromUTF8( p_intf, value ); + SSPush( st, tmp ); + free( tmp ); + free( name ); + } + else if( !strcmp( s, "url_encode" ) ) + { + char *url = SSPop( st ); + char *value; + + value = ToUTF8( p_intf, url ); + free( url ); + url = value; + value = vlc_UrlEncode( url ); + free( url ); + SSPush( st, value ); + free( value ); + } + else if( !strcmp( s, "addslashes" ) ) + { + char *psz_src = SSPop( st ); + char *psz_dest; + char *str = psz_src; + + p = psz_dest = malloc( strlen( str ) * 2 + 1 ); + + while( *str != '\0' ) + { + if( *str == '"' || *str == '\'' ) + { + *p++ = '\\'; + } + *p++ = *str; + str++; + } + *p = '\0'; + + SSPush( st, psz_dest ); + free( psz_src ); + free( psz_dest ); + } + else if( !strcmp( s, "stripslashes" ) ) + { + char *psz_src = SSPop( st ); + char *psz_dest; + + p = psz_dest = strdup( psz_src ); + + while( *psz_src != '\0' ) + { + if( *psz_src == '\\' ) + { + *psz_src++; + } + *p++ = *psz_src; + psz_src++; + } + *p = '\0'; + + SSPush( st, psz_dest ); + free( psz_src ); + free( psz_dest ); + } + else if( !strcmp( s, "htmlspecialchars" ) ) + { + char *psz_src = SSPop( st ); + char *psz_dest; + char *str = psz_src; + + p = psz_dest = malloc( strlen( str ) * 6 + 1 ); + + while( *str != '\0' ) + { + if( *str == '&' ) + { + strcpy( p, "&" ); + p += 5; + } + else if( *str == '\"' ) + { + strcpy( p, """ ); + p += 6; + } + else if( *str == '\'' ) + { + strcpy( p, "'" ); + p += 6; + } + else if( *str == '<' ) + { + strcpy( p, "<" ); + p += 4; + } + else if( *str == '>' ) + { + strcpy( p, ">" ); + p += 4; + } + else + { + *p++ = *str; + } + str++; + } + *p = '\0'; + + SSPush( st, psz_dest ); + free( psz_src ); + free( psz_dest ); + } + else if( !strcmp( s, "realpath" ) ) + { + char dir[MAX_DIR_SIZE], *src; + char *psz_src = SSPop( st ); + char *psz_dir = psz_src; + char sep; + + /* convert all / to native separator */ +#if defined( WIN32 ) + while( (p = strchr( psz_dir, '/' )) ) + { + *p = '\\'; + } + sep = '\\'; +#else + sep = '/'; +#endif + + if( *psz_dir == '~' ) + { + /* This is incomplete : we should also support the ~cmassiot/ syntax. */ + snprintf( dir, sizeof(dir), "%s/%s", p_intf->p_vlc->psz_homedir, + psz_dir + 1 ); + psz_dir = dir; + } + + /* 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 ); + } + } + if( p != psz_dir + 1 && p[-1] == '/' ) p--; + *p = '\0'; + + SSPush( st, psz_dir ); + free( psz_src ); + } /* 4. stack functions */ else if( !strcmp( s, "dup" ) ) { @@ -3157,109 +3753,127 @@ static void EvaluateRPN( mvar_t *vars, rpn_stack_t *st, char *exp ) free( name ); } - else if( !strcmp( s, "url_extract" ) ) + /* 5. player control */ + else if( !strcmp( s, "vlc_play" ) ) { - char *url = mvar_GetValue( vars, "url_value" ); - char *name = SSPop( st ); - char value[512]; + int i_id = SSPopN( st, vars ); + int i_ret; - uri_extract_value( url, name, value, 512 ); - uri_decode_url_encoded( value ); - SSPush( st, value ); + i_ret = playlist_Control( p_sys->p_playlist, PLAYLIST_ITEMPLAY, + playlist_ItemGetById( p_sys->p_playlist, + i_id ) ); + msg_Dbg( p_intf, "requested playlist item: %i", i_id ); + SSPushN( st, i_ret ); } - else + else if( !strcmp( s, "vlc_stop" ) ) { - SSPush( st, s ); + playlist_Control( p_sys->p_playlist, PLAYLIST_STOP ); + msg_Dbg( p_intf, "requested playlist stop" ); } - } -} - -/********************************************************************** - * 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 '\"': + else if( !strcmp( s, "vlc_pause" ) ) + { + playlist_Control( p_sys->p_playlist, PLAYLIST_PAUSE ); + msg_Dbg( p_intf, "requested playlist pause" ); + } + else if( !strcmp( s, "vlc_next" ) ) { - s_sent++; + playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, 1 ); + msg_Dbg( p_intf, "requested playlist next" ); + } + else if( !strcmp( s, "vlc_previous" ) ) + { + playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1 ); + msg_Dbg( p_intf, "requested playlist previous" ); + } + else if( !strcmp( s, "vlc_seek" ) ) + { + char *psz_value = SSPop( st ); + Seek( p_intf, psz_value ); + msg_Dbg( p_intf, "requested playlist seek: %s", psz_value ); + free( psz_value ); + } + else if( !strcmp( s, "vlc_set_var" ) ) + { + char *psz_variable = SSPop( st ); + char *psz_value = NULL; + vlc_value_t val; + int i_type; - while( ( *s_sent != '\"' ) && ( *s_sent != '\0' ) ) + if( p_sys->p_input != NULL ) { - if( *s_sent == '\'' ) - { - s_sent = Find_end_MRL( s_sent ); + i_type = var_Type( p_sys->p_input, psz_variable ); - if( s_sent == NULL ) - { - return NULL; - } - } else + if( (i_type & VLC_VAR_TYPE) == VLC_VAR_INTEGER ) { - s_sent++; + int i_value = SSPopN( st, vars ); + val.i_int = i_value; + msg_Dbg( p_intf, "requested input var change: %s->%d", + psz_variable, i_value ); + } + else + { + psz_value = SSPop( st ); + val.psz_string = psz_value; + msg_Dbg( p_intf, "requested input var change: %s->%s", + psz_variable, psz_value ); } - } - if( *s_sent == '\"' ) - { - s_sent++; - return s_sent; - } else /* *s_sent == '\0' , which means the number of " is incorrect */ + var_Set( p_sys->p_input, psz_variable, val ); + } + if( psz_value != NULL ) + free( psz_value ); + free( psz_variable ); + } + /* 6. playlist functions */ + else if( !strcmp( s, "playlist_add" ) ) + { + char *psz_name = SSPop( st ); + char *mrl = SSPop( st ); + char *tmp; + playlist_item_t *p_item; + int i_id; + + tmp = ToUTF8( p_intf, psz_name ); + free( psz_name ); + psz_name = tmp; + tmp = ToUTF8( p_intf, mrl ); + free( mrl ); + mrl = tmp; + + if( !*psz_name ) { - return NULL; + p_item = parse_MRL( p_intf, mrl, mrl ); } - break; - } - case '\'': - { - s_sent++; - - while( ( *s_sent != '\'' ) && ( *s_sent != '\0' ) ) + else { - if( *s_sent == '\"' ) - { - s_sent = Find_end_MRL( s_sent ); - - if( s_sent == NULL ) - { - return NULL; - } - } else - { - s_sent++; - } + p_item = parse_MRL( p_intf, mrl, psz_name ); } - if( *s_sent == '\'' ) + if( p_item == NULL || p_item->input.psz_uri == NULL || + !*p_item->input.psz_uri ) { - s_sent++; - return s_sent; - } else /* *s_sent == '\0' , which means the number of ' is incorrect */ + i_id = VLC_EGENERIC; + msg_Dbg( p_intf, "invalid requested mrl: %s", mrl ); + } + else { - return NULL; + i_id = playlist_AddItem( p_sys->p_playlist, p_item, + PLAYLIST_APPEND, PLAYLIST_END ); + msg_Dbg( p_intf, "requested mrl add: %s", mrl ); } - break; + SSPushN( st, i_id ); + + free( mrl ); + free( psz_name ); } - default: /* now we can look for spaces */ + else if( !strcmp( s, "playlist_empty" ) ) { - while( ( *s_sent != ' ' ) && ( *s_sent != '\0' ) ) - { - if( ( *s_sent == '\'' ) || ( *s_sent == '\"' ) ) - { - s_sent = Find_end_MRL( s_sent ); - } else - { - s_sent++; - } - } - return s_sent; + playlist_LockClear( p_sys->p_playlist ); + msg_Dbg( p_intf, "requested playlist empty" ); + } + else + { + SSPush( st, s ); } } } @@ -3269,112 +3883,41 @@ static char *Find_end_MRL( char *psz ) * 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 ) +static playlist_item_t *parse_MRL( intf_thread_t *p_intf, char *_psz, + char *psz_name ) { - char **ppsz_options = NULL; - char *mrl; + char *psz = strdup( _psz ); 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 , " :" ); + s_temp = FirstWord( s_mrl, 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'; } + p_item = playlist_ItemNew( p_intf, s_mrl, psz_name ); s_mrl = s_temp; /* now we can take care of the options */ - while( (*s_mrl != '\0') && (i_error == 0) ) + while( *s_mrl != '\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++ ) + s_temp = FirstWord( s_mrl, s_mrl ); + if( s_mrl == '\0' ) + break; + if( s_temp == NULL ) { - playlist_ItemAddOption( p_item, ppsz_options[i] ); + s_temp = s_mrl + strlen( s_mrl ); } + if( *s_mrl != ':' ) + msg_Warn( p_intf, "invalid MRL option: %s", s_mrl ); + else + playlist_ItemAddOption( p_item, s_mrl ); + s_mrl = s_temp; } - for( i = 0; i < i_options; i++ ) free( ppsz_options[i] ); - if( i_options ) free( ppsz_options ); - + free( psz ); return p_item; }