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