1 /*****************************************************************************
2 * http.c : http mini-server ;)
3 *****************************************************************************
4 * Copyright (C) 2001-2005 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
8 * Laurent Aimar <fenrir@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
34 #include <vlc/vout.h> /* for fullscreen */
36 #include "vlc_httpd.h"
42 #ifdef HAVE_SYS_STAT_H
43 # include <sys/stat.h>
54 #elif defined( WIN32 ) && !defined( UNDER_CE )
62 /* stat() support for large files on win32 */
63 #if defined( WIN32 ) && !defined( UNDER_CE )
64 # define stat _stati64
67 /*****************************************************************************
69 *****************************************************************************/
70 static int Open ( vlc_object_t * );
71 static void Close( vlc_object_t * );
73 #define HOST_TEXT N_( "Host address" )
74 #define HOST_LONGTEXT N_( \
75 "You can set the address and port the http interface will bind to." )
76 #define SRC_TEXT N_( "Source directory" )
77 #define SRC_LONGTEXT N_( "Source directory" )
78 #define CHARSET_TEXT N_( "Charset" )
79 #define CHARSET_LONGTEXT N_( \
80 "Charset declared in Content-Type header (default UTF-8)." )
81 #define CERT_TEXT N_( "Certificate file" )
82 #define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \
84 #define KEY_TEXT N_( "Private key file" )
85 #define KEY_LONGTEXT N_( "HTTP interface x509 PEM private key file" )
86 #define CA_TEXT N_( "Root CA file" )
87 #define CA_LONGTEXT N_( "HTTP interface x509 PEM trusted root CA " \
89 #define CRL_TEXT N_( "CRL file" )
90 #define CRL_LONGTEXT N_( "HTTP interace Certificates Revocation List file" )
93 set_shortname( _("HTTP"));
94 set_description( _("HTTP remote control interface") );
95 set_category( CAT_INTERFACE );
96 set_subcategory( SUBCAT_INTERFACE_GENERAL );
97 add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
98 add_string ( "http-src", NULL, NULL, SRC_TEXT, SRC_LONGTEXT, VLC_TRUE );
99 add_string ( "http-charset", "UTF-8", NULL, CHARSET_TEXT, CHARSET_LONGTEXT, VLC_TRUE );
100 set_section( N_("HTTP SSL" ), 0 );
101 add_string ( "http-intf-cert", NULL, NULL, CERT_TEXT, CERT_LONGTEXT, VLC_TRUE );
102 add_string ( "http-intf-key", NULL, NULL, KEY_TEXT, KEY_LONGTEXT, VLC_TRUE );
103 add_string ( "http-intf-ca", NULL, NULL, CA_TEXT, CA_LONGTEXT, VLC_TRUE );
104 add_string ( "http-intf-crl", NULL, NULL, CRL_TEXT, CRL_LONGTEXT, VLC_TRUE );
105 set_capability( "interface", 0 );
106 set_callbacks( Open, Close );
110 /*****************************************************************************
112 *****************************************************************************/
113 static void Run ( intf_thread_t *p_intf );
115 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
118 #if !defined(SYS_DARWIN) && !defined(SYS_BEOS) && !defined(WIN32)
119 static int DirectoryCheck( char *psz_dir )
123 #ifdef HAVE_SYS_STAT_H
124 struct stat stat_info;
126 if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
132 if( ( p_dir = opendir( psz_dir ) ) == NULL )
142 static int HttpCallback( httpd_file_sys_t *p_args,
145 uint8_t **pp_data, int *pi_data );
147 static char *uri_extract_value( char *psz_uri, const char *psz_name,
148 char *psz_value, int i_value_max );
149 static int uri_test_param( char *psz_uri, const char *psz_name );
151 static void uri_decode_url_encoded( char *psz );
153 static char *Find_end_MRL( char *psz );
154 static playlist_item_t *parse_MRL( intf_thread_t *, char *psz, char *psz_name );
156 /*****************************************************************************
158 *****************************************************************************/
159 typedef struct mvar_s
165 struct mvar_s **field;
168 #define STACK_MAX 100
171 char *stack[STACK_MAX];
175 struct httpd_file_sys_t
177 intf_thread_t *p_intf;
178 httpd_file_t *p_file;
179 httpd_redirect_t *p_redir;
180 httpd_redirect_t *p_redir2;
187 /* inited for each access */
194 httpd_host_t *p_httpd_host;
197 httpd_file_sys_t **pp_files;
199 playlist_t *p_playlist;
200 input_thread_t *p_input;
203 vlc_iconv_t iconv_from_utf8, iconv_to_utf8;
208 /*****************************************************************************
209 * Activate: initialize and create stuff
210 *****************************************************************************/
211 static int Open( vlc_object_t *p_this )
213 intf_thread_t *p_intf = (intf_thread_t*)p_this;
216 char *psz_address = "";
217 const char *psz_cert = NULL, *psz_key = NULL, *psz_ca = NULL,
222 psz_host = config_GetPsz( p_intf, "http-host" );
226 psz_address = psz_host;
228 psz_parser = strchr( psz_host, ':' );
231 *psz_parser++ = '\0';
232 i_port = atoi( psz_parser );
236 p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
239 return( VLC_ENOMEM );
241 p_sys->p_playlist = NULL;
242 p_sys->p_input = NULL;
245 /* determine Content-Type value for HTML pages */
246 psz_src = config_GetPsz( p_intf, "http-charset" );
247 if( psz_src == NULL || !*psz_src )
249 if( psz_src != NULL ) free( psz_src );
250 psz_src = strdup("UTF-8");
253 p_sys->psz_html_type = malloc( 20 + strlen( psz_src ) );
254 if( p_sys->psz_html_type == NULL )
260 sprintf( p_sys->psz_html_type, "text/html; charset=%s", psz_src );
261 msg_Dbg( p_intf, "using charset=%s", psz_src );
263 if( strcmp( psz_src, "UTF-8" ) )
265 p_sys->iconv_from_utf8 = vlc_iconv_open( psz_src, "UTF-8" );
266 if( p_sys->iconv_from_utf8 == (vlc_iconv_t)-1 )
267 msg_Warn( p_intf, "unable to perform charset conversion to %s",
271 p_sys->iconv_to_utf8 = vlc_iconv_open( "UTF-8", psz_src );
272 if( p_sys->iconv_to_utf8 == (vlc_iconv_t)-1 )
274 "unable to perform charset conversion from %s",
280 p_sys->iconv_from_utf8 = p_sys->iconv_to_utf8 = (vlc_iconv_t)-1;
285 /* determine SSL configuration */
286 psz_cert = config_GetPsz( p_intf, "http-intf-cert" );
287 if ( psz_cert != NULL )
289 msg_Dbg( p_intf, "enabling TLS for HTTP interface (cert file: %s)",
291 psz_key = config_GetPsz( p_intf, "http-intf-key" );
292 psz_ca = config_GetPsz( p_intf, "http-intf-ca" );
293 psz_crl = config_GetPsz( p_intf, "http-intf-crl" );
304 msg_Dbg( p_intf, "base %s:%d", psz_address, i_port );
306 p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_intf), psz_address,
307 i_port, psz_cert, psz_key, psz_ca,
309 if( p_sys->p_httpd_host == NULL )
311 msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port );
312 free( p_sys->psz_html_type );
323 p_sys->pp_files = NULL;
325 #if defined(SYS_DARWIN) || defined(SYS_BEOS) || defined(WIN32)
326 if ( ( psz_src = config_GetPsz( p_intf, "http-src" )) == NULL )
328 char * psz_vlcpath = p_intf->p_libvlc->psz_vlcpath;
329 psz_src = malloc( strlen(psz_vlcpath) + strlen("/share/http" ) + 1 );
330 if( !psz_src ) return VLC_ENOMEM;
332 sprintf( psz_src, "%s/http", psz_vlcpath);
334 sprintf( psz_src, "%s/share/http", psz_vlcpath);
338 psz_src = config_GetPsz( p_intf, "http-src" );
340 if( !psz_src || *psz_src == '\0' )
342 if( !DirectoryCheck( "share/http" ) )
344 psz_src = strdup( "share/http" );
346 else if( !DirectoryCheck( DATA_PATH "/http" ) )
348 psz_src = strdup( DATA_PATH "/http" );
353 if( !psz_src || *psz_src == '\0' )
355 msg_Err( p_intf, "invalid src dir" );
359 /* remove trainling \ or / */
360 if( psz_src[strlen( psz_src ) - 1] == '\\' ||
361 psz_src[strlen( psz_src ) - 1] == '/' )
363 psz_src[strlen( psz_src ) - 1] = '\0';
366 ParseDirectory( p_intf, psz_src, psz_src );
369 if( p_sys->i_files <= 0 )
371 msg_Err( p_intf, "cannot find any files (%s)", psz_src );
374 p_intf->pf_run = Run;
380 if( psz_src ) free( psz_src );
381 if( p_sys->pp_files )
383 free( p_sys->pp_files );
385 httpd_HostDelete( p_sys->p_httpd_host );
386 free( p_sys->psz_html_type );
387 if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
388 vlc_iconv_close( p_sys->iconv_from_utf8 );
389 if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
390 vlc_iconv_close( p_sys->iconv_to_utf8 );
395 /*****************************************************************************
396 * Close: destroy interface
397 *****************************************************************************/
398 void Close ( vlc_object_t *p_this )
400 intf_thread_t *p_intf = (intf_thread_t *)p_this;
401 intf_sys_t *p_sys = p_intf->p_sys;
407 vlm_Delete( p_sys->p_vlm );
409 for( i = 0; i < p_sys->i_files; i++ )
411 httpd_FileDelete( p_sys->pp_files[i]->p_file );
412 if( p_sys->pp_files[i]->p_redir )
413 httpd_RedirectDelete( p_sys->pp_files[i]->p_redir );
414 if( p_sys->pp_files[i]->p_redir2 )
415 httpd_RedirectDelete( p_sys->pp_files[i]->p_redir2 );
417 free( p_sys->pp_files[i]->file );
418 free( p_sys->pp_files[i]->name );
419 free( p_sys->pp_files[i] );
421 if( p_sys->pp_files )
423 free( p_sys->pp_files );
425 httpd_HostDelete( p_sys->p_httpd_host );
426 free( p_sys->psz_html_type );
428 if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
429 vlc_iconv_close( p_sys->iconv_from_utf8 );
430 if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
431 vlc_iconv_close( p_sys->iconv_to_utf8 );
435 /*****************************************************************************
436 * Run: http interface thread
437 *****************************************************************************/
438 static void Run( intf_thread_t *p_intf )
440 intf_sys_t *p_sys = p_intf->p_sys;
442 while( !p_intf->b_die )
444 /* get the playlist */
445 if( p_sys->p_playlist == NULL )
447 p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
450 /* Manage the input part */
451 if( p_sys->p_input == NULL )
453 if( p_sys->p_playlist )
456 vlc_object_find( p_sys->p_playlist,
461 else if( p_sys->p_input->b_dead )
463 vlc_object_release( p_sys->p_input );
464 p_sys->p_input = NULL;
469 msleep( INTF_IDLE_SLEEP );
474 vlc_object_release( p_sys->p_input );
475 p_sys->p_input = NULL;
478 if( p_sys->p_playlist )
480 vlc_object_release( p_sys->p_playlist );
481 p_sys->p_playlist = NULL;
486 /*****************************************************************************
488 *****************************************************************************/
489 #define MAX_DIR_SIZE 10240
491 /****************************************************************************
492 * FileToUrl: create a good name for an url from filename
493 ****************************************************************************/
494 static char *FileToUrl( char *name, vlc_bool_t *pb_index )
498 url = p = malloc( strlen( name ) + 1 );
500 *pb_index = VLC_FALSE;
507 while( *name == '\\' || *name == '/' )
509 while( *name == '/' )
519 /* convert '\\' into '/' */
532 if( ( p = strrchr( url, '/' ) ) != NULL )
534 if( !strncmp( p, "/index.", 7 ) )
537 *pb_index = VLC_TRUE;
543 static char *FromUTF8( intf_thread_t *p_intf, char *psz_utf8 )
545 intf_sys_t *p_sys = p_intf->p_sys;
547 if ( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
549 char *psz_in = psz_utf8;
550 size_t i_in = strlen(psz_in);
551 size_t i_out = i_in * 2;
552 char *psz_local = malloc(i_out + 1);
553 char *psz_out = psz_local;
555 size_t i_ret = vlc_iconv( p_sys->iconv_from_utf8, &psz_in, &i_in,
557 if( i_ret == (size_t)-1 || i_in )
560 "failed to convert \"%s\" to desired charset (%s)",
561 psz_utf8, strerror(errno) );
563 return strdup( psz_utf8 );
570 return strdup( psz_utf8 );
573 static char *ToUTF8( intf_thread_t *p_intf, char *psz_local )
575 intf_sys_t *p_sys = p_intf->p_sys;
577 if ( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
579 char *psz_in = psz_local;
580 size_t i_in = strlen(psz_in);
581 size_t i_out = i_in * 6;
582 char *psz_utf8 = malloc(i_out + 1);
583 char *psz_out = psz_utf8;
585 size_t i_ret = vlc_iconv( p_sys->iconv_to_utf8, &psz_in, &i_in,
587 if( i_ret == (size_t)-1 || i_in )
590 "failed to convert \"%s\" to desired charset (%s)",
591 psz_local, strerror(errno) );
593 return strdup( psz_local );
600 return strdup( psz_local );
603 /****************************************************************************
604 * ParseDirectory: parse recursively a directory, adding each file
605 ****************************************************************************/
606 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
609 intf_sys_t *p_sys = p_intf->p_sys;
610 char dir[MAX_DIR_SIZE];
611 #ifdef HAVE_SYS_STAT_H
612 struct stat stat_info;
615 struct dirent *p_dir_content;
620 char *password = NULL;
624 #ifdef HAVE_SYS_STAT_H
625 if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
631 if( ( p_dir = opendir( psz_dir ) ) == NULL )
633 msg_Err( p_intf, "cannot open dir (%s)", psz_dir );
637 i_dirlen = strlen( psz_dir );
638 if( i_dirlen + 10 > MAX_DIR_SIZE )
640 msg_Warn( p_intf, "skipping too deep dir (%s)", psz_dir );
644 msg_Dbg( p_intf, "dir=%s", psz_dir );
646 sprintf( dir, "%s/.access", psz_dir );
647 if( ( file = fopen( dir, "r" ) ) != NULL )
652 msg_Dbg( p_intf, "find .access in dir=%s", psz_dir );
654 i_size = fread( line, 1, 1023, file );
658 while( i_size > 0 && ( line[i_size-1] == '\n' ||
659 line[i_size-1] == '\r' ) )
666 p = strchr( line, ':' );
670 user = strdup( line );
671 password = strdup( p );
674 msg_Dbg( p_intf, "using user=%s password=%s (read=%d)",
675 user, password, i_size );
680 sprintf( dir, "%s/.hosts", psz_dir );
681 p_acl = ACL_Create( p_intf, VLC_FALSE );
682 if( ACL_LoadFile( p_acl, dir ) )
684 ACL_Destroy( p_acl );
690 /* parse psz_src dir */
691 if( ( p_dir_content = readdir( p_dir ) ) == NULL )
696 if( ( p_dir_content->d_name[0] == '.' )
697 || ( i_dirlen + strlen( p_dir_content->d_name ) > MAX_DIR_SIZE ) )
700 sprintf( dir, "%s/%s", psz_dir, p_dir_content->d_name );
701 if( ParseDirectory( p_intf, psz_root, dir ) )
703 httpd_file_sys_t *f = malloc( sizeof( httpd_file_sys_t ) );
711 psz_tmp = vlc_fix_readdir_charset( p_intf, dir );
712 f->file = FromUTF8( p_intf, psz_tmp );
714 psz_tmp = vlc_fix_readdir_charset( p_intf,
715 &dir[strlen( psz_root )] );
716 f->name = FileToUrl( psz_tmp, &b_index );
718 f->b_html = strstr( &dir[strlen( psz_root )], ".htm" ) ? VLC_TRUE : VLC_FALSE;
722 msg_Err( p_intf , "unable to parse directory" );
725 return( VLC_ENOMEM );
727 msg_Dbg( p_intf, "file=%s (url=%s)",
730 f->p_file = httpd_FileNew( p_sys->p_httpd_host,
732 f->b_html ? p_sys->psz_html_type : NULL,
733 user, password, p_acl,
738 TAB_APPEND( p_sys->i_files, p_sys->pp_files, f );
740 /* for url that ends by / add
741 * - a redirect from rep to rep/
742 * - in case of index.* rep/index.html to rep/ */
743 if( f && f->name[strlen(f->name) - 1] == '/' )
745 char *psz_redir = strdup( f->name );
747 psz_redir[strlen( psz_redir ) - 1] = '\0';
749 msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
750 f->p_redir = httpd_RedirectNew( p_sys->p_httpd_host, f->name, psz_redir );
753 if( b_index && ( p = strstr( f->file, "index." ) ) )
755 asprintf( &psz_redir, "%s%s", f->name, p );
757 msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
758 f->p_redir2 = httpd_RedirectNew( p_sys->p_httpd_host,
759 f->name, psz_redir );
776 ACL_Destroy( p_acl );
782 /****************************************************************************
783 * var and set handling
784 ****************************************************************************/
786 static mvar_t *mvar_New( const char *name, const char *value )
788 mvar_t *v = malloc( sizeof( mvar_t ) );
790 if( !v ) return NULL;
791 v->name = strdup( name );
792 v->value = strdup( value ? value : "" );
795 v->field = malloc( sizeof( mvar_t * ) );
801 static void mvar_Delete( mvar_t *v )
808 for( i = 0; i < v->i_field; i++ )
810 mvar_Delete( v->field[i] );
816 static void mvar_AppendVar( mvar_t *v, mvar_t *f )
818 v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
819 v->field[v->i_field] = f;
823 static mvar_t *mvar_Duplicate( const mvar_t *v )
828 n = mvar_New( v->name, v->value );
829 for( i = 0; i < v->i_field; i++ )
831 mvar_AppendVar( n, mvar_Duplicate( v->field[i] ) );
837 static void mvar_PushVar( mvar_t *v, mvar_t *f )
839 v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
842 memmove( &v->field[1], &v->field[0], sizeof( mvar_t * ) * v->i_field );
848 static void mvar_RemoveVar( mvar_t *v, mvar_t *f )
851 for( i = 0; i < v->i_field; i++ )
853 if( v->field[i] == f )
858 if( i >= v->i_field )
863 if( i + 1 < v->i_field )
865 memmove( &v->field[i], &v->field[i+1], sizeof( mvar_t * ) * ( v->i_field - i - 1 ) );
868 /* FIXME should do a realloc */
871 static mvar_t *mvar_GetVar( mvar_t *s, const char *name )
874 char base[512], *field, *p;
877 /* format: name[index].field */
879 field = strchr( name, '.' );
882 int i = field - name;
883 strncpy( base, name, i );
889 strcpy( base, name );
892 if( ( p = strchr( base, '[' ) ) )
895 sscanf( p, "%d]", &i_index );
906 for( i = 0; i < s->i_field; i++ )
908 if( !strcmp( s->field[i]->name, base ) )
918 return mvar_GetVar( s->field[i], field );
932 static char *mvar_GetValue( mvar_t *v, char *field )
940 mvar_t *f = mvar_GetVar( v, field );
952 static void mvar_PushNewVar( mvar_t *vars, const char *name,
955 mvar_t *f = mvar_New( name, value );
956 mvar_PushVar( vars, f );
959 static void mvar_AppendNewVar( mvar_t *vars, const char *name,
962 mvar_t *f = mvar_New( name, value );
963 mvar_AppendVar( vars, f );
967 /* arg= start[:stop[:step]],.. */
968 static mvar_t *mvar_IntegerSetNew( const char *name, const char *arg )
970 char *dup = strdup( arg );
972 mvar_t *s = mvar_New( name, "set" );
978 int i_start,i_stop,i_step;
981 p = strchr( str, ',' );
988 i_match = sscanf( str, "%d:%d:%d", &i_start, &i_stop, &i_step );
995 else if( i_match == 2 )
997 i_step = i_start < i_stop ? 1 : -1;
1004 if( ( i_start <= i_stop && i_step > 0 ) ||
1005 ( i_start >= i_stop && i_step < 0 ) )
1007 for( i = i_start; ; i += i_step )
1011 if( ( i_step > 0 && i > i_stop ) ||
1012 ( i_step < 0 && i < i_stop ) )
1017 sprintf( value, "%d", i );
1019 mvar_PushNewVar( s, name, value );
1030 static void PlaylistListNode( intf_thread_t *p_intf, playlist_t *p_pl,
1031 playlist_item_t *p_node, char *name, mvar_t *s,
1034 if( p_node != NULL )
1036 if( p_node->i_children == -1 )
1040 mvar_t *itm = mvar_New( name, "set" );
1042 sprintf( value, "%d", ( p_pl->status.p_item == p_node )? 1 : 0 );
1043 mvar_AppendNewVar( itm, "current", value );
1045 sprintf( value, "%d", p_node->input.i_id );
1046 mvar_AppendNewVar( itm, "index", value );
1048 psz = FromUTF8( p_intf, p_node->input.psz_name );
1049 mvar_AppendNewVar( itm, "name", psz );
1052 psz = FromUTF8( p_intf, p_node->input.psz_uri );
1053 mvar_AppendNewVar( itm, "uri", psz );
1056 sprintf( value, "Item");
1057 mvar_AppendNewVar( itm, "type", value );
1059 sprintf( value, "%d", i_depth );
1060 mvar_AppendNewVar( itm, "depth", value );
1062 mvar_AppendVar( s, itm );
1069 mvar_t *itm = mvar_New( name, "set" );
1071 psz = FromUTF8( p_intf, p_node->input.psz_name );
1072 mvar_AppendNewVar( itm, "name", psz );
1073 mvar_AppendNewVar( itm, "uri", psz );
1076 sprintf( value, "Node" );
1077 mvar_AppendNewVar( itm, "type", value );
1079 sprintf( value, "%d", p_node->input.i_id );
1080 mvar_AppendNewVar( itm, "index", value );
1082 sprintf( value, "%d", p_node->i_children);
1083 mvar_AppendNewVar( itm, "i_children", value );
1085 sprintf( value, "%d", i_depth );
1086 mvar_AppendNewVar( itm, "depth", value );
1088 mvar_AppendVar( s, itm );
1090 for (i_child = 0 ; i_child < p_node->i_children ; i_child++)
1091 PlaylistListNode( p_intf, p_pl, p_node->pp_children[i_child],
1092 name, s, i_depth + 1);
1098 static mvar_t *mvar_PlaylistSetNew( intf_thread_t *p_intf, char *name,
1101 playlist_view_t *p_view;
1102 mvar_t *s = mvar_New( name, "set" );
1105 vlc_mutex_lock( &p_pl->object_lock );
1107 p_view = playlist_ViewFind( p_pl, VIEW_CATEGORY ); /* FIXME */
1109 if( p_view != NULL )
1110 PlaylistListNode( p_intf, p_pl, p_view->p_root, name, s, 0 );
1112 vlc_mutex_unlock( &p_pl->object_lock );
1117 static mvar_t *mvar_InfoSetNew( intf_thread_t *p_intf, char *name,
1118 input_thread_t *p_input )
1120 mvar_t *s = mvar_New( name, "set" );
1123 if( p_input == NULL )
1128 vlc_mutex_lock( &p_input->input.p_item->lock );
1129 for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
1131 info_category_t *p_category = p_input->input.p_item->pp_categories[i];
1134 mvar_t *cat = mvar_New( name, "set" );
1135 mvar_t *iset = mvar_New( "info", "set" );
1137 psz = FromUTF8( p_intf, p_category->psz_name );
1138 mvar_AppendNewVar( cat, "name", psz );
1140 mvar_AppendVar( cat, iset );
1142 for ( j = 0; j < p_category->i_infos; j++ )
1144 info_t *p_info = p_category->pp_infos[j];
1145 mvar_t *info = mvar_New( "info", "" );
1146 char *psz_name = FromUTF8( p_intf, p_info->psz_name );
1147 char *psz_value = FromUTF8( p_intf, p_info->psz_value );
1149 msg_Dbg( p_input, "adding info name=%s value=%s",
1150 psz_name, psz_value );
1151 mvar_AppendNewVar( info, "name", psz_name );
1152 mvar_AppendNewVar( info, "value", psz_value );
1155 mvar_AppendVar( iset, info );
1157 mvar_AppendVar( s, cat );
1159 vlc_mutex_unlock( &p_input->input.p_item->lock );
1164 static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type )
1166 mvar_t *s = mvar_New( name, "set" );
1170 if( !p_httpd->pf_control( p_httpd, i_type, &info, NULL ) )
1172 for( i= 0; i < info.i_count; )
1176 inf = mvar_New( name, "set" );
1179 /* fprintf( stderr," mvar_HttpdInfoSetNew: append name=`%s' value=`%s'\n",
1180 info.info[i].psz_name, info.info[i].psz_value ); */
1181 mvar_AppendNewVar( inf,
1182 info.info[i].psz_name,
1183 info.info[i].psz_value );
1185 } while( i < info.i_count && strcmp( info.info[i].psz_name, "id" ) );
1186 mvar_AppendVar( s, inf );
1191 for( i = 0; i < info.i_count; i++ )
1193 free( info.info[i].psz_name );
1194 free( info.info[i].psz_value );
1196 if( info.i_count > 0 )
1205 /* Utility function for scandir */
1206 static int Filter( const struct dirent *foo )
1211 static mvar_t *mvar_FileSetNew( intf_thread_t *p_intf, char *name,
1214 mvar_t *s = mvar_New( name, "set" );
1215 char tmp[MAX_DIR_SIZE], dir[MAX_DIR_SIZE], *p, *src;
1216 #ifdef HAVE_SYS_STAT_H
1217 struct stat stat_info;
1219 struct dirent **pp_dir_content;
1220 int i_dir_content, i;
1223 /* convert all / to native separator */
1224 #if defined( WIN32 )
1225 while( (p = strchr( psz_dir, '/' )) )
1234 /* remove trailling separator */
1235 while( strlen( psz_dir ) > 1 &&
1236 #if defined( WIN32 )
1237 !( strlen(psz_dir)==3 && psz_dir[1]==':' && psz_dir[2]==sep ) &&
1239 psz_dir[strlen( psz_dir ) -1 ] == sep )
1241 psz_dir[strlen( psz_dir ) -1 ] ='\0';
1243 /* remove double separator */
1244 for( p = src = psz_dir; *src != '\0'; src++, p++ )
1246 if( src[0] == sep && src[1] == sep )
1254 if( *psz_dir == '\0' )
1259 if( *psz_dir == '~' )
1261 /* This is incomplete : we should also support the ~cmassiot/ syntax. */
1262 snprintf( dir, sizeof(dir), "%s/%s", p_intf->p_vlc->psz_homedir,
1267 /* first fix all .. dir */
1271 if( src[0] == '.' && src[1] == '.' )
1274 if( p <= &psz_dir[1] )
1281 while( p > &psz_dir[1] && *p != sep )
1286 else if( *src == sep )
1288 if( p > psz_dir && p[-1] == sep )
1302 } while( *src && *src != sep );
1308 #ifdef HAVE_SYS_STAT_H
1309 if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
1315 /* parse psz_src dir */
1316 if( ( i_dir_content = scandir( psz_dir, &pp_dir_content, Filter,
1317 alphasort ) ) == -1 )
1319 msg_Warn( p_intf, "scandir error on %s (%s)", psz_dir,
1324 /* remove trailing / or \ */
1325 for( p = &psz_dir[strlen( psz_dir) - 1];
1326 p >= psz_dir && ( *p =='/' || *p =='\\' ); p-- )
1331 for( i = 0; i < i_dir_content; i++ )
1333 struct dirent *p_dir_content = pp_dir_content[i];
1335 const char *psz_ext;
1336 char *psz_name, *psz_tmp;
1338 if( !strcmp( p_dir_content->d_name, "." ) )
1343 snprintf( tmp, sizeof(tmp), "%s/%s", psz_dir, p_dir_content->d_name );
1345 #ifdef HAVE_SYS_STAT_H
1346 if( stat( tmp, &stat_info ) == -1 )
1351 f = mvar_New( name, "set" );
1353 psz_tmp = vlc_fix_readdir_charset( p_intf, p_dir_content->d_name );
1354 psz_name = FromUTF8( p_intf, psz_tmp );
1356 snprintf( tmp, sizeof(tmp), "%s/%s", psz_dir, psz_name );
1357 mvar_AppendNewVar( f, "name", tmp );
1358 mvar_AppendNewVar( f, "basename", psz_name );
1360 /* put file extension in 'ext' */
1361 psz_ext = strrchr( psz_name, '.' );
1362 mvar_AppendNewVar( f, "ext", psz_ext != NULL ? psz_ext + 1 : "" );
1366 #ifdef HAVE_SYS_STAT_H
1367 if( S_ISDIR( stat_info.st_mode ) )
1369 mvar_AppendNewVar( f, "type", "directory" );
1371 else if( S_ISREG( stat_info.st_mode ) )
1373 mvar_AppendNewVar( f, "type", "file" );
1377 mvar_AppendNewVar( f, "type", "unknown" );
1380 sprintf( tmp, I64Fd, (int64_t)stat_info.st_size );
1381 mvar_AppendNewVar( f, "size", tmp );
1383 /* FIXME memory leak FIXME */
1385 ctime_r( &stat_info.st_mtime, tmp );
1386 mvar_AppendNewVar( f, "date", tmp );
1388 mvar_AppendNewVar( f, "date", ctime( &stat_info.st_mtime ) );
1392 mvar_AppendNewVar( f, "type", "unknown" );
1393 mvar_AppendNewVar( f, "size", "unknown" );
1394 mvar_AppendNewVar( f, "date", "unknown" );
1396 mvar_AppendVar( s, f );
1402 static mvar_t *mvar_VlmSetNew( char *name, vlm_t *vlm )
1404 mvar_t *s = mvar_New( name, "set" );
1408 if( vlm == NULL ) return s;
1410 if( vlm_ExecuteCommand( vlm, "show", &msg ) )
1415 for( i = 0; i < msg->i_child; i++ )
1417 /* Over media, schedule */
1418 vlm_message_t *ch = msg->child[i];
1421 for( j = 0; j < ch->i_child; j++ )
1424 vlm_message_t *el = ch->child[j];
1425 vlm_message_t *inf, *desc;
1430 sprintf( psz, "show %s", el->psz_name );
1431 if( vlm_ExecuteCommand( vlm, psz, &inf ) )
1433 desc = inf->child[0];
1435 /* Add a node with name and info */
1436 set = mvar_New( name, "set" );
1437 mvar_AppendNewVar( set, "name", el->psz_name );
1439 for( k = 0; k < desc->i_child; k++ )
1441 vlm_message_t *ch = desc->child[k];
1442 if( ch->i_child > 0 )
1445 mvar_t *n = mvar_New( ch->psz_name, "set" );
1447 for( c = 0; c < ch->i_child; c++ )
1449 if( ch->child[c]->psz_value )
1451 mvar_AppendNewVar( n, ch->child[c]->psz_name, ch->child[c]->psz_value );
1455 mvar_t *in = mvar_New( ch->psz_name, ch->child[c]->psz_name );
1456 mvar_AppendVar( n, in );
1459 mvar_AppendVar( set, n );
1463 mvar_AppendNewVar( set, ch->psz_name, ch->psz_value );
1466 vlm_MessageDelete( inf );
1468 mvar_AppendVar( s, set );
1471 vlm_MessageDelete( msg );
1477 static void SSInit( rpn_stack_t * );
1478 static void SSClean( rpn_stack_t * );
1479 static void EvaluateRPN( intf_thread_t *p_intf, mvar_t *, rpn_stack_t *,
1482 static void SSPush ( rpn_stack_t *, char * );
1483 static char *SSPop ( rpn_stack_t * );
1485 static void SSPushN ( rpn_stack_t *, int );
1486 static int SSPopN ( rpn_stack_t *, mvar_t * );
1489 /****************************************************************************
1491 ****************************************************************************/
1499 static int FileLoad( FILE *f, char **pp_data, int *pi_data )
1503 /* just load the file */
1505 *pp_data = malloc( 1025 ); /* +1 for \0 */
1506 while( ( i_read = fread( &(*pp_data)[*pi_data], 1, 1024, f ) ) == 1024 )
1509 *pp_data = realloc( *pp_data, *pi_data + 1025 );
1515 (*pp_data)[*pi_data] = '\0';
1520 static int MacroParse( macro_t *m, char *psz_src )
1522 char *dup = strdup( (char *)psz_src );
1527 #define EXTRACT( name, l ) \
1529 p = strchr( src, '"' ); \
1534 m->name = strdup( src ); \
1555 if( !strncmp( src, "id=\"", 4 ) )
1559 else if( !strncmp( src, "param1=\"", 8 ) )
1561 EXTRACT( param1, 8 );
1563 else if( !strncmp( src, "param2=\"", 8 ) )
1565 EXTRACT( param2, 8 );
1572 if( strstr( src, "/>" ) )
1574 src = strstr( src, "/>" ) + 2;
1578 src += strlen( src );
1583 m->id = strdup( "" );
1585 if( m->param1 == NULL )
1587 m->param1 = strdup( "" );
1589 if( m->param2 == NULL )
1591 m->param2 = strdup( "" );
1600 static void MacroClean( macro_t *m )
1659 StrToMacroTypeTab [] =
1661 { "control", MVLC_CONTROL },
1662 /* player control */
1663 { "play", MVLC_PLAY },
1664 { "stop", MVLC_STOP },
1665 { "pause", MVLC_PAUSE },
1666 { "next", MVLC_NEXT },
1667 { "previous", MVLC_PREVIOUS },
1668 { "seek", MVLC_SEEK },
1669 { "keep", MVLC_KEEP },
1670 { "fullscreen", MVLC_FULLSCREEN },
1671 { "volume", MVLC_VOLUME },
1673 /* playlist management */
1674 { "add", MVLC_ADD },
1675 { "delete", MVLC_DEL },
1676 { "empty", MVLC_EMPTY },
1677 { "sort", MVLC_SORT },
1678 { "move", MVLC_MOVE },
1681 { "close", MVLC_CLOSE },
1682 { "shutdown", MVLC_SHUTDOWN },
1685 { "vlm_new", MVLC_VLM_NEW },
1686 { "vlm_setup", MVLC_VLM_SETUP },
1687 { "vlm_del", MVLC_VLM_DEL },
1688 { "vlm_play", MVLC_VLM_PLAY },
1689 { "vlm_pause", MVLC_VLM_PAUSE },
1690 { "vlm_stop", MVLC_VLM_STOP },
1691 { "vlm_seek", MVLC_VLM_SEEK },
1692 { "vlm_load", MVLC_VLM_LOAD },
1693 { "vlm_save", MVLC_VLM_SAVE },
1695 { "rpn", MVLC_RPN },
1696 { "stack", MVLC_STACK },
1698 { "foreach", MVLC_FOREACH },
1699 { "value", MVLC_VALUE },
1702 { "else", MVLC_ELSE },
1703 { "end", MVLC_END },
1704 { "get", MVLC_GET },
1705 { "set", MVLC_SET },
1706 { "int", MVLC_INT },
1707 { "float", MVLC_FLOAT },
1708 { "string", MVLC_STRING },
1711 { NULL, MVLC_UNKNOWN }
1714 static int StrToMacroType( char *name )
1718 if( !name || *name == '\0')
1720 return MVLC_UNKNOWN;
1722 for( i = 0; StrToMacroTypeTab[i].psz_name != NULL; i++ )
1724 if( !strcmp( name, StrToMacroTypeTab[i].psz_name ) )
1726 return StrToMacroTypeTab[i].i_type;
1729 return MVLC_UNKNOWN;
1732 static void MacroDo( httpd_file_sys_t *p_args,
1734 char *p_request, int i_request,
1735 char **pp_data, int *pi_data,
1738 intf_thread_t *p_intf = p_args->p_intf;
1739 intf_sys_t *p_sys = p_args->p_intf->p_sys;
1742 #define ALLOC( l ) \
1744 int __i__ = *pp_dst - *pp_data; \
1746 *pp_data = realloc( *pp_data, *pi_data ); \
1747 *pp_dst = (*pp_data) + __i__; \
1749 #define PRINT( str ) \
1750 ALLOC( strlen( str ) + 1 ); \
1751 *pp_dst += sprintf( *pp_dst, str );
1753 #define PRINTS( str, s ) \
1754 ALLOC( strlen( str ) + strlen( s ) + 1 ); \
1756 char * psz_cur = *pp_dst; \
1757 *pp_dst += sprintf( *pp_dst, str, s ); \
1758 while( psz_cur && *psz_cur ) \
1760 /* Prevent script injection */ \
1761 if( *psz_cur == '<' ) *psz_cur = '*'; \
1762 if( *psz_cur == '>' ) *psz_cur = '*'; \
1767 switch( StrToMacroType( m->id ) )
1770 if( i_request <= 0 )
1774 uri_extract_value( p_request, "control", control, 512 );
1775 if( *m->param1 && !strstr( m->param1, control ) )
1777 msg_Warn( p_intf, "unauthorized control=%s", control );
1780 switch( StrToMacroType( control ) )
1787 uri_extract_value( p_request, "item", item, 512 );
1788 i_item = atoi( item );
1789 playlist_Control( p_sys->p_playlist, PLAYLIST_ITEMPLAY,
1790 playlist_ItemGetById( p_sys->p_playlist,
1792 msg_Dbg( p_intf, "requested playlist item: %i", i_item );
1796 playlist_Control( p_sys->p_playlist, PLAYLIST_STOP );
1797 msg_Dbg( p_intf, "requested playlist stop" );
1800 playlist_Control( p_sys->p_playlist, PLAYLIST_PAUSE );
1801 msg_Dbg( p_intf, "requested playlist pause" );
1804 playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, 1 );
1805 msg_Dbg( p_intf, "requested playlist next" );
1808 playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1 );
1809 msg_Dbg( p_intf, "requested playlist previous" );
1811 case MVLC_FULLSCREEN:
1813 if( p_sys->p_input )
1815 vout_thread_t *p_vout;
1816 p_vout = vlc_object_find( p_sys->p_input,
1817 VLC_OBJECT_VOUT, FIND_CHILD );
1821 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
1822 vlc_object_release( p_vout );
1823 msg_Dbg( p_intf, "requested fullscreen toggle" );
1837 #define POSITION_ABSOLUTE 12
1838 #define POSITION_REL_FOR 13
1839 #define POSITION_REL_BACK 11
1840 #define VL_TIME_ABSOLUTE 0
1841 #define VL_TIME_REL_FOR 1
1842 #define VL_TIME_REL_BACK -1
1843 if( p_sys->p_input )
1845 uri_extract_value( p_request, "seek_value", value, 20 );
1846 uri_decode_url_encoded( value );
1848 var_Get( p_sys->p_input, "length", &val);
1849 i_length = val.i_time;
1851 while( p_value[0] != '\0' )
1857 i_relative = VL_TIME_REL_FOR;
1863 i_relative = VL_TIME_REL_BACK;
1867 case '0': case '1': case '2': case '3': case '4':
1868 case '5': case '6': case '7': case '8': case '9':
1870 i_stock = strtol( p_value , &p_value , 10 );
1873 case '%': /* for percentage ie position */
1875 i_relative += POSITION_ABSOLUTE;
1883 i_value = 60 * (i_value + i_stock) ;
1888 case 'h': case 'H': /* hours */
1890 i_value += 3600 * i_stock;
1892 /* other characters which are not numbers are not important */
1893 while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1899 case 'm': case 'M': case '\'': /* minutes */
1901 i_value += 60 * i_stock;
1904 while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1910 case 's': case 'S': case '"': /* seconds */
1914 while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1928 /* if there is no known symbol, I consider it as seconds. Otherwise, i_stock = 0 */
1933 case VL_TIME_ABSOLUTE:
1935 if( (uint64_t)( i_value ) * 1000000 <= i_length )
1936 val.i_time = (uint64_t)( i_value ) * 1000000;
1938 val.i_time = i_length;
1940 var_Set( p_sys->p_input, "time", val );
1941 msg_Dbg( p_intf, "requested seek position: %dsec", i_value );
1944 case VL_TIME_REL_FOR:
1946 var_Get( p_sys->p_input, "time", &val );
1947 if( (uint64_t)( i_value ) * 1000000 + val.i_time <= i_length )
1949 val.i_time = ((uint64_t)( i_value ) * 1000000) + val.i_time;
1952 val.i_time = i_length;
1954 var_Set( p_sys->p_input, "time", val );
1955 msg_Dbg( p_intf, "requested seek position forward: %dsec", i_value );
1958 case VL_TIME_REL_BACK:
1960 var_Get( p_sys->p_input, "time", &val );
1961 if( (int64_t)( i_value ) * 1000000 > val.i_time )
1966 val.i_time = val.i_time - ((uint64_t)( i_value ) * 1000000);
1968 var_Set( p_sys->p_input, "time", val );
1969 msg_Dbg( p_intf, "requested seek position backward: %dsec", i_value );
1972 case POSITION_ABSOLUTE:
1974 val.f_float = __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1975 var_Set( p_sys->p_input, "position", val );
1976 msg_Dbg( p_intf, "requested seek percent: %d", i_value );
1979 case POSITION_REL_FOR:
1981 var_Get( p_sys->p_input, "position", &val );
1982 val.f_float += __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1983 var_Set( p_sys->p_input, "position", val );
1984 msg_Dbg( p_intf, "requested seek percent forward: %d", i_value );
1987 case POSITION_REL_BACK:
1989 var_Get( p_sys->p_input, "position", &val );
1990 val.f_float -= __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1991 var_Set( p_sys->p_input, "position", val );
1992 msg_Dbg( p_intf, "requested seek percent backward: %d", i_value );
1997 msg_Dbg( p_intf, "requested seek: what the f*** is going on here ?" );
2002 #undef POSITION_ABSOLUTE
2003 #undef POSITION_REL_FOR
2004 #undef POSITION_REL_BACK
2005 #undef VL_TIME_ABSOLUTE
2006 #undef VL_TIME_REL_FOR
2007 #undef VL_TIME_REL_BACK
2013 audio_volume_t i_volume;
2016 uri_extract_value( p_request, "value", vol, 8 );
2017 aout_VolumeGet( p_intf, &i_volume );
2018 uri_decode_url_encoded( vol );
2022 i_value = atoi( vol + 1 );
2023 if( (i_volume + i_value) > AOUT_VOLUME_MAX )
2025 aout_VolumeSet( p_intf , AOUT_VOLUME_MAX );
2026 msg_Dbg( p_intf, "requested volume set: max" );
2030 aout_VolumeSet( p_intf , (i_volume + i_value) );
2031 msg_Dbg( p_intf, "requested volume set: +%i", (i_volume + i_value) );
2034 else if( vol[0] == '-' )
2036 i_value = atoi( vol + 1 );
2037 if( (i_volume - i_value) < AOUT_VOLUME_MIN )
2039 aout_VolumeSet( p_intf , AOUT_VOLUME_MIN );
2040 msg_Dbg( p_intf, "requested volume set: min" );
2044 aout_VolumeSet( p_intf , (i_volume - i_value) );
2045 msg_Dbg( p_intf, "requested volume set: -%i", (i_volume - i_value) );
2048 else if( strstr(vol, "%") != NULL )
2050 i_value = atoi( vol );
2051 if( (i_value <= 400) && (i_value>=0) ){
2052 aout_VolumeSet( p_intf, (i_value * (AOUT_VOLUME_MAX - AOUT_VOLUME_MIN))/400+AOUT_VOLUME_MIN);
2053 msg_Dbg( p_intf, "requested volume set: %i%%", atoi( vol ));
2058 i_value = atoi( vol );
2059 if( ( i_value <= AOUT_VOLUME_MAX ) && ( i_value >= AOUT_VOLUME_MIN ) )
2061 aout_VolumeSet( p_intf , atoi( vol ) );
2062 msg_Dbg( p_intf, "requested volume set: %i", atoi( vol ) );
2068 /* playlist management */
2071 char mrl[1024], psz_name[1024];
2072 playlist_item_t *p_item;
2074 uri_extract_value( p_request, "mrl", mrl, 1024 );
2075 uri_decode_url_encoded( mrl );
2076 uri_extract_value( p_request, "name", psz_name, 1024 );
2077 uri_decode_url_encoded( psz_name );
2080 memcpy( psz_name, mrl, 1024 );
2082 p_item = parse_MRL( p_intf, mrl, psz_name );
2084 if( !p_item || !p_item->input.psz_uri ||
2085 !*p_item->input.psz_uri )
2087 msg_Dbg( p_intf, "invalid requested mrl: %s", mrl );
2091 playlist_AddItem( p_sys->p_playlist, p_item,
2092 PLAYLIST_APPEND, PLAYLIST_END );
2093 msg_Dbg( p_intf, "requested mrl add: %s", mrl );
2100 int i_item, *p_items = NULL, i_nb_items = 0;
2101 char item[512], *p_parser = p_request;
2103 /* Get the list of items to delete */
2105 uri_extract_value( p_parser, "item", item, 512 )) )
2107 if( !*item ) continue;
2109 i_item = atoi( item );
2110 p_items = realloc( p_items, (i_nb_items + 1) *
2112 p_items[i_nb_items] = i_item;
2119 for( i = 0; i < i_nb_items; i++ )
2121 playlist_LockDelete( p_sys->p_playlist, p_items[i] );
2122 msg_Dbg( p_intf, "requested playlist delete: %d",
2128 if( p_items ) free( p_items );
2133 int i_item, *p_items = NULL, i_nb_items = 0;
2134 char item[512], *p_parser = p_request;
2137 /* Get the list of items to keep */
2139 uri_extract_value( p_parser, "item", item, 512 )) )
2141 if( !*item ) continue;
2143 i_item = atoi( item );
2144 p_items = realloc( p_items, (i_nb_items + 1) *
2146 p_items[i_nb_items] = i_item;
2150 for( i = p_sys->p_playlist->i_size - 1 ; i >= 0; i-- )
2152 /* Check if the item is in the keep list */
2153 for( j = 0 ; j < i_nb_items ; j++ )
2156 p_sys->p_playlist->pp_items[i]->input.i_id ) break;
2158 if( j == i_nb_items )
2160 playlist_LockDelete( p_sys->p_playlist, p_sys->p_playlist->pp_items[i]->input.i_id );
2161 msg_Dbg( p_intf, "requested playlist delete: %d",
2166 if( p_items ) free( p_items );
2171 playlist_LockClear( p_sys->p_playlist );
2172 msg_Dbg( p_intf, "requested playlist empty" );
2183 uri_extract_value( p_request, "type", type, 12 );
2184 uri_extract_value( p_request, "order", order, 2 );
2185 uri_extract_value( p_request, "item", item, 512 );
2186 i_item = atoi( item );
2188 if( order[0] == '0' ) i_order = ORDER_NORMAL;
2189 else i_order = ORDER_REVERSE;
2191 if( !strcmp( type , "title" ) )
2193 playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/
2194 p_sys->p_playlist->pp_views[0]->p_root,
2195 SORT_TITLE_NODES_FIRST,
2196 ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE );
2197 msg_Dbg( p_intf, "requested playlist sort by title (%d)" , i_order );
2199 else if( !strcmp( type , "author" ) )
2201 playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/
2202 p_sys->p_playlist->pp_views[0]->p_root,
2204 ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE );
2205 msg_Dbg( p_intf, "requested playlist sort by author (%d)" , i_order );
2207 else if( !strcmp( type , "shuffle" ) )
2209 playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/
2210 p_sys->p_playlist->pp_views[0]->p_root,
2212 ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE );
2213 msg_Dbg( p_intf, "requested playlist shuffle");
2224 uri_extract_value( p_request, "psz_pos", psz_pos, 6 );
2225 uri_extract_value( p_request, "psz_newpos", psz_newpos, 6 );
2226 i_pos = atoi( psz_pos );
2227 i_newpos = atoi( psz_newpos );
2228 if ( i_pos < i_newpos )
2230 playlist_Move( p_sys->p_playlist, i_pos, i_newpos + 1 );
2234 playlist_Move( p_sys->p_playlist, i_pos, i_newpos );
2236 msg_Dbg( p_intf, "requested move playlist item %d to %d", i_pos, i_newpos);
2240 /* admin function */
2244 uri_extract_value( p_request, "id", id, 512 );
2245 msg_Dbg( p_intf, "requested close id=%s", id );
2247 if( p_sys->p_httpd->pf_control( p_sys->p_httpd, HTTPD_SET_CLOSE, id, NULL ) )
2249 msg_Warn( p_intf, "close failed for id=%s", id );
2256 msg_Dbg( p_intf, "requested shutdown" );
2257 p_intf->p_vlc->b_die = VLC_TRUE;
2262 case MVLC_VLM_SETUP:
2264 static const char *vlm_properties[11] =
2267 "enabled", "disabled", "loop", "unloop",
2269 "input", "output", "option", "date", "period", "repeat", "append",
2271 vlm_message_t *vlm_answer;
2273 char *psz = malloc( strlen( p_request ) + 1000 );
2278 if( p_intf->p_sys->p_vlm == NULL )
2279 p_intf->p_sys->p_vlm = vlm_New( p_intf );
2281 if( p_intf->p_sys->p_vlm == NULL ) break;
2283 uri_extract_value( p_request, "name", name, 512 );
2284 if( StrToMacroType( control ) == MVLC_VLM_NEW )
2287 uri_extract_value( p_request, "type", type, 20 );
2288 p += sprintf( psz, "new %s %s", name, type );
2292 p += sprintf( psz, "setup %s", name );
2294 /* Parse the request */
2295 for( i = 0; i < 11; i++ )
2298 uri_extract_value( p_request, vlm_properties[i], val, 512 );
2299 uri_decode_url_encoded( val );
2300 if( strlen( val ) > 0 && i >= 4 )
2302 p += sprintf( p, " %s %s", vlm_properties[i], val );
2304 else if( uri_test_param( p_request, vlm_properties[i] ) && i < 4 )
2306 p += sprintf( p, " %s", vlm_properties[i] );
2309 vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2310 if( vlm_answer->psz_value == NULL ) /* there is no error */
2312 vlm_error = strdup( "" );
2316 vlm_error = malloc( strlen(vlm_answer->psz_name) +
2317 strlen(vlm_answer->psz_value) +
2318 strlen( " : ") + 1 );
2319 sprintf( vlm_error , "%s : %s" , vlm_answer->psz_name,
2320 vlm_answer->psz_value );
2323 mvar_AppendNewVar( p_args->vars, "vlm_error", vlm_error );
2325 vlm_MessageDelete( vlm_answer );
2333 vlm_message_t *vlm_answer;
2336 if( p_intf->p_sys->p_vlm == NULL )
2337 p_intf->p_sys->p_vlm = vlm_New( p_intf );
2339 if( p_intf->p_sys->p_vlm == NULL ) break;
2341 uri_extract_value( p_request, "name", name, 512 );
2342 sprintf( psz, "del %s", name );
2344 vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2345 /* FIXME do a vlm_answer -> var stack conversion */
2346 vlm_MessageDelete( vlm_answer );
2351 case MVLC_VLM_PAUSE:
2355 vlm_message_t *vlm_answer;
2358 if( p_intf->p_sys->p_vlm == NULL )
2359 p_intf->p_sys->p_vlm = vlm_New( p_intf );
2361 if( p_intf->p_sys->p_vlm == NULL ) break;
2363 uri_extract_value( p_request, "name", name, 512 );
2364 if( StrToMacroType( control ) == MVLC_VLM_PLAY )
2365 sprintf( psz, "control %s play", name );
2366 else if( StrToMacroType( control ) == MVLC_VLM_PAUSE )
2367 sprintf( psz, "control %s pause", name );
2368 else if( StrToMacroType( control ) == MVLC_VLM_STOP )
2369 sprintf( psz, "control %s stop", name );
2370 else if( StrToMacroType( control ) == MVLC_VLM_SEEK )
2373 uri_extract_value( p_request, "percent", percent, 512 );
2374 sprintf( psz, "control %s seek %s", name, percent );
2377 vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2378 /* FIXME do a vlm_answer -> var stack conversion */
2379 vlm_MessageDelete( vlm_answer );
2385 vlm_message_t *vlm_answer;
2389 if( p_intf->p_sys->p_vlm == NULL )
2390 p_intf->p_sys->p_vlm = vlm_New( p_intf );
2392 if( p_intf->p_sys->p_vlm == NULL ) break;
2394 uri_extract_value( p_request, "file", file, 512 );
2395 uri_decode_url_encoded( file );
2397 if( StrToMacroType( control ) == MVLC_VLM_LOAD )
2398 sprintf( psz, "load %s", file );
2400 sprintf( psz, "save %s", file );
2402 vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2403 /* FIXME do a vlm_answer -> var stack conversion */
2404 vlm_MessageDelete( vlm_answer );
2411 PRINTS( "<!-- control param(%s) unsupported -->", control );
2423 if( i_request <= 0 ||
2424 *m->param1 == '\0' ||
2425 strstr( p_request, m->param1 ) == NULL )
2429 uri_extract_value( p_request, m->param1, value, 512 );
2430 uri_decode_url_encoded( value );
2432 switch( StrToMacroType( m->param2 ) )
2436 config_PutInt( p_intf, m->param1, i );
2440 config_PutFloat( p_intf, m->param1, f );
2443 config_PutPsz( p_intf, m->param1, value );
2446 PRINTS( "<!-- invalid type(%s) in set -->", m->param2 )
2457 if( *m->param1 == '\0' )
2462 switch( StrToMacroType( m->param2 ) )
2465 i = config_GetInt( p_intf, m->param1 );
2466 sprintf( value, "%d", i );
2469 f = config_GetFloat( p_intf, m->param1 );
2470 sprintf( value, "%f", f );
2473 psz = config_GetPsz( p_intf, m->param1 );
2476 strncpy( value, psz,sizeof( value ) );
2478 value[sizeof( value ) - 1] = '\0';
2482 msg_Dbg( p_intf, "%d: value = \"%s\"", __LINE__, value );
2485 snprintf( value, sizeof( value ),
2486 "invalid type(%s) in set", m->param2 );
2487 value[sizeof( value ) - 1] = '\0';
2490 PRINTS( "%s", value );
2499 EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m->param1 );
2500 s = SSPop( &p_args->stack );
2501 v = mvar_GetValue( p_args->vars, s );
2505 v = s = SSPop( &p_args->stack );
2513 EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m->param1 );
2516 /* Usefull for learning stack management */
2520 msg_Dbg( p_intf, "stack" );
2521 for (i=0;i<(&p_args->stack)->i_stack;i++)
2522 msg_Dbg( p_intf, "%d -> %s", i, (&p_args->stack)->stack[i] );
2528 PRINTS( "<!-- invalid macro id=`%s' -->", m->id );
2529 msg_Dbg( p_intf, "invalid macro id=`%s'", m->id );
2537 static char *MacroSearch( char *src, char *end, int i_mvlc, vlc_bool_t b_after )
2544 if( src + 4 < end && !strncmp( (char *)src, "<vlc", 4 ) )
2549 i_skip = MacroParse( &m, src );
2551 i_id = StrToMacroType( m.id );
2568 if( ( i_mvlc == MVLC_END && i_level == -1 ) ||
2569 ( i_mvlc != MVLC_END && i_level == 0 && i_mvlc == i_id ) )
2571 return src + ( b_after ? i_skip : 0 );
2573 else if( i_level < 0 )
2589 static void Execute( httpd_file_sys_t *p_args,
2590 char *p_request, int i_request,
2591 char **pp_data, int *pi_data,
2593 char *_src, char *_end )
2595 intf_thread_t *p_intf = p_args->p_intf;
2597 char *src, *dup, *end;
2598 char *dst = *pp_dst;
2600 src = dup = malloc( _end - _src + 1 );
2601 end = src +( _end - _src );
2603 memcpy( src, _src, _end - _src );
2606 /* we parse searching <vlc */
2612 p = (char *)strstr( (char *)src, "<vlc" );
2613 if( p < end && p == src )
2617 src += MacroParse( &m, src );
2619 //msg_Dbg( p_intf, "macro_id=%s", m.id );
2621 switch( StrToMacroType( m.id ) )
2628 EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m.param1 );
2629 if( SSPopN( &p_args->stack, p_args->vars ) )
2637 endif = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2641 char *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE );
2645 char *stop = MacroSearch( start, endif, MVLC_END, VLC_FALSE );
2648 Execute( p_args, p_request, i_request,
2649 pp_data, pi_data, &dst, start, stop );
2653 else if( i_test == 1 )
2656 if( ( stop = MacroSearch( src, endif, MVLC_ELSE, VLC_FALSE ) ) == NULL )
2658 stop = MacroSearch( src, endif, MVLC_END, VLC_FALSE );
2662 Execute( p_args, p_request, i_request,
2663 pp_data, pi_data, &dst, src, stop );
2672 char *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2674 char *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE );
2681 if( !strcmp( m.param2, "integer" ) )
2683 char *arg = SSPop( &p_args->stack );
2684 index = mvar_IntegerSetNew( m.param1, arg );
2687 else if( !strcmp( m.param2, "directory" ) )
2689 char *arg = SSPop( &p_args->stack );
2690 index = mvar_FileSetNew( p_intf, m.param1, arg );
2693 else if( !strcmp( m.param2, "playlist" ) )
2695 index = mvar_PlaylistSetNew( p_intf, m.param1,
2696 p_intf->p_sys->p_playlist );
2698 else if( !strcmp( m.param2, "information" ) )
2700 index = mvar_InfoSetNew( p_intf, m.param1,
2701 p_intf->p_sys->p_input );
2703 else if( !strcmp( m.param2, "vlm" ) )
2705 if( p_intf->p_sys->p_vlm == NULL )
2706 p_intf->p_sys->p_vlm = vlm_New( p_intf );
2707 index = mvar_VlmSetNew( m.param1, p_intf->p_sys->p_vlm );
2710 else if( !strcmp( m.param2, "hosts" ) )
2712 index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_HOSTS );
2714 else if( !strcmp( m.param2, "urls" ) )
2716 index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_URLS );
2718 else if( !strcmp( m.param2, "connections" ) )
2720 index = mvar_HttpdInfoSetNew(m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_CONNECTIONS);
2723 else if( ( v = mvar_GetVar( p_args->vars, m.param2 ) ) )
2725 index = mvar_Duplicate( v );
2729 msg_Dbg( p_intf, "invalid index constructor (%s)", m.param2 );
2734 for( i_idx = 0; i_idx < index->i_field; i_idx++ )
2736 mvar_t *f = mvar_Duplicate( index->field[i_idx] );
2738 //msg_Dbg( p_intf, "foreach field[%d] name=%s value=%s", i_idx, f->name, f->value );
2741 f->name = strdup( m.param1 );
2744 mvar_PushVar( p_args->vars, f );
2745 Execute( p_args, p_request, i_request,
2746 pp_data, pi_data, &dst, start, stop );
2747 mvar_RemoveVar( p_args->vars, f );
2751 mvar_Delete( index );
2758 MacroDo( p_args, &m, p_request, i_request,
2759 pp_data, pi_data, &dst );
2767 i_copy = ( (p == NULL || p > end ) ? end : p ) - src;
2770 int i_index = dst - *pp_data;
2773 *pp_data = realloc( *pp_data, *pi_data );
2774 dst = (*pp_data) + i_index;
2776 memcpy( dst, src, i_copy );
2786 /****************************************************************************
2788 ****************************************************************************
2789 * a file with b_html is parsed and all "macro" replaced
2790 * <vlc id="macro name" [param1="" [param2=""]] />
2793 ****************************************************************************/
2794 static int HttpCallback( httpd_file_sys_t *p_args,
2795 httpd_file_t *p_file,
2796 uint8_t *_p_request,
2797 uint8_t **_pp_data, int *pi_data )
2799 char *p_request = (char *)_p_request;
2800 char **pp_data = (char **)_pp_data;
2801 int i_request = p_request ? strlen( p_request ) : 0;
2805 if( ( f = fopen( p_args->file, "r" ) ) == NULL )
2807 p = *pp_data = malloc( 10240 );
2810 return VLC_EGENERIC;
2812 p += sprintf( p, "<html>\n" );
2813 p += sprintf( p, "<head>\n" );
2814 p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
2815 p += sprintf( p, "</head>\n" );
2816 p += sprintf( p, "<body>\n" );
2817 p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
2818 p += sprintf( p, "<hr />\n" );
2819 p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
2820 p += sprintf( p, "</body>\n" );
2821 p += sprintf( p, "</html>\n" );
2823 *pi_data = strlen( *pp_data );
2828 if( !p_args->b_html )
2830 FileLoad( f, pp_data, pi_data );
2838 char position[4]; /* percentage */
2839 char time[12]; /* in seconds */
2840 char length[12]; /* in seconds */
2841 audio_volume_t i_volume;
2845 #define p_sys p_args->p_intf->p_sys
2846 if( p_sys->p_input )
2848 var_Get( p_sys->p_input, "position", &val);
2849 sprintf( position, "%d" , (int)((val.f_float) * 100.0));
2850 var_Get( p_sys->p_input, "time", &val);
2851 sprintf( time, "%d" , (int)(val.i_time / 1000000) );
2852 var_Get( p_sys->p_input, "length", &val);
2853 sprintf( length, "%d" , (int)(val.i_time / 1000000) );
2855 var_Get( p_sys->p_input, "state", &val );
2856 if( val.i_int == PLAYING_S )
2858 sprintf( state, "playing" );
2859 } else if( val.i_int == PAUSE_S )
2861 sprintf( state, "paused" );
2864 sprintf( state, "stop" );
2868 sprintf( position, "%d", 0 );
2869 sprintf( time, "%d", 0 );
2870 sprintf( length, "%d", 0 );
2871 sprintf( state, "stop" );
2875 aout_VolumeGet( p_args->p_intf , &i_volume );
2876 sprintf( volume , "%d" , (int)i_volume );
2878 p_args->vars = mvar_New( "variables", "" );
2879 mvar_AppendNewVar( p_args->vars, "url_param", i_request > 0 ? "1" : "0" );
2880 mvar_AppendNewVar( p_args->vars, "url_value", p_request );
2881 mvar_AppendNewVar( p_args->vars, "version", VERSION_MESSAGE );
2882 mvar_AppendNewVar( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
2883 mvar_AppendNewVar( p_args->vars, "stream_position", position );
2884 mvar_AppendNewVar( p_args->vars, "stream_time", time );
2885 mvar_AppendNewVar( p_args->vars, "stream_length", length );
2886 mvar_AppendNewVar( p_args->vars, "volume", volume );
2887 mvar_AppendNewVar( p_args->vars, "stream_state", state );
2889 SSInit( &p_args->stack );
2891 /* first we load in a temporary buffer */
2892 FileLoad( f, &p_buffer, &i_buffer );
2894 /* allocate output */
2895 *pi_data = i_buffer + 1000;
2896 dst = *pp_data = malloc( *pi_data );
2898 /* we parse executing all <vlc /> macros */
2899 Execute( p_args, p_request, i_request, pp_data, pi_data, &dst,
2900 &p_buffer[0], &p_buffer[i_buffer] );
2903 *pi_data = dst - *pp_data;
2905 SSClean( &p_args->stack );
2906 mvar_Delete( p_args->vars );
2915 /****************************************************************************
2917 ****************************************************************************/
2918 static int uri_test_param( char *psz_uri, const char *psz_name )
2922 while( (p = strstr( p, psz_name )) )
2924 /* Verify that we are dealing with a post/get argument */
2925 if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
2934 static char *uri_extract_value( char *psz_uri, const char *psz_name,
2935 char *psz_value, int i_value_max )
2939 while( (p = strstr( p, psz_name )) )
2941 /* Verify that we are dealing with a post/get argument */
2942 if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
2951 p += strlen( psz_name );
2952 if( *p == '=' ) p++;
2954 if( strchr( p, '&' ) )
2956 i_len = strchr( p, '&' ) - p;
2960 /* for POST method */
2961 if( strchr( p, '\n' ) )
2963 i_len = strchr( p, '\n' ) - p;
2964 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
2968 i_len = strlen( p );
2971 i_len = __MIN( i_value_max - 1, i_len );
2974 strncpy( psz_value, p, i_len );
2975 psz_value[i_len] = '\0';
2979 strncpy( psz_value, "", i_value_max );
2985 strncpy( psz_value, "", i_value_max );
2991 static void uri_decode_url_encoded( char *psz )
2993 char *dup = strdup( psz );
3011 *psz++ = strtol( val, NULL, 16 );
3013 else if( *p == '+' )
3027 /****************************************************************************
3028 * Light RPN evaluator
3029 ****************************************************************************/
3030 static void SSInit( rpn_stack_t *st )
3035 static void SSClean( rpn_stack_t *st )
3037 while( st->i_stack > 0 )
3039 free( st->stack[--st->i_stack] );
3043 static void SSPush( rpn_stack_t *st, char *s )
3045 if( st->i_stack < STACK_MAX )
3047 st->stack[st->i_stack++] = strdup( s );
3051 static char * SSPop( rpn_stack_t *st )
3053 if( st->i_stack <= 0 )
3055 return strdup( "" );
3059 return st->stack[--st->i_stack];
3063 static int SSPopN( rpn_stack_t *st, mvar_t *vars )
3072 i = strtol( name, &end, 0 );
3075 value = mvar_GetValue( vars, name );
3083 static void SSPushN( rpn_stack_t *st, int i )
3087 sprintf( v, "%d", i );
3091 static void EvaluateRPN( intf_thread_t *p_intf, mvar_t *vars,
3092 rpn_stack_t *st, char *exp )
3094 intf_sys_t *p_sys = p_intf->p_sys;
3101 while( *exp == ' ' )
3108 /* extract string */
3111 while( *exp && *exp != '\'' )
3113 /* strip a level of backslashes */
3114 if( *exp == '\\' && exp[1] != '\0' )
3125 p = strchr( exp, ' ' );
3130 exp += strlen( exp );
3135 strncpy( s, exp, i );
3146 /* 1. Integer function */
3147 if( !strcmp( s, "!" ) )
3149 SSPushN( st, ~SSPopN( st, vars ) );
3151 else if( !strcmp( s, "^" ) )
3153 SSPushN( st, SSPopN( st, vars ) ^ SSPopN( st, vars ) );
3155 else if( !strcmp( s, "&" ) )
3157 SSPushN( st, SSPopN( st, vars ) & SSPopN( st, vars ) );
3159 else if( !strcmp( s, "|" ) )
3161 SSPushN( st, SSPopN( st, vars ) | SSPopN( st, vars ) );
3163 else if( !strcmp( s, "+" ) )
3165 SSPushN( st, SSPopN( st, vars ) + SSPopN( st, vars ) );
3167 else if( !strcmp( s, "-" ) )
3169 int j = SSPopN( st, vars );
3170 int i = SSPopN( st, vars );
3171 SSPushN( st, i - j );
3173 else if( !strcmp( s, "*" ) )
3175 SSPushN( st, SSPopN( st, vars ) * SSPopN( st, vars ) );
3177 else if( !strcmp( s, "/" ) )
3181 j = SSPopN( st, vars );
3182 i = SSPopN( st, vars );
3184 SSPushN( st, j != 0 ? i / j : 0 );
3186 else if( !strcmp( s, "%" ) )
3190 j = SSPopN( st, vars );
3191 i = SSPopN( st, vars );
3193 SSPushN( st, j != 0 ? i % j : 0 );
3195 /* 2. integer tests */
3196 else if( !strcmp( s, "=" ) )
3198 SSPushN( st, SSPopN( st, vars ) == SSPopN( st, vars ) ? -1 : 0 );
3200 else if( !strcmp( s, "!=" ) )
3202 SSPushN( st, SSPopN( st, vars ) != SSPopN( st, vars ) ? -1 : 0 );
3204 else if( !strcmp( s, "<" ) )
3206 int j = SSPopN( st, vars );
3207 int i = SSPopN( st, vars );
3209 SSPushN( st, i < j ? -1 : 0 );
3211 else if( !strcmp( s, ">" ) )
3213 int j = SSPopN( st, vars );
3214 int i = SSPopN( st, vars );
3216 SSPushN( st, i > j ? -1 : 0 );
3218 else if( !strcmp( s, "<=" ) )
3220 int j = SSPopN( st, vars );
3221 int i = SSPopN( st, vars );
3223 SSPushN( st, i <= j ? -1 : 0 );
3225 else if( !strcmp( s, ">=" ) )
3227 int j = SSPopN( st, vars );
3228 int i = SSPopN( st, vars );
3230 SSPushN( st, i >= j ? -1 : 0 );
3232 /* 3. string functions */
3233 else if( !strcmp( s, "strcat" ) )
3235 char *s2 = SSPop( st );
3236 char *s1 = SSPop( st );
3237 char *str = malloc( strlen( s1 ) + strlen( s2 ) + 1 );
3247 else if( !strcmp( s, "strcmp" ) )
3249 char *s2 = SSPop( st );
3250 char *s1 = SSPop( st );
3252 SSPushN( st, strcmp( s1, s2 ) );
3256 else if( !strcmp( s, "strncmp" ) )
3258 int n = SSPopN( st, vars );
3259 char *s2 = SSPop( st );
3260 char *s1 = SSPop( st );
3262 SSPushN( st, strncmp( s1, s2 , n ) );
3266 else if( !strcmp( s, "strsub" ) )
3268 int n = SSPopN( st, vars );
3269 int m = SSPopN( st, vars );
3271 char *s = SSPop( st );
3283 str = malloc( i_len + 1 );
3285 memcpy( str, s + m - 1, i_len );
3286 str[ i_len ] = '\0';
3292 else if( !strcmp( s, "strlen" ) )
3294 char *str = SSPop( st );
3296 SSPushN( st, strlen( str ) );
3299 /* 4. stack functions */
3300 else if( !strcmp( s, "dup" ) )
3302 char *str = SSPop( st );
3307 else if( !strcmp( s, "drop" ) )
3309 char *str = SSPop( st );
3312 else if( !strcmp( s, "swap" ) )
3314 char *s1 = SSPop( st );
3315 char *s2 = SSPop( st );
3322 else if( !strcmp( s, "flush" ) )
3327 else if( !strcmp( s, "store" ) )
3329 char *value = SSPop( st );
3330 char *name = SSPop( st );
3332 mvar_PushNewVar( vars, name, value );
3336 else if( !strcmp( s, "value" ) )
3338 char *name = SSPop( st );
3339 char *value = mvar_GetValue( vars, name );
3341 SSPush( st, value );
3345 else if( !strcmp( s, "url_extract" ) )
3347 char *url = mvar_GetValue( vars, "url_value" );
3348 char *name = SSPop( st );
3352 uri_extract_value( url, name, value, 512 );
3353 uri_decode_url_encoded( value );
3354 tmp = FromUTF8( p_intf, value );
3358 else if( !strcmp( s, "url_encode" ) )
3360 char *url = SSPop( st );
3363 url = ToUTF8( p_intf, url );
3364 value = vlc_UrlEncode( url );
3366 SSPush( st, value );
3369 /* 5. player control */
3370 else if( !strcmp( s, "vlc_play" ) )
3372 int i_id = SSPopN( st, vars );
3375 i_ret = playlist_Control( p_sys->p_playlist, PLAYLIST_ITEMPLAY,
3376 playlist_ItemGetById( p_sys->p_playlist,
3378 msg_Dbg( p_intf, "requested playlist item: %i", i_id );
3379 SSPushN( st, i_ret );
3381 else if( !strcmp( s, "vlc_stop" ) )
3383 playlist_Control( p_sys->p_playlist, PLAYLIST_STOP );
3384 msg_Dbg( p_intf, "requested playlist stop" );
3386 else if( !strcmp( s, "vlc_pause" ) )
3388 playlist_Control( p_sys->p_playlist, PLAYLIST_PAUSE );
3389 msg_Dbg( p_intf, "requested playlist pause" );
3391 else if( !strcmp( s, "vlc_next" ) )
3393 playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, 1 );
3394 msg_Dbg( p_intf, "requested playlist next" );
3396 else if( !strcmp( s, "vlc_previous" ) )
3398 playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1 );
3399 msg_Dbg( p_intf, "requested playlist previous" );
3401 /* 6. playlist functions */
3402 else if( !strcmp( s, "playlist_add" ) )
3404 char *psz_name = SSPop( st );
3405 char *mrl = SSPop( st );
3406 playlist_item_t *p_item;
3409 psz_name = ToUTF8( p_intf, psz_name );
3410 mrl = ToUTF8( p_intf, mrl );
3414 p_item = parse_MRL( p_intf, mrl, mrl );
3418 p_item = parse_MRL( p_intf, mrl, psz_name );
3421 if( p_item == NULL || p_item->input.psz_uri == NULL ||
3422 !*p_item->input.psz_uri )
3424 i_id = VLC_EGENERIC;
3425 msg_Dbg( p_intf, "invalid requested mrl: %s", mrl );
3429 i_id = playlist_AddItem( p_sys->p_playlist, p_item,
3430 PLAYLIST_APPEND, PLAYLIST_END );
3431 msg_Dbg( p_intf, "requested mrl add: %s", mrl );
3433 SSPushN( st, i_id );
3438 else if( !strcmp( s, "playlist_empty" ) )
3440 playlist_LockClear( p_sys->p_playlist );
3441 msg_Dbg( p_intf, "requested playlist empty" );
3450 /**********************************************************************
3451 * Find_end_MRL: Find the end of the sentence :
3452 * this function parses the string psz and find the end of the item
3453 * and/or option with detecting the " and ' problems.
3454 * returns NULL if an error is detected, otherwise, returns a pointer
3455 * of the end of the sentence (after the last character)
3456 **********************************************************************/
3457 static char *Find_end_MRL( char *psz )
3467 while( ( *s_sent != '\"' ) && ( *s_sent != '\0' ) )
3469 if( *s_sent == '\'' )
3471 s_sent = Find_end_MRL( s_sent );
3473 if( s_sent == NULL )
3483 if( *s_sent == '\"' )
3487 } else /* *s_sent == '\0' , which means the number of " is incorrect */
3497 while( ( *s_sent != '\'' ) && ( *s_sent != '\0' ) )
3499 if( *s_sent == '\"' )
3501 s_sent = Find_end_MRL( s_sent );
3503 if( s_sent == NULL )
3513 if( *s_sent == '\'' )
3517 } else /* *s_sent == '\0' , which means the number of ' is incorrect */
3523 default: /* now we can look for spaces */
3525 while( ( *s_sent != ' ' ) && ( *s_sent != '\0' ) )
3527 if( ( *s_sent == '\'' ) || ( *s_sent == '\"' ) )
3529 s_sent = Find_end_MRL( s_sent );
3540 /**********************************************************************
3541 * parse_MRL: parse the MRL, find the mrl string and the options,
3542 * create an item with all information in it, and return the item.
3543 * return NULL if there is an error.
3544 **********************************************************************/
3545 static playlist_item_t *parse_MRL( intf_thread_t *p_intf, char *psz,
3548 char **ppsz_options = NULL;
3555 playlist_item_t * p_item = NULL;
3557 /* In case there is spaces before the mrl */
3558 while( ( *s_mrl == ' ' ) && ( *s_mrl != '\0' ) )
3563 /* extract the mrl */
3564 s_temp = strstr( s_mrl , " :" );
3565 if( s_temp == NULL )
3567 s_temp = s_mrl + strlen( s_mrl );
3570 while( (*s_temp == ' ') && (s_temp != s_mrl ) )
3577 /* if the mrl is between " or ', we must remove them */
3578 if( (*s_mrl == '\'') || (*s_mrl == '\"') )
3580 mrl = (char *)malloc( (s_temp - s_mrl - 1) * sizeof( char ) );
3581 strncpy( mrl , (s_mrl + 1) , s_temp - s_mrl - 2 );
3582 mrl[ s_temp - s_mrl - 2 ] = '\0';
3585 mrl = (char *)malloc( (s_temp - s_mrl + 1) * sizeof( char ) );
3586 strncpy( mrl , s_mrl , s_temp - s_mrl );
3587 mrl[ s_temp - s_mrl ] = '\0';
3592 /* now we can take care of the options */
3593 while( (*s_mrl != '\0') && (i_error == 0) )
3602 case ':': /* an option */
3604 s_temp = Find_end_MRL( s_mrl );
3606 if( s_temp == NULL )
3613 ppsz_options = realloc( ppsz_options , i_options *
3615 ppsz_options[ i_options - 1 ] =
3616 malloc( (s_temp - s_mrl + 1) * sizeof(char) );
3618 strncpy( ppsz_options[ i_options - 1 ] , s_mrl ,
3621 /* don't forget to finish the string with a '\0' */
3622 (ppsz_options[ i_options - 1 ])[ s_temp - s_mrl ] = '\0';
3642 /* now create an item */
3643 p_item = playlist_ItemNew( p_intf, mrl, psz_name );
3644 for( i = 0 ; i< i_options ; i++ )
3646 playlist_ItemAddOption( p_item, ppsz_options[i] );
3650 for( i = 0; i < i_options; i++ ) free( ppsz_options[i] );
3651 if( i_options ) free( ppsz_options );