]> git.sesse.net Git - vlc/blob - modules/demux/playlist/asx.c
* modules/access/file.c, directory.c: bug and memory leak fixes
[vlc] / modules / demux / playlist / asx.c
1 /*****************************************************************************
2  * asx.c : ASX playlist format import
3  *****************************************************************************
4  * Copyright (C) 2005 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 #include <stdlib.h>                                      /* malloc(), free() */
31 #include <ctype.h>                                              /* isspace() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/input.h>
35
36 #include <errno.h>                                                 /* ENOMEM */
37 #include "playlist.h"
38 #include "vlc_meta.h"
39
40 struct demux_sys_t
41 {
42     char    *psz_prefix;
43     char    *psz_data;
44     int64_t i_data_len;
45     vlc_bool_t b_utf8;
46 };
47
48 /*****************************************************************************
49  * Local prototypes
50  *****************************************************************************/
51 static int Demux( demux_t *p_demux);
52 static int Control( demux_t *p_demux, int i_query, va_list args );
53
54 static int StoreString( demux_t *p_demux, char **ppsz_string, char *psz_source_start, char *psz_source_end )
55 {
56     demux_sys_t *p_sys = p_demux->p_sys;
57     int i_strlen = psz_source_end-psz_source_start;
58     if( i_strlen < 1 )
59         return VLC_EGENERIC;
60
61     if( *ppsz_string ) free( *ppsz_string );
62     *ppsz_string = malloc( i_strlen*sizeof( char ) +1);
63     memcpy( *ppsz_string, psz_source_start, i_strlen );
64     (*ppsz_string)[i_strlen] = '\0';
65
66     if( p_sys->b_utf8 )
67         EnsureUTF8( *ppsz_string );
68     else
69     {
70         char *psz_temp;
71         psz_temp = FromLocaleDup( *ppsz_string );
72         if( psz_temp )
73         {
74             free( *ppsz_string );
75             *ppsz_string = psz_temp;
76         } else EnsureUTF8( *ppsz_string );
77     }
78     return VLC_SUCCESS;
79 }
80
81 /*****************************************************************************
82  * Import_ASX: main import function
83  *****************************************************************************/
84 int E_(Import_ASX)( vlc_object_t *p_this )
85 {
86     demux_t *p_demux = (demux_t *)p_this;
87     uint8_t *p_peek;
88     CHECK_PEEK( p_peek, 10 );
89     
90     if( POKE( p_peek, "<asx", 4 ) || isExtension( p_demux, ".asx" ) ||
91         isExtension( p_demux, ".wax" ) || isExtension( p_demux, ".wvx" ) ||
92         isDemux( p_demux, "asx-open" ) )
93     {
94         ;
95     }
96     else
97         return VLC_EGENERIC;
98     
99     STANDARD_DEMUX_INIT_MSG( "found valid ASX playlist" );
100     p_demux->p_sys->psz_prefix = E_(FindPrefix)( p_demux );
101     p_demux->p_sys->psz_data = NULL;
102     p_demux->p_sys->i_data_len = -1;
103     p_demux->p_sys->b_utf8 = VLC_FALSE;
104     
105     return VLC_SUCCESS;
106 }
107
108 /*****************************************************************************
109  * Deactivate: frees unused data
110  *****************************************************************************/
111 void E_(Close_ASX)( vlc_object_t *p_this )
112 {
113     demux_t *p_demux = (demux_t *)p_this;
114     demux_sys_t *p_sys = p_demux->p_sys;
115
116     if( p_sys->psz_prefix ) free( p_sys->psz_prefix );
117     if( p_sys->psz_data ) free( p_sys->psz_data );
118     free( p_sys );
119 }
120
121 static int Demux( demux_t *p_demux )
122 {
123     demux_sys_t *p_sys = p_demux->p_sys;
124     char        *psz_parse = NULL;
125     char        *psz_backup = NULL;
126     vlc_bool_t  b_entry = VLC_FALSE;
127
128     INIT_PLAYLIST_STUFF;
129
130     /* init txt */
131     if( p_sys->i_data_len < 0 )
132     {
133         int64_t i_pos = 0;
134         p_sys->i_data_len = stream_Size( p_demux->s ) +1; /* This is a cheat to prevent unnecessary realloc */
135         if( p_sys->i_data_len <= 0 && p_sys->i_data_len < 16384 ) p_sys->i_data_len = 1024;
136         p_sys->psz_data = malloc( p_sys->i_data_len * sizeof(char) +1);
137         
138         /* load the complete file */
139         for( ;; )
140         {
141             int i_read = stream_Read( p_demux->s, &p_sys->psz_data[i_pos], p_sys->i_data_len - i_pos );
142             p_sys->psz_data[i_read] = '\0';
143            
144             if( i_read < p_sys->i_data_len - i_pos ) break; /* Done */
145             
146             i_pos += i_read;
147             p_sys->i_data_len += 1024;
148             p_sys->psz_data = realloc( p_sys->psz_data, p_sys->i_data_len * sizeof( char * ) +1 );
149         }
150         if( p_sys->i_data_len <= 0 ) return VLC_EGENERIC;
151     }
152
153     psz_parse = p_sys->psz_data;
154     /* Find first element */
155     if( ( psz_parse = strcasestr( psz_parse, "<ASX" ) ) )
156     {
157         /* ASX element */
158         char *psz_string = NULL;
159         int i_strlen = 0;
160
161         char *psz_base_asx = NULL;
162         char *psz_title_asx = NULL;
163         char *psz_author_asx = NULL;
164         char *psz_copyright_asx = NULL;
165         char *psz_moreinfo_asx = NULL;
166         char *psz_abstract_asx = NULL;
167         
168         char *psz_base_entry = NULL;
169         char *psz_title_entry = NULL;
170         char *psz_author_entry = NULL;
171         char *psz_copyright_entry = NULL;
172         char *psz_moreinfo_entry = NULL;
173         char *psz_abstract_entry = NULL;
174         int i_entry_count = 0;
175     
176         psz_parse = strcasestr( psz_parse, ">" );
177
178         while( ( psz_parse = strcasestr( psz_parse, "<" ) ) && psz_parse && *psz_parse )
179         {
180             if( !strncasecmp( psz_parse, "<!--", 4 ) )
181             {
182                 /* this is a comment */
183                 if( ( psz_parse = strcasestr( psz_parse, "-->" ) ) )
184                     psz_parse+=3;
185                 else continue;
186             }
187             else if( !strncasecmp( psz_parse, "<PARAM ", 7 ) )
188             {
189                 vlc_bool_t b_encoding_flag = VLC_FALSE;
190                 psz_parse+=7;
191                 if( !strncasecmp( psz_parse, "name", 4 ) )
192                 {
193                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
194                     {
195                         psz_backup = ++psz_parse;
196                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
197                         {
198                             i_strlen = psz_parse-psz_backup;
199                             if( i_strlen < 1 ) continue;
200                             msg_Dbg( p_demux, "param name strlen: %d", i_strlen);
201                             psz_string = malloc( i_strlen *sizeof( char ) +1);
202                             memcpy( psz_string, psz_backup, i_strlen );
203                             psz_string[i_strlen] = '\0';
204                             msg_Dbg( p_demux, "param name: %s", psz_string);
205                             b_encoding_flag = !strcasecmp( psz_string, "encoding" );
206                             free( psz_string );
207                         }
208                         else continue;
209                     }
210                     else continue;
211                 }
212                 psz_parse++;
213                 if( !strncasecmp( psz_parse, "value", 5 ) )
214                 {
215                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
216                     {
217                         psz_backup = ++psz_parse;
218                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
219                         {
220                             i_strlen = psz_parse-psz_backup;
221                             if( i_strlen < 1 ) continue;
222                             msg_Dbg( p_demux, "param value strlen: %d", i_strlen);
223                             psz_string = malloc( i_strlen *sizeof( char ) +1);
224                             memcpy( psz_string, psz_backup, i_strlen );
225                             psz_string[i_strlen] = '\0';
226                             msg_Dbg( p_demux, "param value: %s", psz_string);
227                             if( b_encoding_flag && !strcasecmp( psz_string, "utf-8" ) ) p_sys->b_utf8 = VLC_TRUE;
228                             free( psz_string );
229                         }
230                         else continue;
231                     }
232                     else continue;
233                 }
234                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
235                     psz_parse += 2;
236                 else continue;
237             }
238             else if( !strncasecmp( psz_parse, "<BANNER", 7 ) )
239             {
240                 /* We skip this element */
241                 if( ( psz_parse = strcasestr( psz_parse, "</BANNER>" ) ) )
242                     psz_parse += 9;
243                 else continue;
244             }
245             else if( !strncasecmp( psz_parse, "<PREVIEWDURATION", 16 ) ||
246                      !strncasecmp( psz_parse, "<LOGURL", 7 ) ||
247                      !strncasecmp( psz_parse, "<Skin", 5 ) )
248             {
249                 /* We skip this element */
250                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
251                     psz_parse += 2;
252                 else continue;
253             }
254             else if( !strncasecmp( psz_parse, "<BASE ", 6 ) )
255             {
256                 psz_parse+=6;
257                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
258                 {
259                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
260                     {
261                         psz_backup = ++psz_parse;
262                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
263                         {
264                             StoreString( p_demux, (b_entry ? &psz_base_entry : &psz_base_asx), psz_backup, psz_parse );
265                         }
266                         else continue;
267                     }
268                     else continue;
269                 }
270                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
271                     psz_parse += 2;
272                 else continue;
273             }
274             else if( !strncasecmp( psz_parse, "<TITLE>", 7 ) )
275             {
276                 psz_backup = psz_parse+=7;
277                 if( ( psz_parse = strcasestr( psz_parse, "</TITLE>" ) ) )
278                 {
279                     StoreString( p_demux, (b_entry ? &psz_title_entry : &psz_title_asx), psz_backup, psz_parse );
280                     psz_parse += 8;
281                 }
282                 else continue;
283             }
284             else if( !strncasecmp( psz_parse, "<Author>", 8 ) )
285             {
286                 psz_backup = psz_parse+=8;
287                 if( ( psz_parse = strcasestr( psz_parse, "</Author>" ) ) )
288                 {
289                     StoreString( p_demux, (b_entry ? &psz_author_entry : &psz_author_asx), psz_backup, psz_parse );
290                     psz_parse += 9;
291                 }
292                 else continue;
293             }
294             else if( !strncasecmp( psz_parse, "<Copyright", 10 ) )
295             {
296                 psz_backup = psz_parse+=11;
297                 if( ( psz_parse = strcasestr( psz_parse, "</Copyright>" ) ) )
298                 {
299                     StoreString( p_demux, (b_entry ? &psz_copyright_entry : &psz_copyright_asx), psz_backup, psz_parse );
300                     psz_parse += 12;
301                 }
302                 else continue;
303             }
304             else if( !strncasecmp( psz_parse, "<MoreInfo ", 10 ) )
305             {
306                 psz_parse+=10;
307                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
308                 {
309                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
310                     {
311                         psz_backup = ++psz_parse;
312                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
313                         {
314                             StoreString( p_demux, (b_entry ? &psz_moreinfo_entry : &psz_moreinfo_asx), psz_backup, psz_parse );
315                         }
316                         else continue;
317                     }
318                     else continue;
319                 }
320                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
321                     psz_parse += 2;
322                 else continue;
323             }
324             else if( !strncasecmp( psz_parse, "<ABSTRACT>", 10 ) )
325             {
326                 psz_backup = psz_parse+=10;
327                 if( ( psz_parse = strcasestr( psz_parse, "</ABSTRACT>" ) ) )
328                 {
329                     StoreString( p_demux, (b_entry ? &psz_abstract_entry : &psz_abstract_asx), psz_backup, psz_parse );
330                     psz_parse += 11;
331                 }
332                 else continue;
333             }
334             else if( !strncasecmp( psz_parse, "<EntryRef ", 10 ) )
335             {
336                 psz_parse+=10;
337                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
338                 {
339                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
340                     {
341                         psz_backup = ++psz_parse;
342                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
343                         {
344                             i_strlen = psz_parse-psz_backup;
345                             if( i_strlen < 1 ) continue;
346                             psz_string = malloc( i_strlen*sizeof( char ) +1);
347                             memcpy( psz_string, psz_backup, i_strlen );
348                             psz_string[i_strlen] = '\0';
349                             p_input = input_ItemNew( p_playlist, psz_string, psz_title_asx );
350                             vlc_input_item_CopyOptions( p_current->p_input, p_input );
351                             playlist_AddWhereverNeeded( p_playlist, p_input, p_current,
352                                  p_item_in_category, (i_parent_id > 0 )? VLC_TRUE : VLC_FALSE,
353                                  PLAYLIST_APPEND );
354                             free( psz_string );
355                         }
356                         else continue;
357                     }
358                     else continue;
359                 }
360                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
361                     psz_parse += 2;
362                 else continue;
363             }
364             else if( !strncasecmp( psz_parse, "</Entry>", 8 ) )
365             {
366                 /* add a new entry */
367                 psz_parse+=8;
368                 if( !b_entry )
369                 {
370                     msg_Err( p_demux, "end of entry without start?" );
371                     continue;
372                 }
373                 /* cleanup entry */
374                 FREENULL( psz_title_entry )
375                 FREENULL( psz_base_entry )
376                 FREENULL( psz_author_entry )
377                 FREENULL( psz_copyright_entry )
378                 FREENULL( psz_moreinfo_entry )
379                 FREENULL( psz_abstract_entry )
380                 b_entry = VLC_FALSE;
381             }
382             else if( !strncasecmp( psz_parse, "<Entry>", 7 ) )
383             {
384                 psz_parse+=7;
385                 if( b_entry )
386                 {
387                     msg_Err( p_demux, "We already are in an entry section" );
388                     continue;
389                 }
390                 i_entry_count += 1;
391                 b_entry = VLC_TRUE;
392             }
393             else if( !strncasecmp( psz_parse, "<Ref ", 5 ) )
394             {
395                 psz_parse+=5;
396                 if( !b_entry )
397                 {
398                     msg_Err( p_demux, "A ref outside an entry section" );
399                     continue;
400                 }
401                 
402                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
403                 {
404                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
405                     {
406                         psz_backup = ++psz_parse;
407                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
408                         {
409                             input_item_t *p_entry = NULL;
410                             char *psz_name = NULL;
411                             i_strlen = psz_parse-psz_backup;
412                             if( i_strlen < 1 ) continue;
413                             psz_string = malloc( i_strlen*sizeof( char ) +1);
414                             memcpy( psz_string, psz_backup, i_strlen );
415                             psz_string[i_strlen] = '\0';
416
417                             /* create the new entry */
418                             asprintf( &psz_name, "%d %s", i_entry_count, ( psz_title_entry ? psz_title_entry : p_current->p_input->psz_name ) );
419                             p_entry = input_ItemNew( p_playlist, psz_string, psz_name );
420                             FREENULL( psz_name );
421                             
422                             vlc_input_item_CopyOptions( p_current->p_input, p_entry );
423                             p_entry->p_meta = vlc_meta_New();
424                             if( psz_title_entry ) vlc_meta_SetTitle( p_entry->p_meta, psz_title_entry );
425                             if( psz_author_entry ) vlc_meta_SetAuthor( p_entry->p_meta, psz_author_entry );
426                             if( psz_copyright_entry ) vlc_meta_SetCopyright( p_entry->p_meta, psz_copyright_entry );
427                             if( psz_moreinfo_entry ) vlc_meta_SetURL( p_entry->p_meta, psz_moreinfo_entry );
428                             if( psz_abstract_entry ) vlc_meta_SetDescription( p_entry->p_meta, psz_abstract_entry );
429                             
430                             playlist_AddWhereverNeeded( p_playlist, p_entry, p_current,
431                                 p_item_in_category, (i_parent_id > 0 )? VLC_TRUE : VLC_FALSE,
432                                 PLAYLIST_APPEND );
433                             free( psz_string );
434                         }
435                         else continue;
436                     }
437                     else continue;
438                 }
439                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
440                     psz_parse += 2;
441                 else continue;
442             }
443             else if( !strncasecmp( psz_parse, "</ASX", 5 ) )
444             {
445                 vlc_mutex_lock( &p_current->p_input->lock );
446                 if( !p_current->p_input->p_meta ) p_current->p_input->p_meta = vlc_meta_New();
447                 if( psz_title_asx ) vlc_meta_SetTitle( p_current->p_input->p_meta, psz_title_asx );
448                 if( psz_author_asx ) vlc_meta_SetAuthor( p_current->p_input->p_meta, psz_author_asx );
449                 if( psz_copyright_asx ) vlc_meta_SetCopyright( p_current->p_input->p_meta, psz_copyright_asx );
450                 if( psz_moreinfo_asx ) vlc_meta_SetURL( p_current->p_input->p_meta, psz_moreinfo_asx );
451                 if( psz_abstract_asx ) vlc_meta_SetDescription( p_current->p_input->p_meta, psz_abstract_asx );
452                 vlc_mutex_unlock( &p_current->p_input->lock );
453                 FREENULL( psz_base_asx );
454                 FREENULL( psz_title_asx );
455                 FREENULL( psz_author_asx );
456                 FREENULL( psz_copyright_asx );
457                 FREENULL( psz_moreinfo_asx );
458                 FREENULL( psz_abstract_asx );
459                 psz_parse++;
460             }
461             else psz_parse++;
462         }
463 #if 0
464 /* FIXME Unsupported elements */
465             PARAM
466             EVENT
467             REPEAT
468             DURATION
469             ENDMARK
470             STARTMARK
471             STARTTIME
472 #endif
473     }
474     HANDLE_PLAY_AND_RELEASE;
475     return VLC_SUCCESS;
476 }
477
478 static int Control( demux_t *p_demux, int i_query, va_list args )
479 {
480     return VLC_EGENERIC;
481 }