xspf.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:32k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*******************************************************************************
  2.  * xspf.c : XSPF playlist import functions
  3.  *******************************************************************************
  4.  * Copyright (C) 2006 the VideoLAN team
  5.  * $Id: 007b966928a8924d9647b492dab59ce92c0d8704 $
  6.  *
  7.  * Authors: Daniel Stränger <vlc at schmaller dot de>
  8.  *          Yoann Peronneau <yoann@videolan.org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  ******************************************************************************/
  24. /**
  25.  * file modules/demux/playlist/xspf.c
  26.  * brief XSPF playlist import functions
  27.  */
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <vlc_demux.h>
  33. #include <vlc_xml.h>
  34. #include <vlc_strings.h>
  35. #include <vlc_url.h>
  36. #include "xspf.h"
  37. #include "playlist.h"
  38. struct demux_sys_t
  39. {
  40.     input_item_t **pp_tracklist;
  41.     int i_tracklist_entries;
  42.     int i_track_id;
  43.     char * psz_base;
  44. };
  45. static int Control( demux_t *, int, va_list );
  46. static int Demux( demux_t * );
  47. /**
  48.  * brief XSPF submodule initialization function
  49.  */
  50. int Import_xspf( vlc_object_t *p_this )
  51. {
  52.     DEMUX_BY_EXTENSION_OR_FORCED_MSG( ".xspf", "xspf-open",
  53.                                       "using XSPF playlist reader" );
  54.     return VLC_SUCCESS;
  55. }
  56. void Close_xspf( vlc_object_t *p_this )
  57. {
  58.     demux_t *p_demux = (demux_t *)p_this;
  59.     int i;
  60.     for(i = 0; i < p_demux->p_sys->i_tracklist_entries; i++)
  61.     {
  62.         if(p_demux->p_sys->pp_tracklist[i])
  63.             vlc_gc_decref( p_demux->p_sys->pp_tracklist[i] );
  64.     }
  65.     free( p_demux->p_sys->pp_tracklist );
  66.     free( p_demux->p_sys->psz_base );
  67.     free( p_demux->p_sys );
  68. }
  69. /**
  70.  * brief demuxer function for XSPF parsing
  71.  */
  72. int Demux( demux_t *p_demux )
  73. {
  74.     int i_ret = 1;
  75.     xml_t *p_xml = NULL;
  76.     xml_reader_t *p_xml_reader = NULL;
  77.     char *psz_name = NULL;
  78.     INIT_PLAYLIST_STUFF;
  79.     p_demux->p_sys->pp_tracklist = NULL;
  80.     p_demux->p_sys->i_tracklist_entries = 0;
  81.     p_demux->p_sys->i_track_id = -1;
  82.     p_demux->p_sys->psz_base = NULL;
  83.     /* create new xml parser from stream */
  84.     p_xml = xml_Create( p_demux );
  85.     if( !p_xml )
  86.         i_ret = -1;
  87.     else
  88.     {
  89.         p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
  90.         if( !p_xml_reader )
  91.             i_ret = -1;
  92.     }
  93.     /* locating the root node */
  94.     if( i_ret == 1 )
  95.     {
  96.         do
  97.         {
  98.             if( xml_ReaderRead( p_xml_reader ) != 1 )
  99.             {
  100.                 msg_Err( p_demux, "can't read xml stream" );
  101.                 i_ret = -1;
  102.             }
  103.         } while( i_ret == VLC_SUCCESS &&
  104.                  xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM );
  105.     }
  106.     /* checking root node name */
  107.     if( i_ret == 1 )
  108.     {
  109.         psz_name = xml_ReaderName( p_xml_reader );
  110.         if( !psz_name || strcmp( psz_name, "playlist" ) )
  111.         {
  112.             msg_Err( p_demux, "invalid root node name: %s", psz_name );
  113.             i_ret = -1;
  114.         }
  115.         FREE_NAME();
  116.     }
  117.     if( i_ret == 1 )
  118.         i_ret = parse_playlist_node( p_demux, p_current_input,
  119.                                      p_xml_reader, "playlist" ) ? 0 : -1;
  120.     int i;
  121.     for( i = 0 ; i < p_demux->p_sys->i_tracklist_entries ; i++ )
  122.     {
  123.         input_item_t *p_new_input = p_demux->p_sys->pp_tracklist[i];
  124.         if( p_new_input )
  125.         {
  126.             input_item_AddSubItem( p_current_input, p_new_input );
  127.         }
  128.     }
  129.     HANDLE_PLAY_AND_RELEASE;
  130.     if( p_xml_reader )
  131.         xml_ReaderDelete( p_xml, p_xml_reader );
  132.     if( p_xml )
  133.         xml_Delete( p_xml );
  134.     return i_ret; /* Needed for correct operation of go back */
  135. }
  136. /** brief dummy function for demux callback interface */
  137. static int Control( demux_t *p_demux, int i_query, va_list args )
  138. {
  139.     VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
  140.     return VLC_EGENERIC;
  141. }
  142. /**
  143.  * brief parse the root node of a XSPF playlist
  144.  * param p_demux demuxer instance
  145.  * param p_input_item current input item
  146.  * param p_xml_reader xml reader instance
  147.  * param psz_element name of element to parse
  148.  */
  149. static bool parse_playlist_node COMPLEX_INTERFACE
  150. {
  151.     char *psz_name = NULL;
  152.     char *psz_value = NULL;
  153.     bool b_version_found = false;
  154.     int i_node;
  155.     xml_elem_hnd_t *p_handler = NULL;
  156.     xml_elem_hnd_t pl_elements[] =
  157.         { {"title",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
  158.           {"creator",      SIMPLE_CONTENT,  {.smpl = set_item_info} },
  159.           {"annotation",   SIMPLE_CONTENT,  {.smpl = set_item_info} },
  160.           {"info",         SIMPLE_CONTENT,  {NULL} },
  161.           {"location",     SIMPLE_CONTENT,  {NULL} },
  162.           {"identifier",   SIMPLE_CONTENT,  {NULL} },
  163.           {"image",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
  164.           {"date",         SIMPLE_CONTENT,  {NULL} },
  165.           {"license",      SIMPLE_CONTENT,  {NULL} },
  166.           {"attribution",  COMPLEX_CONTENT, {.cmplx = skip_element} },
  167.           {"link",         SIMPLE_CONTENT,  {NULL} },
  168.           {"meta",         SIMPLE_CONTENT,  {NULL} },
  169.           {"extension",    COMPLEX_CONTENT, {.cmplx = parse_extension_node} },
  170.           {"trackList",    COMPLEX_CONTENT, {.cmplx = parse_tracklist_node} },
  171.           {NULL,           UNKNOWN_CONTENT, {NULL} }
  172.         };
  173.     /* read all playlist attributes */
  174.     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  175.     {
  176.         psz_name = xml_ReaderName( p_xml_reader );
  177.         psz_value = xml_ReaderValue( p_xml_reader );
  178.         if( !psz_name || !psz_value )
  179.         {
  180.             msg_Err( p_demux, "invalid xml stream @ <playlist>" );
  181.             FREE_ATT();
  182.             return false;
  183.         }
  184.         /* attribute: version */
  185.         if( !strcmp( psz_name, "version" ) )
  186.         {
  187.             b_version_found = true;
  188.             if( strcmp( psz_value, "0" ) && strcmp( psz_value, "1" ) )
  189.                 msg_Warn( p_demux, "unsupported XSPF version" );
  190.         }
  191.         /* attribute: xmlns */
  192.         else if( !strcmp( psz_name, "xmlns" ) || !strcmp( psz_name, "xmlns:vlc" ) )
  193.             ;
  194.         else if( !strcmp( psz_name, "xml:base" ) )
  195.         {
  196.             p_demux->p_sys->psz_base = strdup( psz_value );
  197.         }
  198.         /* unknown attribute */
  199.         else
  200.             msg_Warn( p_demux, "invalid <playlist> attribute:"%s"", psz_name);
  201.         FREE_ATT();
  202.     }
  203.     /* attribute version is mandatory !!! */
  204.     if( !b_version_found )
  205.         msg_Warn( p_demux, "<playlist> requires "version" attribute" );
  206.     /* parse the child elements - we only take care of <trackList> */
  207.     while( xml_ReaderRead( p_xml_reader ) == 1 )
  208.     {
  209.         i_node = xml_ReaderNodeType( p_xml_reader );
  210.         switch( i_node )
  211.         {
  212.             case XML_READER_NONE:
  213.                 break;
  214.             case XML_READER_STARTELEM:
  215.                 /*  element start tag  */
  216.                 psz_name = xml_ReaderName( p_xml_reader );
  217.                 if( !psz_name || !*psz_name )
  218.                 {
  219.                     msg_Err( p_demux, "invalid xml stream" );
  220.                     FREE_ATT();
  221.                     return false;
  222.                 }
  223.                 /* choose handler */
  224.                 for( p_handler = pl_elements;
  225.                      p_handler->name && strcmp( psz_name, p_handler->name );
  226.                      p_handler++ );
  227.                 if( !p_handler->name )
  228.                 {
  229.                     msg_Err( p_demux, "unexpected element <%s>", psz_name );
  230.                     FREE_ATT();
  231.                     return false;
  232.                 }
  233.                 FREE_NAME();
  234.                 /* complex content is parsed in a separate function */
  235.                 if( p_handler->type == COMPLEX_CONTENT )
  236.                 {
  237.                     if( p_handler->pf_handler.cmplx( p_demux,
  238.                                                      p_input_item,
  239.                                                      p_xml_reader,
  240.                                                      p_handler->name ) )
  241.                     {
  242.                         p_handler = NULL;
  243.                         FREE_ATT();
  244.                     }
  245.                     else
  246.                     {
  247.                         FREE_ATT();
  248.                         return false;
  249.                     }
  250.                 }
  251.                 break;
  252.             case XML_READER_TEXT:
  253.                 /* simple element content */
  254.                 FREE_ATT();
  255.                 psz_value = xml_ReaderValue( p_xml_reader );
  256.                 if( !psz_value )
  257.                 {
  258.                     msg_Err( p_demux, "invalid xml stream" );
  259.                     FREE_ATT();
  260.                     return false;
  261.                 }
  262.                 break;
  263.             case XML_READER_ENDELEM:
  264.                 /* element end tag */
  265.                 psz_name = xml_ReaderName( p_xml_reader );
  266.                 if( !psz_name )
  267.                 {
  268.                     msg_Err( p_demux, "invalid xml stream" );
  269.                     FREE_ATT();
  270.                     return false;
  271.                 }
  272.                 /* leave if the current parent node <playlist> is terminated */
  273.                 if( !strcmp( psz_name, psz_element ) )
  274.                 {
  275.                     FREE_ATT();
  276.                     return true;
  277.                 }
  278.                 /* there MUST have been a start tag for that element name */
  279.                 if( !p_handler || !p_handler->name
  280.                     || strcmp( p_handler->name, psz_name ))
  281.                 {
  282.                     msg_Err( p_demux, "there's no open element left for <%s>",
  283.                              psz_name );
  284.                     FREE_ATT();
  285.                     return false;
  286.                 }
  287.                 if( p_handler->pf_handler.smpl )
  288.                 {
  289.                     p_handler->pf_handler.smpl( p_input_item, p_handler->name,
  290.                                                 psz_value );
  291.                 }
  292.                 FREE_ATT();
  293.                 p_handler = NULL;
  294.                 break;
  295.             default:
  296.                 /* unknown/unexpected xml node */
  297.                 msg_Err( p_demux, "unexpected xml node %i", i_node );
  298.                 FREE_ATT();
  299.                 return false;
  300.         }
  301.         FREE_NAME();
  302.     }
  303.     return false;
  304. }
  305. /**
  306.  * brief parses the tracklist node which only may contain <track>s
  307.  */
  308. static bool parse_tracklist_node COMPLEX_INTERFACE
  309. {
  310.     VLC_UNUSED(psz_element);
  311.     char *psz_name = NULL;
  312.     int i_node;
  313.     int i_ntracks = 0;
  314.     /* now parse the <track>s */
  315.     while( xml_ReaderRead( p_xml_reader ) == 1 )
  316.     {
  317.         i_node = xml_ReaderNodeType( p_xml_reader );
  318.         if( i_node == XML_READER_STARTELEM )
  319.         {
  320.             psz_name = xml_ReaderName( p_xml_reader );
  321.             if( !psz_name )
  322.             {
  323.                 msg_Err( p_demux, "unexpected end of xml data" );
  324.                 FREE_NAME();
  325.                 return false;
  326.             }
  327.             if( strcmp( psz_name, "track") )
  328.             {
  329.                 msg_Err( p_demux, "unexpected child of <trackList>: <%s>",
  330.                          psz_name );
  331.                 FREE_NAME();
  332.                 return false;
  333.             }
  334.             FREE_NAME();
  335.             /* parse the track data in a separate function */
  336.             if( parse_track_node( p_demux, p_input_item,
  337.                                    p_xml_reader,"track" ) == true )
  338.                 i_ntracks++;
  339.         }
  340.         else if( i_node == XML_READER_ENDELEM )
  341.             break;
  342.     }
  343.     /* the <trackList> has to be terminated */
  344.     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_ENDELEM )
  345.     {
  346.         msg_Err( p_demux, "there's a missing </trackList>" );
  347.         FREE_NAME();
  348.         return false;
  349.     }
  350.     psz_name = xml_ReaderName( p_xml_reader );
  351.     if( !psz_name || strcmp( psz_name, "trackList" ) )
  352.     {
  353.         msg_Err( p_demux, "expected: </trackList>, found: </%s>", psz_name );
  354.         FREE_NAME();
  355.         return false;
  356.     }
  357.     FREE_NAME();
  358.     msg_Dbg( p_demux, "parsed %i tracks successfully", i_ntracks );
  359.     return true;
  360. }
  361. /**
  362.  * brief parse one track element
  363.  * param COMPLEX_INTERFACE
  364.  */
  365. static bool parse_track_node COMPLEX_INTERFACE
  366. {
  367.     int i_node;
  368.     char *psz_name = NULL;
  369.     char *psz_value = NULL;
  370.     xml_elem_hnd_t *p_handler = NULL;
  371.     xml_elem_hnd_t track_elements[] =
  372.         { {"location",     SIMPLE_CONTENT,  {NULL} },
  373.           {"identifier",   SIMPLE_CONTENT,  {NULL} },
  374.           {"title",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
  375.           {"creator",      SIMPLE_CONTENT,  {.smpl = set_item_info} },
  376.           {"annotation",   SIMPLE_CONTENT,  {.smpl = set_item_info} },
  377.           {"info",         SIMPLE_CONTENT,  {NULL} },
  378.           {"image",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
  379.           {"album",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
  380.           {"trackNum",     SIMPLE_CONTENT,  {.smpl = set_item_info} },
  381.           {"duration",     SIMPLE_CONTENT,  {.smpl = set_item_info} },
  382.           {"link",         SIMPLE_CONTENT,  {NULL} },
  383.           {"meta",         SIMPLE_CONTENT,  {NULL} },
  384.           {"extension",    COMPLEX_CONTENT, {.cmplx = parse_extension_node} },
  385.           {NULL,           UNKNOWN_CONTENT, {NULL} }
  386.         };
  387.     input_item_t *p_new_input = input_item_New( p_demux, NULL, NULL );
  388.     if( !p_new_input )
  389.     {
  390.         /* malloc has failed for input_item_New, so bailout early */
  391.         return false;
  392.     }
  393.     /* reset i_track_id */
  394.     p_demux->p_sys->i_track_id = -1;
  395.     while( xml_ReaderRead( p_xml_reader ) == 1 )
  396.     {
  397.         i_node = xml_ReaderNodeType( p_xml_reader );
  398.         switch( i_node )
  399.         {
  400.             case XML_READER_NONE:
  401.                 break;
  402.             case XML_READER_STARTELEM:
  403.                 /*  element start tag  */
  404.                 psz_name = xml_ReaderName( p_xml_reader );
  405.                 if( !psz_name || !*psz_name )
  406.                 {
  407.                     msg_Err( p_demux, "invalid xml stream" );
  408.                     FREE_ATT();
  409.                     return false;
  410.                 }
  411.                 /* choose handler */
  412.                 for( p_handler = track_elements;
  413.                      p_handler->name && strcmp( psz_name, p_handler->name );
  414.                      p_handler++ );
  415.                 if( !p_handler->name )
  416.                 {
  417.                     msg_Err( p_demux, "unexpected element <%s>", psz_name );
  418.                     FREE_ATT();
  419.                     return false;
  420.                 }
  421.                 FREE_NAME();
  422.                 /* complex content is parsed in a separate function */
  423.                 if( p_handler->type == COMPLEX_CONTENT )
  424.                 {
  425.                     if( !p_new_input )
  426.                     {
  427.                         msg_Err( p_demux,
  428.                                  "at <%s> level no new item has been allocated",
  429.                                  p_handler->name );
  430.                         FREE_ATT();
  431.                         return false;
  432.                     }
  433.                     if( p_handler->pf_handler.cmplx( p_demux,
  434.                                                      p_new_input,
  435.                                                      p_xml_reader,
  436.                                                      p_handler->name ) )
  437.                     {
  438.                         p_handler = NULL;
  439.                         FREE_ATT();
  440.                     }
  441.                     else
  442.                     {
  443.                         FREE_ATT();
  444.                         return false;
  445.                     }
  446.                 }
  447.                 break;
  448.             case XML_READER_TEXT:
  449.                 /* simple element content */
  450.                 FREE_ATT();
  451.                 psz_value = xml_ReaderValue( p_xml_reader );
  452.                 if( !psz_value )
  453.                 {
  454.                     msg_Err( p_demux, "invalid xml stream" );
  455.                     FREE_ATT();
  456.                     return false;
  457.                 }
  458.                 break;
  459.             case XML_READER_ENDELEM:
  460.                 /* element end tag */
  461.                 psz_name = xml_ReaderName( p_xml_reader );
  462.                 if( !psz_name )
  463.                 {
  464.                     msg_Err( p_demux, "invalid xml stream" );
  465.                     FREE_ATT();
  466.                     return false;
  467.                 }
  468.                 /* leave if the current parent node <track> is terminated */
  469.                 if( !strcmp( psz_name, psz_element ) )
  470.                 {
  471.                     FREE_ATT();
  472.                     /* Make sure we have a URI */
  473.                     char *psz_uri = input_item_GetURI( p_new_input );
  474.                     if( !psz_uri )
  475.                     {
  476.                         input_item_SetURI( p_new_input, "vlc://nop" );
  477.                     }
  478.                     free( psz_uri );
  479.                     if( p_demux->p_sys->i_track_id < 0 )
  480.                     {
  481.                         input_item_AddSubItem( p_input_item, p_new_input );
  482.                         vlc_gc_decref( p_new_input );
  483.                         return true;
  484.                     }
  485.                     if( p_demux->p_sys->i_track_id >=
  486.                            p_demux->p_sys->i_tracklist_entries )
  487.                     {
  488.                         input_item_t **pp;
  489.                         pp = realloc( p_demux->p_sys->pp_tracklist,
  490.                             (p_demux->p_sys->i_track_id + 1) * sizeof(*pp) );
  491.                         if( !pp )
  492.                             return false;
  493.                         p_demux->p_sys->pp_tracklist = pp;
  494.                         while( p_demux->p_sys->i_track_id >=
  495.                                p_demux->p_sys->i_tracklist_entries )
  496.                             pp[p_demux->p_sys->i_tracklist_entries++] = NULL;
  497.                     }
  498.                     p_demux->p_sys->pp_tracklist[
  499.                             p_demux->p_sys->i_track_id ] = p_new_input;
  500.                     return true;
  501.                 }
  502.                 /* there MUST have been a start tag for that element name */
  503.                 if( !p_handler || !p_handler->name
  504.                     || strcmp( p_handler->name, psz_name ))
  505.                 {
  506.                     msg_Err( p_demux, "there's no open element left for <%s>",
  507.                              psz_name );
  508.                     FREE_ATT();
  509.                     return false;
  510.                 }
  511.                 /* special case: location */
  512.                 if( !strcmp( p_handler->name, "location" ) )
  513.                 {
  514.                     /* FIXME: This is broken. Scheme-relative (//...) locations
  515.                      * and anchors (#...) are not resolved correctly. Also,
  516.                      * host-relative (/...) and directory-relative locations
  517.                      * ("relative path" in vernacular) should be resolved.
  518.                      * Last, psz_base should default to the XSPF resource
  519.                      * location if missing (not the current working directory).
  520.                      * -- Courmisch */
  521.                     if( p_demux->p_sys->psz_base && !strstr( psz_value, "://" ) )
  522.                     {
  523.                         char* psz_tmp;
  524.                         if( asprintf( &psz_tmp, "%s%s", p_demux->p_sys->psz_base,
  525.                                       psz_value ) == -1 )
  526.                         {
  527.                             FREE_ATT();
  528.                             return NULL;
  529.                         }
  530.                         input_item_SetURI( p_new_input, psz_tmp );
  531.                         free( psz_tmp );
  532.                     }
  533.                     else
  534.                         input_item_SetURI( p_new_input, psz_value );
  535.                     input_item_CopyOptions( p_input_item, p_new_input );
  536.                     FREE_ATT();
  537.                     p_handler = NULL;
  538.                 }
  539.                 else
  540.                 {
  541.                     /* there MUST be an item */
  542.                     if( !p_new_input )
  543.                     {
  544.                         msg_Err( p_demux, "item not yet created at <%s>",
  545.                                  psz_name );
  546.                         FREE_ATT();
  547.                         return false;
  548.                     }
  549.                     if( p_handler->pf_handler.smpl )
  550.                     {
  551.                         p_handler->pf_handler.smpl( p_new_input,
  552.                                                     p_handler->name,
  553.                                                     psz_value );
  554.                         FREE_ATT();
  555.                     }
  556.                 }
  557.                 FREE_ATT();
  558.                 p_handler = NULL;
  559.                 break;
  560.             default:
  561.                 /* unknown/unexpected xml node */
  562.                 msg_Err( p_demux, "unexpected xml node %i", i_node );
  563.                 FREE_ATT();
  564.                 return false;
  565.         }
  566.         FREE_NAME();
  567.     }
  568.     msg_Err( p_demux, "unexpected end of xml data" );
  569.     FREE_ATT();
  570.     return false;
  571. }
  572. /**
  573.  * brief handles the supported <track> sub-elements
  574.  */
  575. static bool set_item_info SIMPLE_INTERFACE
  576. {
  577.     /* exit if setting is impossible */
  578.     if( !psz_name || !psz_value || !p_input )
  579.         return false;
  580.     /* re-convert xml special characters inside psz_value */
  581.     resolve_xml_special_chars( psz_value );
  582.     /* handle each info element in a separate "if" clause */
  583.     if( !strcmp( psz_name, "title" ) )
  584.     {
  585.         input_item_SetTitle( p_input, psz_value );
  586.     }
  587.     else if( !strcmp( psz_name, "creator" ) )
  588.     {
  589.         input_item_SetArtist( p_input, psz_value );
  590.     }
  591.     else if( !strcmp( psz_name, "album" ) )
  592.     {
  593.         input_item_SetAlbum( p_input, psz_value );
  594.     }
  595.     else if( !strcmp( psz_name, "trackNum" ) )
  596.     {
  597.         input_item_SetTrackNum( p_input, psz_value );
  598.     }
  599.     else if( !strcmp( psz_name, "duration" ) )
  600.     {
  601.         long i_num = atol( psz_value );
  602.         input_item_SetDuration( p_input, (mtime_t) i_num*1000 );
  603.     }
  604.     else if( !strcmp( psz_name, "annotation" ) )
  605.     {
  606.         input_item_SetDescription( p_input, psz_value );
  607.     }
  608.     else if( !strcmp( psz_name, "image" ) )
  609.     {
  610.         input_item_SetArtURL( p_input, psz_value );
  611.     }
  612.     return true;
  613. }
  614. /**
  615.  * brief handles the <vlc:option> elements
  616.  */
  617. static bool set_option SIMPLE_INTERFACE
  618. {
  619.     /* exit if setting is impossible */
  620.     if( !psz_name || !psz_value || !p_input )
  621.         return false;
  622.     /* re-convert xml special characters inside psz_value */
  623.     resolve_xml_special_chars( psz_value );
  624.     input_item_AddOption( p_input, psz_value, 0 );
  625.     return true;
  626. }
  627. /**
  628.  * brief parse the extension node of a XSPF playlist
  629.  */
  630. static bool parse_extension_node COMPLEX_INTERFACE
  631. {
  632.     char *psz_name = NULL;
  633.     char *psz_value = NULL;
  634.     char *psz_title = NULL;
  635.     char *psz_application = NULL;
  636.     int i_node;
  637.     bool b_release_input_item = false;
  638.     xml_elem_hnd_t *p_handler = NULL;
  639.     input_item_t *p_new_input = NULL;
  640.     xml_elem_hnd_t pl_elements[] =
  641.         { {"vlc:node",   COMPLEX_CONTENT, {.cmplx = parse_extension_node} },
  642.           {"vlc:item",   COMPLEX_CONTENT, {.cmplx = parse_extitem_node} },
  643.           {"vlc:id",     SIMPLE_CONTENT, {NULL} },
  644.           {"vlc:option", SIMPLE_CONTENT, {.smpl = set_option} },
  645.           {NULL,    UNKNOWN_CONTENT, {NULL} }
  646.         };
  647.     /* read all extension node attributes */
  648.     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  649.     {
  650.         psz_name = xml_ReaderName( p_xml_reader );
  651.         psz_value = xml_ReaderValue( p_xml_reader );
  652.         if( !psz_name || !psz_value )
  653.         {
  654.             msg_Err( p_demux, "invalid xml stream @ <vlc:node>" );
  655.             FREE_ATT();
  656.             return false;
  657.         }
  658.         /* attribute: title */
  659.         if( !strcmp( psz_name, "title" ) )
  660.         {
  661.             resolve_xml_special_chars( psz_value );
  662.             psz_title = strdup( psz_value );
  663.         }
  664.         /* extension attribute: application */
  665.         else if( !strcmp( psz_name, "application" ) )
  666.         {
  667.             psz_application = strdup( psz_value );
  668.         }
  669.         /* unknown attribute */
  670.         else
  671.             msg_Warn( p_demux, "invalid <%s> attribute:"%s"", psz_element,
  672.                       psz_name );
  673.         FREE_ATT();
  674.     }
  675.     /* attribute title is mandatory except for <extension> */
  676.     if( !strcmp( psz_element, "vlc:node" ) )
  677.     {
  678.         if( !psz_title )
  679.         {
  680.             msg_Warn( p_demux, "<vlc:node> requires "title" attribute" );
  681.             return false;
  682.         }
  683.         p_new_input = input_item_NewWithType( VLC_OBJECT( p_demux ),
  684.                           "vlc://nop", psz_title, 0, NULL, 0, -1,
  685.                           ITEM_TYPE_DIRECTORY );
  686.         if( p_new_input )
  687.         {
  688.             input_item_AddSubItem( p_input_item, p_new_input );
  689.             p_input_item = p_new_input;
  690.             b_release_input_item = true;
  691.         }
  692.         free( psz_title );
  693.     }
  694.     else if( !strcmp( psz_element, "extension" ) )
  695.     {
  696.         if( !psz_application )
  697.         {
  698.             msg_Warn( p_demux, "<extension> requires "application" attribute" );
  699.             return false;
  700.         }
  701.         else if( strcmp( psz_application, "http://www.videolan.org/vlc/playlist/0" ) )
  702.         {
  703.             msg_Dbg( p_demux, "Skipping "%s" extension tag", psz_application );
  704.             free( psz_application );
  705.             return false;
  706.         }
  707.     }
  708.     free( psz_application );
  709.     /* parse the child elements */
  710.     while( xml_ReaderRead( p_xml_reader ) == 1 )
  711.     {
  712.         i_node = xml_ReaderNodeType( p_xml_reader );
  713.         switch( i_node )
  714.         {
  715.             case XML_READER_NONE:
  716.                 break;
  717.             case XML_READER_STARTELEM:
  718.                 /*  element start tag  */
  719.                 psz_name = xml_ReaderName( p_xml_reader );
  720.                 if( !psz_name || !*psz_name )
  721.                 {
  722.                     msg_Err( p_demux, "invalid xml stream" );
  723.                     FREE_ATT();
  724.                     if( b_release_input_item ) vlc_gc_decref( p_new_input );
  725.                     return false;
  726.                 }
  727.                 /* choose handler */
  728.                 for( p_handler = pl_elements;
  729.                      p_handler->name && strcmp( psz_name, p_handler->name );
  730.                      p_handler++ );
  731.                 if( !p_handler->name )
  732.                 {
  733.                     msg_Err( p_demux, "unexpected element <%s>", psz_name );
  734.                     FREE_ATT();
  735.                     if( b_release_input_item ) vlc_gc_decref( p_new_input );
  736.                     return false;
  737.                 }
  738.                 FREE_NAME();
  739.                 /* complex content is parsed in a separate function */
  740.                 if( p_handler->type == COMPLEX_CONTENT )
  741.                 {
  742.                     if( p_handler->pf_handler.cmplx( p_demux,
  743.                                                      p_input_item,
  744.                                                      p_xml_reader,
  745.                                                      p_handler->name ) )
  746.                     {
  747.                         p_handler = NULL;
  748.                         FREE_ATT();
  749.                     }
  750.                     else
  751.                     {
  752.                         FREE_ATT();
  753.                         if( b_release_input_item ) vlc_gc_decref( p_new_input );
  754.                         return false;
  755.                     }
  756.                 }
  757.                 break;
  758.             case XML_READER_TEXT:
  759.                 /* simple element content */
  760.                 FREE_ATT();
  761.                 psz_value = xml_ReaderValue( p_xml_reader );
  762.                 if( !psz_value )
  763.                 {
  764.                     msg_Err( p_demux, "invalid xml stream" );
  765.                     FREE_ATT();
  766.                     if( b_release_input_item ) vlc_gc_decref( p_new_input );
  767.                     return false;
  768.                 }
  769.                 break;
  770.             case XML_READER_ENDELEM:
  771.                 /* element end tag */
  772.                 psz_name = xml_ReaderName( p_xml_reader );
  773.                 if( !psz_name )
  774.                 {
  775.                     msg_Err( p_demux, "invalid xml stream" );
  776.                     FREE_ATT();
  777.                     if( b_release_input_item ) vlc_gc_decref( p_new_input );
  778.                     return false;
  779.                 }
  780.                 /* leave if the current parent node is terminated */
  781.                 if( !strcmp( psz_name, psz_element ) )
  782.                 {
  783.                     FREE_ATT();
  784.                     if( b_release_input_item ) vlc_gc_decref( p_new_input );
  785.                     return true;
  786.                 }
  787.                 /* there MUST have been a start tag for that element name */
  788.                 if( !p_handler || !p_handler->name
  789.                     || strcmp( p_handler->name, psz_name ))
  790.                 {
  791.                     msg_Err( p_demux, "there's no open element left for <%s>",
  792.                              psz_name );
  793.                     FREE_ATT();
  794.                     if( b_release_input_item ) vlc_gc_decref( p_new_input );
  795.                     return false;
  796.                 }
  797.                 /* special tag <vlc:id> */
  798.                 if( !strcmp( p_handler->name, "vlc:id" ) )
  799.                 {
  800.                     p_demux->p_sys->i_track_id = atoi( psz_value );
  801.                 }
  802.                 else if( p_handler->pf_handler.smpl )
  803.                 {
  804.                     p_handler->pf_handler.smpl( p_input_item, p_handler->name,
  805.                                                 psz_value );
  806.                 }
  807.                 FREE_ATT();
  808.                 p_handler = NULL;
  809.                 break;
  810.             default:
  811.                 /* unknown/unexpected xml node */
  812.                 msg_Err( p_demux, "unexpected xml node %i", i_node );
  813.                 FREE_ATT();
  814.                 if( b_release_input_item ) vlc_gc_decref( p_new_input );
  815.                 return false;
  816.         }
  817.         FREE_NAME();
  818.     }
  819.     if( b_release_input_item ) vlc_gc_decref( p_new_input );
  820.     return false;
  821. }
  822. /**
  823.  * brief parse the extension item node of a XSPF playlist
  824.  */
  825. static bool parse_extitem_node COMPLEX_INTERFACE
  826. {
  827.     VLC_UNUSED(psz_element);
  828.     input_item_t *p_new_input = NULL;
  829.     char *psz_name = NULL;
  830.     char *psz_value = NULL;
  831.     int i_tid = -1;
  832.     /* read all extension item attributes */
  833.     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  834.     {
  835.         psz_name = xml_ReaderName( p_xml_reader );
  836.         psz_value = xml_ReaderValue( p_xml_reader );
  837.         if( !psz_name || !psz_value )
  838.         {
  839.             msg_Err( p_demux, "invalid xml stream @ <vlc:item>" );
  840.             FREE_ATT();
  841.             return false;
  842.         }
  843.         /* attribute: href */
  844.         if( !strcmp( psz_name, "tid" ) )
  845.         {
  846.             i_tid = atoi( psz_value );
  847.         }
  848.         /* unknown attribute */
  849.         else
  850.             msg_Warn( p_demux, "invalid <vlc:item> attribute:"%s"", psz_name);
  851.         FREE_ATT();
  852.     }
  853.     /* attribute href is mandatory */
  854.     if( i_tid < 0 )
  855.     {
  856.         msg_Warn( p_demux, "<vlc:item> requires "tid" attribute" );
  857.         return false;
  858.     }
  859.     if( i_tid >= p_demux->p_sys->i_tracklist_entries )
  860.     {
  861.         msg_Warn( p_demux, "invalid "tid" attribute" );
  862.         return false;
  863.     }
  864.     p_new_input = p_demux->p_sys->pp_tracklist[ i_tid ];
  865.     if( p_new_input )
  866.     {
  867.         input_item_AddSubItem( p_input_item, p_new_input );
  868.         vlc_gc_decref( p_new_input );
  869.         p_demux->p_sys->pp_tracklist[i_tid] = NULL;
  870.     }
  871.     /* kludge for #1293 - XTAG sends ENDELEM for self closing tag */
  872.     /* (libxml sends NONE) */
  873.     xml_ReaderRead( p_xml_reader );
  874.     return true;
  875. }
  876. /**
  877.  * brief skips complex element content that we can't manage
  878.  */
  879. static bool skip_element COMPLEX_INTERFACE
  880. {
  881.     VLC_UNUSED(p_demux); VLC_UNUSED(p_input_item);
  882.     char *psz_endname;
  883.     while( xml_ReaderRead( p_xml_reader ) == 1 )
  884.     {
  885.         if( xml_ReaderNodeType( p_xml_reader ) == XML_READER_ENDELEM )
  886.         {
  887.             psz_endname = xml_ReaderName( p_xml_reader );
  888.             if( !psz_endname )
  889.                 return false;
  890.             if( !strcmp( psz_element, psz_endname ) )
  891.             {
  892.                 free( psz_endname );
  893.                 return true;
  894.             }
  895.             else
  896.                 free( psz_endname );
  897.         }
  898.     }
  899.     return false;
  900. }