]> git.sesse.net Git - vlc/blob - modules/control/http/mvar.c
Control interfaces first string review.
[vlc] / modules / control / http / mvar.c
1 /*****************************************************************************
2  * mvar.c : Variables handling for the HTTP Interface
3  *****************************************************************************
4  * Copyright (C) 2001-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #include "http.h"
27
28 /* Utility function for scandir */
29 static int Filter( const char *foo )
30 {
31     return strcmp( foo, "." );
32 };
33
34 static int InsensitiveAlphasort( const char **foo1,
35                                  const char **foo2 )
36 {
37     return strcasecmp( *foo1, *foo2 );
38 };
39
40
41
42 mvar_t *E_(mvar_New)( const char *name, const char *value )
43 {
44     mvar_t *v = malloc( sizeof( mvar_t ) );
45
46     if( !v ) return NULL;
47     v->name = strdup( name );
48     v->value = strdup( value ? value : "" );
49
50     v->i_field = 0;
51     v->field = malloc( sizeof( mvar_t * ) );
52     v->field[0] = NULL;
53
54     return v;
55 }
56
57 void E_(mvar_Delete)( mvar_t *v )
58 {
59     int i;
60
61     free( v->name );
62     free( v->value );
63
64     for( i = 0; i < v->i_field; i++ )
65     {
66         E_(mvar_Delete)( v->field[i] );
67     }
68     free( v->field );
69     free( v );
70 }
71
72 void E_(mvar_AppendVar)( mvar_t *v, mvar_t *f )
73 {
74     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
75     v->field[v->i_field] = f;
76     v->i_field++;
77 }
78
79 mvar_t *E_(mvar_Duplicate)( const mvar_t *v )
80 {
81     int i;
82     mvar_t *n;
83
84     n = E_(mvar_New)( v->name, v->value );
85     for( i = 0; i < v->i_field; i++ )
86     {
87         E_(mvar_AppendVar)( n, E_(mvar_Duplicate)( v->field[i] ) );
88     }
89
90     return n;
91 }
92
93 void E_(mvar_PushVar)( mvar_t *v, mvar_t *f )
94 {
95     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
96     if( v->i_field > 0 )
97     {
98         memmove( &v->field[1], &v->field[0], sizeof( mvar_t * ) * v->i_field );
99     }
100     v->field[0] = f;
101     v->i_field++;
102 }
103
104 void E_(mvar_RemoveVar)( mvar_t *v, mvar_t *f )
105 {
106     int i;
107     for( i = 0; i < v->i_field; i++ )
108     {
109         if( v->field[i] == f )
110         {
111             break;
112         }
113     }
114     if( i >= v->i_field )
115     {
116         return;
117     }
118
119     if( i + 1 < v->i_field )
120     {
121         memmove( &v->field[i], &v->field[i+1], sizeof( mvar_t * ) * ( v->i_field - i - 1 ) );
122     }
123     v->i_field--;
124     /* FIXME should do a realloc */
125 }
126
127 mvar_t *E_(mvar_GetVar)( mvar_t *s, const char *name )
128 {
129     int i;
130     char base[512], *field, *p;
131     int  i_index;
132
133     /* format: name[index].field */
134
135     field = strchr( name, '.' );
136     if( field )
137     {
138         int i = field - name;
139         strncpy( base, name, i );
140         base[i] = '\0';
141         field++;
142     }
143     else
144     {
145         strcpy( base, name );
146     }
147
148     if( ( p = strchr( base, '[' ) ) )
149     {
150         *p++ = '\0';
151         sscanf( p, "%d]", &i_index );
152         if( i_index < 0 )
153         {
154             return NULL;
155         }
156     }
157     else
158     {
159         i_index = 0;
160     }
161
162     for( i = 0; i < s->i_field; i++ )
163     {
164         if( !strcmp( s->field[i]->name, base ) )
165         {
166             if( i_index > 0 )
167             {
168                 i_index--;
169             }
170             else
171             {
172                 if( field )
173                 {
174                     return E_(mvar_GetVar)( s->field[i], field );
175                 }
176                 else
177                 {
178                     return s->field[i];
179                 }
180             }
181         }
182     }
183     return NULL;
184 }
185
186 char *E_(mvar_GetValue)( mvar_t *v, char *field )
187 {
188     if( *field == '\0' )
189     {
190         return v->value;
191     }
192     else
193     {
194         mvar_t *f = E_(mvar_GetVar)( v, field );
195         if( f )
196         {
197             return f->value;
198         }
199         else
200         {
201             return field;
202         }
203     }
204 }
205
206 void E_(mvar_PushNewVar)( mvar_t *vars, const char *name,
207                           const char *value )
208 {
209     mvar_t *f = E_(mvar_New)( name, value );
210     E_(mvar_PushVar)( vars, f );
211 }
212
213 void E_(mvar_AppendNewVar)( mvar_t *vars, const char *name,
214                             const char *value )
215 {
216     mvar_t *f = E_(mvar_New)( name, value );
217     E_(mvar_AppendVar)( vars, f );
218 }
219
220
221 /* arg= start[:stop[:step]],.. */
222 mvar_t *E_(mvar_IntegerSetNew)( const char *name, const char *arg )
223 {
224     char *dup = strdup( arg );
225     char *str = dup;
226     mvar_t *s = E_(mvar_New)( name, "set" );
227
228     while( str )
229     {
230         char *p;
231         int  i_start,i_stop,i_step;
232         int  i_match;
233
234         p = strchr( str, ',' );
235         if( p )
236         {
237             *p++ = '\0';
238         }
239
240         i_step = 0;
241         i_match = sscanf( str, "%d:%d:%d", &i_start, &i_stop, &i_step );
242
243         if( i_match == 1 )
244         {
245             i_stop = i_start;
246             i_step = 1;
247         }
248         else if( i_match == 2 )
249         {
250             i_step = i_start < i_stop ? 1 : -1;
251         }
252
253         if( i_match >= 1 )
254         {
255             int i;
256
257             if( ( i_start <= i_stop && i_step > 0 ) ||
258                 ( i_start >= i_stop && i_step < 0 ) )
259             {
260                 for( i = i_start; ; i += i_step )
261                 {
262                     char   value[79];
263
264                     if( ( i_step > 0 && i > i_stop ) ||
265                         ( i_step < 0 && i < i_stop ) )
266                     {
267                         break;
268                     }
269
270                     sprintf( value, "%d", i );
271
272                     E_(mvar_PushNewVar)( s, name, value );
273                 }
274             }
275         }
276         str = p;
277     }
278
279     free( dup );
280     return s;
281 }
282
283 /********************************************************************
284  * Special sets handling
285  ********************************************************************/
286
287 mvar_t *E_(mvar_PlaylistSetNew)( intf_thread_t *p_intf, char *name,
288                                  playlist_t *p_pl )
289 {
290     playlist_view_t *p_view;
291     mvar_t *s = E_(mvar_New)( name, "set" );
292
293
294     vlc_mutex_lock( &p_pl->object_lock );
295
296     p_view = playlist_ViewFind( p_pl, VIEW_CATEGORY ); /* FIXME */
297
298     if( p_view != NULL )
299         E_(PlaylistListNode)( p_intf, p_pl, p_view->p_root, name, s, 0 );
300
301     vlc_mutex_unlock( &p_pl->object_lock );
302
303     return s;
304 }
305
306 mvar_t *E_(mvar_InfoSetNew)( intf_thread_t *p_intf, char *name,
307                              input_thread_t *p_input )
308 {
309     mvar_t *s = E_(mvar_New)( name, "set" );
310     int i, j;
311
312     if( p_input == NULL )
313     {
314         return s;
315     }
316
317     vlc_mutex_lock( &p_input->input.p_item->lock );
318     for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
319     {
320         info_category_t *p_category = p_input->input.p_item->pp_categories[i];
321         char *psz;
322
323         mvar_t *cat  = E_(mvar_New)( name, "set" );
324         mvar_t *iset = E_(mvar_New)( "info", "set" );
325
326         psz = E_(FromUTF8)( p_intf, p_category->psz_name );
327         E_(mvar_AppendNewVar)( cat, "name", psz );
328         free( psz );
329         E_(mvar_AppendVar)( cat, iset );
330
331         for ( j = 0; j < p_category->i_infos; j++ )
332         {
333             info_t *p_info = p_category->pp_infos[j];
334             mvar_t *info = E_(mvar_New)( "info", "" );
335             char *psz_name = E_(FromUTF8)( p_intf, p_info->psz_name );
336             char *psz_value = E_(FromUTF8)( p_intf, p_info->psz_value );
337
338             /* msg_Dbg( p_input, "adding info name=%s value=%s",
339                      psz_name, psz_value ); */
340             E_(mvar_AppendNewVar)( info, "name",  psz_name );
341             E_(mvar_AppendNewVar)( info, "value", psz_value );
342             free( psz_name );
343             free( psz_value );
344             E_(mvar_AppendVar)( iset, info );
345         }
346         E_(mvar_AppendVar)( s, cat );
347     }
348     vlc_mutex_unlock( &p_input->input.p_item->lock );
349
350     return s;
351 }
352
353 mvar_t *E_(mvar_ObjectSetNew)( intf_thread_t *p_intf, char *psz_name,
354                                char *psz_capability )
355 {
356     mvar_t *s = E_(mvar_New)( psz_name, "set" );
357     int i;
358
359     vlc_list_t *p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE,
360                                         FIND_ANYWHERE );
361
362     for( i = 0; i < p_list->i_count; i++ )
363     {
364         module_t *p_parser = (module_t *)p_list->p_values[i].p_object;
365         if( !strcmp( p_parser->psz_capability, psz_capability ) )
366         {
367             mvar_t *sd = E_(mvar_New)( "sd", p_parser->psz_object_name );
368             E_(mvar_AppendNewVar)( sd, "name",
369                 p_parser->psz_longname ? p_parser->psz_longname
370                 : ( p_parser->psz_shortname ? p_parser->psz_shortname
371                 : p_parser->psz_object_name ) );
372             E_(mvar_AppendVar)( s, sd );
373         }
374     }
375
376     vlc_list_release( p_list );
377
378     return s;
379 }
380
381 mvar_t *E_(mvar_InputVarSetNew)( intf_thread_t *p_intf, char *name,
382                                  input_thread_t *p_input,
383                                  const char *psz_variable )
384 {
385     intf_sys_t     *p_sys = p_intf->p_sys;
386     mvar_t *s = E_(mvar_New)( name, "set" );
387     vlc_value_t val, val_list, text_list;
388     int i_type, i;
389
390     if( p_input == NULL )
391     {
392         return s;
393     }
394
395     /* Check the type of the object variable */
396     i_type = var_Type( p_sys->p_input, psz_variable );
397
398     /* Make sure we want to display the variable */
399     if( i_type & VLC_VAR_HASCHOICE )
400     {
401         var_Change( p_sys->p_input, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
402         if( val.i_int == 0 ) return s;
403         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
404             return s;
405     }
406     else
407     {
408         return s;
409     }
410
411     switch( i_type & VLC_VAR_TYPE )
412     {
413     case VLC_VAR_VOID:
414     case VLC_VAR_BOOL:
415     case VLC_VAR_VARIABLE:
416     case VLC_VAR_STRING:
417     case VLC_VAR_INTEGER:
418         break;
419     default:
420         /* Variable doesn't exist or isn't handled */
421         return s;
422     }
423
424     if( var_Get( p_sys->p_input, psz_variable, &val ) < 0 )
425     {
426         return s;
427     }
428
429     if( var_Change( p_sys->p_input, psz_variable, VLC_VAR_GETLIST,
430                     &val_list, &text_list ) < 0 )
431     {
432         if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
433         return s;
434     }
435
436     for( i = 0; i < val_list.p_list->i_count; i++ )
437     {
438         char *psz, psz_int[16];
439         mvar_t *itm;
440
441         switch( i_type & VLC_VAR_TYPE )
442         {
443         case VLC_VAR_STRING:
444             itm = E_(mvar_New)( name, "set" );
445             psz = E_(FromUTF8)( p_intf, text_list.p_list->p_values[i].psz_string );
446             E_(mvar_AppendNewVar)( itm, "name", psz );
447             psz = E_(FromUTF8)( p_intf, val_list.p_list->p_values[i].psz_string );
448             E_(mvar_AppendNewVar)( itm, "id", psz );
449             free( psz );
450             snprintf( psz_int, sizeof(psz_int), "%d",
451                       ( !strcmp( val.psz_string,
452                                    val_list.p_list->p_values[i].psz_string )
453                            && !( i_type & VLC_VAR_ISCOMMAND ) ) );
454             E_(mvar_AppendNewVar)( itm, "selected", psz_int );
455             E_(mvar_AppendVar)( s, itm );
456             break;
457
458         case VLC_VAR_INTEGER:
459             itm = E_(mvar_New)( name, "set" );
460             psz = E_(FromUTF8)( p_intf, text_list.p_list->p_values[i].psz_string );
461             E_(mvar_AppendNewVar)( itm, "name", psz );
462             snprintf( psz_int, sizeof(psz_int), "%d",
463                       val_list.p_list->p_values[i].i_int );
464             E_(mvar_AppendNewVar)( itm, "id", psz_int );
465             snprintf( psz_int, sizeof(psz_int), "%d",
466                       ( val.i_int == val_list.p_list->p_values[i].i_int )
467                          && !( i_type & VLC_VAR_ISCOMMAND ) );
468             E_(mvar_AppendNewVar)( itm, "selected", psz_int );
469             E_(mvar_AppendVar)( s, itm );
470             break;
471
472         default:
473             break;
474         }
475     }
476     /* clean up everything */
477     if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
478     var_Change( p_sys->p_input, psz_variable, VLC_VAR_FREELIST, &val_list,
479                 &text_list );
480     return s;
481 }
482
483 #if 0
484 mvar_t *E_(mvar_HttpdInfoSetNew)( char *name, httpd_t *p_httpd, int i_type )
485 {
486     mvar_t       *s = E_(mvar_New)( name, "set" );
487     httpd_info_t info;
488     int          i;
489
490     if( !p_httpd->pf_control( p_httpd, i_type, &info, NULL ) )
491     {
492         for( i= 0; i < info.i_count; )
493         {
494             mvar_t *inf;
495
496             inf = E_(mvar_New)( name, "set" );
497             do
498             {
499                 /* fprintf( stderr," mvar_HttpdInfoSetNew: append name=`%s' value=`%s'\n",
500                             info.info[i].psz_name, info.info[i].psz_value ); */
501                 E_(mvar_AppendNewVar)( inf,
502                                    info.info[i].psz_name,
503                                    info.info[i].psz_value );
504                 i++;
505             } while( i < info.i_count && strcmp( info.info[i].psz_name, "id" ) );
506             E_(mvar_AppendVar)( s, inf );
507         }
508     }
509
510     /* free mem */
511     for( i = 0; i < info.i_count; i++ )
512     {
513         free( info.info[i].psz_name );
514         free( info.info[i].psz_value );
515     }
516     if( info.i_count > 0 )
517     {
518         free( info.info );
519     }
520
521     return s;
522 }
523 #endif
524
525 mvar_t *E_(mvar_FileSetNew)( intf_thread_t *p_intf, char *name,
526                              char *psz_dir )
527 {
528     mvar_t *s = E_(mvar_New)( name, "set" );
529 #ifdef HAVE_SYS_STAT_H
530     struct stat   stat_info;
531 #endif
532     char        **ppsz_dir_content;
533     int           i_dir_content, i;
534     /* convert all / to native separator */
535 #if defined( WIN32 )
536     const char sep = '\\';
537 #else
538     const char sep = '/';
539 #endif
540
541     psz_dir = E_(RealPath)( p_intf, psz_dir );
542
543 #ifdef HAVE_SYS_STAT_H
544     if( (utf8_stat( psz_dir, &stat_info ) == -1 )
545      || !S_ISDIR( stat_info.st_mode )
546 #   if defined( WIN32 )
547           && psz_dir[0] != '\0' && (psz_dir[0] != '\\' || psz_dir[1] != '\0')
548 #   endif
549       )
550     {
551         free( psz_dir );
552         return s;
553     }
554 #endif
555
556     /* parse psz_src dir */
557     if( ( i_dir_content = utf8_scandir( psz_dir, &ppsz_dir_content, Filter,
558                                         InsensitiveAlphasort ) ) == -1 )
559     {
560         msg_Warn( p_intf, "error while scaning dir %s (%s)", psz_dir,
561                   strerror(errno) );
562         free( psz_dir );
563         return s;
564     }
565
566     for( i = 0; i < i_dir_content; i++ )
567     {
568         char *psz_dir_content = ppsz_dir_content[i];
569         char psz_tmp[strlen( psz_dir ) + 1 + strlen( psz_dir_content ) + 1];
570         mvar_t *f;
571         char *psz_name, *psz_ext, *psz_dummy;
572
573 #if defined( WIN32 )
574         if( psz_dir[0] == '\0' || (psz_dir[0] == '\\' && psz_dir[1] == '\0') )
575         {
576             strcpy( psz_tmp, psz_dir_content );
577         }
578         else
579 #endif
580         {
581             sprintf( psz_tmp, "%s%c%s", psz_dir, sep, psz_dir_content );
582
583 #ifdef HAVE_SYS_STAT_H
584             if( utf8_stat( psz_tmp, &stat_info ) == -1 )
585             {
586                 free( psz_dir_content );
587                 continue;
588             }
589 #endif
590         }
591         f = E_(mvar_New)( name, "set" );
592
593         /* FIXME: merge vlc_fix_readdir_charset with utf8_readir */
594         psz_dummy = vlc_fix_readdir_charset( p_intf, psz_dir_content );
595         psz_name = E_(FromUTF8)( p_intf, psz_dummy );
596         free( psz_dummy );
597
598         /* put lower-case file extension in 'ext' */
599         psz_ext = strrchr( psz_name, '.' );
600         psz_ext = strdup( psz_ext != NULL ? psz_ext + 1 : "" );
601         for( psz_dummy = psz_ext; *psz_dummy != '\0'; psz_dummy++ )
602             *psz_dummy = tolower( *psz_dummy );
603
604         E_(mvar_AppendNewVar)( f, "ext", psz_ext );
605         free( psz_ext );
606
607 #if defined( WIN32 )
608         if( psz_dir[0] == '\0' || (psz_dir[0] == '\\' && psz_dir[1] == '\0') )
609         {
610             char psz_tmp[3];
611             sprintf( psz_tmp, "%c:", psz_name[0] );
612             E_(mvar_AppendNewVar)( f, "name", psz_name );
613             E_(mvar_AppendNewVar)( f, "basename", psz_tmp );
614             E_(mvar_AppendNewVar)( f, "type", "directory" );
615             E_(mvar_AppendNewVar)( f, "size", "unknown" );
616             E_(mvar_AppendNewVar)( f, "date", "unknown" );
617         }
618         else
619 #endif
620         {
621             char psz_ctime[26];
622             char psz_tmp[strlen( psz_dir ) + 1 + strlen( psz_name ) + 1];
623
624             sprintf( psz_tmp, "%s%c%s", psz_dir, sep, psz_name );
625             E_(mvar_AppendNewVar)( f, "name", psz_tmp );
626             E_(mvar_AppendNewVar)( f, "basename", psz_name );
627
628 #ifdef HAVE_SYS_STAT_H
629             if( S_ISDIR( stat_info.st_mode ) )
630             {
631                 E_(mvar_AppendNewVar)( f, "type", "directory" );
632             }
633             else if( S_ISREG( stat_info.st_mode ) )
634             {
635                 E_(mvar_AppendNewVar)( f, "type", "file" );
636             }
637             else
638             {
639                 E_(mvar_AppendNewVar)( f, "type", "unknown" );
640             }
641
642             sprintf( psz_ctime, I64Fd, (int64_t)stat_info.st_size );
643             E_(mvar_AppendNewVar)( f, "size", psz_ctime );
644
645             /* FIXME memory leak FIXME */
646 #   ifdef HAVE_CTIME_R
647             ctime_r( &stat_info.st_mtime, psz_ctime );
648             E_(mvar_AppendNewVar)( f, "date", psz_ctime );
649 #   else
650             E_(mvar_AppendNewVar)( f, "date", ctime( &stat_info.st_mtime ) );
651 #   endif
652
653 #else
654             E_(mvar_AppendNewVar)( f, "type", "unknown" );
655             E_(mvar_AppendNewVar)( f, "size", "unknown" );
656             E_(mvar_AppendNewVar)( f, "date", "unknown" );
657 #endif
658         }
659
660         E_(mvar_AppendVar)( s, f );
661
662         free( psz_name );
663         free( psz_dir_content );
664     }
665
666     free( psz_dir );
667     if( ppsz_dir_content != NULL )
668         free( ppsz_dir_content );
669     return s;
670 }
671
672 void E_(mvar_VlmSetNewLoop)( char *name, vlm_t *vlm, mvar_t *s, vlm_message_t *el, vlc_bool_t b_name );
673 void E_(mvar_VlmSetNewLoop)( char *name, vlm_t *vlm, mvar_t *s, vlm_message_t *el, vlc_bool_t b_name )
674 {
675     /* Over name */
676     mvar_t        *set;
677     int k;
678
679     /* Add a node with name and info */
680     set = E_(mvar_New)( name, "set" );
681     if( b_name == VLC_TRUE )
682     {
683         E_(mvar_AppendNewVar)( set, "name", el->psz_name );
684     }
685
686     for( k = 0; k < el->i_child; k++ )
687     {
688         vlm_message_t *ch = el->child[k];
689         if( ch->i_child > 0 )
690         {
691             E_(mvar_VlmSetNewLoop)( ch->psz_name, vlm, set, ch, VLC_FALSE );
692         }
693         else
694         {
695             if( ch->psz_value )
696             {
697                 E_(mvar_AppendNewVar)( set, ch->psz_name, ch->psz_value );
698             }
699             else
700             {
701                 E_(mvar_AppendNewVar)( set, el->psz_name, ch->psz_name );
702             }
703         }
704     }
705
706     E_(mvar_AppendVar)( s, set );
707 }
708
709 mvar_t *E_(mvar_VlmSetNew)( char *name, vlm_t *vlm )
710 {
711     mvar_t        *s = E_(mvar_New)( name, "set" );
712     vlm_message_t *msg;
713     int    i;
714
715     if( vlm == NULL ) return s;
716
717     if( vlm_ExecuteCommand( vlm, "show", &msg ) )
718     {
719         return s;
720     }
721
722     for( i = 0; i < msg->i_child; i++ )
723     {
724         /* Over media, schedule */
725         vlm_message_t *ch = msg->child[i];
726         int j;
727
728         for( j = 0; j < ch->i_child; j++ )
729         {
730             /* Over name */
731             vlm_message_t *el = ch->child[j];
732             vlm_message_t *inf, *desc;
733             char          psz[500];
734
735             sprintf( psz, "show %s", el->psz_name );
736             if( vlm_ExecuteCommand( vlm, psz, &inf ) )
737                 continue;
738             desc = inf->child[0];
739
740             E_(mvar_VlmSetNewLoop)( el->psz_name, vlm, s, desc, VLC_TRUE );
741
742             vlm_MessageDelete( inf );
743         }
744     }
745     vlm_MessageDelete( msg );
746
747     return s;
748 }