Commit 8e8e6441 authored by Antoine Cellerier's avatar Antoine Cellerier

src/playlist/loadsave.c, modules/demux/playlist/*, modules/gui/*,

modules/misc/playlist/*, modules/access/http.c: XSPF playlist support
(read and write) by Daniel Stranger. Many thanks

src/misc/modules.c, src/misc/strings.c, include/vlc_strings.h: string
handling functions.

modules/control/http/*, modules/services_discovery/upnp_intel.cpp: use
these string handling functions.
parent d78b7c38
/*****************************************************************************
* vlc_strings.h: String functions
*****************************************************************************
* Copyright (C) 2006 the VideoLAN team
* $Id$
*
* Authors: Antoine Cellerier <dionoea at videolan dot 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.
*****************************************************************************/
#ifndef _VLC_STRINGS_H
#define _VLC_STRINGS_H 1
#include <vlc/vlc.h>
/**
* \defgroup strings Strings
* @{
*/
VLC_EXPORT( char *, decode_encoded_URI_duplicate, ( const char *psz ) );
VLC_EXPORT( void, decode_encoded_URI, ( char *psz ) );
VLC_EXPORT( void, resolve_xml_special_chars, ( char *psz_value ) );
VLC_EXPORT( char *, convert_xml_special_chars, ( const char *psz_content ) );
/**
* @}
*/
#endif
...@@ -480,6 +480,10 @@ struct module_symbols_t ...@@ -480,6 +480,10 @@ struct module_symbols_t
double (*us_atof_inner) (const char *); double (*us_atof_inner) (const char *);
double (*us_strtod_inner) (const char *, char **); double (*us_strtod_inner) (const char *, char **);
lldiv_t (*vlc_lldiv_inner) (long long numer, long long denom); lldiv_t (*vlc_lldiv_inner) (long long numer, long long denom);
void (*decode_encoded_URI_inner) (char *psz);
char * (*convert_xml_special_chars_inner) (const char *psz_content);
char * (*decode_encoded_URI_duplicate_inner) (const char *psz);
void (*resolve_xml_special_chars_inner) (char *psz_value);
}; };
# if defined (__PLUGIN__) # if defined (__PLUGIN__)
# define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner # define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner
...@@ -940,6 +944,10 @@ struct module_symbols_t ...@@ -940,6 +944,10 @@ struct module_symbols_t
# define us_atof (p_symbols)->us_atof_inner # define us_atof (p_symbols)->us_atof_inner
# define us_strtod (p_symbols)->us_strtod_inner # define us_strtod (p_symbols)->us_strtod_inner
# define vlc_lldiv (p_symbols)->vlc_lldiv_inner # define vlc_lldiv (p_symbols)->vlc_lldiv_inner
# define decode_encoded_URI (p_symbols)->decode_encoded_URI_inner
# define convert_xml_special_chars (p_symbols)->convert_xml_special_chars_inner
# define decode_encoded_URI_duplicate (p_symbols)->decode_encoded_URI_duplicate_inner
# define resolve_xml_special_chars (p_symbols)->resolve_xml_special_chars_inner
# elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__) # elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__)
/****************************************************************** /******************************************************************
* STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access. * STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access.
...@@ -1403,6 +1411,10 @@ struct module_symbols_t ...@@ -1403,6 +1411,10 @@ struct module_symbols_t
((p_symbols)->us_atof_inner) = us_atof; \ ((p_symbols)->us_atof_inner) = us_atof; \
((p_symbols)->us_strtod_inner) = us_strtod; \ ((p_symbols)->us_strtod_inner) = us_strtod; \
((p_symbols)->vlc_lldiv_inner) = vlc_lldiv; \ ((p_symbols)->vlc_lldiv_inner) = vlc_lldiv; \
((p_symbols)->decode_encoded_URI_inner) = decode_encoded_URI; \
((p_symbols)->convert_xml_special_chars_inner) = convert_xml_special_chars; \
((p_symbols)->decode_encoded_URI_duplicate_inner) = decode_encoded_URI_duplicate; \
((p_symbols)->resolve_xml_special_chars_inner) = resolve_xml_special_chars; \
(p_symbols)->net_ConvertIPv4_deprecated = NULL; \ (p_symbols)->net_ConvertIPv4_deprecated = NULL; \
(p_symbols)->__stats_CounterGet_deprecated = NULL; \ (p_symbols)->__stats_CounterGet_deprecated = NULL; \
(p_symbols)->__stats_TimerDumpAll_deprecated = NULL; \ (p_symbols)->__stats_TimerDumpAll_deprecated = NULL; \
......
...@@ -403,6 +403,9 @@ connect: ...@@ -403,6 +403,9 @@ connect:
/* Grrrr! detect ultravox server and force NSV demuxer */ /* Grrrr! detect ultravox server and force NSV demuxer */
p_access->psz_demux = strdup( "nsv" ); p_access->psz_demux = strdup( "nsv" );
} }
else if( p_sys->psz_mime &&
!strcasecmp( p_sys->psz_mime, "application/xspf+xml" ) )
p_access->psz_demux = strdup( "xspf-open" );
if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" ); if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
......
...@@ -420,44 +420,8 @@ void E_(EvaluateRPN)( intf_thread_t *p_intf, mvar_t *vars, ...@@ -420,44 +420,8 @@ void E_(EvaluateRPN)( intf_thread_t *p_intf, mvar_t *vars,
{ {
char *psz_src = E_(SSPop)( st ); char *psz_src = E_(SSPop)( st );
char *psz_dest; char *psz_dest;
char *str = psz_src;
p = psz_dest = malloc( strlen( str ) * 6 + 1 );
while( *str != '\0' ) psz_dest = convert_xml_special_chars( psz_src );
{
if( *str == '&' )
{
strcpy( p, "&amp;" );
p += 5;
}
else if( *str == '\"' )
{
strcpy( p, "&quot;" );
p += 6;
}
else if( *str == '\'' )
{
strcpy( p, "&#039;" );
p += 6;
}
else if( *str == '<' )
{
strcpy( p, "&lt;" );
p += 4;
}
else if( *str == '>' )
{
strcpy( p, "&gt;" );
p += 4;
}
else
{
*p++ = *str;
}
str++;
}
*p = '\0';
E_(SSPush)( st, psz_dest ); E_(SSPush)( st, psz_dest );
free( psz_src ); free( psz_src );
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
*****************************************************************************/ *****************************************************************************/
#include "http.h" #include "http.h"
#include "vlc_strings.h"
/**************************************************************************** /****************************************************************************
* File and directory functions * File and directory functions
...@@ -747,38 +748,7 @@ char *E_(ExtractURIValue)( char *psz_uri, const char *psz_name, ...@@ -747,38 +748,7 @@ char *E_(ExtractURIValue)( char *psz_uri, const char *psz_name,
void E_(DecodeEncodedURI)( char *psz ) void E_(DecodeEncodedURI)( char *psz )
{ {
char *dup = strdup( psz ); decode_encoded_URI( psz );
char *p = dup;
while( *p )
{
if( *p == '%' )
{
char val[3];
p++;
if( !*p )
{
break;
}
val[0] = *p++;
val[1] = *p++;
val[2] = '\0';
*psz++ = strtol( val, NULL, 16 );
}
else if( *p == '+' )
{
*psz++ = ' ';
p++;
}
else
{
*psz++ = *p++;
}
}
*psz++ = '\0';
free( dup );
} }
/* Since the resulting string is smaller we can work in place, so it is /* Since the resulting string is smaller we can work in place, so it is
......
SOURCES_playlist = playlist.c \ SOURCES_playlist = \
playlist.h \ playlist.c \
old.c \ playlist.h \
m3u.c \ old.c \
b4s.c \ m3u.c \
pls.c \ b4s.c \
dvb.c \ pls.c \
podcast.c dvb.c \
podcast.c \
xspf.c \
xspf.h \
$(NULL)
...@@ -84,6 +84,11 @@ vlc_module_begin(); ...@@ -84,6 +84,11 @@ vlc_module_begin();
add_shortcut( "podcast" ); add_shortcut( "podcast" );
set_capability( "demux2", 10 ); set_capability( "demux2", 10 );
set_callbacks( E_(Import_podcast), E_(Close_podcast) ); set_callbacks( E_(Import_podcast), E_(Close_podcast) );
add_submodule();
set_description( _("XSPF playlist import") );
add_shortcut( "xspf-open" );
set_capability( "demux2", 10 );
set_callbacks( E_(xspf_import_Activate), NULL );
vlc_module_end(); vlc_module_end();
......
...@@ -45,3 +45,5 @@ void E_(Close_DVB) ( vlc_object_t * ); ...@@ -45,3 +45,5 @@ void E_(Close_DVB) ( vlc_object_t * );
int E_(Import_podcast) ( vlc_object_t * ); int E_(Import_podcast) ( vlc_object_t * );
void E_(Close_podcast) ( vlc_object_t * ); void E_(Close_podcast) ( vlc_object_t * );
int E_(xspf_import_Activate) ( vlc_object_t * );
/******************************************************************************
* Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
*
* 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.
*******************************************************************************/
/**
* \file modules/demux/playlist/xspf.c
* \brief XSPF playlist import functions
*/
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/intf.h>
#include "playlist.h"
#include "vlc_xml.h"
#include "vlc_strings.h"
#include "xspf.h"
/**
* \brief XSPF submodule initialization function
*/
int E_(xspf_import_Activate)( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t *)p_this;
char *psz_ext;
psz_ext = strrchr ( p_demux->psz_path, '.' );
if( ( psz_ext && !strcasecmp( psz_ext, ".xspf") ) ||
( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "xspf-open") ) )
{
;
}
else
{
return VLC_EGENERIC;
}
msg_Dbg( p_demux, "using xspf playlist import");
p_demux->pf_control = xspf_import_Control;
p_demux->pf_demux = xspf_import_Demux;
return VLC_SUCCESS;
}
/**
* \brief demuxer function for XSPF parsing
*/
int xspf_import_Demux( demux_t *p_demux )
{
playlist_t *p_playlist = NULL;
playlist_item_t *p_current = NULL;
vlc_bool_t b_play;
int i_ret = VLC_SUCCESS;
xml_t *p_xml = NULL;
xml_reader_t *p_xml_reader = NULL;
char *psz_name = NULL;
/* create new xml parser from stream */
p_xml = xml_Create( p_demux );
if( !p_xml )
i_ret = VLC_ENOMOD;
else
{
p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
if( !p_xml_reader )
i_ret = VLC_EGENERIC;
}
/* start with parsing the root node */
if ( i_ret == VLC_SUCCESS )
if ( xml_ReaderRead( p_xml_reader ) != 1 )
{
msg_Err( p_demux, "can't read xml stream" );
i_ret = VLC_EGENERIC;
}
/* checking root nody type */
if ( i_ret == VLC_SUCCESS )
if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM )
{
msg_Err( p_demux, "invalid root node type: %i", xml_ReaderNodeType( p_xml_reader ) );
i_ret = VLC_EGENERIC;
}
/* checking root node name */
if ( i_ret == VLC_SUCCESS )
psz_name = xml_ReaderName( p_xml_reader );
if ( !psz_name || strcmp( psz_name, "playlist" ) )
{
msg_Err( p_demux, "invalid root node name: %s", psz_name );
i_ret = VLC_EGENERIC;
}
FREE_NAME();
/* get the playlist ... */
if ( i_ret == VLC_SUCCESS )
{
p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST, FIND_PARENT );
if( !p_playlist )
{
msg_Err( p_demux, "can't find playlist" );
i_ret = VLC_ENOOBJ;
}
}
/* ... and its current item (to convert it to a node) */
if ( i_ret == VLC_SUCCESS )
{
b_play = E_(FindItem)( p_demux, p_playlist, &p_current );
playlist_ItemToNode( p_playlist, p_current );
p_current->input.i_type = ITEM_TYPE_PLAYLIST;
/* parse the playlist node */
i_ret = parse_playlist_node( p_demux, p_playlist, p_current,
p_xml_reader, "playlist" );
/* true/false - success/egeneric mapping */
i_ret = ( i_ret==VLC_TRUE ? VLC_SUCCESS : VLC_EGENERIC );
if( b_play )
{
playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
p_playlist->status.i_view,
p_playlist->status.p_item, NULL );
}
}
if ( p_playlist )
vlc_object_release( p_playlist );
if ( p_xml_reader )
xml_ReaderDelete( p_xml, p_xml_reader );
if ( p_xml )
xml_Delete( p_xml );
return i_ret;
}
/** \brief dummy function for demux callback interface */
int xspf_import_Control( demux_t *p_demux, int i_query, va_list args )
{
return VLC_EGENERIC;
}
/**
* \brief parse the root node of a XSPF playlist
* \param p_demux demuxer instance
* \param p_playlist playlist instance
* \param p_item current playlist node
* \param p_xml_reader xml reader instance
* \param psz_element name of element to parse
*/
static vlc_bool_t parse_playlist_node COMPLEX_INTERFACE
{
char *psz_name=NULL;
char *psz_value=NULL;
vlc_bool_t b_version_found = VLC_FALSE;
int i_node;
xml_elem_hnd_t *p_handler=NULL;
xml_elem_hnd_t pl_elements[] =
{ {"title", SIMPLE_CONTENT, {.smpl = set_item_info} },
{"creator", SIMPLE_CONTENT, {.smpl = set_item_info} },
{"annotation", SIMPLE_CONTENT, {NULL} },
{"info", SIMPLE_CONTENT, {NULL} },
{"location", SIMPLE_CONTENT, {NULL} },
{"identifier", SIMPLE_CONTENT, {NULL} },
{"image", SIMPLE_CONTENT, {NULL} },
{"date", SIMPLE_CONTENT, {NULL} },
{"license", SIMPLE_CONTENT, {NULL} },
{"attribution", COMPLEX_CONTENT, {.cmplx = skip_element} },
{"link", SIMPLE_CONTENT, {NULL} },
{"meta", SIMPLE_CONTENT, {NULL} },
{"extension", COMPLEX_CONTENT, {.cmplx = skip_element} },
{"trackList", COMPLEX_CONTENT, {.cmplx = parse_tracklist_node} },
{NULL, UNKNOWN_CONTENT, {NULL} }
};
/* read all playlist attributes */
while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
{
psz_name = xml_ReaderName ( p_xml_reader );
psz_value = xml_ReaderValue ( p_xml_reader );
if ( !psz_name || !psz_value )
{
msg_Err( p_demux, "invalid xml stream @ <playlist>" );
FREE_ATT();
return VLC_FALSE;
}
/* attribute: version */
if ( !strcmp( psz_name, "version" ) )
{
b_version_found = VLC_TRUE;
if ( strcmp( psz_value, "0" ) && strcmp( psz_value, "1" ) )
msg_Warn( p_demux, "unsupported XSPF version" );
}
/* attribute: xmlns */
else if ( !strcmp ( psz_name, "xmlns" ) )
;
/* unknown attribute */
else
msg_Warn( p_demux, "invalid <playlist> attribute:\"%s\"", psz_name);
FREE_ATT();
}
/* attribute version is mandatory !!! */
if ( !b_version_found )
msg_Warn( p_demux, "<playlist> requires \"version\" attribute" );
/* parse the child elements - we only take care of <trackList> */
while ( xml_ReaderRead( p_xml_reader ) == 1 )
{
i_node = xml_ReaderNodeType( p_xml_reader );
switch ( i_node )
{
case XML_READER_NONE:
break;
case XML_READER_STARTELEM:
/* element start tag */
psz_name = xml_ReaderName( p_xml_reader );
if ( !psz_name || !*psz_name )
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
return VLC_FALSE;
}
/* choose handler */
for( p_handler = pl_elements;
p_handler->name && strcmp( psz_name, p_handler->name );
p_handler++ );
if ( !p_handler->name )
{
msg_Err( p_demux, "unexpected element <%s>", psz_name );
FREE_ATT();
return VLC_FALSE;
}
FREE_NAME();
/* complex content is parsed in a separate function */
if ( p_handler->type == COMPLEX_CONTENT )
{
if ( p_handler->pf_handler.cmplx( p_demux,
p_playlist,
p_item,
p_xml_reader,
p_handler->name ) )
{
p_handler = NULL;
FREE_ATT();
}
else
{
FREE_ATT();
return VLC_FALSE;
}
}
break;
case XML_READER_TEXT:
/* simple element content */
FREE_ATT();
psz_value = xml_ReaderValue( p_xml_reader );
if ( !psz_value )
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
return VLC_FALSE;
}
break;
case XML_READER_ENDELEM:
/* element end tag */
psz_name = xml_ReaderName( p_xml_reader );
if ( !psz_name )
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
return VLC_FALSE;
}
/* leave if the current parent node <playlist> is terminated */
if ( !strcmp( psz_name, psz_element ) )
{
FREE_ATT();
return VLC_TRUE;
}
/* there MUST have been a start tag for that element name */
if ( !p_handler || !p_handler->name
|| strcmp( p_handler->name, psz_name ))
{
msg_Err( p_demux, "there's no open element left for <%s>",
psz_name );
FREE_ATT();
return VLC_FALSE;
}
if ( p_handler->pf_handler.smpl )
{
p_handler->pf_handler.smpl( p_item, p_handler->name,
psz_value );
}
FREE_ATT();
p_handler = NULL;
break;
default:
/* unknown/unexpected xml node */
msg_Err( p_demux, "unexpected xml node %i", i_node );
FREE_ATT();
return VLC_FALSE;
}
FREE_NAME();
}
return VLC_FALSE;
}
/**
* \brief parses the tracklist node which only may contain <track>s
*/
static vlc_bool_t parse_tracklist_node COMPLEX_INTERFACE
{
char *psz_name=NULL;
int i_node;
int i_ntracks = 0;
/* now parse the <track>s */
while ( xml_ReaderRead( p_xml_reader ) == 1 )
{
i_node = xml_ReaderNodeType( p_xml_reader );
if ( i_node == XML_READER_STARTELEM )
{
psz_name = xml_ReaderName( p_xml_reader );
if ( !psz_name )
{
msg_Err( p_demux, "unexpected end of xml data" );
FREE_NAME();
return VLC_FALSE;
}
if ( strcmp( psz_name, "track") )
{
msg_Err( p_demux, "unexpected child of <trackList>: <%s>",
psz_name );
FREE_NAME();
return VLC_FALSE;
}
FREE_NAME();
/* parse the track data in a separate function */
if ( parse_track_node( p_demux, p_playlist, p_item, p_xml_reader,
"track" ) == VLC_TRUE )
i_ntracks++;
}
else if ( i_node == XML_READER_ENDELEM )
break;
}
/* the <trackList> has to be terminated */
if ( xml_ReaderNodeType( p_xml_reader ) != XML_READER_ENDELEM )
{
msg_Err( p_demux, "there's a missing </trackList>" );
FREE_NAME();
return VLC_FALSE;
}
psz_name = xml_ReaderName( p_xml_reader );
if ( !psz_name || strcmp( psz_name, "trackList" ) )
{
msg_Err( p_demux, "expected: </trackList>, found: </%s>", psz_name );
FREE_NAME();
return VLC_FALSE;
}
FREE_NAME();
msg_Dbg( p_demux, "parsed %i tracks successfully", i_ntracks );
return VLC_TRUE;
}
/**
* \brief parse one track element
* \param COMPLEX_INTERFACE
*/
static vlc_bool_t parse_track_node COMPLEX_INTERFACE
{
playlist_item_t *p_new=NULL;
int i_node;
char *psz_name=NULL;
char *psz_value=NULL;
xml_elem_hnd_t *p_handler=NULL;
xml_elem_hnd_t track_elements[] =
{ {"location", SIMPLE_CONTENT, {NULL} },
{"identifier", SIMPLE_CONTENT, {NULL} },
{"title", SIMPLE_CONTENT, {.smpl = set_item_info} },
{"creator", SIMPLE_CONTENT, {.smpl = set_item_info} },
{"annotation", SIMPLE_CONTENT, {NULL} },
{"info", SIMPLE_CONTENT, {NULL} },
{"image", SIMPLE_CONTENT, {NULL} },
{"album", SIMPLE_CONTENT, {.smpl = set_item_info} },
{"trackNum", SIMPLE_CONTENT, {.smpl = set_item_info} },
{"duration", SIMPLE_CONTENT, {.smpl = set_item_info} },
{"link", SIMPLE_CONTENT, {NULL} },
{"meta", SIMPLE_CONTENT, {NULL} },
{"extension", COMPLEX_CONTENT, {.cmplx = skip_element} },
{NULL, UNKNOWN_CONTENT, {NULL} }
};
while ( xml_ReaderRead( p_xml_reader ) == 1 )
{
i_node = xml_ReaderNodeType( p_xml_reader );
switch ( i_node )
{
case XML_READER_NONE:
break;
case XML_READER_STARTELEM:
/* element start tag */
psz_name = xml_ReaderName( p_xml_reader );
if ( !psz_name || !*psz_name )
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
return VLC_FALSE;
}
/* choose handler */
for( p_handler = track_elements;
p_handler->name && strcmp( psz_name, p_handler->name );
p_handler++ );
if ( !p_handler->name )
{
msg_Err( p_demux, "unexpected element <%s>", psz_name );
FREE_ATT();
return VLC_FALSE;
}
FREE_NAME();
/* complex content is parsed in a separate function */
if ( p_handler->type == COMPLEX_CONTENT )
{
if ( !p_new )
{
msg_Err( p_demux,
"at <%s> level no new item has been allocated",
p_handler->name );
FREE_ATT();
return VLC_FALSE;
}
if ( p_handler->pf_handler.cmplx( p_demux,
p_playlist,
p_new,
p_xml_reader,
p_handler->name ) )
{
p_handler = NULL;
FREE_ATT();
}
else
{
FREE_ATT();
return VLC_FALSE;
}
}
break;
case XML_READER_TEXT:
/* simple element content */
FREE_ATT();
psz_value = xml_ReaderValue( p_xml_reader );
if ( !psz_value )
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
return VLC_FALSE;
}
break;
case XML_READER_ENDELEM:
/* element end tag */
psz_name = xml_ReaderName( p_xml_reader );
if ( !psz_name )
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
return VLC_FALSE;
}
/* leave if the current parent node <track> is terminated */
if ( !strcmp( psz_name, psz_element ) )
{
FREE_ATT();
return VLC_TRUE;
}
/* there MUST have been a start tag for that element name */
if ( !p_handler || !p_handler->name
|| strcmp( p_handler->name, psz_name ))
{
msg_Err( p_demux, "there's no open element left for <%s>",
psz_name );
FREE_ATT();
return VLC_FALSE;
}
/* special case: location */
if ( !strcmp( p_handler->name, "location" ) )
{
/* there MUST NOT be an item */
if ( p_new )
{
msg_Err( p_demux,
"a new item has just been created <%s>",
psz_name );
FREE_ATT();
return VLC_FALSE;
}
/* create it now */
if ( insert_new_item( p_playlist, p_item,
&p_new, psz_value ) )
{
FREE_ATT();
p_handler = NULL;
}
else
{
FREE_ATT();
return VLC_FALSE;
}
}
else
{
/* there MUST be an item */
if ( !p_new )
{
msg_Err( p_demux,
"an item hasn't been created yet <%s>",
psz_name );
FREE_ATT();
return VLC_FALSE;
}
if ( p_handler->pf_handler.smpl )
{
p_handler->pf_handler.smpl( p_new, p_handler->name,
psz_value );
FREE_ATT();
}
}
FREE_ATT();
p_handler = NULL;
break;
default:
/* unknown/unexpected xml node */
msg_Err( p_demux, "unexpected xml node %i", i_node );
FREE_ATT();
return VLC_FALSE;
}
FREE_NAME();
}
msg_Err( p_demux, "unexpected end of xml data" );
FREE_ATT();
return VLC_FALSE;
}
/**
* \brief handles the supported <track> sub-elements
*/
static vlc_bool_t set_item_info SIMPLE_INTERFACE
{
/* exit if setting is impossible */
if ( !psz_name || !psz_value || !p_item )
return VLC_FALSE;
/* re-convert xml special characters inside psz_value */
resolve_xml_special_chars ( psz_value );
/* handle each info element in a separate "if" clause */
if ( !strcmp( psz_name, "title" ) )
{
if ( playlist_ItemSetName ( p_item, (char *)psz_value ) == VLC_SUCCESS )
return VLC_TRUE;
return VLC_FALSE;
}
else if ( !strcmp( psz_name, "creator" ) )
{
if ( vlc_input_item_AddInfo( &(p_item->input),
_(VLC_META_INFO_CAT), _(VLC_META_ARTIST),
"%s", psz_value ) == VLC_SUCCESS )
return VLC_TRUE;
return VLC_FALSE;
}
else if ( !strcmp( psz_name, "album" ) )
{
if ( vlc_input_item_AddInfo( &(p_item->input),
_(VLC_META_INFO_CAT),
_(VLC_META_COLLECTION),
"%s", psz_value ) == VLC_SUCCESS )
return VLC_TRUE;
return VLC_FALSE;
} else if ( !strcmp( psz_name, "trackNum" ) )
{
long i_num = atol( psz_value );
if ( i_num > 0
&& vlc_input_item_AddInfo( &(p_item->input),
_(VLC_META_INFO_CAT),
_(VLC_META_SEQ_NUM),
"%s", psz_value ) == VLC_SUCCESS )
return VLC_TRUE;
return VLC_FALSE;
} else if ( !strcmp( psz_name, "duration" ) )
{
long i_num = atol( psz_value );
if ( i_num > 0
&& playlist_ItemSetDuration( p_item, i_num*1000 ) == VLC_SUCCESS )
return VLC_TRUE;
return VLC_FALSE;
}
return VLC_TRUE;
}
/**
* \brief skips complex element content that we can't manage
*/
static vlc_bool_t skip_element COMPLEX_INTERFACE
{
char *psz_endname;
while ( xml_ReaderRead( p_xml_reader ) == 1 )
{
if ( xml_ReaderNodeType( p_xml_reader ) == XML_READER_ENDELEM )
{
psz_endname = xml_ReaderName( p_xml_reader );
if ( !psz_endname )
return VLC_FALSE;
if ( !strcmp( psz_element, psz_endname ) )
{
free( psz_endname );
return VLC_TRUE;
}
else
free( psz_endname );
}
}
return VLC_FALSE;
}
/**
* \brief creates a new playlist item from the given mrl
*/
static vlc_bool_t insert_new_item( playlist_t *p_pl, playlist_item_t *p_cur,
playlist_item_t **pp_new, char *psz_location )
{
char *psz_uri=NULL;
psz_uri = decode_encoded_URI_duplicate( psz_location );
if ( psz_uri )
{
*pp_new = playlist_ItemNew( p_pl, psz_uri, NULL );
free( psz_uri );
psz_uri = NULL;
}
if ( !*pp_new )
return VLC_FALSE;
playlist_NodeAddItem( p_pl, *pp_new, p_cur->pp_parents[0]->i_view,
p_cur, PLAYLIST_APPEND, PLAYLIST_END );
playlist_CopyParents( p_cur, *pp_new );
vlc_input_item_CopyOptions( &p_cur->input, &((*pp_new)->input) );
return VLC_TRUE;
}
/*****************************************************************************
* Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
*
* 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.
*******************************************************************************/
/**
* \file modules/demux/playlist/xspf.h
* \brief XSPF playlist import: prototypes, datatypes, defines
*/
/* defines */
#define FREE_NAME() if (psz_name) {free(psz_name);psz_name=NULL;}
#define FREE_VALUE() if (psz_value) {free(psz_value);psz_value=NULL;}
#define FREE_ATT() FREE_NAME();FREE_VALUE()
#define UNKNOWN_CONTENT 0
#define SIMPLE_CONTENT 1
#define COMPLEX_CONTENT 2
#define SIMPLE_INTERFACE (playlist_item_t *p_item,\
const char *psz_name,\
char *psz_value)
#define COMPLEX_INTERFACE (demux_t *p_demux,\
playlist_t *p_playlist,\
playlist_item_t *p_item,\
xml_reader_t *p_xml_reader,\
const char *psz_element)
/* prototypes */
int xspf_import_Demux( demux_t *);
int xspf_import_Control( demux_t *, int, va_list );
static vlc_bool_t parse_playlist_node COMPLEX_INTERFACE;
static vlc_bool_t parse_tracklist_node COMPLEX_INTERFACE;
static vlc_bool_t parse_track_node COMPLEX_INTERFACE;
static vlc_bool_t set_item_info SIMPLE_INTERFACE;
static vlc_bool_t skip_element COMPLEX_INTERFACE;
static vlc_bool_t insert_new_item( playlist_t *, playlist_item_t *, playlist_item_t **, char *);
/* datatypes */
typedef struct
{
const char *name;
int type;
union
{
vlc_bool_t (*smpl) SIMPLE_INTERFACE;
vlc_bool_t (*cmplx) COMPLEX_INTERFACE;
} pf_handler;
} xml_elem_hnd_t;
...@@ -229,14 +229,16 @@ void Dialogs::showChangeSkin() ...@@ -229,14 +229,16 @@ void Dialogs::showChangeSkin()
void Dialogs::showPlaylistLoad() void Dialogs::showPlaylistLoad()
{ {
showFileGeneric( _("Open playlist"), showFileGeneric( _("Open playlist"),
_("All playlists|*.pls;*.m3u;*.asx;*.b4s|M3U files|*.m3u"), _("All playlists|*.pls;*.m3u;*.asx;*.b4s;*.xspf|"
"M3U files|*.m3u|"
"XSPF playlist|*.xspf"),
showPlaylistLoadCB, kOPEN ); showPlaylistLoadCB, kOPEN );
} }
void Dialogs::showPlaylistSave() void Dialogs::showPlaylistSave()
{ {
showFileGeneric( _("Save playlist"), _("M3U file|*.m3u"), showFileGeneric( _("Save playlist"), _("M3U file|*.m3u|XSPF playlist|*.xspf"),
showPlaylistSaveCB, kSAVE ); showPlaylistSaveCB, kSAVE );
} }
......
...@@ -908,7 +908,9 @@ void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) ) ...@@ -908,7 +908,9 @@ void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) )
char *psz_desc; char *psz_desc;
char *psz_filter; char *psz_filter;
char *psz_module; char *psz_module;
} formats[] = {{ _("M3U file"), "*.m3u", "export-m3u" }}; } formats[] = {{ _("M3U file"), "*.m3u", "export-m3u" },
{ _("XSPF playlist"), "*.xspf", "export-xspf"}
};
wxString filter = wxT(""); wxString filter = wxT("");
...@@ -943,7 +945,7 @@ void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) ) ...@@ -943,7 +945,7 @@ void Playlist::OnSave( wxCommandEvent& WXUNUSED(event) )
void Playlist::OnOpen( wxCommandEvent& WXUNUSED(event) ) void Playlist::OnOpen( wxCommandEvent& WXUNUSED(event) )
{ {
wxFileDialog dialog( this, wxU(_("Open playlist")), wxT(""), wxT(""), wxFileDialog dialog( this, wxU(_("Open playlist")), wxT(""), wxT(""),
wxT("All playlists|*.pls;*.m3u;*.asx;*.b4s|M3U files|*.m3u"), wxOPEN ); wxT("All playlists|*.pls;*.m3u;*.asx;*.b4s;*.xspf|XSPF playlist|*.xspf|M3U files|*.m3u"), wxOPEN );
if( dialog.ShowModal() == wxID_OK ) if( dialog.ShowModal() == wxID_OK )
{ {
......
SOURCES_export = export.c \ SOURCES_export = \
m3u.c \ export.c \
old.c m3u.c \
xspf.c \
xspf.h \
old.c \
$(NULL)
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
***************************************************************************/ ***************************************************************************/
int Export_M3U ( vlc_object_t *p_intf ); int Export_M3U ( vlc_object_t *p_intf );
int Export_Old ( vlc_object_t *p_intf ); int Export_Old ( vlc_object_t *p_intf );
int E_(xspf_export_playlist)( vlc_object_t *p_intf );
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
...@@ -52,4 +52,10 @@ vlc_module_begin(); ...@@ -52,4 +52,10 @@ vlc_module_begin();
set_capability( "playlist export" , 0); set_capability( "playlist export" , 0);
set_callbacks( Export_Old , NULL ); set_callbacks( Export_Old , NULL );
add_submodule();
set_description( _("XSPF playlist export") );
add_shortcut( "export-xspf" );
set_capability( "playlist export" , 0);
set_callbacks( E_(xspf_export_playlist) , NULL );
vlc_module_end(); vlc_module_end();
/******************************************************************************
* Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
*
* 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.
*******************************************************************************/
/**
* \file modules/misc/playlist/xspf.c
* \brief XSPF playlist export functions
*/
#include <stdio.h>
#include <vlc/vlc.h>
#include <vlc/intf.h>
#include "vlc_meta.h"
#include "vlc_strings.h"
#include "xspf.h"
/**
* \brief Prints the XSPF header to file, writes each item by xspf_export_item()
* and closes the open xml elements
* \param p_this the VLC playlist object
* \return VLC_SUCCESS if some memory is available, otherwise VLC_ENONMEM
*/
int E_(xspf_export_playlist)( vlc_object_t *p_this )
{
const playlist_t *p_playlist = (playlist_t *)p_this;
const playlist_export_t *p_export =
(playlist_export_t *)p_playlist->p_private;
int i;
char *psz;
char *psz_temp;
playlist_item_t **pp_items = NULL;
int i_size;
playlist_item_t *p_node;
/* write XSPF XML header - since we don't use <extension>,
* we get by with version 0 */
fprintf( p_export->p_file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
fprintf( p_export->p_file,
"<playlist version=\"0\" xmlns=\"http://xspf.org/ns/0/\">\n" );
/* save tho whole playlist or only the current node */
#define p_item p_playlist->status.p_item
if ( p_item )
{
for (i = 0; i < p_item->i_parents; i++ )
{
if ( p_item->pp_parents[i]->p_parent->input.i_type
== ITEM_TYPE_PLAYLIST )
{
/* set the current node and its children */
p_node = p_item->pp_parents[i]->p_parent;
pp_items = p_node->pp_children;
i_size = p_node->i_children;
#undef p_item
/* save name of the playlist node */
psz_temp = convert_xml_special_chars( p_node->input.psz_name );
if ( *psz_temp )
fprintf( p_export->p_file, "\t<title>%s</title>\n",
psz_temp );
free( psz_temp );
/* save the creator of the playlist node */
psz = vlc_input_item_GetInfo( &p_node->input,
_(VLC_META_INFO_CAT),
_(VLC_META_ARTIST) );
if ( psz && !*psz )
{
free ( psz );
psz = NULL;
}
if ( !psz )
psz = vlc_input_item_GetInfo( &p_node->input,
_(VLC_META_INFO_CAT),
_(VLC_META_AUTHOR) );
psz_temp = convert_xml_special_chars( psz );
if ( psz ) free( psz );
if ( *psz_temp )
fprintf( p_export->p_file, "\t<creator>%s</creator>\n",
psz_temp );
free( psz_temp );
/* save location of the playlist node */
psz = assertUTF8URI( p_export->psz_filename );
if ( psz && *psz )
{
fprintf( p_export->p_file, "\t<location>%s</location>\n",
psz );
free( psz );
}
break;
}
}
}
/* prepare all the playlist children for export */
if ( !pp_items )
{
pp_items = p_playlist->pp_items;
i_size = p_playlist->i_size;
}
/* export all items */
fprintf( p_export->p_file, "\t<trackList>\n" );
for ( i = 0; i < i_size; i++ )
{
xspf_export_item( pp_items[i], p_export->p_file );
}
/* close the header elements */
fprintf( p_export->p_file, "\t</trackList>\n" );
fprintf( p_export->p_file, "</playlist>\n" );
return VLC_SUCCESS;
}
/**
* \brief exports one item to file or traverse if item is a node
* \param p_item playlist item to export
* \param p_file file to write xml-converted item to
*/
static void xspf_export_item( playlist_item_t *p_item, FILE *p_file )
{
int i; /**< iterator for all children if the current item is a node */
char *psz;
char *psz_temp;
if ( !p_item )
return;
/** \todo only "flat" playlists supported at this time.
* extend to save the tree structure.
*/
/* if we get a node here, we must traverse it */
if ( p_item->i_children > 0 )
{
for ( i = 0; i < p_item->i_children; i++ )
{
xspf_export_item( p_item->pp_children[i], p_file );
}
return;
}
/* leaves can be written directly */
fprintf( p_file, "\t\t<track>\n" );
/* -> the location */
if ( p_item->input.psz_uri && *p_item->input.psz_uri )
{
psz = assertUTF8URI( p_item->input.psz_uri );
fprintf( p_file, "\t\t\t<location>%s</location>\n", psz );
free( psz );
}
/* -> the name/title (only if different from uri)*/
if ( p_item->input.psz_name &&
p_item->input.psz_uri &&
strcmp( p_item->input.psz_uri, p_item->input.psz_name ) )
{
psz_temp = convert_xml_special_chars( p_item->input.psz_name );
if ( *psz_temp )
fprintf( p_file, "\t\t\t<title>%s</title>\n", psz_temp );
free( psz_temp );
}
/* -> the artist/creator */
psz = vlc_input_item_GetInfo( &p_item->input,
_(VLC_META_INFO_CAT),
_(VLC_META_ARTIST) );
if ( psz && !*psz )
{
free ( psz );
psz = NULL;
}
if ( !psz )
psz = vlc_input_item_GetInfo( &p_item->input,
_(VLC_META_INFO_CAT),
_(VLC_META_AUTHOR) );
psz_temp = convert_xml_special_chars( psz );
if ( psz ) free( psz );
if ( *psz_temp )
fprintf( p_file, "\t\t\t<creator>%s</creator>\n", psz_temp );
free( psz_temp );
/* -> the album */
psz = vlc_input_item_GetInfo( &p_item->input,
_(VLC_META_INFO_CAT),
_(VLC_META_COLLECTION) );
psz_temp = convert_xml_special_chars( psz );
if ( psz ) free( psz );
if ( *psz_temp )
fprintf( p_file, "\t\t\t<album>%s</album>\n", psz_temp );
free( psz_temp );
/* -> the track number */
psz = vlc_input_item_GetInfo( &p_item->input,
_(VLC_META_INFO_CAT),
_(VLC_META_SEQ_NUM) );
if ( psz )
{
if ( *psz )
fprintf( p_file, "\t\t\t<trackNum>%i</trackNum>\n", atoi( psz ) );
free( psz );
}
/* -> the duration */
if ( p_item->input.i_duration > 0 )
{
fprintf( p_file, "\t\t\t<duration>%ld</duration>\n",
(long)(p_item->input.i_duration / 1000) );
}
fprintf( p_file, "\t\t</track>\n" );
return;
}
/**
* \param psz_name the location of the media ressource (e.g. local file,
* device, network stream, etc.)
* \return a new char buffer which asserts that the location is valid UTF-8
* and a valid URI
* \note the returned buffer must be freed, when it isn't used anymore
*/
static char *assertUTF8URI( char *psz_name )
{
char *psz_ret = NULL; /**< the new result buffer to return */
char *psz_s = NULL, *psz_d = NULL; /**< src & dest pointers for URI conversion */
vlc_bool_t b_name_is_uri = VLC_FALSE;
if ( !psz_name || !*psz_name )
return NULL;
/* check that string is valid UTF-8 */
/* XXX: Why do we even need to do that ? (all strings in core are UTF-8 encoded */
if( !( psz_s = EnsureUTF8( psz_name ) ) )
return NULL;
/* max. 3x for URI conversion (percent escaping) and
8 bytes for "file://" and NULL-termination */
psz_ret = (char *)malloc( sizeof(char)*strlen(psz_name)*6*3+8 );
if ( !psz_ret )
return NULL;
/** \todo check for a valid scheme part preceding the colon */
if ( strchr( psz_s, ':' ) )
{
psz_d = psz_ret;
b_name_is_uri = VLC_TRUE;
}
/* assume "file" scheme if no scheme-part is included */
else
{
strcpy( psz_ret, "file://" );
psz_d = psz_ret + 7;
}
while ( *psz_s )
{
/* percent-encode all non-ASCII and the XML special characters and the percent sign itself */
if ( *psz_s & B10000000 ||
*psz_s == '<' ||
*psz_s == '>' ||
*psz_s == '&' ||
*psz_s == ' ' ||
( *psz_s == '%' && !b_name_is_uri ) )
{
*psz_d++ = '%';
*psz_d++ = hexchars[(*psz_s >> 4) & B00001111];
*psz_d++ = hexchars[*psz_s & B00001111];
} else
*psz_d++ = *psz_s;
psz_s++;
}
*psz_d = '\0';
return (char *)realloc( psz_ret, sizeof(char)*strlen( psz_ret ) + 1 );
}
/*****************************************************************************
* Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
*
* 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.
*******************************************************************************/
/**
* \file modules/misc/playlist/xspf.h
* \brief XSPF playlist export module header file
*/
/* defs */
#define B10000000 0x80
#define B01000000 0x40
#define B11000000 0xc0
#define B00001111 0x0f
#define XSPF_MAX_CONTENT 2000
/* constants */
const char hexchars[16] = "0123456789ABCDEF";
/* prototypes */
int E_(xspf_export_playlist)( vlc_object_t * );
static void xspf_export_item( playlist_item_t *, FILE * );
static char *assertUTF8URI( char * );
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
* $Id$ * $Id$
* *
* Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin) * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
* Christian Henz <henz # c-lab.de> * Christian Henz <henz # c-lab.de>
* *
* UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc/intf.h> #include <vlc/intf.h>
#include "vlc_strings.h"
// VLC handle // VLC handle
...@@ -57,7 +59,7 @@ const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer: ...@@ -57,7 +59,7 @@ const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:
const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1"; const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
// Classes // Classes
class MediaServer; class MediaServer;
class MediaServerList; class MediaServerList;
...@@ -66,51 +68,51 @@ class Container; ...@@ -66,51 +68,51 @@ class Container;
// Cookie that is passed to the callback // Cookie that is passed to the callback
typedef struct typedef struct
{ {
services_discovery_t* serviceDiscovery; services_discovery_t* serviceDiscovery;
UpnpClient_Handle clientHandle; UpnpClient_Handle clientHandle;
MediaServerList* serverList; MediaServerList* serverList;
} Cookie; } Cookie;
// Class definitions... // Class definitions...
class Lockable class Lockable
{ {
public: public:
Lockable( Cookie* c ) Lockable( Cookie* c )
{ {
vlc_mutex_init( c->serviceDiscovery, &_mutex ); vlc_mutex_init( c->serviceDiscovery, &_mutex );
} }
~Lockable() ~Lockable()
{ {
vlc_mutex_destroy( &_mutex ); vlc_mutex_destroy( &_mutex );
} }
void lock() { vlc_mutex_lock( &_mutex ); } void lock() { vlc_mutex_lock( &_mutex ); }
void unlock() { vlc_mutex_unlock( &_mutex ); } void unlock() { vlc_mutex_unlock( &_mutex ); }
private: private:
vlc_mutex_t _mutex; vlc_mutex_t _mutex;
}; };
class Locker class Locker
{ {
public: public:
Locker( Lockable* l ) Locker( Lockable* l )
{ {
_lockable = l; _lockable = l;
_lockable->lock(); _lockable->lock();
} }
~Locker() ~Locker()
{ {
_lockable->unlock(); _lockable->unlock();
} }
private: private:
...@@ -123,10 +125,10 @@ class MediaServer ...@@ -123,10 +125,10 @@ class MediaServer
public: public:
static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie ); static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie ); MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
~MediaServer(); ~MediaServer();
const char* getUDN() const; const char* getUDN() const;
const char* getFriendlyName() const; const char* getFriendlyName() const;
...@@ -150,13 +152,13 @@ private: ...@@ -150,13 +152,13 @@ private:
IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* ); IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
Cookie* _cookie; Cookie* _cookie;
Container* _contents; Container* _contents;
playlist_item_t* _playlistNode; playlist_item_t* _playlistNode;
std::string _UDN; std::string _UDN;
std::string _friendlyName; std::string _friendlyName;
std::string _contentDirectoryEventURL; std::string _contentDirectoryEventURL;
std::string _contentDirectoryControlURL; std::string _contentDirectoryControlURL;
...@@ -186,7 +188,7 @@ private: ...@@ -186,7 +188,7 @@ private:
}; };
class Item class Item
{ {
public: public:
...@@ -202,7 +204,7 @@ public: ...@@ -202,7 +204,7 @@ public:
private: private:
playlist_item_t* _playlistNode; playlist_item_t* _playlistNode;
Container* _parent; Container* _parent;
std::string _objectID; std::string _objectID;
std::string _title; std::string _title;
...@@ -210,7 +212,7 @@ private: ...@@ -210,7 +212,7 @@ private:
}; };
class Container class Container
{ {
public: public:
...@@ -268,7 +270,6 @@ vlc_module_end(); ...@@ -268,7 +270,6 @@ vlc_module_end();
static Lockable* CallbackLock; static Lockable* CallbackLock;
static int Callback( Upnp_EventType eventType, void* event, void* pCookie ); static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
char* xml_makeSpecialChars( const char* in );
const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName ); const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
IXML_Document* parseBrowseResult( IXML_Document* doc ); IXML_Document* parseBrowseResult( IXML_Document* doc );
...@@ -279,8 +280,8 @@ static int Open( vlc_object_t *p_this ) ...@@ -279,8 +280,8 @@ static int Open( vlc_object_t *p_this )
{ {
services_discovery_t *p_sd = ( services_discovery_t* )p_this; services_discovery_t *p_sd = ( services_discovery_t* )p_this;
services_discovery_sys_t *p_sys = ( services_discovery_sys_t * ) services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
malloc( sizeof( services_discovery_sys_t ) ); malloc( sizeof( services_discovery_sys_t ) );
playlist_view_t *p_view; playlist_view_t *p_view;
vlc_value_t val; vlc_value_t val;
...@@ -289,12 +290,12 @@ static int Open( vlc_object_t *p_this ) ...@@ -289,12 +290,12 @@ static int Open( vlc_object_t *p_this )
/* Create our playlist node */ /* Create our playlist node */
p_sys->p_playlist = ( playlist_t * )vlc_object_find( p_sd, p_sys->p_playlist = ( playlist_t * )vlc_object_find( p_sd,
VLC_OBJECT_PLAYLIST, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE ); FIND_ANYWHERE );
if( !p_sys->p_playlist ) if( !p_sys->p_playlist )
{ {
msg_Warn( p_sd, "unable to find playlist, cancelling UPnP listening" ); msg_Warn( p_sd, "unable to find playlist, cancelling UPnP listening" );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY ); p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
...@@ -315,9 +316,9 @@ static void Close( vlc_object_t *p_this ) ...@@ -315,9 +316,9 @@ static void Close( vlc_object_t *p_this )
if( p_sys->p_playlist ) if( p_sys->p_playlist )
{ {
playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE, playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
VLC_TRUE ); VLC_TRUE );
vlc_object_release( p_sys->p_playlist ); vlc_object_release( p_sys->p_playlist );
} }
free( p_sys ); free( p_sys );
...@@ -326,12 +327,12 @@ static void Close( vlc_object_t *p_this ) ...@@ -326,12 +327,12 @@ static void Close( vlc_object_t *p_this )
static void Run( services_discovery_t* p_sd ) static void Run( services_discovery_t* p_sd )
{ {
int res; int res;
res = UpnpInit( 0, 0 ); res = UpnpInit( 0, 0 );
if( res != UPNP_E_SUCCESS ) if( res != UPNP_E_SUCCESS )
{ {
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) ); msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
return; return;
} }
Cookie cookie; Cookie cookie;
...@@ -341,23 +342,23 @@ static void Run( services_discovery_t* p_sd ) ...@@ -341,23 +342,23 @@ static void Run( services_discovery_t* p_sd )
CallbackLock = new Lockable( &cookie ); CallbackLock = new Lockable( &cookie );
res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle ); res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle );
if( res != UPNP_E_SUCCESS ) if( res != UPNP_E_SUCCESS )
{ {
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) ); msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
goto shutDown; goto shutDown;
} }
res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie ); res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie );
if( res != UPNP_E_SUCCESS ) if( res != UPNP_E_SUCCESS )
{ {
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) ); msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
goto shutDown; goto shutDown;
} }
msg_Dbg( p_sd, "UPnP discovery started" ); msg_Dbg( p_sd, "UPnP discovery started" );
while( !p_sd->b_die ) while( !p_sd->b_die )
{ {
msleep( 500 ); msleep( 500 );
} }
msg_Dbg( p_sd, "UPnP discovery stopped" ); msg_Dbg( p_sd, "UPnP discovery stopped" );
...@@ -372,7 +373,7 @@ static void Run( services_discovery_t* p_sd ) ...@@ -372,7 +373,7 @@ static void Run( services_discovery_t* p_sd )
// XML utility functions: // XML utility functions:
// Returns the value of a child element, or 0 on error // Returns the value of a child element, or 0 on error
const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName ) const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
{ {
if ( !parent ) return 0; if ( !parent ) return 0;
if ( !tagName ) return 0; if ( !tagName ) return 0;
...@@ -392,72 +393,14 @@ const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName ...@@ -392,72 +393,14 @@ const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName
return ixmlNode_getNodeValue( textNode ); return ixmlNode_getNodeValue( textNode );
} }
// Replaces "&lt;" with "<" etc.
// Returns a newly created string that has to be freed by the caller.
// Returns 0 on error ( out of mem )
// \TODO: Probably not very robust!!!
char* xml_makeSpecialChars( const char* in )
{
if ( !in ) return 0;
char* result = ( char* )malloc( strlen( in ) + 1 );
if ( !result ) return 0;
char* out = result;
while( *in )
{
if ( strncmp( "&amp;", in, 5 ) == 0 )
{
*out = '&';
in += 5;
out++;
}
else if ( strncmp( "&quot;", in, 6 ) == 0 )
{
*out = '"';
in += 6;
out++;
}
else if ( strncmp( "&gt;", in, 4 ) == 0 )
{
*out = '>';
in += 4;
out++;
}
else if ( strncmp( "&lt;", in, 4 ) == 0 )
{
*out = '<';
in += 4;
out++;
}
else
{
*out = *in;
in++;
out++;
}
}
*out = '\0';
return result;
}
// Extracts the result document from a SOAP response // Extracts the result document from a SOAP response
IXML_Document* parseBrowseResult( IXML_Document* doc ) IXML_Document* parseBrowseResult( IXML_Document* doc )
{ {
if ( !doc ) return 0; if ( !doc ) return 0;
IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" ); IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
if ( !resultList ) return 0; if ( !resultList ) return 0;
IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 ); IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
ixmlNodeList_free( resultList ); ixmlNodeList_free( resultList );
...@@ -468,7 +411,7 @@ IXML_Document* parseBrowseResult( IXML_Document* doc ) ...@@ -468,7 +411,7 @@ IXML_Document* parseBrowseResult( IXML_Document* doc )
if ( !textNode ) return 0; if ( !textNode ) return 0;
const char* resultString = ixmlNode_getNodeValue( textNode ); const char* resultString = ixmlNode_getNodeValue( textNode );
char* resultXML = xml_makeSpecialChars( resultString ); char* resultXML = convert_xml_special_chars( resultString );
IXML_Document* browseDoc = ixmlParseBuffer( resultXML ); IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
...@@ -479,67 +422,67 @@ IXML_Document* parseBrowseResult( IXML_Document* doc ) ...@@ -479,67 +422,67 @@ IXML_Document* parseBrowseResult( IXML_Document* doc )
// Handles all UPnP events // Handles all UPnP events
static int Callback( Upnp_EventType eventType, void* event, void* pCookie ) static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
{ {
Locker locker( CallbackLock ); Locker locker( CallbackLock );
Cookie* cookie = ( Cookie* )pCookie; Cookie* cookie = ( Cookie* )pCookie;
switch( eventType ) { switch( eventType ) {
case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
case UPNP_DISCOVERY_SEARCH_RESULT: case UPNP_DISCOVERY_SEARCH_RESULT:
{ {
struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event; struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
IXML_Document *descriptionDoc = 0; IXML_Document *descriptionDoc = 0;
int res; int res;
res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc ); res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
if ( res != UPNP_E_SUCCESS ) if ( res != UPNP_E_SUCCESS )
{ {
msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ ); msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
return res; return res;
} }
MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie ); MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
ixmlDocument_free( descriptionDoc ); ixmlDocument_free( descriptionDoc );
} }
break; break;
case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
{ {
struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event; struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
cookie->serverList->removeServer( discovery->DeviceId ); cookie->serverList->removeServer( discovery->DeviceId );
} }
break; break;
case UPNP_EVENT_RECEIVED: case UPNP_EVENT_RECEIVED:
{ {
Upnp_Event* e = ( Upnp_Event* )event; Upnp_Event* e = ( Upnp_Event* )event;
MediaServer* server = cookie->serverList->getServerBySID( e->Sid ); MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
if ( server ) server->fetchContents(); if ( server ) server->fetchContents();
} }
break; break;
case UPNP_EVENT_AUTORENEWAL_FAILED: case UPNP_EVENT_AUTORENEWAL_FAILED:
case UPNP_EVENT_SUBSCRIPTION_EXPIRED: case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
{ {
// Re-subscribe... // Re-subscribe...
Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event; Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
if ( server ) server->subscribeToContentDirectory();
}
break;
MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
if ( server ) server->subscribeToContentDirectory();
}
break;
default: default:
msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType ); msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
break; break;
} }
return UPNP_E_SUCCESS; return UPNP_E_SUCCESS;
...@@ -550,127 +493,127 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie ) ...@@ -550,127 +493,127 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
// MediaServer... // MediaServer...
void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie ) void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
{ {
if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; } if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; } if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
const char* baseURL = location; const char* baseURL = location;
// Try to extract baseURL // Try to extract baseURL
IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" ); IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
if ( urlList ) if ( urlList )
{ {
if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) ) if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
{ {
IXML_Node* textNode = ixmlNode_getFirstChild( urlNode ); IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode ); if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
} }
ixmlNodeList_free( urlList ); ixmlNodeList_free( urlList );
} }
// Get devices // Get devices
IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" ); IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
if ( deviceList ) if ( deviceList )
{ {
for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ ) for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
{ {
IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i ); IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" ); const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; } if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue; if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
const char* UDN = xml_getChildElementValue( deviceElement, "UDN" ); const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; } if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
if ( cookie->serverList->getServer( UDN ) != 0 ) continue; if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" ); const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; } if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
MediaServer* server = new MediaServer( UDN, friendlyName, cookie ); MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
if ( !cookie->serverList->addServer( server ) ) { if ( !cookie->serverList->addServer( server ) ) {
delete server; delete server;
server = 0; server = 0;
continue; continue;
} }
// Check for ContentDirectory service... // Check for ContentDirectory service...
IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" ); IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
if ( serviceList ) if ( serviceList )
{ {
for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ ) for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
{ {
IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j ); IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" ); const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
if ( !serviceType ) continue; if ( !serviceType ) continue;
if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue; if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" ); const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
if ( !eventSubURL ) continue; if ( !eventSubURL ) continue;
const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" ); const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
if ( !controlURL ) continue; if ( !controlURL ) continue;
// Try to subscribe to ContentDirectory service // Try to subscribe to ContentDirectory service
char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 ); char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
if ( url ) if ( url )
{ {
char* s1 = strdup( baseURL ); char* s1 = strdup( baseURL );
char* s2 = strdup( eventSubURL ); char* s2 = strdup( eventSubURL );
if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS ) if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
{ {
// msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url ); // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
server->setContentDirectoryEventURL( url ); server->setContentDirectoryEventURL( url );
server->subscribeToContentDirectory(); server->subscribeToContentDirectory();
} }
free( s1 ); free( s1 );
free( s2 ); free( s2 );
free( url ); free( url );
} }
// Try to browse content directory... // Try to browse content directory...
url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 ); url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
if ( url ) if ( url )
{ {
char* s1 = strdup( baseURL ); char* s1 = strdup( baseURL );
char* s2 = strdup( controlURL ); char* s2 = strdup( controlURL );
if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS ) if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
{ {
// msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url ); // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
server->setContentDirectoryControlURL( url ); server->setContentDirectoryControlURL( url );
server->fetchContents(); server->fetchContents();
} }
free( s1 ); free( s1 );
free( s2 ); free( s2 );
free( url ); free( url );
} }
} }
ixmlNodeList_free( serviceList ); ixmlNodeList_free( serviceList );
} }
} }
ixmlNodeList_free( deviceList ); ixmlNodeList_free( deviceList );
} }
} }
MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie ) MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
{ {
_cookie = cookie; _cookie = cookie;
...@@ -681,14 +624,14 @@ MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* coo ...@@ -681,14 +624,14 @@ MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* coo
_playlistNode = 0; _playlistNode = 0;
} }
MediaServer::~MediaServer() MediaServer::~MediaServer()
{ {
if ( _contents ) if ( _contents )
{ {
playlist_NodeDelete( _cookie->serviceDiscovery->p_sys->p_playlist, playlist_NodeDelete( _cookie->serviceDiscovery->p_sys->p_playlist,
_playlistNode, _playlistNode,
true, true,
true ); true );
} }
delete _contents; delete _contents;
...@@ -706,7 +649,7 @@ const char* MediaServer::getFriendlyName() const ...@@ -706,7 +649,7 @@ const char* MediaServer::getFriendlyName() const
return s; return s;
} }
void MediaServer::setContentDirectoryEventURL( const char* url ) void MediaServer::setContentDirectoryEventURL( const char* url )
{ {
_contentDirectoryEventURL = url; _contentDirectoryEventURL = url;
} }
...@@ -717,7 +660,7 @@ const char* MediaServer::getContentDirectoryEventURL() const ...@@ -717,7 +660,7 @@ const char* MediaServer::getContentDirectoryEventURL() const
return s; return s;
} }
void MediaServer::setContentDirectoryControlURL( const char* url ) void MediaServer::setContentDirectoryControlURL( const char* url )
{ {
_contentDirectoryControlURL = url; _contentDirectoryControlURL = url;
} }
...@@ -727,33 +670,33 @@ const char* MediaServer::getContentDirectoryControlURL() const ...@@ -727,33 +670,33 @@ const char* MediaServer::getContentDirectoryControlURL() const
return _contentDirectoryControlURL.c_str(); return _contentDirectoryControlURL.c_str();
} }
void MediaServer::subscribeToContentDirectory() void MediaServer::subscribeToContentDirectory()
{ {
const char* url = getContentDirectoryEventURL(); const char* url = getContentDirectoryEventURL();
if ( !url || strcmp( url, "" ) == 0 ) if ( !url || strcmp( url, "" ) == 0 )
{ {
msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" ); msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
return; return;
} }
int timeOut = 1810; int timeOut = 1810;
Upnp_SID sid; Upnp_SID sid;
int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid ); int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
if ( res == UPNP_E_SUCCESS ) if ( res == UPNP_E_SUCCESS )
{ {
_subscriptionTimeOut = timeOut; _subscriptionTimeOut = timeOut;
memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) ); memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
} }
else else
{ {
msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) ); msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) );
} }
} }
IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter, IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter,
const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria ) const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria )
{ {
IXML_Document* action = 0; IXML_Document* action = 0;
IXML_Document* response = 0; IXML_Document* response = 0;
...@@ -789,18 +732,18 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pB ...@@ -789,18 +732,18 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pB
res = UpnpAddToAction( &action, "Browse", serviceType, "SortCriteria", SortCriteria ); res = UpnpAddToAction( &action, "Browse", serviceType, "SortCriteria", SortCriteria );
if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; } if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
res = UpnpSendAction( _cookie->clientHandle, res = UpnpSendAction( _cookie->clientHandle,
url, url,
CONTENT_DIRECTORY_SERVICE_TYPE, CONTENT_DIRECTORY_SERVICE_TYPE,
0, 0,
action, action,
&response ); &response );
if ( res != UPNP_E_SUCCESS ) if ( res != UPNP_E_SUCCESS )
{ {
msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
ixmlDocument_free( response ); ixmlDocument_free( response );
response = 0; response = 0;
} }
browseActionCleanup: browseActionCleanup:
...@@ -818,22 +761,22 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pB ...@@ -818,22 +761,22 @@ IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pB
return response; return response;
} }
void MediaServer::fetchContents() void MediaServer::fetchContents()
{ {
Container* root = new Container( 0, "0", getFriendlyName() ); Container* root = new Container( 0, "0", getFriendlyName() );
_fetchContents( root ); _fetchContents( root );
if ( _contents ) if ( _contents )
{ {
vlc_mutex_lock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock ); vlc_mutex_lock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
playlist_NodeEmpty( _cookie->serviceDiscovery->p_sys->p_playlist, playlist_NodeEmpty( _cookie->serviceDiscovery->p_sys->p_playlist,
_playlistNode, _playlistNode,
true ); true );
vlc_mutex_unlock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock ); vlc_mutex_unlock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
delete _contents; delete _contents;
} }
_contents = root; _contents = root;
...@@ -842,7 +785,7 @@ void MediaServer::fetchContents() ...@@ -842,7 +785,7 @@ void MediaServer::fetchContents()
_buildPlaylist( _contents ); _buildPlaylist( _contents );
} }
bool MediaServer::_fetchContents( Container* parent ) bool MediaServer::_fetchContents( Container* parent )
{ {
if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; } if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; }
...@@ -854,111 +797,111 @@ bool MediaServer::_fetchContents( Container* parent ) ...@@ -854,111 +797,111 @@ bool MediaServer::_fetchContents( Container* parent )
if ( !result ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; } if ( !result ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" ); IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" );
if ( containerNodeList ) if ( containerNodeList )
{ {
for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ ) for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
{ {
IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i ); IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
const char* objectID = ixmlElement_getAttribute( containerElement, "id" ); const char* objectID = ixmlElement_getAttribute( containerElement, "id" );
if ( !objectID ) continue; if ( !objectID ) continue;
const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" ); const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
if ( !childCountStr ) continue; if ( !childCountStr ) continue;
int childCount = atoi( childCountStr ); int childCount = atoi( childCountStr );
const char* title = xml_getChildElementValue( containerElement, "dc:title" ); const char* title = xml_getChildElementValue( containerElement, "dc:title" );
if ( !title ) continue; if ( !title ) continue;
const char* resource = xml_getChildElementValue( containerElement, "res" ); const char* resource = xml_getChildElementValue( containerElement, "res" );
if ( resource && childCount < 1 ) if ( resource && childCount < 1 )
{ {
Item* item = new Item( parent, objectID, title, resource ); Item* item = new Item( parent, objectID, title, resource );
parent->addItem( item ); parent->addItem( item );
} }
else else
{ {
Container* container = new Container( parent, objectID, title ); Container* container = new Container( parent, objectID, title );
parent->addContainer( container ); parent->addContainer( container );
if ( childCount > 0 ) _fetchContents( container ); if ( childCount > 0 ) _fetchContents( container );
} }
} }
ixmlNodeList_free( containerNodeList ); ixmlNodeList_free( containerNodeList );
} }
IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result, "item" ); IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result, "item" );
if ( itemNodeList ) if ( itemNodeList )
{
for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
{ {
for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ ) IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
{
IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i ); const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
if ( !objectID ) continue;
const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
if ( !objectID ) continue; const char* title = xml_getChildElementValue( itemElement, "dc:title" );
if ( !title ) continue;
const char* title = xml_getChildElementValue( itemElement, "dc:title" );
if ( !title ) continue; const char* resource = xml_getChildElementValue( itemElement, "res" );
if ( !resource ) continue;
const char* resource = xml_getChildElementValue( itemElement, "res" );
if ( !resource ) continue; Item* item = new Item( parent, objectID, title, resource );
parent->addItem( item );
Item* item = new Item( parent, objectID, title, resource ); }
parent->addItem( item );
} ixmlNodeList_free( itemNodeList );
ixmlNodeList_free( itemNodeList );
} }
ixmlDocument_free( result ); ixmlDocument_free( result );
return true; return true;
} }
void MediaServer::_buildPlaylist( Container* parent ) void MediaServer::_buildPlaylist( Container* parent )
{ {
for ( unsigned int i = 0; i < parent->getNumContainers(); i++ ) for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
{ {
Container* container = parent->getContainer( i ); Container* container = parent->getContainer( i );
playlist_item_t* parentNode = parent->getPlaylistNode(); playlist_item_t* parentNode = parent->getPlaylistNode();
char* title = strdup( container->getTitle() ); char* title = strdup( container->getTitle() );
playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist, playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist,
VIEW_CATEGORY, VIEW_CATEGORY,
title, title,
parentNode ); parentNode );
free( title ); free( title );
container->setPlaylistNode( node ); container->setPlaylistNode( node );
_buildPlaylist( container ); _buildPlaylist( container );
} }
for ( unsigned int i = 0; i < parent->getNumItems(); i++ ) for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
{ {
Item* item = parent->getItem( i ); Item* item = parent->getItem( i );
playlist_item_t* parentNode = parent->getPlaylistNode(); playlist_item_t* parentNode = parent->getPlaylistNode();
playlist_item_t* node = playlist_ItemNew( _cookie->serviceDiscovery, playlist_item_t* node = playlist_ItemNew( _cookie->serviceDiscovery,
item->getResource(), item->getResource(),
item->getTitle() ); item->getTitle() );
playlist_NodeAddItem( _cookie->serviceDiscovery->p_sys->p_playlist, playlist_NodeAddItem( _cookie->serviceDiscovery->p_sys->p_playlist,
node, node,
VIEW_CATEGORY, VIEW_CATEGORY,
parentNode, PLAYLIST_APPEND, PLAYLIST_END ); parentNode, PLAYLIST_APPEND, PLAYLIST_END );
item->setPlaylistNode( node ); item->setPlaylistNode( node );
} }
} }
void MediaServer::setPlaylistNode( playlist_item_t* playlistNode ) void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
{ {
_playlistNode = playlistNode; _playlistNode = playlistNode;
} }
bool MediaServer::compareSID( const char* sid ) bool MediaServer::compareSID( const char* sid )
{ {
return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 ); return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
} }
...@@ -966,20 +909,20 @@ bool MediaServer::compareSID( const char* sid ) ...@@ -966,20 +909,20 @@ bool MediaServer::compareSID( const char* sid )
// MediaServerList... // MediaServerList...
MediaServerList::MediaServerList( Cookie* cookie ) MediaServerList::MediaServerList( Cookie* cookie )
{ {
_cookie = cookie; _cookie = cookie;
} }
MediaServerList::~MediaServerList() MediaServerList::~MediaServerList()
{ {
for ( unsigned int i = 0; i < _list.size(); i++ ) for ( unsigned int i = 0; i < _list.size(); i++ )
{ {
delete _list[i]; delete _list[i];
} }
} }
bool MediaServerList::addServer( MediaServer* s ) bool MediaServerList::addServer( MediaServer* s )
{ {
if ( getServer( s->getUDN() ) != 0 ) return false; if ( getServer( s->getUDN() ) != 0 ) return false;
...@@ -989,73 +932,73 @@ bool MediaServerList::addServer( MediaServer* s ) ...@@ -989,73 +932,73 @@ bool MediaServerList::addServer( MediaServer* s )
char* name = strdup( s->getFriendlyName() ); char* name = strdup( s->getFriendlyName() );
playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist, playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist,
VIEW_CATEGORY, VIEW_CATEGORY,
name, name,
_cookie->serviceDiscovery->p_sys->p_node ); _cookie->serviceDiscovery->p_sys->p_node );
free( name ); free( name );
s->setPlaylistNode( node ); s->setPlaylistNode( node );
return true; return true;
} }
MediaServer* MediaServerList::getServer( const char* UDN ) MediaServer* MediaServerList::getServer( const char* UDN )
{ {
MediaServer* result = 0; MediaServer* result = 0;
for ( unsigned int i = 0; i < _list.size(); i++ ) for ( unsigned int i = 0; i < _list.size(); i++ )
{
if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
{ {
if( strcmp( UDN, _list[i]->getUDN() ) == 0 ) result = _list[i];
{ break;
result = _list[i]; }
break;
}
} }
return result; return result;
} }
MediaServer* MediaServerList::getServerBySID( const char* sid ) MediaServer* MediaServerList::getServerBySID( const char* sid )
{ {
MediaServer* server = 0; MediaServer* server = 0;
for ( unsigned int i = 0; i < _list.size(); i++ ) for ( unsigned int i = 0; i < _list.size(); i++ )
{
if ( _list[i]->compareSID( sid ) )
{ {
if ( _list[i]->compareSID( sid ) ) server = _list[i];
{ break;
server = _list[i];
break;
}
} }
}
return server; return server;
} }
void MediaServerList::removeServer( const char* UDN ) void MediaServerList::removeServer( const char* UDN )
{ {
MediaServer* server = getServer( UDN ); MediaServer* server = getServer( UDN );
if ( !server ) return; if ( !server ) return;
msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() ); msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
std::vector<MediaServer*>::iterator it; std::vector<MediaServer*>::iterator it;
for ( it = _list.begin(); it != _list.end(); it++ ) for ( it = _list.begin(); it != _list.end(); it++ )
{ {
if ( *it == server ) if ( *it == server )
{ {
_list.erase( it ); _list.erase( it );
delete server; delete server;
break; break;
} }
} }
} }
// Item... // Item...
Item::Item( Container* parent, const char* objectID, const char* title, const char* resource ) Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
{ {
_parent = parent; _parent = parent;
_objectID = objectID; _objectID = objectID;
_title = title; _title = title;
_resource = resource; _resource = resource;
...@@ -1063,105 +1006,105 @@ Item::Item( Container* parent, const char* objectID, const char* title, const ch ...@@ -1063,105 +1006,105 @@ Item::Item( Container* parent, const char* objectID, const char* title, const ch
_playlistNode = 0; _playlistNode = 0;
} }
const char* Item::getObjectID() const const char* Item::getObjectID() const
{ {
return _objectID.c_str(); return _objectID.c_str();
} }
const char* Item::getTitle() const const char* Item::getTitle() const
{ {
return _title.c_str(); return _title.c_str();
} }
const char* Item::getResource() const const char* Item::getResource() const
{ {
return _resource.c_str(); return _resource.c_str();
} }
void Item::setPlaylistNode( playlist_item_t* node ) void Item::setPlaylistNode( playlist_item_t* node )
{ {
_playlistNode = node; _playlistNode = node;
} }
playlist_item_t* Item::getPlaylistNode() const playlist_item_t* Item::getPlaylistNode() const
{ {
return _playlistNode; return _playlistNode;
} }
// Container... // Container...
Container::Container( Container* parent, const char* objectID, const char* title ) Container::Container( Container* parent, const char* objectID, const char* title )
{ {
_parent = parent; _parent = parent;
_objectID = objectID; _objectID = objectID;
_title = title; _title = title;
_playlistNode = 0; _playlistNode = 0;
} }
Container::~Container() Container::~Container()
{ {
for ( unsigned int i = 0; i < _containers.size(); i++ ) for ( unsigned int i = 0; i < _containers.size(); i++ )
{ {
delete _containers[i]; delete _containers[i];
} }
for ( unsigned int i = 0; i < _items.size(); i++ ) for ( unsigned int i = 0; i < _items.size(); i++ )
{ {
delete _items[i]; delete _items[i];
} }
} }
void Container::addItem( Item* item ) void Container::addItem( Item* item )
{ {
_items.push_back( item ); _items.push_back( item );
} }
void Container::addContainer( Container* container ) void Container::addContainer( Container* container )
{ {
_containers.push_back( container ); _containers.push_back( container );
} }
const char* Container::getObjectID() const const char* Container::getObjectID() const
{ {
return _objectID.c_str(); return _objectID.c_str();
} }
const char* Container::getTitle() const const char* Container::getTitle() const
{ {
return _title.c_str(); return _title.c_str();
} }
unsigned int Container::getNumItems() const unsigned int Container::getNumItems() const
{ {
return _items.size(); return _items.size();
} }
unsigned int Container::getNumContainers() const unsigned int Container::getNumContainers() const
{ {
return _containers.size(); return _containers.size();
} }
Item* Container::getItem( unsigned int i ) const Item* Container::getItem( unsigned int i ) const
{ {
if ( i < _items.size() ) return _items[i]; if ( i < _items.size() ) return _items[i];
return 0; return 0;
} }
Container* Container::getContainer( unsigned int i ) const Container* Container::getContainer( unsigned int i ) const
{ {
if ( i < _containers.size() ) return _containers[i]; if ( i < _containers.size() ) return _containers[i];
return 0; return 0;
} }
void Container::setPlaylistNode( playlist_item_t* node ) void Container::setPlaylistNode( playlist_item_t* node )
{ {
_playlistNode = node; _playlistNode = node;
} }
playlist_item_t* Container::getPlaylistNode() const playlist_item_t* Container::getPlaylistNode() const
{ {
return _playlistNode; return _playlistNode;
} }
...@@ -89,6 +89,7 @@ HEADERS_include = \ ...@@ -89,6 +89,7 @@ HEADERS_include = \
../include/vlc_playlist.h \ ../include/vlc_playlist.h \
../include/vlc_spu.h \ ../include/vlc_spu.h \
../include/vlc_stream.h \ ../include/vlc_stream.h \
../include/vlc_strings.h \
../include/vlc_symbols.h \ ../include/vlc_symbols.h \
../include/vlc_threads_funcs.h \ ../include/vlc_threads_funcs.h \
../include/vlc_threads.h \ ../include/vlc_threads.h \
...@@ -321,6 +322,7 @@ SOURCES_libvlc_common = \ ...@@ -321,6 +322,7 @@ SOURCES_libvlc_common = \
misc/modules.c \ misc/modules.c \
misc/threads.c \ misc/threads.c \
misc/stats.c \ misc/stats.c \
misc/strings.c \
misc/unicode.c \ misc/unicode.c \
misc/cpu.c \ misc/cpu.c \
misc/configuration.c \ misc/configuration.c \
......
...@@ -110,6 +110,7 @@ ...@@ -110,6 +110,7 @@
#include "vlc_osd.h" #include "vlc_osd.h"
#include "vlc_update.h" #include "vlc_update.h"
#include "vlc_strings.h"
#if defined( _MSC_VER ) && defined( UNDER_CE ) #if defined( _MSC_VER ) && defined( UNDER_CE )
# include "modules_builtin_evc.h" # include "modules_builtin_evc.h"
......
/*****************************************************************************
* strings.c: String related functions
*****************************************************************************
* Copyright (C) 2006 the VideoLAN team
* $Id$
*
* Authors: Antoine Cellerier <dionoea at videolan dot org>
* Daniel Stranger <vlc at schmaller dot de>
*
* 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
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "vlc_strings.h"
/**
* Decode URI encoded string
* \return decoded duplicated string
*/
char *decode_encoded_URI_duplicate( const char *psz )
{
char *psz_dup = strdup( psz );
decode_encoded_URI( psz_dup );
return psz_dup;
}
/**
* Decode URI encoded string
* \return nothing
*/
void decode_encoded_URI( char *psz )
{
char *dup = strdup( psz );
char *p = dup;
while( *p )
{
if( *p == '%' )
{
char val[3];
p++;
if( !*p )
{
break;
}
val[0] = *p++;
val[1] = *p++;
val[2] = '\0';
*psz++ = strtol( val, NULL, 16 );
}
else if( *p == '+' )
{
*psz++ = ' ';
p++;
}
else
{
*psz++ = *p++;
}
}
*psz++ = '\0';
free( dup );
}
/**
* Converts "&lt;", "&gt;" and "&amp;" to "<", ">" and "&"
* \param string to convert
*/
void resolve_xml_special_chars( char *psz_value )
{
char *p_pos = psz_value;
while ( *psz_value )
{
if( !strncmp( psz_value, "&lt;", 4 ) )
{
*p_pos = '<';
psz_value += 4;
}
else if( !strncmp( psz_value, "&gt;", 4 ) )
{
*p_pos = '>';
psz_value += 4;
}
else if( !strncmp( psz_value, "&amp;", 5 ) )
{
*p_pos = '&';
psz_value += 5;
}
else if( !strncmp( psz_value, "&quot;", 6 ) )
{
*p_pos = '\"';
psz_value += 6;
}
else if( !strncmp( psz_value, "&#039;", 6 ) )
{
*p_pos = '\'';
psz_value += 6;
}
else
{
*p_pos = *psz_value;
psz_value++;
}
p_pos++;
}
*p_pos = '\0';
}
/**
* Converts '<', '>', '\"', '\'' and '&' to their html entities
* \param psz_content simple element content that is to be converted
*/
char *convert_xml_special_chars( const char *psz_content )
{
char *psz_temp = malloc( 6 * strlen( psz_content ) + 1 );
const char *p_from = psz_content;
char *p_to = psz_temp;
while ( *p_from )
{
if ( *p_from == '<' )
{
strcpy( p_to, "&lt;" );
p_to += 4;
}
else if ( *p_from == '>' )
{
strcpy( p_to, "&gt;" );
p_to += 4;
}
else if ( *p_from == '&' )
{
strcpy( p_to, "&amp;" );
p_to += 5;
}
else if( *p_from == '\"' )
{
strcpy( p_to, "&quot;" );
p_to += 6;
}
else if( *p_from == '\'' )
{
strcpy( p_to, "&#039;" );
p_to += 6;
}
else
{
*p_to = *p_from;
p_to++;
}
p_from++;
}
*p_to = '\0';
return psz_temp;
}
...@@ -127,6 +127,9 @@ int playlist_Export( playlist_t * p_playlist, const char *psz_filename , ...@@ -127,6 +127,9 @@ int playlist_Export( playlist_t * p_playlist, const char *psz_filename ,
msg_Err( p_playlist, "out of memory"); msg_Err( p_playlist, "out of memory");
return VLC_ENOMEM; return VLC_ENOMEM;
} }
p_export->psz_filename = NULL;
if ( psz_filename )
p_export->psz_filename = strdup( psz_filename );
p_export->p_file = utf8_fopen( psz_filename, "wt" ); p_export->p_file = utf8_fopen( psz_filename, "wt" );
if( !p_export->p_file ) if( !p_export->p_file )
{ {
...@@ -149,8 +152,12 @@ int playlist_Export( playlist_t * p_playlist, const char *psz_filename , ...@@ -149,8 +152,12 @@ int playlist_Export( playlist_t * p_playlist, const char *psz_filename ,
} }
module_Unneed( p_playlist , p_module ); module_Unneed( p_playlist , p_module );
/* Clean up */
fclose( p_export->p_file ); fclose( p_export->p_file );
if ( p_export->psz_filename )
free( p_export->psz_filename );
free ( p_export );
p_playlist->p_private = NULL;
vlc_mutex_unlock( &p_playlist->object_lock ); vlc_mutex_unlock( &p_playlist->object_lock );
return VLC_SUCCESS; return VLC_SUCCESS;
......
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