]> git.sesse.net Git - vlc/blob - modules/access/zip/zipstream.c
0ccaa86d95538aeddb517efe31719f5c2aa43158
[vlc] / modules / access / zip / zipstream.c
1 /*****************************************************************************
2  * zipstream.c: stream_filter that creates a XSPF playlist from a Zip archive
3  *****************************************************************************
4  * Copyright (C) 2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Philippe André <jpeg@videolan.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 /** **************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include "zip.h"
33 #include <stddef.h>
34
35 /* FIXME remove */
36 #include <vlc_input.h>
37
38 #define FILENAME_TEXT N_( "Media in Zip" )
39 #define FILENAME_LONGTEXT N_( "Path to the media in the Zip archive" )
40
41 /** **************************************************************************
42  * Module descriptor
43  *****************************************************************************/
44 vlc_module_begin()
45     set_shortname( "Zip" )
46     set_category( CAT_INPUT )
47     set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
48     set_description( N_( "Zip files filter" ) )
49     set_capability( "stream_filter", 1 )
50     set_callbacks( StreamOpen, StreamClose )
51     add_submodule()
52         set_subcategory( SUBCAT_INPUT_ACCESS )
53         set_description( N_( "Zip access" ) )
54         set_capability( "access", 0 )
55         add_shortcut( "unzip" )
56         add_shortcut( "zip" )
57         set_callbacks( AccessOpen, AccessClose )
58 vlc_module_end()
59
60 /** *************************************************************************
61  * Local prototypes
62  ****************************************************************************/
63 static int Read   ( stream_t *, void *p_read, unsigned int i_read );
64 static int Peek   ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
65 static int Control( stream_t *, int i_query, va_list );
66
67 typedef struct node node;
68 typedef struct item item;
69
70 static int Fill( stream_t * );
71 static int CreatePlaylist( stream_t *s, char **pp_buffer );
72 static int GetFilesInZip( stream_t*, unzFile, vlc_array_t*, vlc_array_t* );
73 static node* findOrCreateParentNode( node *root, const char *fullpath );
74 static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames,
75                       const char *psz_zippath );
76 static int nodeToXSPF( char **pp_buffer, node *n, bool b_root );
77 static node* findOrCreateParentNode( node *root, const char *fullpath );
78
79 /** **************************************************************************
80  * Struct definitions
81  *****************************************************************************/
82 struct stream_sys_t
83 {
84     /* zlib / unzip members */
85     unzFile zipFile;
86     zlib_filefunc_def *fileFunctions;
87     char *psz_path;
88
89     /* xspf data */
90     char *psz_xspf;
91     size_t i_len;
92     size_t i_pos;
93 };
94
95 struct item {
96     int id;
97     item *next;
98 };
99
100 struct node {
101     char *name;
102     item *media;
103     node *child;
104     node *next;
105 };
106
107 /** **************************************************************************
108  * Some helpers
109  *****************************************************************************/
110
111 inline static node* new_node( char *name )
112 {
113     node *n = (node*) calloc( 1, sizeof(node) );
114     n->name = convert_xml_special_chars( name );
115     return n;
116 }
117
118 inline static item* new_item( int id )
119 {
120     item *media = (item*) calloc( 1, sizeof(item) );
121     media->id = id;
122     return media;
123 }
124
125 inline static void free_all_node( node *root )
126 {
127     while( root )
128     {
129         free_all_node( root->child );
130         free( root->name );
131         node *tmp = root->next;
132         free( root );
133         root = tmp;
134     }
135 }
136
137 /* Allocate strcat and format */
138 static int astrcatf( char **ppsz_dest, const char *psz_fmt_src, ... )
139 {
140     va_list args;
141     va_start( args, psz_fmt_src );
142
143     char *psz_tmp;
144     int i_ret = vasprintf( &psz_tmp, psz_fmt_src, args );
145     if( i_ret == -1 ) return -1;
146
147     va_end( args );
148
149     int i_len = strlen( *ppsz_dest ) + strlen( psz_tmp ) + 1;
150     char *psz_out = realloc( *ppsz_dest, i_len );
151     if( !psz_out ) return -1;
152
153     strcat( psz_out, psz_tmp );
154     free( psz_tmp );
155
156     *ppsz_dest = psz_out;
157     return i_len;
158 }
159
160 /** **************************************************************************
161  * Zip file identifier
162  *****************************************************************************/
163 static const uint8_t p_zip_marker[] = { 0x50, 0x4b, 0x03, 0x04 }; // "PK^C^D"
164 static const int i_zip_marker = 4;
165
166
167 /** **************************************************************************
168  * Open
169  *****************************************************************************/
170 int StreamOpen( vlc_object_t *p_this )
171 {
172     stream_t *s = (stream_t*) p_this;
173     stream_sys_t *p_sys;
174
175     /* Verify file format */
176     const uint8_t *p_peek;
177     if( stream_Peek( s->p_source, &p_peek, i_zip_marker ) < i_zip_marker )
178         return VLC_EGENERIC;
179     if( memcmp( p_peek, p_zip_marker, i_zip_marker ) )
180         return VLC_EGENERIC;
181
182     s->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
183     if( !p_sys )
184         return VLC_ENOMEM;
185
186     s->pf_read = Read;
187     s->pf_peek = Peek;
188     s->pf_control = Control;
189
190     p_sys->fileFunctions = ( zlib_filefunc_def * )
191             calloc( 1, sizeof( zlib_filefunc_def ) );
192     if( !p_sys->fileFunctions )
193     {
194         free( p_sys );
195         return VLC_ENOMEM;
196     }
197     p_sys->fileFunctions->zopen_file   = ZipIO_Open;
198     p_sys->fileFunctions->zread_file   = ZipIO_Read;
199     p_sys->fileFunctions->zwrite_file  = ZipIO_Write;
200     p_sys->fileFunctions->ztell_file   = ZipIO_Tell;
201     p_sys->fileFunctions->zseek_file   = ZipIO_Seek;
202     p_sys->fileFunctions->zclose_file  = ZipIO_Close;
203     p_sys->fileFunctions->zerror_file  = ZipIO_Error;
204     p_sys->fileFunctions->opaque       = ( void * ) s;
205     p_sys->zipFile = unzOpen2( NULL /* path */, p_sys->fileFunctions );
206     if( !p_sys->zipFile )
207     {
208         msg_Warn( s, "unable to open file" );
209         free( p_sys->fileFunctions );
210         free( p_sys );
211         return VLC_EGENERIC;
212     }
213
214     /* Find the stream uri */
215     char *psz_tmp;
216     if( asprintf( &psz_tmp, "%s.xspf", s->psz_path ) == -1 )
217     {
218         free( p_sys->fileFunctions );
219         free( p_sys );
220         return VLC_ENOMEM;
221     }
222     p_sys->psz_path = s->psz_path;
223     s->psz_path = psz_tmp;
224
225     return VLC_SUCCESS;
226 }
227
228 /** *************************************************************************
229  * Close
230  ****************************************************************************/
231 void StreamClose( vlc_object_t *p_this )
232 {
233     stream_t *s = (stream_t*)p_this;
234     stream_sys_t *p_sys = s->p_sys;
235
236     if( p_sys->zipFile )
237         unzClose( p_sys->zipFile );
238
239     free( p_sys->fileFunctions );
240     free( p_sys->psz_xspf );
241     free( p_sys->psz_path );
242     free( p_sys );
243 }
244
245 /** *************************************************************************
246  * Stream filters functions
247  ****************************************************************************/
248
249 /** *************************************************************************
250  * Read
251  ****************************************************************************/
252 static int Read( stream_t *s, void *p_read, unsigned int i_read )
253 {
254     stream_sys_t *p_sys = s->p_sys;
255
256     if( !p_read ) return 0;
257
258     /* Fill the buffer */
259     if( Fill( s ) )
260         return -1;
261
262     /* Read the buffer */
263     int i_len = __MIN( i_read, p_sys->i_len - p_sys->i_pos );
264     memcpy( p_read, p_sys->psz_xspf + p_sys->i_pos, i_len );
265     p_sys->i_pos += i_len;
266
267     return i_len;
268 }
269
270 /** *************************************************************************
271  * Peek
272  ****************************************************************************/
273 static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
274 {
275     stream_sys_t *p_sys = s->p_sys;
276
277     /* Fill the buffer */
278     if( Fill( s ) )
279         return -1;
280
281     /* Point to the buffer */
282     int i_len = __MIN( i_peek, p_sys->i_len - p_sys->i_pos );
283     *pp_peek = (uint8_t*) p_sys->psz_xspf + p_sys->i_pos;
284
285     return i_len;
286 }
287
288 /** *************************************************************************
289  * Control
290  ****************************************************************************/
291 static int Control( stream_t *s, int i_query, va_list args )
292 {
293     stream_sys_t *p_sys = s->p_sys;
294
295     switch( i_query )
296     {
297         case STREAM_SET_POSITION:
298         {
299             uint64_t i_position = va_arg( args, uint64_t );
300             if( i_position >= p_sys->i_len )
301                 return VLC_EGENERIC;
302             else
303             {
304                 p_sys->i_pos = (size_t) i_position;
305                 return VLC_SUCCESS;
306             }
307         }
308
309         case STREAM_GET_POSITION:
310         {
311             uint64_t *pi_position = va_arg( args, uint64_t* );
312             *pi_position = p_sys->i_pos;
313             return VLC_SUCCESS;
314         }
315
316         case STREAM_GET_SIZE:
317         {
318             uint64_t *pi_size = va_arg( args, uint64_t* );
319             *pi_size = p_sys->i_len;
320             return VLC_SUCCESS;
321         }
322
323         case STREAM_GET_CONTENT_TYPE:
324             return VLC_EGENERIC;
325
326         case STREAM_UPDATE_SIZE:
327         case STREAM_CONTROL_ACCESS:
328         case STREAM_CAN_SEEK:
329         case STREAM_CAN_FASTSEEK:
330         case STREAM_SET_RECORD_STATE:
331             return stream_vaControl( s->p_source, i_query, args );
332
333         default:
334             return VLC_EGENERIC;
335     }
336 }
337
338 static int Fill( stream_t *s )
339 {
340     stream_sys_t *p_sys = s->p_sys;
341
342     if( p_sys->psz_xspf )
343         return VLC_SUCCESS;
344
345     if( CreatePlaylist( s, &p_sys->psz_xspf ) < 0 )
346         return VLC_EGENERIC;
347
348     p_sys->i_len = strlen( p_sys->psz_xspf );
349     p_sys->i_pos = 0;
350     return VLC_SUCCESS;
351 }
352
353 static int CreatePlaylist( stream_t *s, char **pp_buffer )
354 {
355     stream_sys_t *p_sys = s->p_sys;
356
357     /* Get some infos about zip archive */
358     int i_ret = 0;
359     unzFile file = p_sys->zipFile;
360     vlc_array_t *p_filenames = vlc_array_new(); /* Will contain char* */
361
362     /* List all file names in Zip archive */
363     i_ret = GetFilesInZip( s, file, p_filenames, NULL );
364     if( i_ret < 0 )
365     {
366         unzClose( file );
367         i_ret = -1;
368         goto exit;
369     }
370
371     /* Close archive */
372     unzClose( file );
373     p_sys->zipFile = NULL;
374
375     /* Construct the xspf playlist */
376     i_ret = WriteXSPF( pp_buffer, p_filenames, p_sys->psz_path );
377     if( i_ret > 0 )
378         i_ret = 1;
379     else if( i_ret < 0 )
380         i_ret = -1;
381
382 exit:
383     for( int i = 0; i < vlc_array_count( p_filenames ); i++ )
384     {
385         free( vlc_array_item_at_index( p_filenames, i ) );
386     }
387     vlc_array_destroy( p_filenames );
388     return i_ret;
389 }
390
391
392 /** **************************************************************************
393  * Zip utility functions
394  *****************************************************************************/
395
396 /** **************************************************************************
397  * \brief List files in zip and append their names to p_array
398  * \param p_this
399  * \param file Opened zip file
400  * \param p_array vlc_array_t which will receive all filenames
401  *
402  * In case of error, returns VLC_EGENERIC.
403  * In case of success, returns number of files found, and goes back to first file.
404  *****************************************************************************/
405 static int GetFilesInZip( stream_t *p_this, unzFile file,
406                           vlc_array_t *p_filenames, vlc_array_t *p_fileinfos )
407 {
408     if( !p_filenames || !p_this )
409         return VLC_EGENERIC;
410
411     int i_ret = 0;
412
413     /* Get global info */
414     unz_global_info info;
415
416     if( unzGetGlobalInfo( file, &info ) != UNZ_OK )
417     {
418         msg_Warn( p_this, "this is not a valid zip archive" );
419         return VLC_EGENERIC;
420     }
421
422     /* Go to first file in archive */
423     unzGoToFirstFile( file );
424
425     /* Get info about each file */
426     for( unsigned long i = 0; i < info.number_entry; i++ )
427     {
428         char *psz_fileName = calloc( ZIP_FILENAME_LEN, 1 );
429         unz_file_info *p_fileInfo = calloc( 1, sizeof( unz_file_info ) );
430
431         if( !p_fileInfo || !psz_fileName )
432         {
433             free( psz_fileName );
434             free( p_fileInfo );
435             return VLC_ENOMEM;
436         }
437
438         if( unzGetCurrentFileInfo( file, p_fileInfo, psz_fileName,
439                                    ZIP_FILENAME_LEN, NULL, 0, NULL, 0 )
440             != UNZ_OK )
441         {
442             msg_Warn( p_this, "can't get info about file in zip" );
443             return VLC_EGENERIC;
444         }
445
446         if( p_filenames )
447             vlc_array_append( p_filenames, strdup( psz_fileName ) );
448         free( psz_fileName );
449
450         if( p_fileinfos )
451             vlc_array_append( p_fileinfos, p_fileInfo );
452         else
453             free( p_fileInfo );
454
455         if( i < ( info.number_entry - 1 ) )
456         {
457             /* Go the next file in the archive */
458             if( unzGoToNextFile( file ) != UNZ_OK )
459             {
460                 msg_Warn( p_this, "can't go to next file in zip" );
461                 return VLC_EGENERIC;
462             }
463         }
464
465         i_ret++;
466     }
467
468     /* i_ret should be equal to info.number_entry */
469     unzGoToFirstFile( file );
470     return i_ret;
471 }
472
473
474 /** **************************************************************************
475  * XSPF generation functions
476  *****************************************************************************/
477
478 /** **************************************************************************
479  * \brief Check a character for allowance in the Xml.
480  * Allowed chars are: a-z, A-Z, 0-9, \, /, ., ' ', _ and :
481  *****************************************************************************/
482 bool isAllowedChar( char c )
483 {
484     return ( c >= 'a' && c <= 'z' )
485            || ( c >= 'A' && c <= 'Z' )
486            || ( c >= '0' && c <= '9' )
487            || ( c == ':' ) || ( c == '/' )
488            || ( c == '\\' ) || ( c == '.' )
489            || ( c == ' ' ) || ( c == '_' );
490 }
491
492 /** **************************************************************************
493  * \brief Escape string to be XML valid
494  * Allowed chars are defined by the above function isAllowedChar()
495  * Invalid chars are escaped using non standard '?XX' notation.
496  * NOTE: We cannot trust VLC internal Web encoding functions
497  *       because they are not able to encode and decode some rare utf-8
498  *       characters properly. Also, we don't control exactly when they are
499  *       called (from this module).
500  *****************************************************************************/
501 static int escapeToXml( char **ppsz_encoded, const char *psz_url )
502 {
503     char *psz_iter, *psz_tmp;
504
505     /* Count number of unallowed characters in psz_url */
506     size_t i_num = 0, i_len = 0;
507     for( psz_iter = (char*) psz_url; *psz_iter; ++psz_iter )
508     {
509         if( isAllowedChar( *psz_iter ) )
510         {
511             i_len++;
512         }
513         else
514         {
515             i_len++;
516             i_num++;
517         }
518     }
519
520     /* Special case */
521     if( i_num == 0 )
522     {
523         *ppsz_encoded = malloc( i_len + 1 );
524         memcpy( *ppsz_encoded, psz_url, i_len + 1 );
525         return VLC_SUCCESS;
526     }
527
528     /* Copy string, replacing invalid characters */
529     char *psz_ret = malloc( i_len + 3*i_num + 2 );
530     if( !psz_ret ) return VLC_ENOMEM;
531
532     for( psz_iter = (char*) psz_url, psz_tmp = psz_ret;
533          *psz_iter; ++psz_iter, ++psz_tmp )
534     {
535         if( isAllowedChar( *psz_iter ) )
536         {
537             *psz_tmp = *psz_iter;
538         }
539         else
540         {
541             *(psz_tmp++) = '?';
542             snprintf( psz_tmp, 3, "%02x", ( *psz_iter & 0x000000FF ) );
543             psz_tmp++;
544         }
545     }
546     *psz_tmp = '\0';
547
548     /* Return success */
549     *ppsz_encoded = psz_ret;
550     return VLC_SUCCESS;
551 }
552
553 /** **************************************************************************
554  * \brief Write the XSPF playlist given the list of files
555  *****************************************************************************/
556 static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames,
557                       const char *psz_zippath )
558 {
559     char *psz_zip = strrchr( psz_zippath, DIR_SEP_CHAR );
560     psz_zip = convert_xml_special_chars( psz_zip ? (psz_zip+1) : psz_zippath );
561
562     if( asprintf( pp_buffer, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
563         "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" "
564                 "xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n"
565                 " <title>%s</title>\n"
566                 " <trackList>\n", psz_zip ) == -1)
567         return -1;
568
569     /* Root node */
570     node *playlist = new_node( psz_zip );
571
572     /* Encode the URI and append ZIP_SEP */
573     char *psz_pathtozip;
574     escapeToXml( &psz_pathtozip, psz_zippath );
575     if( astrcatf( &psz_pathtozip, "%s", ZIP_SEP ) < 0 ) return -1;
576
577     int i_track = 0;
578     for( int i = 0; i < vlc_array_count( p_filenames ); ++i )
579     {
580         char *psz_name = (char*) vlc_array_item_at_index( p_filenames, i );
581         int i_len = strlen( psz_name );
582
583         if( !i_len ) continue;
584
585         /* Is it a folder ? */
586         if( psz_name[i_len-1] == '/' )
587         {
588             /* Do nothing */
589         }
590         else /* File */
591         {
592             /* Extract file name */
593             char *psz_file = strrchr( psz_name, '/' );
594             psz_file = convert_xml_special_chars( psz_file ?
595                     (psz_file+1) : psz_name );
596
597             /* Build full MRL */
598             char *psz_path = strdup( psz_pathtozip );
599             char *psz_escapedName;
600             escapeToXml( &psz_escapedName, psz_name );
601             if( astrcatf( &psz_path, "%s", psz_escapedName ) < 0 ) return -1;
602
603             /* Track information */
604             if( astrcatf( pp_buffer,
605                         "  <track>\n"
606                         "   <location>zip://%s</location>\n"
607                         "   <title>%s</title>\n"
608                         "   <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
609                         "    <vlc:id>%d</vlc:id>\n"
610                         "   </extension>\n"
611                         "  </track>\n",
612                         psz_path, psz_file, i_track ) < 0 ) return -1;
613
614             free( psz_file );
615             free( psz_path );
616
617             /* Find the parent node */
618             node *parent = findOrCreateParentNode( playlist, psz_name );
619             assert( parent );
620
621             /* Add the item to this node */
622             item *tmp = parent->media;
623             if( !tmp )
624             {
625                 parent->media = new_item( i_track );
626             }
627             else
628             {
629                 while( tmp->next )
630                 {
631                     tmp = tmp->next;
632                 }
633                 tmp->next = new_item( i_track );
634             }
635
636             ++i_track;
637         }
638     }
639
640     free( psz_pathtozip );
641
642     /* Close tracklist, open the extension */
643     if( astrcatf( pp_buffer,
644         " </trackList>\n"
645         " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
646                 ) < 0 ) return -1;
647
648     /* Write the tree */
649     if( nodeToXSPF( pp_buffer, playlist, true ) < 0 ) return -1;
650
651     /* Close extension and playlist */
652     if( astrcatf( pp_buffer, " </extension>\n</playlist>\n" ) < 0 ) return -1;
653
654     /* printf( "%s", *pp_buffer ); */
655
656     free_all_node( playlist );
657
658     return VLC_SUCCESS;
659 }
660
661 /** **************************************************************************
662  * \brief Recursively convert a node to its XSPF representation
663  *****************************************************************************/
664 static int nodeToXSPF( char **pp_buffer, node *n, bool b_root )
665 {
666     if( !b_root )
667     {
668         if( astrcatf( pp_buffer, "  <vlc:node title=\"%s\">\n", n->name ) < 0 )
669             return -1;
670     }
671     if( n->child )
672         nodeToXSPF( pp_buffer, n->child, false );
673     item *i = n->media;
674     while( i )
675     {
676         if( astrcatf( pp_buffer, "   <vlc:item tid=\"%d\" />\n", i->id ) < 0 )
677             return -1;
678         i = i->next;
679     }
680     if( !b_root )
681     {
682         if( astrcatf( pp_buffer, "  </vlc:node>\n" ) < 0 )
683             return -1;
684     }
685     return VLC_SUCCESS;
686 }
687
688 /** **************************************************************************
689  * \brief Either create or find the parent node of the item
690  *****************************************************************************/
691 static node* findOrCreateParentNode( node *root, const char *fullpath )
692 {
693     char *folder;
694     char *path = strdup( fullpath );
695     folder = path;
696
697     assert( root );
698
699     char *sep = strchr( folder, '/' );
700     if( !sep )
701     {
702         free( path );
703         return root;
704     }
705
706     *sep = '\0';
707     ++sep;
708
709     node *current = root->child;
710
711     while( current )
712     {
713         if( !strcmp( current->name, folder ) )
714         {
715             /* We found the folder, go recursively deeper */
716             return findOrCreateParentNode( current, sep );
717         }
718         current = current->next;
719     }
720
721     /* If we are here, then it means that we did not find the parent */
722     node *ret = new_node( folder );
723     if( !root->child )
724         root->child = ret;
725     else
726     {
727         current = root->child;
728         while( current->next )
729         {
730             current = current->next;
731         }
732         current->next = ret;
733     }
734
735     /* And now, create the subfolders */
736     ret = findOrCreateParentNode( ret, sep );
737
738     free( path );
739     return ret;
740 }
741
742
743 /** **************************************************************************
744  * ZipIO function definitions : how to use vlc_stream to read the zip
745  *****************************************************************************/
746
747 /** **************************************************************************
748  * \brief interface for unzip module to open a file using a vlc_stream
749  * \param opaque
750  * \param filename
751  * \param mode how to open the file (read/write ?). We support only read
752  * \return opaque
753  *****************************************************************************/
754 static void ZCALLBACK *ZipIO_Open( void *opaque, const char *file, int mode )
755 {
756     (void) file;
757     stream_t *s = (stream_t*) opaque;
758     if( mode & ( ZLIB_FILEFUNC_MODE_CREATE | ZLIB_FILEFUNC_MODE_WRITE ) )
759     {
760         msg_Dbg( s, "ZipIO_Open: we cannot write into zip files" );
761         return NULL;
762     }
763     return s;
764 }
765
766 /** **************************************************************************
767  * \brief read something from stream into buffer
768  * \param opaque should be the stream
769  * \param stream stream created by ZipIO_Open
770  * \param buf buffer to read the file
771  * \param size length of this buffer
772  * \return return the number of bytes read (<= size)
773  *****************************************************************************/
774 static unsigned long ZCALLBACK ZipIO_Read( void *opaque, void *stream,
775                                            void *buf, unsigned long size )
776 {
777     (void) stream;
778     stream_t *s = (stream_t*) opaque;
779     return (unsigned long) stream_Read( s->p_source, buf, (int) size );
780 }
781
782 /** **************************************************************************
783  * \brief tell size of stream
784  * \param opaque should be the stream
785  * \param stream stream created by ZipIO_Open
786  * \return size of the file / stream
787  * ATTENTION: this is not stream_Tell, but stream_Size !
788  *****************************************************************************/
789 static long ZCALLBACK ZipIO_Tell( void *opaque, void *stream )
790 {
791     (void) stream;
792     stream_t *s = (stream_t*) opaque;
793     return (long) stream_Size( s->p_source ); /* /!\ not stream_Tell /!\ */
794 }
795
796 /** **************************************************************************
797  * \brief seek in the stream
798  * \param opaque should be the stream
799  * \param stream stream created by ZipIO_Open
800  * \param offset positive offset to seek
801  * \param origin current position in stream
802  * \return ¿ VLC_SUCCESS or an error code ?
803  *****************************************************************************/
804 static long ZCALLBACK ZipIO_Seek ( void *opaque, void *stream,
805                                    unsigned long offset, int origin )
806 {
807     (void) stream;
808     stream_t *s = (stream_t*) opaque;
809     long l_ret;
810
811     uint64_t pos = offset + origin;
812     l_ret = (long) stream_Seek( s->p_source, pos );
813     return l_ret;
814 }
815
816 /** **************************************************************************
817  * \brief close the stream
818  * \param opaque should be the stream
819  * \param stream stream created by ZipIO_Open
820  * \return always VLC_SUCCESS
821  * This closes zip archive
822  *****************************************************************************/
823 static int ZCALLBACK ZipIO_Close ( void *opaque, void *stream )
824 {
825     (void) stream;
826     (void) opaque;
827 //     stream_t *s = (stream_t*) opaque;
828 //    if( p_demux->p_sys && p_demux->p_sys->zipFile )
829 //        p_demux->p_sys->zipFile = NULL;
830 //     stream_Seek( s->p_source, 0 );
831     return VLC_SUCCESS;
832 }
833
834 /** **************************************************************************
835  * \brief I/O functions for the ioapi: write (assert insteadof segfault)
836  *****************************************************************************/
837 static uLong ZCALLBACK ZipIO_Write( void* opaque, void* stream,
838                                     const void* buf, uLong size )
839 {
840     (void)opaque; (void)stream; (void)buf; (void)size;
841     int ERROR_zip_cannot_write_this_should_not_happen = 0;
842     assert( ERROR_zip_cannot_write_this_should_not_happen );
843     return 0;
844 }
845
846 /** **************************************************************************
847  * \brief I/O functions for the ioapi: test error (man 3 ferror)
848  *****************************************************************************/
849 static int ZCALLBACK ZipIO_Error( void* opaque, void* stream )
850 {
851     (void)opaque;
852     (void)stream;
853     return 0;
854 }
855