Commit bc72d967 authored by Jean-Philippe Andre's avatar Jean-Philippe Andre

Zip: add a stream_filter and an access

The stream_filter does the following job:
[zip stream] -->filter--> [xspf playlist]

The access is called zip or unzip and takes MRLs of the form:
zip://[web-encoded-path]![file-in-zip]

As there is some automatic web-form decoding of the MRL,
the MRLs in the playlist generated by zipstream are encoded
two times.
parent 7032aafa
if HAVE_ZLIB
SOURCES_zip = \
zipstream.c \
zipaccess.c \
$(NULL)
endif
/*****************************************************************************
* zip.h: Module (access+demux) 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.
*****************************************************************************/
/** **************************************************************************
* Common includes and shared headers
*****************************************************************************/
#ifndef ZIP_ACCESSDEMUX_H
#define ZIP_ACCESSDEMUX_H
#include <vlc/vlc.h>
#include <vlc_common.h>
#include <vlc_url.h>
#include <vlc_strings.h>
#include <vlc_arrays.h>
#include <vlc_plugin.h>
#include <vlc_stream.h>
#include "unzip.h"
#include "ioapi.h"
#include <assert.h>
#define ZIP_FILENAME_LEN 512
#define ZIP_BUFFER_LEN 32768
#define ZIP_SEP "|"
#define ZIP_SEP_CHAR '|'
/** **************************************************************************
* Module access points: stream_filter
*****************************************************************************/
int StreamOpen( vlc_object_t* );
void StreamClose( vlc_object_t* );
/** **************************************************************************
* Module access points: access
*****************************************************************************/
int AccessOpen( vlc_object_t *p_this );
void AccessClose( vlc_object_t *p_this );
/** **************************************************************************
* zipIO function headers : how to use vlc_stream to read the zip
* Note: static because the implementations differ
*****************************************************************************/
static void* ZCALLBACK ZipIO_Open( void* opaque, const char* filename, int m );
static uLong ZCALLBACK ZipIO_Read( void*, void* stream, void* buf, uLong sz );
static uLong ZCALLBACK ZipIO_Write( void*, void* stream, const void*, uLong );
static long ZCALLBACK ZipIO_Tell( void*, void* stream );
static long ZCALLBACK ZipIO_Seek( void*, void* stream, uLong offset, int ori );
static int ZCALLBACK ZipIO_Close( void*, void* stream );
static int ZCALLBACK ZipIO_Error( void*, void* stream );
#endif /* ZIP_ACCESSDEMUX_H */
/*****************************************************************************
* 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
/*****************************************************************************
* 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment