--- /dev/null
+/*****************************************************************************
+ * zipaccess.c: Module (access) to extract different archives, based on zlib
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/** @todo:
+ * - implement crypto (using url zip://user:password@path-to-archive#ZIP#file
+ * - read files in zip with long name (use unz_file_info.size_filename)
+ * - multi-volume archive support ?
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_ZLIB_H
+
+#include "zip.h"
+#include <vlc_access.h>
+
+/** **************************************************************************
+ * This is our own access_sys_t for zip files
+ *****************************************************************************/
+struct access_sys_t
+{
+ /* zlib / unzip members */
+ unzFile zipFile;
+ zlib_filefunc_def *fileFunctions;
+
+ /* file in zip information */
+ char *psz_fileInzip;
+};
+
+static int AccessControl( access_t *p_access, int i_query, va_list args );
+static ssize_t AccessRead( access_t *, uint8_t *, size_t );
+static int AccessSeek( access_t *, int64_t );
+static int OpenFileInZip( access_t *p_access, int i_pos );
+
+/** **************************************************************************
+ * \brief Open access
+ *****************************************************************************/
+int AccessOpen( vlc_object_t *p_this )
+{
+ access_t *p_access = (access_t*)p_this;
+ access_sys_t *p_sys;
+ int i_ret = VLC_EGENERIC;
+ unzFile file = 0;
+
+ char *psz_pathToZip = NULL, *psz_path = NULL, *psz_sep = NULL;
+
+ p_access->p_sys = p_sys = (access_sys_t*)
+ calloc( sizeof( access_sys_t ), 1 );
+ if( !p_sys )
+ return VLC_ENOMEM;
+
+ /* Split the MRL */
+ psz_path = strdup( p_access->psz_path );
+ psz_sep = strchr( psz_path, ZIP_SEP_CHAR );
+ if( !psz_sep )
+ return VLC_EGENERIC;
+
+ *psz_sep = '\0';
+ psz_pathToZip = unescape_URI_duplicate( psz_path );
+ p_sys->psz_fileInzip = strdup( psz_sep + 1 );
+
+ /* Define IO functions */
+ zlib_filefunc_def *p_func = (zlib_filefunc_def*)
+ calloc( 1, sizeof( zlib_filefunc_def ) );
+ p_func->zopen_file = ZipIO_Open;
+ p_func->zread_file = ZipIO_Read;
+ p_func->zwrite_file = ZipIO_Write; // see comment
+ p_func->ztell_file = ZipIO_Tell;
+ p_func->zseek_file = ZipIO_Seek;
+ p_func->zclose_file = ZipIO_Close;
+ p_func->zerror_file = ZipIO_Error;
+ p_func->opaque = p_access;
+
+ /* Open zip archive */
+ file = p_access->p_sys->zipFile = unzOpen2( psz_pathToZip, p_func );
+ if( !file )
+ {
+ msg_Err( p_access, "not a valid zip archive: '%s'", psz_pathToZip );
+ goto exit;
+ }
+
+ /* Open file in zip */
+ OpenFileInZip( p_access, 0 );
+
+ /* Set callback */
+ ACCESS_SET_CALLBACKS( AccessRead, NULL, AccessControl, AccessSeek );
+
+ /* Get some infos about current file. Maybe we could want some more ? */
+ unz_file_info z_info;
+ unzGetCurrentFileInfo( file, &z_info, NULL, 0, NULL, 0, NULL, 0 );
+
+ /* Set access informations: size is needed for AccessSeek */
+ p_access->info.i_size = z_info.uncompressed_size;
+ p_access->info.i_pos = 0;
+ p_access->info.b_eof = false;
+
+ i_ret = VLC_SUCCESS;
+
+exit:
+ if( i_ret != VLC_SUCCESS )
+ {
+ if( file )
+ {
+ unzCloseCurrentFile( file );
+ unzClose( file );
+ }
+ free( p_sys->psz_fileInzip );
+ free( p_sys->fileFunctions );
+ free( p_sys );
+ }
+
+ free( psz_pathToZip );
+ free( psz_path );
+ return i_ret;
+}
+
+/** **************************************************************************
+ * \brief Close access: free structures
+ *****************************************************************************/
+void AccessClose( vlc_object_t *p_this )
+{
+ access_t *p_access = (access_t*)p_this;
+ access_sys_t *p_sys = p_access->p_sys;
+ if( p_sys )
+ {
+ unzFile file = p_sys->zipFile;
+ if( file )
+ {
+ unzCloseCurrentFile( file );
+ unzClose( file );
+ }
+ free( p_sys->psz_fileInzip );
+ free( p_sys->fileFunctions );
+ free( p_sys );
+ }
+ var_Destroy( p_access, "zip-no-xspf" );
+}
+
+/** **************************************************************************
+ * \brief Control access
+ *****************************************************************************/
+static int AccessControl( access_t *p_access, int i_query, va_list args )
+{
+ bool *pb_bool;
+ int *pi_int;
+ int64_t *pi_64;
+
+ switch( i_query )
+ {
+ /* */
+ case ACCESS_CAN_SEEK:
+ case ACCESS_CAN_PAUSE:
+ case ACCESS_CAN_CONTROL_PACE:
+ pb_bool = (bool*)va_arg( args, bool* );
+ *pb_bool = true;
+ break;
+
+ case ACCESS_CAN_FASTSEEK:
+ pb_bool = (bool*)va_arg( args, bool* );
+ *pb_bool = false;
+ break;
+
+ /* */
+ case ACCESS_GET_MTU:
+ pi_int = (int*)va_arg( args, int * );
+ *pi_int = 0;
+ break;
+
+ case ACCESS_GET_PTS_DELAY:
+ pi_64 = (int64_t*)va_arg( args, int64_t * );
+ *pi_64 = DEFAULT_PTS_DELAY;
+ break;
+
+ /* */
+ case ACCESS_SET_PAUSE_STATE:
+ /* Nothing to do */
+ break;
+
+ case ACCESS_GET_TITLE_INFO:
+ case ACCESS_SET_TITLE:
+ case ACCESS_SET_SEEKPOINT:
+ case ACCESS_SET_PRIVATE_ID_STATE:
+ case ACCESS_GET_META:
+ case ACCESS_GET_PRIVATE_ID_STATE:
+ case ACCESS_GET_CONTENT_TYPE:
+ return VLC_EGENERIC;
+
+ default:
+ msg_Warn( p_access, "unimplemented query %d in control", i_query );
+ return VLC_EGENERIC;
+
+ }
+ return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Read access
+ * Reads current opened file in zip. This does not open the file in zip.
+ * Return -1 if no data yet, 0 if no more data, else real data read
+ *****************************************************************************/
+static ssize_t AccessRead( access_t *p_access, uint8_t *p_buffer, size_t sz )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ assert( p_sys );
+ unzFile file = p_sys->zipFile;
+ if( !file )
+ {
+ msg_Err( p_access, "archive not opened !" );
+ return VLC_EGENERIC;
+ }
+
+ int i_read = 0;
+ i_read = unzReadCurrentFile( file, p_buffer, sz );
+
+ p_access->info.i_pos = unztell( file );
+ return ( i_read >= 0 ? i_read : VLC_EGENERIC );
+}
+
+/** **************************************************************************
+ * \brief Seek inside zip file
+ *****************************************************************************/
+static int AccessSeek( access_t *p_access, int64_t seek_len )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ assert( p_sys );
+ unzFile file = p_sys->zipFile;
+ if( !file )
+ {
+ msg_Err( p_access, "archive not opened !" );
+ return VLC_EGENERIC;
+ }
+
+ /* Reopen file in zip if needed */
+ if( p_access->info.i_pos != 0 )
+ {
+ OpenFileInZip( p_access, p_access->info.i_pos + seek_len );
+ }
+
+ /* Read seek_len data and drop it */
+ int i_seek = 0;
+ int i_read = 1;
+ char *p_buffer = ( char* ) calloc( 1, ZIP_BUFFER_LEN );
+ while( ( i_seek < seek_len ) && ( i_read > 0 ) )
+ {
+ i_read = ( seek_len - i_seek < ZIP_BUFFER_LEN )
+ ? ( seek_len - i_seek ) : ZIP_BUFFER_LEN;
+ i_read = unzReadCurrentFile( file, p_buffer, i_read );
+ if( i_read < 0 )
+ {
+ msg_Warn( p_access, "could not seek in file" );
+ free( p_buffer );
+ return VLC_EGENERIC;
+ }
+ else
+ {
+ i_seek += i_read;
+ }
+ }
+ free( p_buffer );
+
+ p_access->info.i_pos = unztell( file );
+ return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Open file in zip
+ *****************************************************************************/
+static int OpenFileInZip( access_t *p_access, int i_pos )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ unzFile file = p_sys->zipFile;
+ if( !p_sys->psz_fileInzip )
+ {
+ return VLC_EGENERIC;
+ }
+
+ i_pos = __MIN( i_pos, 0 );
+ p_access->info.i_pos = 0;
+
+ unzCloseCurrentFile( file ); /* returns UNZ_PARAMERROR if file not opened */
+ if( unzLocateFile( file, p_sys->psz_fileInzip, 0 ) != UNZ_OK )
+ {
+ msg_Err( p_access, "could not [re]locate file in zip: '%s'",
+ p_sys->psz_fileInzip );
+ return VLC_EGENERIC;
+ }
+ if( unzOpenCurrentFile( file ) != UNZ_OK )
+ {
+ msg_Err( p_access, "could not [re]open file in zip: '%s'",
+ p_sys->psz_fileInzip );
+ return VLC_EGENERIC;
+ }
+ if( i_pos > 0 )
+ return AccessSeek( p_access, i_pos );
+ else
+ return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: open (read only)
+ *****************************************************************************/
+static void* ZCALLBACK ZipIO_Open( void* opaque, const char* file, int mode )
+{
+ assert(opaque != NULL);
+ assert(mode == (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING));
+
+ access_t *p_access = (access_t*) opaque;
+
+ return stream_UrlNew( p_access, file );
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: read
+ *****************************************************************************/
+static uLong ZCALLBACK ZipIO_Read( void* opaque, void* stream,
+ void* buf, uLong size )
+{
+ (void)opaque;
+ //access_t *p_access = (access_t*) opaque;
+ //msg_Dbg(p_access, "read %d", size);
+ return stream_Read( (stream_t*) stream, buf, size );
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: write (assert insteadof segfault)
+ *****************************************************************************/
+static uLong ZCALLBACK ZipIO_Write( void* opaque, void* stream,
+ const void* buf, uLong size )
+{
+ (void)opaque; (void)stream; (void)buf; (void)size;
+ int zip_access_cannot_write_this_should_not_happen = 0;
+ assert(zip_access_cannot_write_this_should_not_happen);
+ return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: tell
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Tell( void* opaque, void* stream )
+{
+ (void)opaque;
+ int64_t i64_tell = stream_Tell( (stream_t*) stream );
+ //access_t *p_access = (access_t*) opaque;
+ //msg_Dbg(p_access, "tell %" PRIu64, i64_tell);
+ return (long)i64_tell;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: seek
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Seek( void* opaque, void* stream,
+ uLong offset, int origin )
+{
+ (void)opaque;
+ //access_t *p_access = (access_t*) opaque;
+ int64_t pos = offset;
+ switch( origin )
+ {
+ case SEEK_CUR:
+ pos += stream_Tell( (stream_t*) stream );
+ break;
+ case SEEK_SET:
+ break;
+ case SEEK_END:
+ pos += stream_Size( (stream_t*) stream );
+ break;
+ default:
+ return -1;
+ }
+ //msg_Dbg( p_access, "seek (%d,%d): %" PRIu64, offset, origin, pos );
+ stream_Seek( (stream_t*) stream, pos );
+ /* Note: in unzip.c, unzlocal_SearchCentralDir seeks to the end of
+ the stream, which is doable but returns an error in VLC.
+ That's why we always assume this was OK. FIXME */
+ return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: close
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Close( void* opaque, void* stream )
+{
+ (void)opaque;
+ stream_Delete( (stream_t*) stream );
+ return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: test error (man 3 ferror)
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Error( void* opaque, void* stream )
+{
+ (void)opaque;
+ (void)stream;
+ //msg_Dbg( p_access, "error" );
+ return 0;
+}
+
+
+
+#else
+# error Can not compile zip demuxer without zlib support
+#endif
--- /dev/null
+/*****************************************************************************
+ * zipstream.c: stream_filter that creates a XSPF playlist from a Zip archive
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/** **************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "zip.h"
+#include <stddef.h>
+
+/* FIXME remove */
+#include <vlc_input.h>
+
+#define FILENAME_TEXT N_( "Media in Zip" )
+#define FILENAME_LONGTEXT N_( "Path to the media in the Zip archive" )
+
+/** **************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin()
+ set_shortname( "Zip" )
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
+ set_description( _( "Zip files filter" ) )
+ set_capability( "stream_filter", 1 )
+ set_callbacks( StreamOpen, StreamClose )
+ add_submodule()
+ set_subcategory( SUBCAT_INPUT_ACCESS )
+ set_description( _( "Zip access" ) )
+ set_capability( "access", 70 )
+ add_shortcut( "unzip" )
+ add_shortcut( "zip" )
+ set_callbacks( AccessOpen, AccessClose )
+vlc_module_end()
+
+/** *************************************************************************
+ * Local prototypes
+ ****************************************************************************/
+static int Read ( stream_t *, void *p_read, unsigned int i_read );
+static int Peek ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
+static int Control( stream_t *, int i_query, va_list );
+
+typedef struct node node;
+typedef struct item item;
+
+static int CreatePlaylist( stream_t *s, char **pp_buffer );
+static int GetFilesInZip( stream_t*, unzFile, vlc_array_t*, vlc_array_t* );
+static node* findOrCreateParentNode( node *root, const char *fullpath );
+static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames,
+ const char *psz_zippath );
+static int nodeToXSPF( char **pp_buffer, node *n, bool b_root );
+static node* findOrCreateParentNode( node *root, const char *fullpath );
+
+/** **************************************************************************
+ * Struct definitions
+ *****************************************************************************/
+struct stream_sys_t
+{
+ /* zlib / unzip members */
+ unzFile zipFile;
+ zlib_filefunc_def *fileFunctions;
+ char *psz_path;
+
+ /* xspf data */
+ char *psz_xspf;
+ size_t i_len;
+ size_t i_pos;
+};
+
+struct item {
+ int id;
+ item *next;
+};
+
+struct node {
+ char *name;
+ item *media;
+ node *child;
+ node *next;
+};
+
+/** **************************************************************************
+ * Some helpers
+ *****************************************************************************/
+
+inline static node* new_node( char *name )
+{
+ node *n = (node*) calloc( 1, sizeof(node) );
+ n->name = convert_xml_special_chars( name );
+ return n;
+}
+
+inline static item* new_item( int id )
+{
+ item *media = (item*) calloc( 1, sizeof(item) );
+ media->id = id;
+ return media;
+}
+
+inline static void free_all_node( node *root )
+{
+ while( root )
+ {
+ free_all_node( root->child );
+ free( root->name );
+ node *tmp = root->next;
+ free( root );
+ root = tmp;
+ }
+}
+
+/* Allocate strcat and format */
+static int astrcatf( char **ppsz_dest, const char *psz_fmt_src, ... )
+{
+ va_list args;
+ va_start( args, psz_fmt_src );
+
+ char *psz_tmp;
+ int i_ret = vasprintf( &psz_tmp, psz_fmt_src, args );
+ if( i_ret == -1 ) return -1;
+
+ va_end( args );
+
+ int i_len = strlen( *ppsz_dest ) + strlen( psz_tmp ) + 1;
+ char *psz_out = realloc( *ppsz_dest, i_len );
+ if( !psz_out ) return -1;
+
+ strcat( psz_out, psz_tmp );
+ free( psz_tmp );
+
+ *ppsz_dest = psz_out;
+ return i_len;
+}
+
+/** **************************************************************************
+ * Zip file identifier
+ *****************************************************************************/
+static const uint8_t p_zip_marker[] = { 0x50, 0x4b, 0x03, 0x04 }; // "PK^C^D"
+static const int i_zip_marker = 4;
+
+
+/** **************************************************************************
+ * Open
+ *****************************************************************************/
+int StreamOpen( vlc_object_t *p_this )
+{
+ stream_t *s = (stream_t*) p_this;
+ stream_sys_t *p_sys;
+
+ /* Verify file format */
+ const uint8_t *p_peek;
+ if( stream_Peek( s->p_source, &p_peek, i_zip_marker ) < i_zip_marker )
+ return VLC_EGENERIC;
+ if( memcmp( p_peek, p_zip_marker, i_zip_marker ) )
+ return VLC_EGENERIC;
+
+ s->p_sys = p_sys = calloc( sizeof( *p_sys ), 1 );
+ if( !p_sys )
+ return VLC_ENOMEM;
+
+ s->pf_read = Read;
+ s->pf_peek = Peek;
+ s->pf_control = Control;
+
+ p_sys->fileFunctions = ( zlib_filefunc_def * )
+ calloc( 1, sizeof( zlib_filefunc_def ) );
+ if( !p_sys->fileFunctions )
+ {
+ free( p_sys );
+ return VLC_ENOMEM;
+ }
+ p_sys->fileFunctions->zopen_file = ZipIO_Open;
+ p_sys->fileFunctions->zread_file = ZipIO_Read;
+ p_sys->fileFunctions->zwrite_file = ZipIO_Write;
+ p_sys->fileFunctions->ztell_file = ZipIO_Tell;
+ p_sys->fileFunctions->zseek_file = ZipIO_Seek;
+ p_sys->fileFunctions->zclose_file = ZipIO_Close;
+ p_sys->fileFunctions->zerror_file = ZipIO_Error;
+ p_sys->fileFunctions->opaque = ( void * ) s;
+ p_sys->zipFile = unzOpen2( NULL /* path */, p_sys->fileFunctions );
+ if( !p_sys->zipFile )
+ {
+ msg_Warn( s, "unable to open file" );
+ free( p_sys );
+ free( p_sys->fileFunctions );
+ return VLC_EGENERIC;
+ }
+
+ /* Find the stream uri */
+ /* FIXME FIXME FIXME */
+ input_thread_t *p_input_thread = (input_thread_t*)
+ vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
+ if( !p_input_thread )
+ {
+ free( p_sys );
+ free( p_sys->fileFunctions );
+ return VLC_EGENERIC;
+ }
+ input_item_t *p_input_item = input_GetItem( p_input_thread );
+ if( !p_input_item )
+ {
+ free( p_sys );
+ free( p_sys->fileFunctions );
+ return VLC_EGENERIC;
+ }
+ s->p_sys->psz_path = strdup( p_input_item->psz_uri );
+ vlc_gc_decref( p_input_item );
+// vlc_object_release( p_input_thread );
+ /* FIXME FIXME FIXME */
+
+ return VLC_SUCCESS;
+}
+
+/** *************************************************************************
+ * Close
+ ****************************************************************************/
+void StreamClose( vlc_object_t *p_this )
+{
+ stream_t *s = (stream_t*)p_this;
+ stream_sys_t *p_sys = s->p_sys;
+
+ free( p_sys->fileFunctions );
+ free( p_sys->psz_xspf );
+ free( p_sys->psz_path );
+ free( p_sys );
+}
+
+/** *************************************************************************
+ * Stream filters functions
+ ****************************************************************************/
+
+/** *************************************************************************
+ * Read
+ ****************************************************************************/
+static int Read( stream_t *s, void *p_read, unsigned int i_read )
+{
+ stream_sys_t *p_sys = s->p_sys;
+
+ if( !p_read ) return 0;
+
+ /* Fill the buffer */
+ if( p_sys->psz_xspf == NULL )
+ {
+ int i_ret = CreatePlaylist( s, &p_sys->psz_xspf );
+ if( i_ret < 0 )
+ return -1;
+ p_sys->i_len = strlen( p_sys->psz_xspf );
+ p_sys->i_pos = 0;
+ }
+
+ /* Read the buffer */
+ int i_len = __MIN( i_read, p_sys->i_len - p_sys->i_pos );
+ memcpy( p_read, p_sys->psz_xspf + p_sys->i_pos, i_len );
+ p_sys->i_pos += i_len;
+
+ return i_len;
+}
+
+/** *************************************************************************
+ * Peek
+ ****************************************************************************/
+static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
+{
+ stream_sys_t *p_sys = s->p_sys;
+
+ /* Fill the buffer */
+ if( p_sys->psz_xspf == NULL )
+ {
+ int i_ret = CreatePlaylist( s, &p_sys->psz_xspf );
+ if( i_ret < 0 )
+ return -1;
+ p_sys->i_len = strlen( p_sys->psz_xspf );
+ p_sys->i_pos = 0;
+ }
+
+
+ /* Point to the buffer */
+ int i_len = __MIN( i_peek, p_sys->i_len - p_sys->i_pos );
+ *pp_peek = (uint8_t*) p_sys->psz_xspf + p_sys->i_pos;
+
+ return i_len;
+}
+
+/** *************************************************************************
+ * Control
+ ****************************************************************************/
+static int Control( stream_t *s, int i_query, va_list args )
+{
+ stream_sys_t *p_sys = s->p_sys;
+
+ switch( i_query )
+ {
+ case STREAM_SET_POSITION:
+ {
+ int64_t i_position = (int64_t)va_arg( args, int64_t );
+ if( i_position >= p_sys->i_len )
+ return VLC_EGENERIC;
+ else
+ {
+ p_sys->i_len = (size_t) i_position;
+ return VLC_SUCCESS;
+ }
+ }
+
+ case STREAM_GET_POSITION:
+ {
+ int64_t *pi_position = (int64_t*)va_arg( args, int64_t* );
+ *pi_position = p_sys->i_pos;
+ return VLC_SUCCESS;
+ }
+
+ case STREAM_GET_SIZE:
+ {
+ int64_t *pi_size = (int64_t*)va_arg( args, int64_t* );
+ *pi_size = (int64_t) p_sys->i_len;
+ return VLC_SUCCESS;
+ }
+
+ case STREAM_GET_MTU:
+ {
+ int *pi_mtu = (int*)va_arg( args, int* );
+ *pi_mtu = 0;
+ return VLC_SUCCESS;
+ }
+
+ case STREAM_GET_CONTENT_TYPE:
+ return VLC_EGENERIC;
+
+ case STREAM_UPDATE_SIZE:
+ case STREAM_CONTROL_ACCESS:
+ case STREAM_CAN_SEEK:
+ case STREAM_CAN_FASTSEEK:
+ case STREAM_SET_RECORD_STATE:
+ return stream_vaControl( s->p_source, i_query, args );
+
+ default:
+ return VLC_EGENERIC;
+ }
+}
+
+static int CreatePlaylist( stream_t *s, char **pp_buffer )
+{
+ /* Get some infos about zip archive */
+ int i_ret = 0;
+ unzFile file = s->p_sys->zipFile;
+ vlc_array_t *p_filenames = vlc_array_new(); /* Will contain char* */
+
+ /* List all file names in Zip archive */
+ i_ret = GetFilesInZip( s, file, p_filenames, NULL );
+ if( i_ret < 0 )
+ {
+ unzClose( file );
+ i_ret = -1;
+ goto exit;
+ }
+
+ // msg_Dbg( s, "%d files in Zip", vlc_array_count( p_filenames ) );
+
+ /* Close archive */
+ unzClose( file );
+ s->p_sys->zipFile = NULL;
+
+ /* Construct the xspf playlist */
+ i_ret = WriteXSPF( pp_buffer, p_filenames, s->p_sys->psz_path );
+ if( i_ret > 0 )
+ i_ret = 1;
+ else if( i_ret < 0 )
+ i_ret = -1;
+
+exit:
+ for( int i = 0; i < vlc_array_count( p_filenames ); i++ )
+ {
+ free( vlc_array_item_at_index( p_filenames, i ) );
+ }
+ vlc_array_destroy( p_filenames );
+ return i_ret;
+}
+
+
+/** **************************************************************************
+ * Zip utility functions
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief List files in zip and append their names to p_array
+ * \param p_this
+ * \param file Opened zip file
+ * \param p_array vlc_array_t which will receive all filenames
+ *
+ * In case of error, returns VLC_EGENERIC.
+ * In case of success, returns number of files found, and goes back to first file.
+ *****************************************************************************/
+static int GetFilesInZip( stream_t *p_this, unzFile file,
+ vlc_array_t *p_filenames, vlc_array_t *p_fileinfos )
+{
+ if( !p_filenames || !p_this )
+ return VLC_EGENERIC;
+
+ int i_ret = 0;
+
+ /* Get global info */
+ unz_global_info info;
+
+ if( unzGetGlobalInfo( file, &info ) != UNZ_OK )
+ {
+ msg_Warn( p_this, "this is not a valid zip archive" );
+ return VLC_EGENERIC;
+ }
+
+ /* Go to first file in archive */
+ unzGoToFirstFile( file );
+
+ /* Get info about each file */
+ for( unsigned long i = 0; i < info.number_entry; i++ )
+ {
+ char *psz_fileName = calloc( ZIP_FILENAME_LEN, 1 );
+ unz_file_info *p_fileInfo = calloc( 1, sizeof( unz_file_info ) );
+
+ if( !p_fileInfo || !psz_fileName )
+ {
+ msg_Warn( p_this, "not enough memory" );
+ return VLC_ENOMEM;
+ }
+
+ if( unzGetCurrentFileInfo( file, p_fileInfo, psz_fileName,
+ ZIP_FILENAME_LEN, NULL, 0, NULL, 0 )
+ != UNZ_OK )
+ {
+ msg_Warn( p_this, "can't get info about file in zip" );
+ return VLC_EGENERIC;
+ }
+
+ if( p_filenames )
+ vlc_array_append( p_filenames, strdup( psz_fileName ) );
+ free( psz_fileName );
+
+ if( p_fileinfos )
+ vlc_array_append( p_fileinfos, p_fileInfo );
+ else
+ free( p_fileInfo );
+
+ if( i < ( info.number_entry - 1 ) )
+ {
+ /* Go the next file in the archive */
+ if( unzGoToNextFile( file ) != UNZ_OK )
+ {
+ msg_Warn( p_this, "can't go to next file in zip" );
+ return VLC_EGENERIC;
+ }
+ }
+
+ i_ret++;
+ }
+
+ /* i_ret should be equal to info.number_entry */
+ unzGoToFirstFile( file );
+ return i_ret;
+}
+
+
+/** **************************************************************************
+ * XSPF generation functions
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief Write the XSPF playlist given the list of files
+ *****************************************************************************/
+static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames,
+ const char *psz_zippath )
+{
+ char *psz_zip = strrchr( psz_zippath, DIR_SEP_CHAR );
+ psz_zip = convert_xml_special_chars( psz_zip ? (psz_zip+1) : psz_zippath );
+
+ if( asprintf( pp_buffer, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" "
+ "xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n"
+ " <title>%s</title>\n"
+ " <trackList>\n", psz_zip ) == -1)
+ return -1;
+
+ /* Root node */
+ node *playlist = new_node( psz_zip );
+
+ /* Web-Encode the URI and append '!' */
+ char *psz_pathtozip = vlc_UrlEncode( psz_zippath );
+ if( astrcatf( &psz_pathtozip, ZIP_SEP ) < 0 ) return -1;
+
+ int i_track = 0;
+ for( int i = 0; i < vlc_array_count( p_filenames ); ++i )
+ {
+ char *psz_name = (char*) vlc_array_item_at_index( p_filenames, i );
+ int i_len = strlen( psz_name );
+
+ if( !i_len ) continue;
+
+ /* Is it a folder ? */
+ if( psz_name[i_len-1] == '/' )
+ {
+ /* Do nothing */
+ }
+ else /* File */
+ {
+ /* Extract file name */
+ char *psz_file = strrchr( psz_name, '/' );
+ psz_file = convert_xml_special_chars( psz_file ?
+ (psz_file+1) : psz_name );
+
+ /* Build full MRL */
+ char *psz_path = strdup( psz_pathtozip );
+ if( astrcatf( &psz_path, psz_name ) < 0 ) return -1;
+
+ /* Double url-encode */
+ char *psz_tmp = psz_path;
+ psz_path = vlc_UrlEncode( psz_tmp );
+ free( psz_tmp );
+
+ /* Track information */
+ if( astrcatf( pp_buffer,
+ " <track>\n"
+ " <location>zip://%s</location>\n"
+ " <title>%s</title>\n"
+ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
+ " <vlc:id>%d</vlc:id>\n"
+ " </extension>\n"
+ " </track>\n",
+ psz_path, psz_file, i_track ) < 0 ) return -1;
+
+ free( psz_file );
+ free( psz_path );
+
+ /* Find the parent node */
+ node *parent = findOrCreateParentNode( playlist, psz_name );
+ assert( parent );
+
+ /* Add the item to this node */
+ item *tmp = parent->media;
+ if( !tmp )
+ {
+ parent->media = new_item( i_track );
+ }
+ else
+ {
+ while( tmp->next )
+ {
+ tmp = tmp->next;
+ }
+ tmp->next = new_item( i_track );
+ }
+
+ ++i_track;
+ }
+ }
+
+ free( psz_pathtozip );
+
+ /* Close tracklist, open the extension */
+ if( astrcatf( pp_buffer,
+ " </trackList>\n"
+ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n"
+ ) < 0 ) return -1;
+
+ /* Write the tree */
+ if( nodeToXSPF( pp_buffer, playlist, true ) < 0 ) return -1;
+
+ /* Close extension and playlist */
+ if( astrcatf( pp_buffer, " </extension>\n</playlist>\n" ) < 0 ) return -1;
+
+ /* printf( "%s", *pp_buffer ); */
+
+ free_all_node( playlist );
+
+ return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Recursively convert a node to its XSPF representation
+ *****************************************************************************/
+static int nodeToXSPF( char **pp_buffer, node *n, bool b_root )
+{
+ if( !b_root )
+ {
+ if( astrcatf( pp_buffer, " <vlc:node title=\"%s\">\n", n->name ) < 0 )
+ return -1;
+ }
+ if( n->child )
+ nodeToXSPF( pp_buffer, n->child, false );
+ item *i = n->media;
+ while( i )
+ {
+ if( astrcatf( pp_buffer, " <vlc:item tid=\"%d\" />\n", i->id ) < 0 )
+ return -1;
+ i = i->next;
+ }
+ if( !b_root )
+ {
+ if( astrcatf( pp_buffer, " </vlc:node>\n" ) < 0 )
+ return -1;
+ }
+ return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Either create or find the parent node of the item
+ *****************************************************************************/
+static node* findOrCreateParentNode( node *root, const char *fullpath )
+{
+ char *folder;
+ char *path = strdup( fullpath );
+ folder = path;
+
+ assert( root );
+
+ char *sep = strchr( folder, '/' );
+ if( !sep )
+ {
+ free( path );
+ return root;
+ }
+
+ *sep = '\0';
+ ++sep;
+
+ node *current = root->child;
+
+ while( current )
+ {
+ if( !strcmp( current->name, folder ) )
+ {
+ /* We found the folder, go recursively deeper */
+ return findOrCreateParentNode( current, sep );
+ }
+ current = current->next;
+ }
+
+ /* If we are here, then it means that we did not find the parent */
+ node *ret = new_node( folder );
+ if( !root->child )
+ root->child = ret;
+ else
+ {
+ current = root->child;
+ while( current->next )
+ {
+ current = current->next;
+ }
+ current->next = ret;
+ }
+
+ /* And now, create the subfolders */
+ ret = findOrCreateParentNode( ret, sep );
+
+ free( path );
+ return ret;
+}
+
+
+/** **************************************************************************
+ * ZipIO function definitions : how to use vlc_stream to read the zip
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief interface for unzip module to open a file using a vlc_stream
+ * \param opaque
+ * \param filename
+ * \param mode how to open the file (read/write ?). We support only read
+ * \return opaque
+ *****************************************************************************/
+static void ZCALLBACK *ZipIO_Open( void *opaque, const char *file, int mode )
+{
+ (void) file;
+ stream_t *s = (stream_t*) opaque;
+ if( mode & ( ZLIB_FILEFUNC_MODE_CREATE | ZLIB_FILEFUNC_MODE_WRITE ) )
+ {
+ msg_Dbg( s, "ZipIO_Open: we cannot write into zip files" );
+ return NULL;
+ }
+ return s;
+}
+
+/** **************************************************************************
+ * \brief read something from stream into buffer
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \param buf buffer to read the file
+ * \param size length of this buffer
+ * \return return the number of bytes read (<= size)
+ *****************************************************************************/
+static unsigned long ZCALLBACK ZipIO_Read( void *opaque, void *stream,
+ void *buf, unsigned long size )
+{
+ (void) stream;
+ stream_t *s = (stream_t*) opaque;
+ return (unsigned long) stream_Read( s->p_source, buf, (int) size );
+}
+
+/** **************************************************************************
+ * \brief tell size of stream
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \return size of the file / stream
+ * ATTENTION: this is not stream_Tell, but stream_Size !
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Tell( void *opaque, void *stream )
+{
+ (void) stream;
+ stream_t *s = (stream_t*) opaque;
+ return (long) stream_Size( s->p_source ); /* /!\ not stream_Tell /!\ */
+}
+
+/** **************************************************************************
+ * \brief seek in the stream
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \param offset positive offset to seek
+ * \param origin current position in stream
+ * \return ¿ VLC_SUCCESS or an error code ?
+ *****************************************************************************/
+static long ZCALLBACK ZipIO_Seek ( void *opaque, void *stream,
+ unsigned long offset, int origin )
+{
+ (void) stream;
+ stream_t *s = (stream_t*) opaque;
+ long l_ret;
+
+ uint64_t pos = offset + origin;
+ l_ret = (long) stream_Seek( s->p_source, pos );
+ return l_ret;
+}
+
+/** **************************************************************************
+ * \brief close the stream
+ * \param opaque should be the stream
+ * \param stream stream created by ZipIO_Open
+ * \return always VLC_SUCCESS
+ * This closes zip archive
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Close ( void *opaque, void *stream )
+{
+ (void) stream;
+ (void) opaque;
+// stream_t *s = (stream_t*) opaque;
+// if( p_demux->p_sys && p_demux->p_sys->zipFile )
+// p_demux->p_sys->zipFile = NULL;
+// stream_Seek( s->p_source, 0 );
+ return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: write (assert insteadof segfault)
+ *****************************************************************************/
+static uLong ZCALLBACK ZipIO_Write( void* opaque, void* stream,
+ const void* buf, uLong size )
+{
+ (void)opaque; (void)stream; (void)buf; (void)size;
+ int ERROR_zip_cannot_write_this_should_not_happen = 0;
+ assert( ERROR_zip_cannot_write_this_should_not_happen );
+ return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: test error (man 3 ferror)
+ *****************************************************************************/
+static int ZCALLBACK ZipIO_Error( void* opaque, void* stream )
+{
+ (void)opaque;
+ (void)stream;
+ //msg_Dbg( p_access, "error" );
+ return 0;
+}
+