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