]> git.sesse.net Git - vlc/blob - modules/access/zip/zipstream.c
ae6316f9bae4fc3ad97c00d77763f86f4f461810
[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     /* Fill the buffer */
257     if( Fill( s ) )
258         return -1;
259
260     /* Read the buffer */
261     unsigned i_len = __MIN( i_read, p_sys->i_len - p_sys->i_pos );
262     if( p_read )
263         memcpy( p_read, p_sys->psz_xspf + p_sys->i_pos, i_len );
264     p_sys->i_pos += i_len;
265
266     return i_len;
267 }
268
269 /** *************************************************************************
270  * Peek
271  ****************************************************************************/
272 static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
273 {
274     stream_sys_t *p_sys = s->p_sys;
275
276     /* Fill the buffer */
277     if( Fill( s ) )
278         return -1;
279
280     /* Point to the buffer */
281     int i_len = __MIN( i_peek, p_sys->i_len - p_sys->i_pos );
282     *pp_peek = (uint8_t*) p_sys->psz_xspf + p_sys->i_pos;
283
284     return i_len;
285 }
286
287 /** *************************************************************************
288  * Control
289  ****************************************************************************/
290 static int Control( stream_t *s, int i_query, va_list args )
291 {
292     stream_sys_t *p_sys = s->p_sys;
293
294     switch( i_query )
295     {
296         case STREAM_SET_POSITION:
297         {
298             uint64_t i_position = va_arg( args, uint64_t );
299             if( i_position >= p_sys->i_len )
300                 return VLC_EGENERIC;
301             else
302             {
303                 p_sys->i_pos = (size_t) i_position;
304                 return VLC_SUCCESS;
305             }
306         }
307
308         case STREAM_GET_POSITION:
309         {
310             uint64_t *pi_position = va_arg( args, uint64_t* );
311             *pi_position = p_sys->i_pos;
312             return VLC_SUCCESS;
313         }
314
315         case STREAM_GET_SIZE:
316         {
317             uint64_t *pi_size = va_arg( args, uint64_t* );
318             *pi_size = p_sys->i_len;
319             return VLC_SUCCESS;
320         }
321
322         case STREAM_GET_CONTENT_TYPE:
323             return VLC_EGENERIC;
324
325         case STREAM_UPDATE_SIZE:
326         case STREAM_CONTROL_ACCESS:
327         case STREAM_CAN_SEEK:
328         case STREAM_CAN_FASTSEEK:
329         case STREAM_SET_RECORD_STATE:
330             return stream_vaControl( s->p_source, i_query, args );
331
332         default:
333             return VLC_EGENERIC;
334     }
335 }
336
337 static int Fill( stream_t *s )
338 {
339     stream_sys_t *p_sys = s->p_sys;
340
341     if( p_sys->psz_xspf )
342         return VLC_SUCCESS;
343
344     if( CreatePlaylist( s, &p_sys->psz_xspf ) < 0 )
345         return VLC_EGENERIC;
346
347     p_sys->i_len = strlen( p_sys->psz_xspf );
348     p_sys->i_pos = 0;
349     return VLC_SUCCESS;
350 }
351
352 static int CreatePlaylist( stream_t *s, char **pp_buffer )
353 {
354     stream_sys_t *p_sys = s->p_sys;
355
356     unzFile file = p_sys->zipFile;
357     if( !file )
358         return -1;
359
360     /* Get some infos about zip archive */
361     int i_ret = 0;
362     vlc_array_t *p_filenames = vlc_array_new(); /* Will contain char* */
363
364     /* List all file names in Zip archive */
365     i_ret = GetFilesInZip( s, file, p_filenames, NULL );
366     if( i_ret < 0 )
367     {
368         i_ret = -1;
369         goto exit;
370     }
371
372     /* Construct the xspf playlist */
373     i_ret = WriteXSPF( pp_buffer, p_filenames, p_sys->psz_path );
374     if( i_ret > 0 )
375         i_ret = 1;
376     else if( i_ret < 0 )
377         i_ret = -1;
378
379 exit:
380     /* Close archive */
381     unzClose( file );
382     p_sys->zipFile = NULL;
383
384     for( int i = 0; i < vlc_array_count( p_filenames ); i++ )
385     {
386         free( vlc_array_item_at_index( p_filenames, i ) );
387     }
388     vlc_array_destroy( p_filenames );
389     return i_ret;
390 }
391
392
393 /** **************************************************************************
394  * Zip utility functions
395  *****************************************************************************/
396
397 /** **************************************************************************
398  * \brief List files in zip and append their names to p_array
399  * \param p_this
400  * \param file Opened zip file
401  * \param p_array vlc_array_t which will receive all filenames
402  *
403  * In case of error, returns VLC_EGENERIC.
404  * In case of success, returns number of files found, and goes back to first file.
405  *****************************************************************************/
406 static int GetFilesInZip( stream_t *p_this, unzFile file,
407                           vlc_array_t *p_filenames, vlc_array_t *p_fileinfos )
408 {
409     if( !p_filenames || !p_this )
410         return VLC_EGENERIC;
411
412     int i_ret = 0;
413
414     /* Get global info */
415     unz_global_info info;
416
417     if( unzGetGlobalInfo( file, &info ) != UNZ_OK )
418     {
419         msg_Warn( p_this, "this is not a valid zip archive" );
420         return VLC_EGENERIC;
421     }
422
423     /* Go to first file in archive */
424     unzGoToFirstFile( file );
425
426     /* Get info about each file */
427     for( unsigned long i = 0; i < info.number_entry; i++ )
428     {
429         char *psz_fileName = calloc( ZIP_FILENAME_LEN, 1 );
430         unz_file_info *p_fileInfo = calloc( 1, sizeof( unz_file_info ) );
431
432         if( !p_fileInfo || !psz_fileName )
433         {
434             free( psz_fileName );
435             free( p_fileInfo );
436             return VLC_ENOMEM;
437         }
438
439         if( unzGetCurrentFileInfo( file, p_fileInfo, psz_fileName,
440                                    ZIP_FILENAME_LEN, NULL, 0, NULL, 0 )
441             != UNZ_OK )
442         {
443             msg_Warn( p_this, "can't get info about file in zip" );
444             return VLC_EGENERIC;
445         }
446
447         if( p_filenames )
448             vlc_array_append( p_filenames, strdup( psz_fileName ) );
449         free( psz_fileName );
450
451         if( p_fileinfos )
452             vlc_array_append( p_fileinfos, p_fileInfo );
453         else
454             free( p_fileInfo );
455
456         if( i < ( info.number_entry - 1 ) )
457         {
458             /* Go the next file in the archive */
459             if( unzGoToNextFile( file ) != UNZ_OK )
460             {
461                 msg_Warn( p_this, "can't go to next file in zip" );
462                 return VLC_EGENERIC;
463             }
464         }
465
466         i_ret++;
467     }
468
469     /* i_ret should be equal to info.number_entry */
470     unzGoToFirstFile( file );
471     return i_ret;
472 }
473
474
475 /** **************************************************************************
476  * XSPF generation functions
477  *****************************************************************************/
478
479 /** **************************************************************************
480  * \brief Check a character for allowance in the Xml.
481  * Allowed chars are: a-z, A-Z, 0-9, \, /, ., ' ', _ and :
482  *****************************************************************************/
483 bool isAllowedChar( char c )
484 {
485     return ( c >= 'a' && c <= 'z' )
486            || ( c >= 'A' && c <= 'Z' )
487            || ( c >= '0' && c <= '9' )
488            || ( c == ':' ) || ( c == '/' )
489            || ( c == '\\' ) || ( c == '.' )
490            || ( c == ' ' ) || ( c == '_' );
491 }
492
493 /** **************************************************************************
494  * \brief Escape string to be XML valid
495  * Allowed chars are defined by the above function isAllowedChar()
496  * Invalid chars are escaped using non standard '?XX' notation.
497  * NOTE: We cannot trust VLC internal Web encoding functions
498  *       because they are not able to encode and decode some rare utf-8
499  *       characters properly. Also, we don't control exactly when they are
500  *       called (from this module).
501  *****************************************************************************/
502 static int escapeToXml( char **ppsz_encoded, const char *psz_url )
503 {
504     char *psz_iter, *psz_tmp;
505
506     /* Count number of unallowed characters in psz_url */
507     size_t i_num = 0, i_len = 0;
508     for( psz_iter = (char*) psz_url; *psz_iter; ++psz_iter )
509     {
510         if( isAllowedChar( *psz_iter ) )
511         {
512             i_len++;
513         }
514         else
515         {
516             i_len++;
517             i_num++;
518         }
519     }
520
521     /* Special case */
522     if( i_num == 0 )
523     {
524         *ppsz_encoded = malloc( i_len + 1 );
525         memcpy( *ppsz_encoded, psz_url, i_len + 1 );
526         return VLC_SUCCESS;
527     }
528
529     /* Copy string, replacing invalid characters */
530     char *psz_ret = malloc( i_len + 3*i_num + 2 );
531     if( !psz_ret ) return VLC_ENOMEM;
532
533     for( psz_iter = (char*) psz_url, psz_tmp = psz_ret;
534          *psz_iter; ++psz_iter, ++psz_tmp )
535     {
536         if( isAllowedChar( *psz_iter ) )
537         {
538             *psz_tmp = *psz_iter;
539         }
540         else
541         {
542             *(psz_tmp++) = '?';
543             snprintf( psz_tmp, 3, "%02x", ( *psz_iter & 0x000000FF ) );
544             psz_tmp++;
545         }
546     }
547     *psz_tmp = '\0';
548
549     /* Return success */
550     *ppsz_encoded = psz_ret;
551     return VLC_SUCCESS;
552 }
553
554 /** **************************************************************************
555  * \brief Write the XSPF playlist given the list of files
556  *****************************************************************************/
557 static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames,
558                       const char *psz_zippath )
559 {
560     char *psz_zip = strrchr( psz_zippath, DIR_SEP_CHAR );
561     psz_zip = convert_xml_special_chars( psz_zip ? (psz_zip+1) : psz_zippath );
562
563     if( asprintf( pp_buffer, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
564         "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" "
565                 "xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n"
566                 " <title>%s</title>\n"
567                 " <trackList>\n", psz_zip ) == -1)
568         return -1;
569
570     /* Root node */
571     node *playlist = new_node( psz_zip );
572
573     /* Encode the URI and append ZIP_SEP */
574     char *psz_pathtozip;
575     escapeToXml( &psz_pathtozip, psz_zippath );
576     if( astrcatf( &psz_pathtozip, "%s", ZIP_SEP ) < 0 ) return -1;
577
578     int i_track = 0;
579     for( int i = 0; i < vlc_array_count( p_filenames ); ++i )
580     {
581         char *psz_name = (char*) vlc_array_item_at_index( p_filenames, i );
582         int i_len = strlen( psz_name );
583
584         if( !i_len ) continue;
585
586         /* Is it a folder ? */
587         if( psz_name[i_len-1] == '/' )
588         {
589             /* Do nothing */
590         }
591         else /* File */
592         {
593             /* Extract file name */
594             char *psz_file = strrchr( psz_name, '/' );
595             psz_file = convert_xml_special_chars( psz_file ?
596                     (psz_file+1) : psz_name );
597
598             /* Build full MRL */
599             char *psz_path = strdup( psz_pathtozip );
600             char *psz_escapedName;
601             escapeToXml( &psz_escapedName, psz_name );
602             if( astrcatf( &psz_path, "%s", psz_escapedName ) < 0 ) return -1;
603
604             /* Track information */
605             if( astrcatf( pp_buffer,
606                         "  <track>\n"
607                         "   <location>zip://%s</location>\n"
608                         "   <title>%s</title>\n"
609                         "   <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
610                         "    <vlc:id>%d</vlc:id>\n"
611                         "   </extension>\n"
612                         "  </track>\n",
613                         psz_path, psz_file, i_track ) < 0 ) return -1;
614
615             free( psz_file );
616             free( psz_path );
617
618             /* Find the parent node */
619             node *parent = findOrCreateParentNode( playlist, psz_name );
620             assert( parent );
621
622             /* Add the item to this node */
623             item *tmp = parent->media;
624             if( !tmp )
625             {
626                 parent->media = new_item( i_track );
627             }
628             else
629             {
630                 while( tmp->next )
631                 {
632                     tmp = tmp->next;
633                 }
634                 tmp->next = new_item( i_track );
635             }
636
637             ++i_track;
638         }
639     }
640
641     free( psz_pathtozip );
642
643     /* Close tracklist, open the extension */
644     if( astrcatf( pp_buffer,
645         " </trackList>\n"
646         " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
647                 ) < 0 ) return -1;
648
649     /* Write the tree */
650     if( nodeToXSPF( pp_buffer, playlist, true ) < 0 ) return -1;
651
652     /* Close extension and playlist */
653     if( astrcatf( pp_buffer, " </extension>\n</playlist>\n" ) < 0 ) return -1;
654
655     /* printf( "%s", *pp_buffer ); */
656
657     free_all_node( playlist );
658
659     return VLC_SUCCESS;
660 }
661
662 /** **************************************************************************
663  * \brief Recursively convert a node to its XSPF representation
664  *****************************************************************************/
665 static int nodeToXSPF( char **pp_buffer, node *n, bool b_root )
666 {
667     if( !b_root )
668     {
669         if( astrcatf( pp_buffer, "  <vlc:node title=\"%s\">\n", n->name ) < 0 )
670             return -1;
671     }
672     if( n->child )
673         nodeToXSPF( pp_buffer, n->child, false );
674     item *i = n->media;
675     while( i )
676     {
677         if( astrcatf( pp_buffer, "   <vlc:item tid=\"%d\" />\n", i->id ) < 0 )
678             return -1;
679         i = i->next;
680     }
681     if( !b_root )
682     {
683         if( astrcatf( pp_buffer, "  </vlc:node>\n" ) < 0 )
684             return -1;
685     }
686     return VLC_SUCCESS;
687 }
688
689 /** **************************************************************************
690  * \brief Either create or find the parent node of the item
691  *****************************************************************************/
692 static node* findOrCreateParentNode( node *root, const char *fullpath )
693 {
694     char *folder;
695     char *path = strdup( fullpath );
696     folder = path;
697
698     assert( root );
699
700     char *sep = strchr( folder, '/' );
701     if( !sep )
702     {
703         free( path );
704         return root;
705     }
706
707     *sep = '\0';
708     ++sep;
709
710     node *current = root->child;
711
712     while( current )
713     {
714         if( !strcmp( current->name, folder ) )
715         {
716             /* We found the folder, go recursively deeper */
717             return findOrCreateParentNode( current, sep );
718         }
719         current = current->next;
720     }
721
722     /* If we are here, then it means that we did not find the parent */
723     node *ret = new_node( folder );
724     if( !root->child )
725         root->child = ret;
726     else
727     {
728         current = root->child;
729         while( current->next )
730         {
731             current = current->next;
732         }
733         current->next = ret;
734     }
735
736     /* And now, create the subfolders */
737     ret = findOrCreateParentNode( ret, sep );
738
739     free( path );
740     return ret;
741 }
742
743
744 /** **************************************************************************
745  * ZipIO function definitions : how to use vlc_stream to read the zip
746  *****************************************************************************/
747
748 /** **************************************************************************
749  * \brief interface for unzip module to open a file using a vlc_stream
750  * \param opaque
751  * \param filename
752  * \param mode how to open the file (read/write ?). We support only read
753  * \return opaque
754  *****************************************************************************/
755 static void ZCALLBACK *ZipIO_Open( void *opaque, const char *file, int mode )
756 {
757     (void) file;
758     stream_t *s = (stream_t*) opaque;
759     if( mode & ( ZLIB_FILEFUNC_MODE_CREATE | ZLIB_FILEFUNC_MODE_WRITE ) )
760     {
761         msg_Dbg( s, "ZipIO_Open: we cannot write into zip files" );
762         return NULL;
763     }
764     return s;
765 }
766
767 /** **************************************************************************
768  * \brief read something from stream into buffer
769  * \param opaque should be the stream
770  * \param stream stream created by ZipIO_Open
771  * \param buf buffer to read the file
772  * \param size length of this buffer
773  * \return return the number of bytes read (<= size)
774  *****************************************************************************/
775 static unsigned long ZCALLBACK ZipIO_Read( void *opaque, void *stream,
776                                            void *buf, unsigned long size )
777 {
778     (void) stream;
779     stream_t *s = (stream_t*) opaque;
780     return (unsigned long) stream_Read( s->p_source, buf, (int) size );
781 }
782
783 /** **************************************************************************
784  * \brief tell size of stream
785  * \param opaque should be the stream
786  * \param stream stream created by ZipIO_Open
787  * \return size of the file / stream
788  * ATTENTION: this is not stream_Tell, but stream_Size !
789  *****************************************************************************/
790 static long ZCALLBACK ZipIO_Tell( void *opaque, void *stream )
791 {
792     (void) stream;
793     stream_t *s = (stream_t*) opaque;
794     return (long) stream_Size( s->p_source ); /* /!\ not stream_Tell /!\ */
795 }
796
797 /** **************************************************************************
798  * \brief seek in the stream
799  * \param opaque should be the stream
800  * \param stream stream created by ZipIO_Open
801  * \param offset positive offset to seek
802  * \param origin current position in stream
803  * \return ¿ VLC_SUCCESS or an error code ?
804  *****************************************************************************/
805 static long ZCALLBACK ZipIO_Seek ( void *opaque, void *stream,
806                                    unsigned long offset, int origin )
807 {
808     (void) stream;
809     stream_t *s = (stream_t*) opaque;
810     long l_ret;
811
812     uint64_t pos = offset + origin;
813     l_ret = (long) stream_Seek( s->p_source, pos );
814     return l_ret;
815 }
816
817 /** **************************************************************************
818  * \brief close the stream
819  * \param opaque should be the stream
820  * \param stream stream created by ZipIO_Open
821  * \return always VLC_SUCCESS
822  * This closes zip archive
823  *****************************************************************************/
824 static int ZCALLBACK ZipIO_Close ( void *opaque, void *stream )
825 {
826     (void) stream;
827     (void) opaque;
828 //     stream_t *s = (stream_t*) opaque;
829 //    if( p_demux->p_sys && p_demux->p_sys->zipFile )
830 //        p_demux->p_sys->zipFile = NULL;
831 //     stream_Seek( s->p_source, 0 );
832     return VLC_SUCCESS;
833 }
834
835 /** **************************************************************************
836  * \brief I/O functions for the ioapi: write (assert insteadof segfault)
837  *****************************************************************************/
838 static uLong ZCALLBACK ZipIO_Write( void* opaque, void* stream,
839                                     const void* buf, uLong size )
840 {
841     (void)opaque; (void)stream; (void)buf; (void)size;
842     int ERROR_zip_cannot_write_this_should_not_happen = 0;
843     assert( ERROR_zip_cannot_write_this_should_not_happen );
844     return 0;
845 }
846
847 /** **************************************************************************
848  * \brief I/O functions for the ioapi: test error (man 3 ferror)
849  *****************************************************************************/
850 static int ZCALLBACK ZipIO_Error( void* opaque, void* stream )
851 {
852     (void)opaque;
853     (void)stream;
854     return 0;
855 }
856