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