]> git.sesse.net Git - vlc/blob - src/misc/vlm.c
19bad42aafb6d726aae0acae7899b82e63455aee
[vlc] / src / misc / vlm.c
1 /*****************************************************************************
2  * vlm.c: VLM interface plugin
3  *****************************************************************************
4  * Copyright (C) 2000-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Simon Latapie <garf@videolan.org>
8  *          Laurent Aimar <fenrir@videolan.org>
9  *          Gildas Bazin <gbazin@videolan.org>
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 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <ctype.h>                                              /* tolower() */
31
32 #include <vlc/vlc.h>
33
34 #ifdef ENABLE_VLM
35
36 #include <vlc/intf.h>
37 #include <vlc/input.h>
38
39 #ifdef HAVE_TIME_H
40 #   include <time.h>                                              /* ctime() */
41 #   include <sys/timeb.h>                                         /* ftime() */
42 #endif
43
44 #include <vlc_vlm.h>
45 #include <vlc_vod.h>
46 #include <charset.h>
47
48 #define FREE( p ) \
49         if( p ) { free( p ); (p) = NULL; }
50
51 /*****************************************************************************
52  * Local prototypes.
53  *****************************************************************************/
54 static vlm_message_t *vlm_Show( vlm_t *, vlm_media_t *, vlm_schedule_t *, char * );
55 static vlm_message_t *vlm_Help( vlm_t *, char * );
56
57 static vlm_media_instance_t *vlm_MediaInstanceSearch( vlm_t *, vlm_media_t *, const char * );
58
59 static vlm_message_t *vlm_MessageNew( char *, const char *, ... );
60 static vlm_message_t *vlm_MessageAdd( vlm_message_t *, vlm_message_t * );
61
62 static vlm_schedule_t *vlm_ScheduleSearch( vlm_t *, const char * );
63
64 static char *Save( vlm_t * );
65 static int Load( vlm_t *, char * );
66 static int ExecuteCommand( vlm_t *, const char *, vlm_message_t ** );
67 static int Manage( vlc_object_t * );
68
69 /*****************************************************************************
70  * vlm_New:
71  *****************************************************************************/
72 vlm_t *__vlm_New ( vlc_object_t *p_this )
73 {
74     vlc_value_t lockval;
75     vlm_t *p_vlm = NULL;
76     char *psz_vlmconf;
77
78     /* to be sure to avoid multiple creation */
79     var_Create( p_this->p_libvlc, "vlm_mutex", VLC_VAR_MUTEX );
80     var_Get( p_this->p_libvlc, "vlm_mutex", &lockval );
81     vlc_mutex_lock( lockval.p_address );
82
83     if( !(p_vlm = vlc_object_find( p_this, VLC_OBJECT_VLM, FIND_ANYWHERE )) )
84     {
85         msg_Info( p_this, "creating VLM" );
86         if( ( p_vlm = vlc_object_create( p_this, VLC_OBJECT_VLM ) ) == NULL )
87         {
88             vlc_mutex_unlock( lockval.p_address );
89             return NULL;
90         }
91
92         vlc_mutex_init( p_this->p_vlc, &p_vlm->lock );
93         p_vlm->i_media      = 0;
94         p_vlm->media        = NULL;
95         p_vlm->i_vod        = 0;
96         p_vlm->i_schedule   = 0;
97         p_vlm->schedule     = NULL;
98
99         vlc_object_yield( p_vlm );
100         vlc_object_attach( p_vlm, p_this->p_vlc );
101     }
102     vlc_mutex_unlock( lockval.p_address );
103
104     if( vlc_thread_create( p_vlm, "vlm thread",
105                            Manage, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
106     {
107         vlc_mutex_destroy( &p_vlm->lock );
108         vlc_object_destroy( p_vlm );
109         return NULL;
110     }
111
112     /* Try loading the vlm conf file given by --vlm-conf */
113     psz_vlmconf = config_GetPsz( p_vlm, "vlm-conf" );
114
115     if( psz_vlmconf && *psz_vlmconf )
116     {
117         vlm_message_t *p_message = NULL;
118         char *psz_buffer = NULL;
119
120         msg_Dbg( p_this, "loading VLM configuration" );
121         asprintf(&psz_buffer, "load %s", psz_vlmconf );
122         if( psz_buffer )
123         {
124             msg_Dbg( p_this, psz_buffer);
125             if( vlm_ExecuteCommand( p_vlm, psz_buffer, &p_message ) ){
126                 msg_Warn( p_this, "error while loading the configuration file" );
127             }
128             vlm_MessageDelete(p_message);
129             free(psz_buffer);
130         }
131    }
132    free(psz_vlmconf);
133
134    return p_vlm;
135 }
136
137 /*****************************************************************************
138  * vlm_Delete:
139  *****************************************************************************/
140 void vlm_Delete( vlm_t *p_vlm )
141 {
142     vlc_value_t lockval;
143
144     var_Get( p_vlm->p_libvlc, "vlm_mutex", &lockval );
145     vlc_mutex_lock( lockval.p_address );
146
147     vlc_object_release( p_vlm );
148
149     if( p_vlm->i_refcount > 0 )
150     {
151         vlc_mutex_unlock( lockval.p_address );
152         return;
153     }
154
155     p_vlm->b_die = VLC_TRUE;
156     vlc_thread_join( p_vlm );
157
158     vlc_mutex_destroy( &p_vlm->lock );
159
160     while( p_vlm->i_media ) vlm_MediaDelete( p_vlm, p_vlm->media[0], NULL );
161     FREE( p_vlm->media );
162
163     while( p_vlm->i_schedule ) vlm_ScheduleDelete( p_vlm,
164                                                    p_vlm->schedule[0], NULL );
165     FREE( p_vlm->schedule );
166
167     vlc_object_detach( p_vlm );
168     vlc_object_destroy( p_vlm );
169     vlc_mutex_unlock( lockval.p_address );
170 }
171
172 /*****************************************************************************
173  * vlm_ExecuteCommand:
174  *****************************************************************************/
175 int vlm_ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
176                         vlm_message_t **pp_message)
177 {
178     int i_result;
179
180     vlc_mutex_lock( &p_vlm->lock );
181     i_result = ExecuteCommand( p_vlm, psz_command, pp_message );
182     vlc_mutex_unlock( &p_vlm->lock );
183
184     return i_result;
185 }
186
187 /*****************************************************************************
188  * vlm_Save:
189  *****************************************************************************/
190 int vlm_Save( vlm_t *p_vlm, const char *psz_file )
191 {
192     FILE *file;
193     char *psz_save;
194
195     if( !p_vlm || !psz_file ) return 1;
196
197     file = utf8_fopen( psz_file, "wt" );
198     if( file == NULL ) return 1;
199
200     psz_save = Save( p_vlm );
201     if( psz_save == NULL )
202     {
203         fclose( file );
204         return 1;
205     }
206     fwrite( psz_save, strlen( psz_save ), 1, file );
207     fclose( file );
208     free( psz_save );
209
210     return 0;
211 }
212
213 /*****************************************************************************
214  * vlm_Load:
215  *****************************************************************************/
216 int vlm_Load( vlm_t *p_vlm, const char *psz_file )
217 {
218     stream_t *p_stream;
219     int64_t i_size;
220     char *psz_buffer;
221
222     if( !p_vlm || !psz_file ) return 1;
223
224     p_stream = stream_UrlNew( p_vlm, psz_file );
225     if( p_stream == NULL ) return 1;
226
227     if( stream_Seek( p_stream, 0 ) != 0 )
228     {
229         stream_Delete( p_stream );
230         return 2;
231     }
232
233     i_size = stream_Size( p_stream );
234
235     psz_buffer = malloc( i_size + 1 );
236     if( !psz_buffer )
237     {
238         stream_Delete( p_stream );
239         return 2;
240     }
241
242     stream_Read( p_stream, psz_buffer, i_size );
243     psz_buffer[ i_size ] = '\0';
244
245     stream_Delete( p_stream );
246
247     if( Load( p_vlm, psz_buffer ) )
248     {
249         free( psz_buffer );
250         return 3;
251     }
252
253     free( psz_buffer );
254
255     return 0;
256 }
257
258 /*****************************************************************************
259  * FindEndCommand
260  *****************************************************************************/
261 static const char *FindEndCommand( const char *psz_sent )
262 {
263     vlc_bool_t b_escape = VLC_FALSE;
264
265     switch( *psz_sent )
266     {
267     case '\"':
268         psz_sent++;
269         while( ( *psz_sent != '\"' || b_escape == VLC_TRUE )
270                && ( *psz_sent != '\0' ) )
271         {
272             if( *psz_sent == '\'' && b_escape == VLC_FALSE )
273             {
274                 psz_sent = FindEndCommand( psz_sent );
275                 if( psz_sent == NULL ) return NULL;
276             }
277             else if( *psz_sent++ == '\\' && b_escape == VLC_FALSE )
278                 b_escape = VLC_TRUE;
279             else
280                 b_escape = VLC_FALSE;
281         }
282
283         if( *psz_sent == '\"' )
284         {
285             psz_sent++;
286             return psz_sent;
287         }
288
289         /* *psz_sent == '\0' -> number of " is incorrect */
290         else return NULL;
291
292         break;
293
294     case '\'':
295         psz_sent++;
296         while( ( *psz_sent != '\'' || b_escape == VLC_TRUE )
297                  && ( *psz_sent != '\0' ) )
298         {
299             if( *psz_sent == '\"' && b_escape == VLC_FALSE )
300             {
301                 psz_sent = FindEndCommand( psz_sent );
302                 if( psz_sent == NULL ) return NULL;
303             }
304             else if( *psz_sent++ == '\\' && b_escape == VLC_FALSE )
305                 b_escape = VLC_TRUE;
306             else
307                 b_escape = VLC_FALSE;
308         }
309
310         if( *psz_sent == '\'' )
311         {
312             psz_sent++;
313             return psz_sent;
314         }
315
316         /* *psz_sent == '\0' -> number of " is incorrect */
317         else return NULL;
318
319         break;
320
321     default: /* now we can look for spaces */
322         while( ( *psz_sent != ' ' ) && ( *psz_sent != '\0' ) )
323         {
324             if( ( ( *psz_sent == '\'' ) || ( *psz_sent == '\"' ) )
325                 && b_escape == VLC_FALSE )
326             {
327                 psz_sent = FindEndCommand( psz_sent );
328                 if( psz_sent == NULL ) return NULL;
329             }
330             else if( *psz_sent++ == '\\' && b_escape == VLC_FALSE )
331                 b_escape = VLC_TRUE;
332             else
333                 b_escape = VLC_FALSE;
334
335         }
336
337         return psz_sent;
338     }
339 }
340
341 /*****************************************************************************
342  * ExecuteCommand: The main state machine
343  *****************************************************************************
344  * Execute a command which ends with '\0' (string)
345  *****************************************************************************/
346 static int ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
347                            vlm_message_t **pp_message )
348 {
349     int i_command = 0;
350     char **ppsz_command = NULL;
351     const char *psz_cmd = psz_command;
352     vlm_message_t *p_message = NULL;
353     int i, j;
354
355     /* First, parse the line and cut it */
356     while( *psz_cmd != '\0' )
357     {
358
359         if( *psz_cmd == ' ' || *psz_cmd == '\t' )
360         {
361             psz_cmd++;
362         }
363         else
364         {
365             const char *psz_temp;
366             const char *psz_buf;
367             char *psz_dst;
368             vlc_bool_t b_escape = VLC_FALSE;
369             int   i_temp;
370
371             /* support for comments */
372             if( i_command == 0 && *psz_cmd == '#')
373             {
374                 p_message = vlm_MessageNew( "", NULL );
375                 goto success;
376             }
377
378             psz_temp = FindEndCommand( psz_cmd );
379
380             if( psz_temp == NULL )
381             {
382                 p_message = vlm_MessageNew( "Incomplete command", psz_cmd );
383                 goto error;
384             }
385
386             i_temp = psz_temp - psz_cmd;
387
388             ppsz_command = realloc( ppsz_command, (i_command + 1) *
389                                     sizeof(char*) );
390             ppsz_command[ i_command ] = malloc( (i_temp + 1) * sizeof(char) );
391
392             /* unescape ", ' and \ ... and everything else */
393             psz_buf = psz_cmd;
394             psz_dst = ppsz_command[ i_command ];
395             while( i_temp-- )
396             {
397                 if( *psz_buf == '\\' && b_escape == VLC_FALSE )
398                     b_escape = VLC_TRUE;
399                 else
400                 {
401                     b_escape = VLC_FALSE;
402                     *psz_dst = *psz_buf;
403                     psz_dst++;
404                 }
405                 psz_buf++;
406             }
407             *psz_dst = '\0';
408
409             i_command++;
410
411             psz_cmd = psz_temp;
412         }
413     }
414
415     /*
416      * And then Interpret it
417      */
418
419     if( i_command == 0 )
420     {
421         p_message = vlm_MessageNew( "", NULL );
422         goto success;
423     }
424
425     if( !strcmp(ppsz_command[0], "new") )
426     {
427         int i_type;
428
429         /* Check the number of arguments */
430         if( i_command < 3 ) goto syntax_error;
431
432         /* Get type */
433         if( !strcmp(ppsz_command[2], "vod") )
434         {
435             i_type = VOD_TYPE;
436         }
437         else if( !strcmp(ppsz_command[2], "broadcast") )
438         {
439             i_type = BROADCAST_TYPE;
440         }
441         else if( !strcmp(ppsz_command[2], "schedule") )
442         {
443             i_type = SCHEDULE_TYPE;
444         }
445         else
446         {
447             p_message =
448                 vlm_MessageNew( "new", "%s: Choose between vod, "
449                                 "broadcast or schedule", ppsz_command[1] );
450             goto error;
451         }
452
453         /* Check for forbidden media names */
454         if( !strcmp(ppsz_command[1], "all") ||
455             !strcmp(ppsz_command[1], "media") ||
456             !strcmp(ppsz_command[1], "schedule") )
457         {
458             p_message = vlm_MessageNew( "new", "\"all\", \"media\" and "
459                                         "\"schedule\" are reserved names" );
460             goto error;
461         }
462
463         /* Check the name is not already in use */
464         if( vlm_ScheduleSearch( p_vlm, ppsz_command[1] ) ||
465             vlm_MediaSearch( p_vlm, ppsz_command[1] ) )
466         {
467             p_message = vlm_MessageNew( "new", "%s: Name already in use",
468                                         ppsz_command[1] );
469             goto error;
470         }
471
472         /* Schedule */
473         if( i_type == SCHEDULE_TYPE )
474         {
475             vlm_schedule_t *p_schedule;
476             p_schedule = vlm_ScheduleNew( p_vlm, ppsz_command[1] );
477             if( !p_schedule )
478             {
479                 p_message = vlm_MessageNew( "new", "could not create schedule" );
480                 goto error;
481             }
482         }
483
484         /* Media */
485         else
486         {
487             vlm_media_t *p_media;
488             p_media = vlm_MediaNew( p_vlm, ppsz_command[1], i_type );
489             if( !p_media )
490             {
491                 p_message = vlm_MessageNew( "new", "could not create media" );
492                 goto error;
493             }
494         }
495
496         if( i_command <= 3 )
497         {
498             p_message = vlm_MessageNew( "new", NULL );
499             goto success;
500         }
501
502         /* Properties will be dealt with later on */
503     }
504
505     else if( !strcmp(ppsz_command[0], "setup") )
506     {
507         if( i_command < 2 ) goto syntax_error;
508
509         /* Properties will be dealt with later on */
510     }
511
512     else if( !strcmp(ppsz_command[0], "del") )
513     {
514         vlm_media_t *p_media;
515         vlm_schedule_t *p_schedule;
516
517         if( i_command < 2 ) goto syntax_error;
518
519         p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] );
520         p_schedule = vlm_ScheduleSearch( p_vlm, ppsz_command[1] );
521
522         if( p_schedule != NULL )
523         {
524             vlm_ScheduleDelete( p_vlm, p_schedule, NULL );
525         }
526         else if( p_media != NULL )
527         {
528             vlm_MediaDelete( p_vlm, p_media, NULL );
529         }
530         else if( !strcmp(ppsz_command[1], "media") )
531         {
532             while( p_vlm->i_media ) vlm_MediaDelete( p_vlm, p_vlm->media[0],
533                                                      NULL );
534         }
535         else if( !strcmp(ppsz_command[1], "schedule") )
536         {
537             while( p_vlm->i_schedule )
538                 vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0], NULL );
539         }
540         else if( !strcmp(ppsz_command[1], "all") )
541         {
542             while( p_vlm->i_media ) vlm_MediaDelete( p_vlm, p_vlm->media[0],
543                                                      NULL );
544
545             while( p_vlm->i_schedule )
546                 vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0], NULL );
547         }
548         else
549         {
550             p_message = vlm_MessageNew( "del", "%s: media unknown",
551                                       ppsz_command[1] );
552             goto error;
553         }
554
555         p_message = vlm_MessageNew( "del", NULL );
556         goto success;
557     }
558
559     else if( !strcmp(ppsz_command[0], "show") )
560     {
561         vlm_media_t *p_media;
562         vlm_schedule_t *p_schedule;
563
564         if( i_command == 1 )
565         {
566             p_message = vlm_Show( p_vlm, NULL, NULL, NULL );
567             goto success;
568         }
569         else if( i_command > 2 ) goto syntax_error;
570
571         p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] );
572         p_schedule = vlm_ScheduleSearch( p_vlm, ppsz_command[1] );
573
574         if( p_schedule != NULL )
575         {
576             p_message = vlm_Show( p_vlm, NULL, p_schedule, NULL );
577         }
578         else if( p_media != NULL )
579         {
580             p_message = vlm_Show( p_vlm, p_media, NULL, NULL );
581         }
582         else
583         {
584             p_message = vlm_Show( p_vlm, NULL, NULL, ppsz_command[1] );
585         }
586
587         goto success;
588     }
589
590     else if( !strcmp(ppsz_command[0], "help") )
591     {
592         if( i_command != 1 ) goto syntax_error;
593
594         p_message = vlm_Help( p_vlm, NULL );
595         goto success;
596     }
597
598     else if( !strcmp(ppsz_command[0], "control") )
599     {
600         vlm_media_t *p_media;
601
602         if( i_command < 3 ) goto syntax_error;
603
604         if( !(p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] ) ) )
605         {
606             p_message = vlm_MessageNew( "control", "%s: media unknown",
607                                       ppsz_command[1] );
608             goto error;
609         }
610         else
611         {
612             char *psz_command, *psz_arg = 0, *psz_instance = 0;
613             int i_index = 2;
614
615             if( strcmp( ppsz_command[2], "play" ) &&
616                 strcmp( ppsz_command[2], "stop" ) &&
617                 strcmp( ppsz_command[2], "pause" ) &&
618                 strcmp( ppsz_command[2], "seek" ) )
619             {
620                 i_index++;
621                 psz_instance = ppsz_command[2];
622
623                 if( i_command < 4 ) goto syntax_error;
624             }
625
626             psz_command = ppsz_command[i_index];
627
628             if( i_command >= i_index + 2 ) psz_arg = ppsz_command[i_index + 1];
629
630             vlm_MediaControl( p_vlm, p_media, psz_instance, psz_command,
631                              psz_arg );
632             p_message = vlm_MessageNew( "control", NULL );
633             goto success;
634         }
635     }
636
637     else if( !strcmp(ppsz_command[0], "save") )
638     {
639         if( i_command != 2 ) goto syntax_error;
640
641         if( vlm_Save( p_vlm, ppsz_command[1] ) )
642         {
643             p_message = vlm_MessageNew( "save", "Unable to save to file" );
644             goto error;
645         }
646         else
647         {
648             p_message = vlm_MessageNew( "save", NULL );
649             goto success;
650         }
651     }
652
653     else if( !strcmp(ppsz_command[0], "export" ) )
654     {
655         char *psz_buf;
656
657         if( i_command != 1 ) goto syntax_error;
658
659         p_message = vlm_MessageNew( "export", psz_buf = Save( p_vlm ) );
660         free( psz_buf );
661
662         goto success;
663     }
664
665     else if( !strcmp(ppsz_command[0], "load") )
666     {
667         if( i_command != 2 ) goto syntax_error;
668
669         switch( vlm_Load( p_vlm, ppsz_command[1] ) )
670         {
671             case 0:
672                 p_message = vlm_MessageNew( "load", NULL );
673                 goto success;
674             case 2:
675                 p_message = vlm_MessageNew( "load", "Read file error" );
676                 goto error;
677             case 3:
678                 p_message =
679                     vlm_MessageNew( "load", "Error while loading file" );
680                 goto error;
681             default:
682                 p_message =
683                     vlm_MessageNew( "load", "Unable to load from file" );
684                 goto error;
685         }
686     }
687
688     else
689     {
690         p_message = vlm_MessageNew( ppsz_command[0], "Unknown command" );
691         goto error;
692     }
693
694     /* Common code between "new" and "setup" */
695     if( !strcmp(ppsz_command[0], "new") ||
696         !strcmp(ppsz_command[0], "setup") )
697     {
698         int i_command_start = strcmp(ppsz_command[0], "new") ? 2 : 3;
699         vlm_media_t *p_media;
700         vlm_schedule_t *p_schedule;
701
702         if( i_command < i_command_start ) goto syntax_error;
703
704         p_media = vlm_MediaSearch( p_vlm, ppsz_command[1] );
705         p_schedule = vlm_ScheduleSearch( p_vlm, ppsz_command[1] );
706
707         if( !p_media && !p_schedule )
708         {
709             p_message = vlm_MessageNew( ppsz_command[0], "%s unknown",
710                                         ppsz_command[1] );
711             goto error;
712         }
713
714         if( p_schedule != NULL )
715         {
716             for( i = i_command_start ; i < i_command ; i++ )
717             {
718                 if( !strcmp( ppsz_command[i], "enabled" ) ||
719                     !strcmp( ppsz_command[i], "disabled" ) )
720                 {
721                     vlm_ScheduleSetup( p_schedule, ppsz_command[i], NULL );
722                 }
723
724                 /* Beware: everything behind append is considered as
725                  * command line */
726                 else if( !strcmp( ppsz_command[i], "append" ) )
727                 {
728                     if( ++i >= i_command ) break;
729
730                     for( j = i + 1; j < i_command; j++ )
731                     {
732                         ppsz_command[i] =
733                             realloc( ppsz_command[i], strlen(ppsz_command[i]) +
734                                      strlen(ppsz_command[j]) + 1 + 1 );
735                         strcat( ppsz_command[i], " " );
736                         strcat( ppsz_command[i], ppsz_command[j] );
737                     }
738
739                     vlm_ScheduleSetup( p_schedule, ppsz_command[i - 1],
740                                        ppsz_command[i] );
741                     break;
742                 }
743                 else
744                 {
745                     if( i + 1 >= i_command && !strcmp(ppsz_command[0], "new") )
746                     {
747                         vlm_ScheduleDelete( p_vlm, p_schedule, NULL );
748                         p_message =
749                             vlm_MessageNew( ppsz_command[0],
750                                             "Wrong properties syntax" );
751                         goto error;
752                     }
753                     else if( i + 1 >= i_command )
754                     {
755                         p_message =
756                             vlm_MessageNew( ppsz_command[0],
757                                             "Wrong properties syntax" );
758                         goto error;
759                     }
760
761                     vlm_ScheduleSetup( p_schedule, ppsz_command[i],
762                                        ppsz_command[i+1] );
763                     i++;
764                 }
765             }
766         }
767
768         else if( p_media != NULL )
769         {
770             for( i = i_command_start ; i < i_command ; i++ )
771             {
772                 if( !strcmp( ppsz_command[i], "enabled" ) ||
773                     !strcmp( ppsz_command[i], "disabled" ) )
774                 {
775                     vlm_MediaSetup( p_vlm, p_media, ppsz_command[i], NULL );
776                 }
777                 else if( i + 1 >= i_command &&
778                          !strcmp( ppsz_command[i], "mux") )
779                 {
780                     if( p_media->i_type != VOD_TYPE )
781                     {
782                         p_message = vlm_MessageNew( ppsz_command[0],
783                                   "mux only available for broadcast" );
784                     }
785                     else
786                     {
787                         vlm_MediaSetup( p_vlm, p_media, ppsz_command[i],
788                                         ppsz_command[i+1] );
789                         i++;
790                     }
791                 }
792                 else if( !strcmp( ppsz_command[i], "loop" ) ||
793                          !strcmp( ppsz_command[i], "unloop" ) )
794                 {
795                     if( p_media->i_type != BROADCAST_TYPE )
796                     {
797                         p_message = vlm_MessageNew( ppsz_command[0],
798                                   "loop only available for broadcast" );
799                     }
800                     else
801                     {
802                         vlm_MediaSetup( p_vlm, p_media, ppsz_command[i], NULL );
803                     }
804                 }
805                 else
806                 {
807                     if( ( (i + 1) >= i_command ) &&
808                         !strcmp(ppsz_command[0], "new") )
809                     {
810                         vlm_MediaDelete( p_vlm, p_media, NULL );
811                         p_message =
812                             vlm_MessageNew( ppsz_command[0],
813                                             "Wrong properties syntax" );
814                         goto error;
815                     }
816                     else if( (i + 1) >= i_command )
817                     {
818                         p_message =
819                             vlm_MessageNew( ppsz_command[0],
820                                             "Wrong properties syntax" );
821                         goto error;
822                     }
823
824                     vlm_MediaSetup( p_vlm, p_media, ppsz_command[i],
825                                     ppsz_command[i+1] );
826                     i++;
827                 }
828             }
829         }
830
831         p_message = vlm_MessageNew( ppsz_command[0], NULL );
832         goto success;
833     }
834
835 success:
836     for( i = 0 ; i < i_command ; i++ ) FREE( ppsz_command[i] );
837     FREE( ppsz_command );
838     *pp_message = p_message;
839
840     return VLC_SUCCESS;
841
842 syntax_error:
843     p_message = vlm_MessageNew( ppsz_command[0], "Wrong command syntax" );
844
845 error:
846     for( i = 0 ; i < i_command ; i++ ) FREE( ppsz_command[i] );
847     FREE( ppsz_command );
848     *pp_message = p_message;
849
850     return VLC_EGENERIC;
851 }
852
853 vlm_media_t *vlm_MediaSearch( vlm_t *vlm, const char *psz_name )
854 {
855     int i;
856
857     for( i = 0; i < vlm->i_media; i++ )
858     {
859         if( strcmp( psz_name, vlm->media[i]->psz_name ) == 0 )
860         {
861             return vlm->media[i];
862         }
863     }
864
865     return NULL;
866 }
867
868 /*****************************************************************************
869  * Media handling
870  *****************************************************************************/
871 static vlm_media_instance_t *
872 vlm_MediaInstanceSearch( vlm_t *vlm, vlm_media_t *media,
873                          const char *psz_name )
874 {
875     int i;
876
877     for( i = 0; i < media->i_instance; i++ )
878     {
879         if( ( !psz_name && !media->instance[i]->psz_name ) ||
880             ( psz_name && media->instance[i]->psz_name &&
881               !strcmp( psz_name, media->instance[i]->psz_name ) ) )
882         {
883             return media->instance[i];
884         }
885     }
886
887     return NULL;
888 }
889
890 vlm_media_t *vlm_MediaNew( vlm_t *vlm, const char *psz_name, int i_type )
891 {
892     vlm_media_t *media = malloc( sizeof( vlm_media_t ) );
893
894     if( !media )
895     {
896         msg_Err( vlm, "out of memory" );
897         return NULL;
898     }
899
900     /* Check if we need to load the VOD server */
901     if( i_type == VOD_TYPE && !vlm->i_vod )
902     {
903         vlm->vod = vlc_object_create( vlm, VLC_OBJECT_VOD );
904         vlc_object_attach( vlm->vod, vlm );
905         vlm->vod->p_module = module_Need( vlm->vod, "vod server", 0, 0 );
906         if( !vlm->vod->p_module )
907         {
908             msg_Err( vlm, "cannot find vod server" );
909             vlc_object_detach( vlm->vod );
910             vlc_object_destroy( vlm->vod );
911             vlm->vod = 0;
912             free( media );
913             return NULL;
914         }
915
916         vlm->vod->p_data = vlm;
917         vlm->vod->pf_media_control = vlm_MediaVodControl;
918     }
919
920     if( i_type == VOD_TYPE ) vlm->i_vod++;
921
922     media->psz_name = strdup( psz_name );
923     media->b_enabled = VLC_FALSE;
924     media->b_loop = VLC_FALSE;
925     media->vod_media = NULL;
926     media->psz_vod_output = NULL;
927     media->psz_mux = NULL;
928     media->i_input = 0;
929     media->input = NULL;
930     media->psz_output = NULL;
931     media->i_option = 0;
932     media->option = NULL;
933     media->i_type = i_type;
934     media->i_instance = 0;
935     media->instance = NULL;
936
937     vlc_input_item_Init( VLC_OBJECT(vlm), &media->item );
938
939     TAB_APPEND( vlm->i_media, vlm->media, media );
940
941     return media;
942 }
943
944 /* for now, simple delete. After, del with options (last arg) */
945 void vlm_MediaDelete( vlm_t *vlm, vlm_media_t *media, const char *psz_name )
946 {
947     if( media == NULL ) return;
948
949     while( media->i_instance )
950     {
951         vlm_media_instance_t *p_instance = media->instance[0];
952         vlm_MediaControl( vlm, media, p_instance->psz_name, "stop", 0 );
953     }
954
955     TAB_REMOVE( vlm->i_media, vlm->media, media );
956
957     if( media->i_type == VOD_TYPE )
958     {
959         vlm_MediaSetup( vlm, media, "disabled", 0 );
960         vlm->i_vod--;
961     }
962
963     /* Check if we need to unload the VOD server */
964     if( media->i_type == VOD_TYPE && !vlm->i_vod )
965     {
966         module_Unneed( vlm->vod, vlm->vod->p_module );
967         vlc_object_detach( vlm->vod );
968         vlc_object_destroy( vlm->vod );
969         vlm->vod = 0;
970     }
971
972     if( vlm->i_media == 0 && vlm->media ) free( vlm->media );
973
974     free( media->psz_name );
975
976     while( media->i_input-- ) free( media->input[media->i_input] );
977     if( media->input ) free( media->input );
978
979     if( media->psz_output ) free( media->psz_output );
980     if( media->psz_mux ) free( media->psz_mux );
981
982     while( media->i_option-- ) free( media->option[media->i_option] );
983     if( media->option ) free( media->option );
984
985     vlc_input_item_Clean( &media->item );
986
987     free( media );
988 }
989
990 int vlm_MediaSetup( vlm_t *vlm, vlm_media_t *media, const char *psz_cmd,
991                     const char *psz_value )
992 {
993     if( !psz_cmd) return VLC_EGENERIC;
994
995     if( !strcmp( psz_cmd, "loop" ) )
996     {
997         media->b_loop = VLC_TRUE;
998     }
999     else if( !strcmp( psz_cmd, "unloop" ) )
1000     {
1001         media->b_loop = VLC_FALSE;
1002     }
1003     else if( !strcmp( psz_cmd, "enabled" ) )
1004     {
1005         media->b_enabled = VLC_TRUE;
1006     }
1007     else if( !strcmp( psz_cmd, "disabled" ) )
1008     {
1009         media->b_enabled = VLC_FALSE;
1010     }
1011     else if( !strcmp( psz_cmd, "mux" ) )
1012     {
1013         if( media->psz_mux ) free( media->psz_mux );
1014         media->psz_mux = NULL;
1015         if( psz_value ) media->psz_mux = strdup( psz_value );
1016     }
1017     else if( !strcmp( psz_cmd, "input" ) )
1018     {
1019         char *input;
1020
1021         if( psz_value != NULL && strlen(psz_value) > 1 &&
1022             ( psz_value[0] == '\'' || psz_value[0] == '\"' ) &&
1023             ( psz_value[ strlen(psz_value) - 1 ] == '\'' ||
1024               psz_value[ strlen(psz_value) - 1 ] == '\"' )  )
1025         {
1026             input = malloc( strlen(psz_value) - 1 );
1027
1028             memcpy( input, psz_value + 1, strlen(psz_value) - 2 );
1029             input[ strlen(psz_value) - 2 ] = '\0';
1030         }
1031         else
1032         {
1033             input = strdup( psz_value );
1034         }
1035
1036         TAB_APPEND( media->i_input, media->input, input );
1037     }
1038     else if( !strcmp( psz_cmd, "inputdel" ) && !strcmp( psz_value, "all" ) )
1039     {
1040         while( media->i_input > 0 )
1041         {
1042             TAB_REMOVE( media->i_input, media->input, media->input[0] );
1043         }
1044     }
1045     else if( !strcmp( psz_cmd, "inputdel" ) )
1046     {
1047         char *input;
1048         int i;
1049
1050         if( psz_value != NULL && strlen(psz_value) > 1 &&
1051             ( psz_value[0] == '\'' || psz_value[0] == '\"' ) &&
1052             ( psz_value[ strlen(psz_value) - 1 ] == '\'' ||
1053               psz_value[ strlen(psz_value) - 1 ] == '\"' )  )
1054         {
1055             input = malloc( strlen(psz_value) - 1 );
1056
1057             memcpy( input, psz_value + 1, strlen(psz_value) - 2 );
1058             input[ strlen(psz_value) - 2 ] = '\0';
1059         }
1060         else
1061         {
1062             input = strdup( psz_value );
1063         }
1064
1065         for( i = 0; i < media->i_input; i++ )
1066         {
1067             if( !strcmp( input, media->input[i] ) )
1068             {
1069                 TAB_REMOVE( media->i_input, media->input, media->input[i] );
1070                 break;
1071             }
1072         }
1073     }
1074     else if( !strcmp( psz_cmd, "inputdeln" ) )
1075     {
1076         int index = atoi( psz_value );
1077         if( index > 0 && index <= media->i_input )
1078         {
1079             TAB_REMOVE( media->i_input, media->input, media->input[index-1] );
1080         }
1081     }
1082     else if( !strcmp( psz_cmd, "output" ) )
1083     {
1084         if( media->psz_output != NULL )
1085         {
1086             free( media->psz_output );
1087         }
1088         media->psz_output = strdup( psz_value );
1089     }
1090     else if( !strcmp( psz_cmd, "option" ) )
1091     {
1092         char *psz_option;
1093         psz_option = strdup( psz_value );
1094
1095         TAB_APPEND( media->i_option, media->option, psz_option );
1096     }
1097     else
1098     {
1099         return VLC_EGENERIC;
1100     }
1101
1102     /* Check if we need to create/delete a vod media */
1103     if( media->i_type == VOD_TYPE )
1104     {
1105         if( !media->b_enabled && media->vod_media )
1106         {
1107             vlm->vod->pf_media_del( vlm->vod, media->vod_media );
1108             media->vod_media = 0;
1109         }
1110         else if( media->b_enabled && !media->vod_media && media->i_input )
1111         {
1112             /* Pre-parse the input */
1113             input_thread_t *p_input;
1114             char *psz_output;
1115             char *psz_header;
1116             int i;
1117
1118             vlc_input_item_Clean( &media->item );
1119             vlc_input_item_Init( VLC_OBJECT(vlm), &media->item );
1120
1121             if( media->psz_output )
1122                 asprintf( &psz_output, "%s:description", media->psz_output );
1123             else
1124                 asprintf( &psz_output, "#description" );
1125
1126             media->item.psz_uri = strdup( media->input[0] );
1127             media->item.ppsz_options = malloc( sizeof( char* ) );
1128             asprintf( &media->item.ppsz_options[0], "sout=%s", psz_output);
1129             media->item.i_options = 1;
1130             for( i = 0; i < media->i_option; i++ )
1131             {
1132                 media->item.i_options++;
1133                 media->item.ppsz_options =
1134                     realloc( media->item.ppsz_options,
1135                              media->item.i_options * sizeof( char* ) );
1136                 media->item.ppsz_options[ media->item.i_options - 1 ] =
1137                     strdup( media->option[i] );
1138             }
1139
1140             asprintf( &psz_header, _("Media: %s"), media->psz_name );
1141
1142             if( (p_input = input_CreateThread2( vlm, &media->item, psz_header
1143                                               ) ) )
1144             {
1145                 while( !p_input->b_eof && !p_input->b_error ) msleep( 100000 );
1146
1147                 input_StopThread( p_input );
1148                 input_DestroyThread( p_input );
1149                 vlc_object_detach( p_input );
1150                 vlc_object_destroy( p_input );
1151             }
1152             free( psz_output );
1153             free( psz_header );
1154
1155             if( media->psz_mux )
1156             {
1157                 input_item_t item;
1158                 es_format_t es, *p_es = &es;
1159                 char fourcc[5];
1160
1161                 sprintf( fourcc, "%4.4s", media->psz_mux );
1162                 fourcc[0] = tolower(fourcc[0]); fourcc[1] = tolower(fourcc[1]);
1163                 fourcc[2] = tolower(fourcc[2]); fourcc[3] = tolower(fourcc[3]);
1164
1165                 item = media->item;
1166                 item.i_es = 1;
1167                 item.es = &p_es;
1168                 es_format_Init( &es, VIDEO_ES, *((int *)fourcc) );
1169
1170                 media->vod_media =
1171                   vlm->vod->pf_media_new( vlm->vod, media->psz_name, &item );
1172                 return VLC_SUCCESS;
1173             }
1174
1175             media->vod_media =
1176                 vlm->vod->pf_media_new( vlm->vod, media->psz_name,
1177                                         &media->item );
1178         }
1179     }
1180
1181     return VLC_SUCCESS;
1182 }
1183
1184 int vlm_MediaControl( vlm_t *vlm, vlm_media_t *media, const char *psz_id,
1185                       const char *psz_command, const char *psz_args )
1186 {
1187     vlm_media_instance_t *p_instance;
1188     int i;
1189     char *psz_header;
1190
1191     p_instance = vlm_MediaInstanceSearch( vlm, media, psz_id );
1192
1193     if( !strcmp( psz_command, "play" ) )
1194     {
1195         if( !media->b_enabled || media->i_input == 0 ) return 0;
1196
1197         if( !p_instance )
1198         {
1199             p_instance = malloc( sizeof(vlm_media_instance_t) );
1200             if( !p_instance ) return VLC_EGENERIC;
1201             memset( p_instance, 0, sizeof(vlm_media_instance_t) );
1202             vlc_input_item_Init( VLC_OBJECT(vlm), &p_instance->item );
1203             p_instance->p_input = NULL;
1204
1205             if( ( media->psz_output != NULL ) || ( media->psz_vod_output != NULL ) )
1206             {
1207                 p_instance->item.ppsz_options = malloc( sizeof( char* ) );
1208                 asprintf( &p_instance->item.ppsz_options[0], "sout=%s%s%s",
1209                           media->psz_output ? media->psz_output : "",
1210                           (media->psz_output && media->psz_vod_output) ?
1211                           ":" : media->psz_vod_output ? "#" : "",
1212                           media->psz_vod_output ? media->psz_vod_output : "" );
1213                 p_instance->item.i_options = 1;
1214             }
1215
1216             for( i = 0; i < media->i_option; i++ )
1217             {
1218                 p_instance->item.i_options++;
1219                 p_instance->item.ppsz_options =
1220                     realloc( p_instance->item.ppsz_options,
1221                              p_instance->item.i_options * sizeof( char* ) );
1222                 p_instance->item.ppsz_options[p_instance->item.i_options - 1] =
1223                     strdup( media->option[i] );
1224             }
1225
1226             p_instance->psz_name = psz_id ? strdup( psz_id ) : NULL;
1227             TAB_APPEND( media->i_instance, media->instance, p_instance );
1228         }
1229
1230         if( ( psz_args && sscanf(psz_args, "%d", &i) == 1 ) && ( i < media->i_input ) )
1231         {
1232             p_instance->i_index = i;
1233         }
1234
1235         if( p_instance->item.psz_uri ) free( p_instance->item.psz_uri );
1236         p_instance->item.psz_uri =
1237             strdup( media->input[p_instance->i_index] );
1238
1239         if( p_instance->p_input )
1240         {
1241             input_StopThread( p_instance->p_input );
1242             input_DestroyThread( p_instance->p_input );
1243             vlc_object_detach( p_instance->p_input );
1244             vlc_object_destroy( p_instance->p_input );
1245         }
1246
1247         asprintf( &psz_header, _("Media: %s"), media->psz_name );
1248         p_instance->p_input = input_CreateThread2( vlm, &p_instance->item,
1249                                                    psz_header );
1250         if( !p_instance->p_input )
1251         {
1252             TAB_REMOVE( media->i_instance, media->instance, p_instance );
1253             vlc_input_item_Clean( &p_instance->item );
1254             if( p_instance->psz_name ) free( p_instance->psz_name );
1255         }
1256         free( psz_header );
1257
1258         return VLC_SUCCESS;
1259     }
1260
1261     if( !p_instance ) return VLC_EGENERIC;
1262
1263     if( !strcmp( psz_command, "seek" ) )
1264     {
1265         vlc_value_t val;
1266         float f_percentage;
1267
1268         if( psz_args )
1269         {
1270             f_percentage = i18n_atof( psz_args );
1271             if( f_percentage >= 0.0 && f_percentage <= 100.0 )
1272             {
1273                 val.f_float = f_percentage / 100.0 ;
1274                 var_Set( p_instance->p_input, "position", val );
1275                 return VLC_SUCCESS;
1276             }
1277         }
1278     }
1279     else if( !strcmp( psz_command, "stop" ) )
1280     {
1281         TAB_REMOVE( media->i_instance, media->instance, p_instance );
1282
1283         if( p_instance->p_input )
1284         {
1285             input_StopThread( p_instance->p_input );
1286             input_DestroyThread( p_instance->p_input );
1287             vlc_object_detach( p_instance->p_input );
1288             vlc_object_destroy( p_instance->p_input );
1289         }
1290
1291         vlc_input_item_Clean( &p_instance->item );
1292         if( p_instance->psz_name ) free( p_instance->psz_name );
1293         free( p_instance );
1294
1295         return VLC_SUCCESS;
1296     }
1297     else if( !strcmp( psz_command, "pause" ) )
1298     {
1299         vlc_value_t val;
1300
1301         if( !p_instance->p_input ) return VLC_SUCCESS;
1302
1303         var_Get( p_instance->p_input, "state", &val );
1304
1305         if( val.i_int == PAUSE_S ) val.i_int = PLAYING_S;
1306         else val.i_int = PAUSE_S;
1307         var_Set( p_instance->p_input, "state", val );
1308
1309         return VLC_SUCCESS;
1310     }
1311
1312     return VLC_EGENERIC;
1313 }
1314
1315 /*****************************************************************************
1316  * Schedule handling
1317  *****************************************************************************/
1318 static int64_t vlm_Date()
1319 {
1320 #ifdef WIN32
1321     struct timeb tm;
1322     ftime( &tm );
1323     return ((int64_t)tm.time) * 1000000 + ((int64_t)tm.millitm) * 1000;
1324 #else
1325     return mdate();
1326 #endif
1327 }
1328
1329 vlm_schedule_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name )
1330 {
1331     vlm_schedule_t *p_sched = malloc( sizeof( vlm_schedule_t ) );
1332
1333     if( !p_sched )
1334     {
1335         return NULL;
1336     }
1337
1338     if( !psz_name )
1339     {
1340         return NULL;
1341     }
1342
1343     p_sched->psz_name = strdup( psz_name );
1344     p_sched->b_enabled = VLC_FALSE;
1345     p_sched->i_command = 0;
1346     p_sched->command = NULL;
1347     p_sched->i_date = 0;
1348     p_sched->i_period = 0;
1349     p_sched->i_repeat = -1;
1350
1351     TAB_APPEND( vlm->i_schedule, vlm->schedule, p_sched );
1352
1353     return p_sched;
1354 }
1355
1356 /* for now, simple delete. After, del with options (last arg) */
1357 void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_t *sched,
1358                          const char *psz_name )
1359 {
1360     if( sched == NULL ) return;
1361
1362     TAB_REMOVE( vlm->i_schedule, vlm->schedule, sched );
1363
1364     if( vlm->i_schedule == 0 && vlm->schedule ) free( vlm->schedule );
1365     free( sched->psz_name );
1366     while( sched->i_command )
1367     {
1368         char *psz_cmd = sched->command[0];
1369         TAB_REMOVE( sched->i_command, sched->command, psz_cmd );
1370         free( psz_cmd );
1371     }
1372     free( sched );
1373 }
1374
1375 static vlm_schedule_t *vlm_ScheduleSearch( vlm_t *vlm, const char *psz_name )
1376 {
1377     int i;
1378
1379     for( i = 0; i < vlm->i_schedule; i++ )
1380     {
1381         if( strcmp( psz_name, vlm->schedule[i]->psz_name ) == 0 )
1382         {
1383             return vlm->schedule[i];
1384         }
1385     }
1386
1387     return NULL;
1388 }
1389
1390 /* Ok, setup schedule command will be able to support only one (argument value) at a time  */
1391 int vlm_ScheduleSetup( vlm_schedule_t *schedule, const char *psz_cmd,
1392                        const char *psz_value )
1393 {
1394     if( !strcmp( psz_cmd, "enabled" ) )
1395     {
1396         schedule->b_enabled = VLC_TRUE;
1397     }
1398     else if( !strcmp( psz_cmd, "disabled" ) )
1399     {
1400         schedule->b_enabled = VLC_FALSE;
1401     }
1402 #if !defined( UNDER_CE )
1403     else if( !strcmp( psz_cmd, "date" ) )
1404     {
1405         struct tm time;
1406         const char *p;
1407         time_t date;
1408
1409         time.tm_sec = 0;         /* seconds */
1410         time.tm_min = 0;         /* minutes */
1411         time.tm_hour = 0;        /* hours */
1412         time.tm_mday = 0;        /* day of the month */
1413         time.tm_mon = 0;         /* month */
1414         time.tm_year = 0;        /* year */
1415         time.tm_wday = 0;        /* day of the week */
1416         time.tm_yday = 0;        /* day in the year */
1417         time.tm_isdst = -1;       /* daylight saving time */
1418
1419         /* date should be year/month/day-hour:minutes:seconds */
1420         p = strchr( psz_value, '-' );
1421
1422         if( !strcmp( psz_value, "now" ) )
1423         {
1424             schedule->i_date = 0;
1425         }
1426         else if( (p == NULL) && sscanf( psz_value, "%d:%d:%d", &time.tm_hour,
1427                                         &time.tm_min, &time.tm_sec ) != 3 )
1428                                         /* it must be a hour:minutes:seconds */
1429         {
1430             return 1;
1431         }
1432         else
1433         {
1434             unsigned i,j,k;
1435
1436             switch( sscanf( p + 1, "%u:%u:%u", &i, &j, &k ) )
1437             {
1438                 case 1:
1439                     time.tm_sec = i;
1440                     break;
1441                 case 2:
1442                     time.tm_min = i;
1443                     time.tm_sec = j;
1444                     break;
1445                 case 3:
1446                     time.tm_hour = i;
1447                     time.tm_min = j;
1448                     time.tm_sec = k;
1449                     break;
1450                 default:
1451                     return 1;
1452             }
1453
1454             switch( sscanf( psz_value, "%d/%d/%d", &i, &j, &k ) )
1455             {
1456                 case 1:
1457                     time.tm_mday = i;
1458                     break;
1459                 case 2:
1460                     time.tm_mon = i - 1;
1461                     time.tm_mday = j;
1462                     break;
1463                 case 3:
1464                     time.tm_year = i - 1900;
1465                     time.tm_mon = j - 1;
1466                     time.tm_mday = k;
1467                     break;
1468                 default:
1469                     return 1;
1470             }
1471
1472             date = mktime( &time );
1473             schedule->i_date = ((mtime_t) date) * 1000000;
1474         }
1475     }
1476     else if( !strcmp( psz_cmd, "period" ) )
1477     {
1478         struct tm time;
1479         const char *p;
1480         const char *psz_time = NULL, *psz_date = NULL;
1481         time_t date;
1482         unsigned i,j,k;
1483
1484         /* First, if date or period are modified, repeat should be equal to -1 */
1485         schedule->i_repeat = -1;
1486
1487         time.tm_sec = 0;         /* seconds */
1488         time.tm_min = 0;         /* minutes */
1489         time.tm_hour = 0;        /* hours */
1490         time.tm_mday = 0;        /* day of the month */
1491         time.tm_mon = 0;         /* month */
1492         time.tm_year = 0;        /* year */
1493         time.tm_wday = 0;        /* day of the week */
1494         time.tm_yday = 0;        /* day in the year */
1495         time.tm_isdst = -1;       /* daylight saving time */
1496
1497         /* date should be year/month/day-hour:minutes:seconds */
1498         p = strchr( psz_value, '-' );
1499         if( p )
1500         {
1501             psz_date = psz_value;
1502             psz_time = p + 1;
1503         }
1504         else
1505         {
1506             psz_time = psz_value;
1507         }
1508
1509         switch( sscanf( psz_time, "%u:%u:%u", &i, &j, &k ) )
1510         {
1511             case 1:
1512                 time.tm_sec = i;
1513                 break;
1514             case 2:
1515                 time.tm_min = i;
1516                 time.tm_sec = j;
1517                 break;
1518             case 3:
1519                 time.tm_hour = i;
1520                 time.tm_min = j;
1521                 time.tm_sec = k;
1522                 break;
1523             default:
1524                 return 1;
1525         }
1526         if( psz_date )
1527         {
1528             switch( sscanf( psz_date, "%u/%u/%u", &i, &j, &k ) )
1529             {
1530                 case 1:
1531                     time.tm_mday = i;
1532                     break;
1533                 case 2:
1534                     time.tm_mon = i;
1535                     time.tm_mday = j;
1536                     break;
1537                 case 3:
1538                     time.tm_year = i;
1539                     time.tm_mon = j;
1540                     time.tm_mday = k;
1541                     break;
1542                 default:
1543                     return 1;
1544             }
1545         }
1546
1547         /* ok, that's stupid... who is going to schedule streams every 42 years ? */
1548         date = (((( time.tm_year * 12 + time.tm_mon ) * 30 + time.tm_mday ) * 24 + time.tm_hour ) * 60 + time.tm_min ) * 60 + time.tm_sec ;
1549         schedule->i_period = ((mtime_t) date) * 1000000;
1550     }
1551 #endif /* UNDER_CE */
1552     else if( !strcmp( psz_cmd, "repeat" ) )
1553     {
1554         int i;
1555
1556         if( sscanf( psz_value, "%d", &i ) == 1 )
1557         {
1558             schedule->i_repeat = i;
1559         }
1560         else
1561         {
1562             return 1;
1563         }
1564     }
1565     else if( !strcmp( psz_cmd, "append" ) )
1566     {
1567         char *command = strdup( psz_value );
1568
1569         TAB_APPEND( schedule->i_command, schedule->command, command );
1570     }
1571     else
1572     {
1573         return 1;
1574     }
1575     return 0;
1576 }
1577
1578 /*****************************************************************************
1579  * Message handling functions
1580  *****************************************************************************/
1581 static vlm_message_t *vlm_MessageNew( char *psz_name,
1582                                       const char *psz_format, ... )
1583 {
1584     vlm_message_t *p_message;
1585     va_list args;
1586
1587     if( !psz_name ) return 0;
1588
1589     p_message = malloc( sizeof(vlm_message_t) );
1590     if( !p_message)
1591     {
1592         return NULL;
1593     }
1594
1595     p_message->psz_value = 0;
1596
1597     if( psz_format )
1598     {
1599         va_start( args, psz_format );
1600         if( vasprintf( &p_message->psz_value, psz_format, args ) < 0 )
1601         {
1602             va_end( args );
1603             free( p_message );
1604             return 0;
1605         }
1606         va_end( args );
1607     }
1608
1609     p_message->psz_name = strdup( psz_name );
1610     p_message->i_child = 0;
1611     p_message->child = NULL;
1612
1613     return p_message;
1614 }
1615
1616 void vlm_MessageDelete( vlm_message_t *p_message )
1617 {
1618     if( p_message->psz_name ) free( p_message->psz_name );
1619     if( p_message->psz_value ) free( p_message->psz_value );
1620     while( p_message->i_child-- )
1621         vlm_MessageDelete( p_message->child[p_message->i_child] );
1622     if( p_message->child ) free( p_message->child );
1623     free( p_message );
1624 }
1625
1626 /* Add a child */
1627 static vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message,
1628                                       vlm_message_t *p_child )
1629 {
1630     if( p_message == NULL ) return NULL;
1631
1632     if( p_child )
1633     {
1634         TAB_APPEND( p_message->i_child, p_message->child, p_child );
1635     }
1636
1637     return p_child;
1638 }
1639
1640 /*****************************************************************************
1641  * Misc utility functions
1642  *****************************************************************************/
1643 static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_t *media,
1644                                 vlm_schedule_t *schedule, char *psz_filter )
1645 {
1646     if( media != NULL )
1647     {
1648         int i;
1649         vlm_message_t *msg;
1650         vlm_message_t *msg_media;
1651         vlm_message_t *msg_child;
1652
1653         msg = vlm_MessageNew( "show", NULL );
1654         msg_media = vlm_MessageAdd( msg, vlm_MessageNew( media->psz_name, 0 ));
1655
1656         vlm_MessageAdd( msg_media,
1657                         vlm_MessageNew( "type", media->i_type == VOD_TYPE ?
1658                                         "vod" : "broadcast" ) );
1659         vlm_MessageAdd( msg_media,
1660                         vlm_MessageNew( "enabled", media->b_enabled ?
1661                                         "yes" : "no" ) );
1662
1663         vlm_MessageAdd( msg_media,
1664                         vlm_MessageNew( "loop", media->b_loop ?
1665                                         "yes" : "no" ) );
1666
1667         if( media->i_type == VOD_TYPE && media->psz_mux )
1668             vlm_MessageAdd( msg_media,
1669                             vlm_MessageNew( "mux", media->psz_mux ) );
1670
1671         msg_child = vlm_MessageAdd( msg_media,
1672                                     vlm_MessageNew( "inputs", NULL ) );
1673
1674         for( i = 0; i < media->i_input; i++ )
1675         {
1676             vlm_MessageAdd( msg_child,
1677                             vlm_MessageNew( media->input[i], NULL ) );
1678         }
1679
1680         vlm_MessageAdd( msg_media,
1681                         vlm_MessageNew( "output", media->psz_output ?
1682                                         media->psz_output : "" ) );
1683
1684         msg_child = vlm_MessageAdd( msg_media, vlm_MessageNew( "options", 0 ));
1685
1686         for( i = 0; i < media->i_option; i++ )
1687         {
1688             vlm_MessageAdd( msg_child, vlm_MessageNew( media->option[i], 0 ) );
1689         }
1690
1691         msg_child = vlm_MessageAdd( msg_media,
1692                                     vlm_MessageNew( "instances", NULL ) );
1693
1694         for( i = 0; i < media->i_instance; i++ )
1695         {
1696             vlm_media_instance_t *p_instance = media->instance[i];
1697             vlc_value_t val;
1698             vlm_message_t *msg_instance;
1699             char *psz_tmp;
1700
1701             if( !p_instance->p_input ) val.i_int = END_S;
1702             else var_Get( p_instance->p_input, "state", &val );
1703
1704             msg_instance = vlm_MessageNew( "instance" , NULL );
1705             vlm_MessageAdd( msg_instance, vlm_MessageNew( "name" , p_instance->psz_name ? p_instance->psz_name : "default" ) );
1706             vlm_MessageAdd( msg_instance, vlm_MessageNew( "state",
1707                                 val.i_int == PLAYING_S ? "playing" :
1708                                 val.i_int == PAUSE_S ? "paused" :
1709                                 "stopped" ) );
1710 #define APPEND_INPUT_INFO( a, format, type ) \
1711             asprintf( &psz_tmp, format, \
1712                       var_Get ## type( p_instance->p_input, a ) ); \
1713             vlm_MessageAdd( msg_instance, vlm_MessageNew( a, psz_tmp ) ); \
1714             free( psz_tmp );
1715             APPEND_INPUT_INFO( "position", "%f", Float );
1716             APPEND_INPUT_INFO( "time", "%d", Integer );
1717             APPEND_INPUT_INFO( "length", "%d", Integer );
1718             APPEND_INPUT_INFO( "rate", "%d", Integer );
1719             APPEND_INPUT_INFO( "title", "%d", Integer );
1720             APPEND_INPUT_INFO( "chapter", "%d", Integer );
1721             APPEND_INPUT_INFO( "seekable", "%d", Bool );
1722 #undef APPEND_INPUT_INFO
1723             asprintf( &psz_tmp, "%d", p_instance->i_index + 1 );
1724             vlm_MessageAdd( msg_instance, vlm_MessageNew( "playlistindex", psz_tmp ) );
1725             free( psz_tmp );
1726             vlm_MessageAdd( msg_child, msg_instance );
1727         }
1728
1729         return msg;
1730
1731     }
1732
1733     else if( schedule != NULL )
1734     {
1735         int i;
1736         vlm_message_t *msg;
1737         vlm_message_t *msg_schedule;
1738         vlm_message_t *msg_child;
1739         char buffer[100];
1740
1741         msg = vlm_MessageNew( "show", NULL );
1742         msg_schedule =
1743             vlm_MessageAdd( msg, vlm_MessageNew( schedule->psz_name, 0 ) );
1744
1745         vlm_MessageAdd( msg_schedule, vlm_MessageNew("type", "schedule") );
1746
1747         vlm_MessageAdd( msg_schedule,
1748                         vlm_MessageNew( "enabled", schedule->b_enabled ?
1749                                         "yes" : "no" ) );
1750
1751 #if !defined( UNDER_CE )
1752         if( schedule->i_date != 0 )
1753         {
1754             struct tm date;
1755             time_t i_time = (time_t)( schedule->i_date / 1000000 );
1756             char *psz_date;
1757
1758 #ifdef HAVE_LOCALTIME_R
1759             localtime_r( &i_time, &date);
1760 #else
1761             struct tm *p_date = localtime( &i_time );
1762             date = *p_date;
1763 #endif
1764
1765             asprintf( &psz_date, "%d/%d/%d-%d:%d:%d",
1766                       date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
1767                       date.tm_hour, date.tm_min, date.tm_sec );
1768
1769             vlm_MessageAdd( msg_schedule,
1770                             vlm_MessageNew( "date", psz_date ) );
1771             free( psz_date );
1772         }
1773         else
1774         {
1775             vlm_MessageAdd( msg_schedule, vlm_MessageNew("date", "now") );
1776         }
1777
1778         if( schedule->i_period != 0 )
1779         {
1780             time_t i_time = (time_t) ( schedule->i_period / 1000000 );
1781             struct tm date;
1782
1783             date.tm_sec = (int)( i_time % 60 );
1784             i_time = i_time / 60;
1785             date.tm_min = (int)( i_time % 60 );
1786             i_time = i_time / 60;
1787             date.tm_hour = (int)( i_time % 24 );
1788             i_time = i_time / 24;
1789             date.tm_mday = (int)( i_time % 30 );
1790             i_time = i_time / 30;
1791             /* okay, okay, months are not always 30 days long */
1792             date.tm_mon = (int)( i_time % 12 );
1793             i_time = i_time / 12;
1794             date.tm_year = (int)i_time;
1795
1796             sprintf( buffer, "%d/%d/%d-%d:%d:%d", date.tm_year, date.tm_mon,
1797                      date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
1798
1799             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", buffer) );
1800         }
1801         else
1802         {
1803             vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", "0") );
1804         }
1805 #endif /* UNDER_CE */
1806
1807         sprintf( buffer, "%d", schedule->i_repeat );
1808         vlm_MessageAdd( msg_schedule, vlm_MessageNew( "repeat", buffer ) );
1809
1810         msg_child =
1811             vlm_MessageAdd( msg_schedule, vlm_MessageNew("commands", 0) );
1812
1813         for( i = 0; i < schedule->i_command; i++ )
1814         {
1815            vlm_MessageAdd( msg_child,
1816                            vlm_MessageNew( schedule->command[i], NULL ) );
1817         }
1818
1819         return msg;
1820
1821     }
1822
1823     else if( psz_filter && !strcmp( psz_filter, "media" ) )
1824     {
1825         int i, j;
1826         vlm_message_t *msg;
1827         vlm_message_t *msg_child;
1828         int i_vod = 0, i_broadcast = 0;
1829         char *psz_count;
1830
1831         for( i = 0; i < vlm->i_media; i++ )
1832         {
1833             if( vlm->media[i]->i_type == VOD_TYPE )
1834                 i_vod ++;
1835             else
1836                 i_broadcast ++;
1837         }
1838
1839         asprintf( &psz_count, "( %d broadcast - %d vod )", i_broadcast, i_vod);
1840
1841         msg = vlm_MessageNew( "show", NULL );
1842         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "media", psz_count ) );
1843         free( psz_count );
1844
1845         for( i = 0; i < vlm->i_media; i++ )
1846         {
1847             vlm_media_t *m = vlm->media[i];
1848             vlm_message_t *msg_media, *msg_instance;
1849
1850             msg_media = vlm_MessageAdd( msg_child,
1851                                         vlm_MessageNew( m->psz_name, 0 ) );
1852
1853             vlm_MessageAdd( msg_media,
1854                             vlm_MessageNew( "type", m->i_type == VOD_TYPE ?
1855                                             "vod" : "broadcast" ) );
1856
1857             vlm_MessageAdd( msg_media,
1858                             vlm_MessageNew( "enabled", m->b_enabled ?
1859                                             "yes" : "no" ) );
1860
1861             if( m->i_type == VOD_TYPE && m->psz_mux )
1862                 vlm_MessageAdd( msg_media,
1863                                 vlm_MessageNew( "mux", m->psz_mux ) );
1864
1865             msg_instance = vlm_MessageAdd( msg_media,
1866                                            vlm_MessageNew( "instances", 0 ) );
1867
1868             for( j = 0; j < m->i_instance; j++ )
1869             {
1870                 vlm_media_instance_t *p_instance = m->instance[j];
1871                 vlc_value_t val;
1872
1873                 if( !p_instance->p_input ) val.i_int = END_S;
1874                 else var_Get( p_instance->p_input, "state", &val );
1875
1876                 vlm_MessageAdd( msg_instance,
1877                     vlm_MessageNew( p_instance->psz_name ?
1878                                     p_instance->psz_name : "default",
1879                                     val.i_int == PLAYING_S ? "playing" :
1880                                     val.i_int == PAUSE_S ? "paused" :
1881                                     "stopped" ) );
1882             }
1883         }
1884
1885         return msg;
1886     }
1887
1888     else if( psz_filter && !strcmp( psz_filter, "schedule" ) )
1889     {
1890         int i;
1891         vlm_message_t *msg;
1892         vlm_message_t *msg_child;
1893
1894         msg = vlm_MessageNew( "show", NULL );
1895         msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "schedule", NULL ) );
1896
1897         for( i = 0; i < vlm->i_schedule; i++ )
1898         {
1899             vlm_schedule_t *s = vlm->schedule[i];
1900             vlm_message_t *msg_schedule;
1901             mtime_t i_time, i_next_date;
1902
1903             msg_schedule = vlm_MessageAdd( msg_child,
1904                                            vlm_MessageNew( s->psz_name, 0 ) );
1905             vlm_MessageAdd( msg_schedule,
1906                             vlm_MessageNew( "enabled", s->b_enabled ?
1907                                             "yes" : "no" ) );
1908
1909             /* calculate next date */
1910             i_time = vlm_Date();
1911             i_next_date = s->i_date;
1912
1913             if( s->i_period != 0 )
1914             {
1915                 int j = 0;
1916                 while( s->i_date + j * s->i_period <= i_time &&
1917                        s->i_repeat > j )
1918                 {
1919                     j++;
1920                 }
1921
1922                 i_next_date = s->i_date + j * s->i_period;
1923             }
1924
1925             if( i_next_date > i_time )
1926             {
1927                 time_t i_date = (time_t) (i_next_date / 1000000) ;
1928
1929 #if !defined( UNDER_CE )
1930 #ifdef HAVE_CTIME_R
1931                 char psz_date[500];
1932                 ctime_r( &i_date, psz_date );
1933 #else
1934                 char *psz_date = ctime( &i_date );
1935 #endif
1936
1937                 vlm_MessageAdd( msg_schedule,
1938                                 vlm_MessageNew( "next launch", psz_date ) );
1939 #endif
1940             }
1941         }
1942
1943         return msg;
1944     }
1945
1946     else if( ( psz_filter == NULL ) && ( media == NULL ) && ( schedule == NULL ) )
1947     {
1948         vlm_message_t *show1 = vlm_Show( vlm, NULL, NULL, "media" );
1949         vlm_message_t *show2 = vlm_Show( vlm, NULL, NULL, "schedule" );
1950
1951         vlm_MessageAdd( show1, show2->child[0] );
1952
1953         /* We must destroy the parent node "show" of show2
1954          * and not the children */
1955         free( show2->psz_name );
1956         free( show2 );
1957
1958         return show1;
1959     }
1960
1961     else
1962     {
1963         return vlm_MessageNew( "show", NULL );
1964     }
1965 }
1966
1967 static vlm_message_t *vlm_Help( vlm_t *vlm, char *psz_filter )
1968 {
1969     vlm_message_t *message, *message_child;
1970
1971 #define MessageAdd( a ) \
1972         vlm_MessageAdd( message, vlm_MessageNew( a, NULL ) );
1973 #define MessageAddChild( a ) \
1974         vlm_MessageAdd( message_child, vlm_MessageNew( a, NULL ) );
1975
1976     if( psz_filter == NULL )
1977     {
1978         message = vlm_MessageNew( "help", NULL );
1979
1980         message_child = MessageAdd( "Commands Syntax:" );
1981         MessageAddChild( "new (name) vod|broadcast|schedule [properties]" );
1982         MessageAddChild( "setup (name) (properties)" );
1983         MessageAddChild( "show [(name)|media|schedule]" );
1984         MessageAddChild( "del (name)|all|media|schedule" );
1985         MessageAddChild( "control (name) [instance_name] (command)" );
1986         MessageAddChild( "save (config_file)" );
1987         MessageAddChild( "export" );
1988         MessageAddChild( "load (config_file)" );
1989
1990         message_child = MessageAdd( "Media Proprieties Syntax:" );
1991         MessageAddChild( "input (input_name)" );
1992         MessageAddChild( "inputdel (input_name)|all" );
1993         MessageAddChild( "inputdeln input_number" );
1994         MessageAddChild( "output (output_name)" );
1995         MessageAddChild( "option (option_name)[=value]" );
1996         MessageAddChild( "enabled|disabled" );
1997         MessageAddChild( "loop|unloop (broadcast only)" );
1998         MessageAddChild( "mux (mux_name)" );
1999
2000         message_child = MessageAdd( "Schedule Proprieties Syntax:" );
2001         MessageAddChild( "enabled|disabled" );
2002         MessageAddChild( "append (command_until_rest_of_the_line)" );
2003         MessageAddChild( "date (year)/(month)/(day)-(hour):(minutes):"
2004                          "(seconds)|now" );
2005         MessageAddChild( "period (years_aka_12_months)/(months_aka_30_days)/"
2006                          "(days)-(hours):(minutes):(seconds)" );
2007         MessageAddChild( "repeat (number_of_repetitions)" );
2008
2009         message_child = MessageAdd( "Control Commands Syntax:" );
2010         MessageAddChild( "play" );
2011         MessageAddChild( "pause" );
2012         MessageAddChild( "stop" );
2013         MessageAddChild( "seek (percentage)" );
2014
2015         return message;
2016     }
2017
2018     return vlm_MessageNew( "help", NULL );
2019 }
2020
2021 /*****************************************************************************
2022  * Config handling functions
2023  *****************************************************************************/
2024 static int Load( vlm_t *vlm, char *file )
2025 {
2026     char *pf = file;
2027     int  i_line = 1;
2028
2029     while( *pf != '\0' )
2030     {
2031         vlm_message_t *message = NULL;
2032         int i_end = 0;
2033
2034         while( pf[i_end] != '\n' && pf[i_end] != '\0' && pf[i_end] != '\r' )
2035         {
2036             i_end++;
2037         }
2038
2039         if( pf[i_end] == '\r' || pf[i_end] == '\n' )
2040         {
2041             pf[i_end] = '\0';
2042             i_end++;
2043             if( pf[i_end] == '\n' ) i_end++;
2044         }
2045
2046         if( *pf && ExecuteCommand( vlm, pf, &message ) )
2047         {
2048             if( message )
2049             {
2050                 if( message->psz_value )
2051                     msg_Err( vlm, "Load error on line %d: %s: %s",
2052                              i_line, message->psz_name, message->psz_value );
2053                 vlm_MessageDelete( message );
2054             }
2055             return 1;
2056         }
2057         if( message ) vlm_MessageDelete( message );
2058
2059         pf += i_end;
2060         i_line++;
2061     }
2062
2063     return 0;
2064 }
2065
2066 static char *Save( vlm_t *vlm )
2067 {
2068     char *save = NULL;
2069     char psz_header[] = "\n"
2070                         "# VLC media player VLM command batch\n"
2071                         "# http://www.videolan.org/vlc/\n\n" ;
2072     char *p;
2073     int i,j;
2074     int i_length = strlen( psz_header );
2075
2076     for( i = 0; i < vlm->i_media; i++ )
2077     {
2078         vlm_media_t *media = vlm->media[i];
2079
2080         if( media->i_type == VOD_TYPE )
2081         {
2082             i_length += strlen( "new  vod " ) + strlen(media->psz_name);
2083         }
2084         else
2085         {
2086             i_length += strlen( "new  broadcast " ) + strlen(media->psz_name);
2087         }
2088
2089         if( media->b_enabled == VLC_TRUE )
2090         {
2091             i_length += strlen( "enabled" );
2092         }
2093         else
2094         {
2095             i_length += strlen( "disabled" );
2096         }
2097
2098         if( media->b_loop == VLC_TRUE )
2099         {
2100             i_length += strlen( " loop\n" );
2101         }
2102         else
2103         {
2104             i_length += strlen( "\n" );
2105         }
2106
2107         for( j = 0; j < media->i_input; j++ )
2108         {
2109             i_length += strlen( "setup  input \"\"\n" ) +
2110                 strlen( media->psz_name ) + strlen( media->input[j] );
2111         }
2112
2113         if( media->psz_output != NULL )
2114         {
2115             i_length += strlen(media->psz_name) + strlen(media->psz_output) +
2116                 strlen( "setup  output \n" );
2117         }
2118
2119         for( j=0 ; j < media->i_option ; j++ )
2120         {
2121             i_length += strlen(media->psz_name) + strlen(media->option[j]) +
2122                 strlen("setup  option \n");
2123         }
2124     }
2125
2126     for( i = 0; i < vlm->i_schedule; i++ )
2127     {
2128         vlm_schedule_t *schedule = vlm->schedule[i];
2129
2130         i_length += strlen( "new  schedule " ) + strlen( schedule->psz_name );
2131
2132         if( schedule->b_enabled == VLC_TRUE )
2133         {
2134             i_length += strlen( "date //-:: enabled\n" ) + 14;
2135         }
2136         else
2137         {
2138             i_length += strlen( "date //-:: disabled\n" ) + 14;
2139         }
2140
2141
2142         if( schedule->i_period != 0 )
2143         {
2144             i_length += strlen( "setup  " ) + strlen( schedule->psz_name ) +
2145                 strlen( "period //-::\n" ) + 14;
2146         }
2147
2148         if( schedule->i_repeat >= 0 )
2149         {
2150             char buffer[12];
2151
2152             sprintf( buffer, "%d", schedule->i_repeat );
2153             i_length += strlen( "setup  repeat \n" ) +
2154                 strlen( schedule->psz_name ) + strlen( buffer );
2155         }
2156         else
2157         {
2158             i_length++;
2159         }
2160
2161         for( j = 0; j < schedule->i_command; j++ )
2162         {
2163             i_length += strlen( "setup  append \n" ) +
2164                 strlen( schedule->psz_name ) + strlen( schedule->command[j] );
2165         }
2166
2167     }
2168
2169     /* Don't forget the '\0' */
2170     i_length++;
2171     /* now we have the length of save */
2172
2173     p = save = malloc( i_length );
2174     *save = '\0';
2175
2176     p += sprintf( p, "%s", psz_header );
2177
2178     /* finally we can write in it */
2179     for( i = 0; i < vlm->i_media; i++ )
2180     {
2181         vlm_media_t *media = vlm->media[i];
2182
2183         if( media->i_type == VOD_TYPE )
2184         {
2185             p += sprintf( p, "new %s vod ", media->psz_name);
2186         }
2187         else
2188         {
2189             p += sprintf( p, "new %s broadcast ", media->psz_name);
2190         }
2191
2192         if( media->b_enabled == VLC_TRUE )
2193         {
2194             p += sprintf( p, "enabled" );
2195         }
2196         else
2197         {
2198             p += sprintf( p, "disabled" );
2199         }
2200
2201         if( media->b_loop == VLC_TRUE )
2202         {
2203             p += sprintf( p, " loop\n" );
2204         }
2205         else
2206         {
2207             p += sprintf( p, "\n" );
2208         }
2209
2210         for( j = 0; j < media->i_input; j++ )
2211         {
2212             p += sprintf( p, "setup %s input \"%s\"\n", media->psz_name,
2213                           media->input[j] );
2214         }
2215
2216         if( media->psz_output != NULL )
2217         {
2218             p += sprintf( p, "setup %s output %s\n", media->psz_name,
2219                           media->psz_output );
2220         }
2221
2222         for( j = 0; j < media->i_option; j++ )
2223         {
2224             p += sprintf( p, "setup %s option %s\n", media->psz_name,
2225                           media->option[j] );
2226         }
2227     }
2228
2229     /* and now, the schedule scripts */
2230 #if !defined( UNDER_CE )
2231     for( i = 0; i < vlm->i_schedule; i++ )
2232     {
2233         vlm_schedule_t *schedule = vlm->schedule[i];
2234         struct tm date;
2235         time_t i_time = (time_t) ( schedule->i_date / 1000000 );
2236
2237 #ifdef HAVE_LOCALTIME_R
2238         localtime_r( &i_time, &date);
2239 #else
2240         struct tm *p_date = localtime( &i_time );
2241         date = *p_date;
2242 #endif
2243
2244         p += sprintf( p, "new %s schedule ", schedule->psz_name);
2245
2246         if( schedule->b_enabled == VLC_TRUE )
2247         {
2248             p += sprintf( p, "date %d/%d/%d-%d:%d:%d enabled\n",
2249                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
2250                           date.tm_hour, date.tm_min, date.tm_sec );
2251         }
2252         else
2253         {
2254             p += sprintf( p, "date %d/%d/%d-%d:%d:%d disabled\n",
2255                           date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
2256                           date.tm_hour, date.tm_min, date.tm_sec);
2257         }
2258
2259         if( schedule->i_period != 0 )
2260         {
2261             p += sprintf( p, "setup %s ", schedule->psz_name );
2262
2263             i_time = (time_t) ( schedule->i_period / 1000000 );
2264
2265             date.tm_sec = (int)( i_time % 60 );
2266             i_time = i_time / 60;
2267             date.tm_min = (int)( i_time % 60 );
2268             i_time = i_time / 60;
2269             date.tm_hour = (int)( i_time % 24 );
2270             i_time = i_time / 24;
2271             date.tm_mday = (int)( i_time % 30 );
2272             i_time = i_time / 30;
2273             /* okay, okay, months are not always 30 days long */
2274             date.tm_mon = (int)( i_time % 12 );
2275             i_time = i_time / 12;
2276             date.tm_year = (int)i_time;
2277
2278             p += sprintf( p, "period %d/%d/%d-%d:%d:%d\n",
2279                           date.tm_year, date.tm_mon, date.tm_mday,
2280                           date.tm_hour, date.tm_min, date.tm_sec);
2281         }
2282
2283         if( schedule->i_repeat >= 0 )
2284         {
2285             p += sprintf( p, "setup %s repeat %d\n",
2286                           schedule->psz_name, schedule->i_repeat );
2287         }
2288         else
2289         {
2290             p += sprintf( p, "\n" );
2291         }
2292
2293         for( j = 0; j < schedule->i_command; j++ )
2294         {
2295             p += sprintf( p, "setup %s append %s\n",
2296                           schedule->psz_name, schedule->command[j] );
2297         }
2298
2299     }
2300 #endif /* UNDER_CE */
2301
2302     return save;
2303 }
2304
2305 /*****************************************************************************
2306  * Manage:
2307  *****************************************************************************/
2308 int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
2309                          const char *psz_id, int i_query, va_list args )
2310 {
2311     vlm_t *vlm = (vlm_t *)p_private;
2312     int i, i_ret = VLC_EGENERIC;
2313
2314     if( !p_private || !p_vod_media ) return VLC_EGENERIC;
2315
2316     vlc_mutex_lock( &vlm->lock );
2317
2318     /* Find media */
2319     for( i = 0; i < vlm->i_media; i++ )
2320     {
2321         if( p_vod_media == vlm->media[i]->vod_media ) break;
2322     }
2323
2324     if( i == vlm->i_media )
2325     {
2326         vlc_mutex_unlock( &vlm->lock );
2327         return VLC_EGENERIC;
2328     }
2329
2330     switch( i_query )
2331     {
2332     case VOD_MEDIA_PLAY:
2333         vlm->media[i]->psz_vod_output = (char *)va_arg( args, char * );
2334         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "play", 0 );
2335         vlm->media[i]->psz_vod_output = 0;
2336         break;
2337
2338     case VOD_MEDIA_PAUSE:
2339         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "pause", 0 );
2340         break;
2341
2342     case VOD_MEDIA_STOP:
2343         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "stop", 0 );
2344         break;
2345
2346     case VOD_MEDIA_SEEK:
2347     {
2348         double f_pos = (double)va_arg( args, double );
2349         char psz_pos[50];
2350         lldiv_t div = lldiv( f_pos * 10000000, 10000000 );
2351         sprintf( psz_pos, I64Fd".%07u", div.quot, (unsigned int) div.rem );
2352         i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "seek", psz_pos);
2353         break;
2354     }
2355
2356     default:
2357         break;
2358     }
2359
2360     vlc_mutex_unlock( &vlm->lock );
2361
2362     return i_ret;
2363 }
2364
2365 /*****************************************************************************
2366  * Manage:
2367  *****************************************************************************/
2368 static int Manage( vlc_object_t* p_object )
2369 {
2370     vlm_t *vlm = (vlm_t*)p_object;
2371     int i, j;
2372     mtime_t i_lastcheck;
2373     mtime_t i_time;
2374
2375     i_lastcheck = vlm_Date();
2376
2377     msleep( 100000 );
2378
2379     while( !vlm->b_die )
2380     {
2381         char **ppsz_scheduled_commands = NULL;
2382         int    i_scheduled_commands = 0;
2383         vlc_mutex_lock( &vlm->lock );
2384
2385         /* destroy the inputs that wants to die, and launch the next input */
2386         for( i = 0; i < vlm->i_media; i++ )
2387         {
2388             vlm_media_t *p_media = vlm->media[i];
2389
2390             for( j = 0; j < p_media->i_instance; j++ )
2391             {
2392                 vlm_media_instance_t *p_instance = p_media->instance[j];
2393
2394                 if( !p_instance->p_input ||
2395                     ( !p_instance->p_input->b_eof &&
2396                       !p_instance->p_input->b_error ) ) continue;
2397
2398                 input_StopThread( p_instance->p_input );
2399                 input_DestroyThread( p_instance->p_input );
2400                 vlc_object_detach( p_instance->p_input );
2401                 vlc_object_destroy( p_instance->p_input );
2402
2403                 p_instance->i_index++;
2404                 if( p_instance->i_index == p_media->i_input &&
2405                     p_media->b_loop ) p_instance->i_index = 0;
2406
2407                 if( p_instance->i_index < p_media->i_input )
2408                 {
2409                     /* FIXME, find a way to select the right instance */
2410                     char buffer[12];
2411                     sprintf( buffer, "%d", p_instance->i_index );
2412                     vlm_MediaControl( vlm, p_media, p_instance->psz_name,
2413                                       "play", buffer );
2414                 }
2415                 else
2416                 {
2417                     if( vlm_MediaControl( vlm, p_media, p_instance->psz_name,
2418                                           "stop", 0 ) == VLC_SUCCESS ) i--;
2419                 }
2420             }
2421         }
2422
2423         /* scheduling */
2424         i_time = vlm_Date();
2425
2426         for( i = 0; i < vlm->i_schedule; i++ )
2427         {
2428             mtime_t i_real_date = vlm->schedule[i]->i_date;
2429
2430             if( vlm->schedule[i]->b_enabled == VLC_TRUE )
2431             {
2432                 if( vlm->schedule[i]->i_date == 0 ) // now !
2433                 {
2434                     vlm->schedule[i]->i_date = (i_time / 1000000) * 1000000 ;
2435                     i_real_date = i_time;
2436                 }
2437                 else if( vlm->schedule[i]->i_period != 0 )
2438                 {
2439                     int j = 0;
2440                     while( vlm->schedule[i]->i_date + j *
2441                            vlm->schedule[i]->i_period <= i_lastcheck &&
2442                            ( vlm->schedule[i]->i_repeat > j ||
2443                              vlm->schedule[i]->i_repeat == -1 ) )
2444                     {
2445                         j++;
2446                     }
2447
2448                     i_real_date = vlm->schedule[i]->i_date + j *
2449                         vlm->schedule[i]->i_period;
2450                 }
2451
2452                 if( i_real_date <= i_time && i_real_date > i_lastcheck )
2453                 {
2454                     for( j = 0; j < vlm->schedule[i]->i_command; j++ )
2455                     {
2456                         TAB_APPEND( i_scheduled_commands,
2457                                     ppsz_scheduled_commands,
2458                                     strdup(vlm->schedule[i]->command[j] ) );
2459                     }
2460                 }
2461             }
2462         }
2463         while( i_scheduled_commands )
2464         {
2465             vlm_message_t *message = NULL;
2466             char *psz_command = ppsz_scheduled_commands[0];
2467             ExecuteCommand( vlm, psz_command,&message );
2468
2469             /* for now, drop the message */
2470             vlm_MessageDelete( message );
2471             TAB_REMOVE( i_scheduled_commands,
2472                         ppsz_scheduled_commands,
2473                         psz_command );
2474             free( psz_command );
2475         }
2476
2477         i_lastcheck = i_time;
2478
2479         vlc_mutex_unlock( &vlm->lock );
2480
2481         msleep( 100000 );
2482     }
2483
2484     return VLC_SUCCESS;
2485 }
2486
2487 #else /* ENABLE_VLM */
2488
2489 /* We just define an empty wrapper */
2490 vlm_t *__vlm_New( vlc_object_t *a )
2491 {
2492     msg_Err( a, "VideoLAN manager support is disabled" );
2493     return 0;
2494 }
2495 void vlm_Delete( vlm_t *a ){}
2496 int vlm_ExecuteCommand( vlm_t *a, char *b, vlm_message_t **c ){ return -1; }
2497 void vlm_MessageDelete( vlm_message_t *a ){}
2498 vlm_media_t *vlm_MediaNew( vlm_t *a, char *b, int c ){ return NULL; }
2499 vlm_media_t *vlm_MediaSearch (vlm_t *a, const char *b ) { return NULL; }
2500 void vlm_MediaDelete( vlm_t *a, vlm_media_t *b, char *c ){}
2501 int vlm_MediaSetup( vlm_t *a, vlm_media_t *b, char *c, char *d ){ return -1; }
2502 int vlm_MediaControl( vlm_t *a, vlm_media_t *b, char *c, char *d, char *e )
2503     { return -1; }
2504 vlm_schedule_t * vlm_ScheduleNew( vlm_t *a, char *b ){ return NULL; }
2505 void  vlm_ScheduleDelete( vlm_t *a, vlm_schedule_t *b, char *c ){}
2506 int vlm_ScheduleSetup( vlm_schedule_t *a, char *b, char *c ){ return -1; }
2507 int vlm_MediaVodControl( void *a, vod_media_t *b, char *c, int d, va_list e )
2508     { return -1; }
2509 int vlm_Save( vlm_t *a, char *b ){ return -1; }
2510 int vlm_Load( vlm_t *a, char *b ){ return -1; }
2511
2512 #endif /* ENABLE_VLM */