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