]> git.sesse.net Git - vlc/blob - modules/demux/playlist/asx.c
Replace argument = realloc( argument, size ); with realloc_or_free() in modules/...
[vlc] / modules / demux / playlist / asx.c
1 /*****************************************************************************
2  * asx.c : ASX playlist format import
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /* See also: http://msdn.microsoft.com/library/en-us/wmplay10/mmp_sdk/windowsmediametafilereference.asp
25  */
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <assert.h>
35
36 #include <vlc_common.h>
37 #include <vlc_demux.h>
38
39 #include <ctype.h>
40 #include <vlc_charset.h>
41 #include "playlist.h"
42 #include <vlc_meta.h>
43 #include <vlc_memory.h>
44
45 struct demux_sys_t
46 {
47     char    *psz_prefix;
48     char    *psz_data;
49     int64_t i_data_len;
50     bool b_utf8;
51     bool b_skip_ads;
52 };
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57 static int Demux( demux_t *p_demux);
58 static int Control( demux_t *p_demux, int i_query, va_list args );
59
60 static int StoreString( demux_t *p_demux, char **ppsz_string,
61                         const char *psz_source_start,
62                         const char *psz_source_end )
63 {
64     demux_sys_t *p_sys = p_demux->p_sys;
65     unsigned len = psz_source_end - psz_source_start;
66
67     free( *ppsz_string );
68
69     char *buf = *ppsz_string = malloc ((len * (1 + !p_sys->b_utf8)) + 1);
70     if (buf == NULL)
71         return VLC_ENOMEM;
72
73     if( p_sys->b_utf8 )
74     {
75         memcpy (buf, psz_source_start, len);
76         (*ppsz_string)[len] = '\0';
77         EnsureUTF8 (*ppsz_string);
78     }
79     else
80     {
81         /* Latin-1 -> UTF-8 */
82         for (unsigned i = 0; i < len; i++)
83         {
84             unsigned char c = psz_source_start[i];
85             if (c & 0x80)
86             {
87                 *buf++ = 0xc0 | (c >> 6);
88                 *buf++ = 0x80 | (c & 0x3f);
89             }
90             else
91                 *buf++ = c;
92         }
93         *buf++ = '\0';
94
95         buf = realloc (*ppsz_string, buf - *ppsz_string);
96         if( buf )
97             *ppsz_string = buf;
98     }
99     return VLC_SUCCESS;
100 }
101
102 static char *SkipBlanks(char *s, size_t i_strlen )
103 {
104     while( i_strlen > 0 ) {
105         switch( *s )
106         {
107             case ' ':
108             case '\t':
109             case '\r':
110             case '\n':
111                 --i_strlen;
112                 ++s;
113                 break;
114             default:
115                 i_strlen = 0;
116         }
117     }
118     return s;
119 }
120
121 static int ParseTime(char *s, size_t i_strlen)
122 {
123     // need to parse hour:minutes:sec.fraction string
124     int result = 0;
125     int val;
126     const char *end = s + i_strlen;
127     // skip leading spaces if any
128     s = SkipBlanks(s, i_strlen);
129
130     val = 0;
131     while( (s < end) && isdigit(*s) )
132     {
133         int newval = val*10 + (*s - '0');
134         if( newval < val )
135         {
136             // overflow
137             val = 0;
138             break;
139         }
140         val = newval;
141         ++s;
142     }
143     result = val;
144     s = SkipBlanks(s, end-s);
145     if( *s == ':' )
146     {
147         ++s;
148         s = SkipBlanks(s, end-s);
149         result = result * 60;
150         val = 0;
151         while( (s < end) && isdigit(*s) )
152         {
153             int newval = val*10 + (*s - '0');
154             if( newval < val )
155             {
156                 // overflow
157                 val = 0;
158                 break;
159             }
160             val = newval;
161             ++s;
162         }
163         result += val;
164         s = SkipBlanks(s, end-s);
165         if( *s == ':' )
166         {
167             ++s;
168             s = SkipBlanks(s, end-s);
169             result = result * 60;
170             val = 0;
171             while( (s < end) && isdigit(*s) )
172             {
173                 int newval = val*10 + (*s - '0');
174                 if( newval < val )
175                 {
176                     // overflow
177                     val = 0;
178                     break;
179                 }
180                 val = newval;
181                 ++s;
182             }
183             result += val;
184             // TODO: one day, we may need to parse fraction for sub-second resolution
185         }
186     }
187     return result;
188 }
189
190 /*****************************************************************************
191  * Import_ASX: main import function
192  *****************************************************************************/
193 int Import_ASX( vlc_object_t *p_this )
194 {
195     demux_t *p_demux = (demux_t *)p_this;
196     const uint8_t *p_peek;
197     CHECK_PEEK( p_peek, 10 );
198
199     // skip over possible leading empty lines and empty spaces
200     p_peek = (uint8_t *)SkipBlanks((char *)p_peek, 6);
201
202     if( POKE( p_peek, "<asx", 4 ) || demux_IsPathExtension( p_demux, ".asx" ) ||
203         demux_IsPathExtension( p_demux, ".wax" ) || demux_IsPathExtension( p_demux, ".wvx" ) ||
204         demux_IsForced( p_demux, "asx-open" ) )
205     {
206         ;
207     }
208     else
209         return VLC_EGENERIC;
210
211     STANDARD_DEMUX_INIT_MSG( "found valid ASX playlist" );
212     p_demux->p_sys->psz_prefix = FindPrefix( p_demux );
213     p_demux->p_sys->psz_data = NULL;
214     p_demux->p_sys->i_data_len = -1;
215     p_demux->p_sys->b_utf8 = false;
216     p_demux->p_sys->b_skip_ads = config_GetInt( p_demux, "playlist-skip-ads" );
217
218     return VLC_SUCCESS;
219 }
220
221 /*****************************************************************************
222  * Deactivate: frees unused data
223  *****************************************************************************/
224 void Close_ASX( vlc_object_t *p_this )
225 {
226     demux_t *p_demux = (demux_t *)p_this;
227     demux_sys_t *p_sys = p_demux->p_sys;
228
229     free( p_sys->psz_prefix );
230     free( p_sys->psz_data );
231     free( p_sys );
232 }
233
234 static int Demux( demux_t *p_demux )
235 {
236     demux_sys_t *p_sys = p_demux->p_sys;
237     char        *psz_parse = NULL;
238     char        *psz_backup = NULL;
239     bool  b_entry = false;
240     input_item_t *p_current_input = GetCurrentItem(p_demux);
241
242     /* init txt */
243     if( p_sys->i_data_len < 0 )
244     {
245         int64_t i_pos = 0;
246         p_sys->i_data_len = stream_Size( p_demux->s ) + 1; /* This is a cheat to prevent unnecessary realloc */
247         if( p_sys->i_data_len <= 0 || p_sys->i_data_len > 16384 ) p_sys->i_data_len = 1024;
248         p_sys->psz_data = malloc( p_sys->i_data_len +1);
249         assert( p_sys->psz_data );
250
251         /* load the complete file */
252         for( ;; )
253         {
254             int i_read = stream_Read( p_demux->s, &p_sys->psz_data[i_pos], p_sys->i_data_len - i_pos );
255             p_sys->psz_data[i_pos + i_read] = '\0';
256
257             if( i_read < p_sys->i_data_len - i_pos ) break; /* Done */
258
259             i_pos += i_read;
260             p_sys->i_data_len <<= 1 ;
261             p_sys->psz_data = realloc_or_free( p_sys->psz_data,
262                                    p_sys->i_data_len * sizeof( char * ) + 1 );
263             assert( p_sys->psz_data );
264         }
265         if( p_sys->i_data_len <= 0 ) return -1;
266     }
267
268     psz_parse = p_sys->psz_data;
269     /* Find first element */
270     if( ( psz_parse = strcasestr( psz_parse, "<ASX" ) ) )
271     {
272         /* ASX element */
273         char *psz_string = NULL;
274         int i_strlen = 0;
275
276         char *psz_base_asx = NULL;
277         char *psz_title_asx = NULL;
278         char *psz_artist_asx = NULL;
279         char *psz_copyright_asx = NULL;
280         char *psz_moreinfo_asx = NULL;
281         char *psz_abstract_asx = NULL;
282
283         char *psz_base_entry = NULL;
284         char *psz_title_entry = NULL;
285         char *psz_artist_entry = NULL;
286         char *psz_copyright_entry = NULL;
287         char *psz_moreinfo_entry = NULL;
288         char *psz_abstract_entry = NULL;
289         int i_entry_count = 0;
290         bool b_skip_entry = false;
291
292         char *psz_href = NULL;
293         int i_starttime = 0;
294         int i_duration = 0;
295
296         psz_parse = strcasestr( psz_parse, ">" );
297
298         while( psz_parse && ( psz_parse = strcasestr( psz_parse, "<" ) ) )
299         {
300             if( !strncasecmp( psz_parse, "<!--", 4 ) )
301             {
302                 /* this is a comment */
303                 if( ( psz_parse = strcasestr( psz_parse, "-->" ) ) )
304                     psz_parse+=3;
305                 else continue;
306             }
307             else if( !strncasecmp( psz_parse, "<PARAM ", 7 ) )
308             {
309                 bool b_encoding_flag = false;
310                 psz_parse = SkipBlanks(psz_parse+7, (unsigned)-1);
311                 if( !strncasecmp( psz_parse, "name", 4 ) )
312                 {
313                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
314                     {
315                         psz_backup = ++psz_parse;
316                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
317                         {
318                             i_strlen = psz_parse-psz_backup;
319                             if( i_strlen < 1 ) continue;
320                             msg_Dbg( p_demux, "param name strlen: %d", i_strlen);
321                             psz_string = malloc( i_strlen + 1);
322                             assert( psz_string );
323                             memcpy( psz_string, psz_backup, i_strlen );
324                             psz_string[i_strlen] = '\0';
325                             msg_Dbg( p_demux, "param name: %s", psz_string);
326                             b_encoding_flag = !strcasecmp( psz_string, "encoding" );
327                             free( psz_string );
328                         }
329                         else continue;
330                     }
331                     else continue;
332                 }
333                 psz_parse++;
334                 if( !strncasecmp( psz_parse, "value", 5 ) )
335                 {
336                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
337                     {
338                         psz_backup = ++psz_parse;
339                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
340                         {
341                             i_strlen = psz_parse-psz_backup;
342                             if( i_strlen < 1 ) continue;
343                             msg_Dbg( p_demux, "param value strlen: %d", i_strlen);
344                             psz_string = malloc( i_strlen +1);
345                             assert( psz_string );
346                             memcpy( psz_string, psz_backup, i_strlen );
347                             psz_string[i_strlen] = '\0';
348                             msg_Dbg( p_demux, "param value: %s", psz_string);
349                             if( b_encoding_flag && !strcasecmp( psz_string, "utf-8" ) ) p_sys->b_utf8 = true;
350                             free( psz_string );
351                         }
352                         else continue;
353                     }
354                     else continue;
355                 }
356                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
357                     psz_parse += 2;
358                 else continue;
359             }
360             else if( !strncasecmp( psz_parse, "<BANNER", 7 ) )
361             {
362                 /* We skip this element */
363                 if( ( psz_parse = strcasestr( psz_parse, "</BANNER>" ) ) )
364                     psz_parse += 9;
365                 else continue;
366             }
367             else if( !strncasecmp( psz_parse, "<PREVIEWDURATION", 16 ) ||
368                      !strncasecmp( psz_parse, "<LOGURL", 7 ) ||
369                      !strncasecmp( psz_parse, "<Skin", 5 ) )
370             {
371                 /* We skip this element */
372                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
373                     psz_parse += 2;
374                 else continue;
375             }
376             else if( !strncasecmp( psz_parse, "<BASE ", 6 ) )
377             {
378                 psz_parse = SkipBlanks(psz_parse+6, (unsigned)-1);
379                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
380                 {
381                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
382                     {
383                         psz_backup = ++psz_parse;
384                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
385                         {
386                             StoreString( p_demux, (b_entry ? &psz_base_entry : &psz_base_asx), psz_backup, psz_parse );
387                         }
388                         else continue;
389                     }
390                     else continue;
391                 }
392                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
393                     psz_parse += 2;
394                 else continue;
395             }
396             else if( !strncasecmp( psz_parse, "<TITLE>", 7 ) )
397             {
398                 psz_backup = psz_parse+=7;
399                 if( ( psz_parse = strcasestr( psz_parse, "</TITLE>" ) ) )
400                 {
401                     StoreString( p_demux, (b_entry ? &psz_title_entry : &psz_title_asx), psz_backup, psz_parse );
402                     psz_parse += 8;
403                 }
404                 else continue;
405             }
406             else if( !strncasecmp( psz_parse, "<Author>", 8 ) )
407             {
408                 psz_backup = psz_parse+=8;
409                 if( ( psz_parse = strcasestr( psz_parse, "</Author>" ) ) )
410                 {
411                     StoreString( p_demux, (b_entry ? &psz_artist_entry : &psz_artist_asx), psz_backup, psz_parse );
412                     psz_parse += 9;
413                 }
414                 else continue;
415             }
416             else if( !strncasecmp( psz_parse, "<Copyright", 10 ) )
417             {
418                 psz_backup = psz_parse+=11;
419                 if( ( psz_parse = strcasestr( psz_parse, "</Copyright>" ) ) )
420                 {
421                     StoreString( p_demux, (b_entry ? &psz_copyright_entry : &psz_copyright_asx), psz_backup, psz_parse );
422                     psz_parse += 12;
423                 }
424                 else continue;
425             }
426             else if( !strncasecmp( psz_parse, "<MoreInfo ", 10 ) )
427             {
428                 psz_parse = SkipBlanks(psz_parse+10, (unsigned)-1);
429                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
430                 {
431                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
432                     {
433                         psz_backup = ++psz_parse;
434                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
435                         {
436                             StoreString( p_demux, (b_entry ? &psz_moreinfo_entry : &psz_moreinfo_asx), psz_backup, psz_parse );
437                         }
438                         else continue;
439                     }
440                     else continue;
441                 }
442                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
443                     psz_parse += 2;
444                 else if( ( psz_parse = strcasestr( psz_parse, "</MoreInfo>") ) )
445                     psz_parse += 11;
446                 else continue;
447             }
448             else if( !strncasecmp( psz_parse, "<ABSTRACT>", 10 ) )
449             {
450                 psz_backup = psz_parse+=10;
451                 if( ( psz_parse = strcasestr( psz_parse, "</ABSTRACT>" ) ) )
452                 {
453                     StoreString( p_demux, (b_entry ? &psz_abstract_entry : &psz_abstract_asx), psz_backup, psz_parse );
454                     psz_parse += 11;
455                 }
456                 else continue;
457             }
458             else if( !strncasecmp( psz_parse, "<EntryRef ", 10 ) )
459             {
460                 psz_parse = SkipBlanks(psz_parse+10, (unsigned)-1);
461                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
462                 {
463                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
464                     {
465                         psz_backup = ++psz_parse;
466                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
467                         {
468                             i_strlen = psz_parse-psz_backup;
469                             if( i_strlen < 1 ) continue;
470                             psz_string = malloc( i_strlen +1);
471                             assert( psz_string );
472                             memcpy( psz_string, psz_backup, i_strlen );
473                             psz_string[i_strlen] = '\0';
474                             input_item_t *p_input;
475                             p_input = input_item_New( p_demux, psz_string, psz_title_asx );
476                             input_item_CopyOptions( p_current_input, p_input );
477                             input_item_AddSubItem( p_current_input, p_input );
478                             vlc_gc_decref( p_input );
479                             free( psz_string );
480                         }
481                         else continue;
482                     }
483                     else continue;
484                 }
485                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
486                     psz_parse += 2;
487                 else continue;
488             }
489             else if( !strncasecmp( psz_parse, "</Entry>", 8 ) )
490             {
491                 input_item_t *p_entry = NULL;
492                 char *psz_name = NULL;
493
494                 char * ppsz_options[2];
495                 int i_options = 0;
496
497                 /* add a new entry */
498                 psz_parse+=8;
499                 if( !b_entry )
500                 {
501                     msg_Err( p_demux, "end of entry without start?" );
502                     continue;
503                 }
504
505                 if( !psz_href )
506                 {
507                     msg_Err( p_demux, "entry without href?" );
508                     continue;
509                 }
510
511                 if( p_sys->b_skip_ads && b_skip_entry )
512                 {
513                     char *psz_current_input_name = input_item_GetName( p_current_input );
514
515                     msg_Dbg( p_demux, "skipped entry %d %s (%s)",
516                              i_entry_count,
517                              ( psz_title_entry ? psz_title_entry : psz_current_input_name ), psz_href );
518                     free( psz_current_input_name );
519                 }
520                 else
521                 {
522                     if( i_starttime || i_duration )
523                     {
524                         if( i_starttime )
525                         {
526                             if( asprintf(ppsz_options+i_options, ":start-time=%d", i_starttime) == -1 )
527                                 *(ppsz_options+i_options) = NULL;
528                             else
529                                 ++i_options;
530                         }
531                         if( i_duration )
532                         {
533                             if( asprintf(ppsz_options+i_options, ":stop-time=%d", i_starttime + i_duration) == -1 )
534                                 *(ppsz_options+i_options) = NULL;
535                             else
536                                 ++i_options;
537                         }
538                     }
539
540                     /* create the new entry */
541                     char *psz_current_input_name = input_item_GetName( p_current_input );
542                     if( asprintf( &psz_name, "%d %s", i_entry_count, ( psz_title_entry ? psz_title_entry : psz_current_input_name ) ) != -1 )
543                     {
544                         char *psz_mrl = ProcessMRL( psz_href, p_demux->p_sys->psz_prefix );
545                         p_entry = input_item_NewExt( p_demux, psz_mrl, psz_name,
546                                                      i_options, (const char * const *)ppsz_options, VLC_INPUT_OPTION_TRUSTED, -1 );
547                         free( psz_name );
548                         free( psz_mrl );
549                         input_item_CopyOptions( p_current_input, p_entry );
550                         while( i_options )
551                         {
552                             psz_name = ppsz_options[--i_options];
553                             free( psz_name );
554                         }
555                         psz_name = NULL;
556
557                         if( psz_title_entry ) input_item_SetTitle( p_entry, psz_title_entry );
558                         if( psz_artist_entry ) input_item_SetArtist( p_entry, psz_artist_entry );
559                         if( psz_copyright_entry ) input_item_SetCopyright( p_entry, psz_copyright_entry );
560                         if( psz_moreinfo_entry ) input_item_SetURL( p_entry, psz_moreinfo_entry );
561                         if( psz_abstract_entry ) input_item_SetDescription( p_entry, psz_abstract_entry );
562                         input_item_AddSubItem( p_current_input, p_entry );
563                         vlc_gc_decref( p_entry );
564                     }
565                     free( psz_current_input_name );
566                 }
567
568                 /* cleanup entry */;
569                 FREENULL( psz_href );
570                 FREENULL( psz_title_entry );
571                 FREENULL( psz_base_entry );
572                 FREENULL( psz_artist_entry );
573                 FREENULL( psz_copyright_entry );
574                 FREENULL( psz_moreinfo_entry );
575                 FREENULL( psz_abstract_entry );
576                 b_entry = false;
577             }
578             else if( !strncasecmp( psz_parse, "<Entry", 6 ) )
579             {
580                 char *psz_clientskip;
581                 psz_parse+=6;
582                 if( b_entry )
583                 {
584                     msg_Err( p_demux, "We already are in an entry section" );
585                     continue;
586                 }
587                 i_entry_count += 1;
588                 b_entry = true;
589                 psz_clientskip = strcasestr( psz_parse, "clientskip=\"no\"" );
590                 psz_parse = strcasestr( psz_parse, ">" );
591
592                 /* If clientskip was enabled ... this is an ad */
593                 b_skip_entry = (NULL != psz_clientskip) && (psz_clientskip < psz_parse);
594
595                 // init entry details
596                 FREENULL(psz_href);
597                 i_starttime = 0;
598                 i_duration = 0;
599             }
600             else if( !strncasecmp( psz_parse, "<Ref ", 5 ) )
601             {
602                 psz_parse = SkipBlanks(psz_parse+5, (unsigned)-1);
603                 if( !b_entry )
604                 {
605                     msg_Err( p_demux, "A ref outside an entry section" );
606                     continue;
607                 }
608
609                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
610                 {
611                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
612                     {
613                         psz_backup = ++psz_parse;
614                         psz_backup = SkipBlanks(psz_backup, (unsigned)-1);
615                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
616                         {
617                             char *psz_tmp;
618                             i_strlen = psz_parse-psz_backup;
619                             if( i_strlen < 1 ) continue;
620
621                             if( psz_href )
622                             {
623                                 /* we have allready one href in this entry, lets make new input from it and
624                                 continue with new href, don't free meta/options*/
625                                 input_item_t *p_entry = NULL;
626                                 char *psz_name = input_item_GetName( p_current_input );
627
628                                 char *psz_mrl = ProcessMRL( psz_href, p_demux->p_sys->psz_prefix );
629                                 p_entry = input_item_NewExt( p_demux, psz_mrl, psz_name,
630                                                      0, NULL, VLC_INPUT_OPTION_TRUSTED, -1 );
631                                 free( psz_mrl );
632                                 input_item_CopyOptions( p_current_input, p_entry );
633                                 if( psz_title_entry ) input_item_SetTitle( p_entry, psz_title_entry );
634                                 if( psz_artist_entry ) input_item_SetArtist( p_entry, psz_artist_entry );
635                                 if( psz_copyright_entry ) input_item_SetCopyright( p_entry, psz_copyright_entry );
636                                 if( psz_moreinfo_entry ) input_item_SetURL( p_entry, psz_moreinfo_entry );
637                                 if( psz_abstract_entry ) input_item_SetDescription( p_entry, psz_abstract_entry );
638                                 input_item_AddSubItem( p_current_input, p_entry );
639                                 vlc_gc_decref( p_entry );
640                             }
641
642                             free( psz_href );
643                             psz_href = malloc( i_strlen +1);
644                             assert( psz_string );
645                             memcpy( psz_href, psz_backup, i_strlen );
646                             psz_href[i_strlen] = '\0';
647                             psz_tmp = psz_href + (i_strlen-1);
648                             while( psz_tmp >= psz_href &&
649                                  ( *psz_tmp == '\r' || *psz_tmp == '\n' ) )
650                             {
651                                 *psz_tmp = '\0';
652                                 psz_tmp++;
653                             }
654                         }
655                         else continue;
656                     }
657                     else continue;
658                 }
659                 if( ( psz_parse = strcasestr( psz_parse, ">" ) ) )
660                     psz_parse++;
661                 else continue;
662             }
663             else if( !strncasecmp( psz_parse, "<starttime ", 11 ) )
664             {
665                 psz_parse = SkipBlanks(psz_parse+11, (unsigned)-1);
666                 if( !b_entry )
667                 {
668                     msg_Err( p_demux, "starttime outside an entry section" );
669                     continue;
670                 }
671
672                 if( !strncasecmp( psz_parse, "value", 5 ) )
673                 {
674                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
675                     {
676                         psz_backup = ++psz_parse;
677                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
678                         {
679                             i_strlen = psz_parse-psz_backup;
680                             if( i_strlen < 1 ) continue;
681
682                             i_starttime = ParseTime(psz_backup, i_strlen);
683                         }
684                         else continue;
685                     }
686                     else continue;
687                 }
688                 if( ( psz_parse = strcasestr( psz_parse, ">" ) ) )
689                     psz_parse++;
690                 else continue;
691             }
692             else if( !strncasecmp( psz_parse, "<duration ", 11 ) )
693             {
694                 psz_parse = SkipBlanks(psz_parse+5, (unsigned)-1);
695                 if( !b_entry )
696                 {
697                     msg_Err( p_demux, "duration outside an entry section" );
698                     continue;
699                 }
700
701                 if( !strncasecmp( psz_parse, "value", 5 ) )
702                 {
703                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
704                     {
705                         psz_backup = ++psz_parse;
706                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
707                         {
708                             i_strlen = psz_parse-psz_backup;
709                             if( i_strlen < 1 ) continue;
710
711                             i_duration = ParseTime(psz_backup, i_strlen);
712                         }
713                         else continue;
714                     }
715                     else continue;
716                 }
717                 if( ( psz_parse = strcasestr( psz_parse, ">" ) ) )
718                     psz_parse++;
719                 else continue;
720             }
721             else if( !strncasecmp( psz_parse, "</ASX", 5 ) )
722             {
723                 if( psz_title_asx ) input_item_SetTitle( p_current_input, psz_title_asx );
724                 if( psz_artist_asx ) input_item_SetArtist( p_current_input, psz_artist_asx );
725                 if( psz_copyright_asx ) input_item_SetCopyright( p_current_input, psz_copyright_asx );
726                 if( psz_moreinfo_asx ) input_item_SetURL( p_current_input, psz_moreinfo_asx );
727                 if( psz_abstract_asx ) input_item_SetDescription( p_current_input, psz_abstract_asx );
728                 FREENULL( psz_base_asx );
729                 FREENULL( psz_title_asx );
730                 FREENULL( psz_artist_asx );
731                 FREENULL( psz_copyright_asx );
732                 FREENULL( psz_moreinfo_asx );
733                 FREENULL( psz_abstract_asx );
734                 psz_parse++;
735             }
736             else psz_parse++;
737         }
738 #if 0
739 /* FIXME Unsupported elements */
740             PARAM
741             EVENT
742             REPEAT
743             ENDMARK
744             STARTMARK
745 #endif
746     }
747     vlc_gc_decref(p_current_input);
748     return 0; /* Needed for correct operation of go back */
749 }
750
751 static int Control( demux_t *p_demux, int i_query, va_list args )
752 {
753     VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
754     return VLC_EGENERIC;
755 }