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