DCP: read encrypted DCP with KDM files
[vlc.git] / modules / access / dcp / dcpparser.cpp
1 /*****************************************************************************
2  * Copyright (C) 2013 VLC authors and VideoLAN
3  *
4  * Authors:
5  *          Nicolas Bertrand <nico@isf.cc>
6  *          Jean-Baptiste Kempf <jb@videolan.org>
7  *          Guillaume Gonnaud
8  *          Valentin Vetter <vvetter@outlook.com>
9  *          Anthony Giniers
10  *          Ludovic Hoareau
11  *          Loukmane Dessai
12  *          Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
13  *
14  * This program is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU Lesser General Public License as published by
16  * the Free Software Foundation; either version 2.1 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with this program; if not, write to the Free Software Foundation,
26  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 /**
30  * @file dcpparser.cpp
31  * @brief Parsing of DCP XML files
32  */
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 /* VLC core API headers */
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_xml.h>
41 #include <vlc_url.h>
42
43 #include <iostream>
44 #include <string>
45 #include <list>
46 #include <vector>
47
48 #include "dcpparser.h"
49
50 using namespace std;
51
52 typedef enum {
53     CHUNK_UNKNOWN = 0,
54     CHUNK_PATH,
55     CHUNK_VOL_INDEX,
56     CHUNK_OFFSET,
57     CHUNK_LENGTH
58 } ChunkTag_t;
59
60
61 typedef enum {
62     ASSET_UNKNOWN = 0,
63     ASSET_ID,
64     ASSET_ANNOTATION_TEXT,
65     ASSET_PACKING_LIST,
66     ASSET_CHUNK_LIST,
67     ASSET_HASH,
68     ASSET_SIZE,
69     ASSET_TYPE,
70     ASSET_ORIGINAL_FILENAME
71 } AssetTag_t;
72
73 static const string g_asset_names[] = {
74     "Id",
75     "AnnotationText",
76     "PackingList",
77     "ChunkList",
78     "Hash",
79     "Size",
80     "Type",
81     "OriginalFileName"
82 };
83
84
85 typedef enum {
86     PKL_UNKNOWN = 0,
87     PKL_ID,
88     PKL_ISSUE_DATE,
89     PKL_ISSUER,
90     PKL_CREATOR,
91     PKL_ASSET_LIST,
92     PKL_ANNOTATION_TEXT, /* start of optional tags */
93     PKL_ICON_ID,
94     PKL_GROUP_ID,
95     PKL_SIGNER,
96     PKL_SIGNATURE,
97 } PKLTag_t;
98
99 typedef enum {
100     CPL_UNKNOWN = 0,
101     CPL_ID,
102     CPL_ANNOTATION_TEXT,        /* optional */
103     CPL_ICON_ID,                /* optional */
104     CPL_ISSUE_DATE,
105     CPL_ISSUER,                 /* optional */
106     CPL_CREATOR,                /* optional */
107     CPL_CONTENT_TITLE,
108     CPL_CONTENT_KIND,
109     CPL_CONTENT_VERSION,        /* not optional, but not always present*/
110     CPL_RATING_LIST,            /* not handled */
111     CPL_REEL_LIST,
112     CPL_SIGNER,                 /* optional - not handled */
113     CPL_SIGNATURE               /* optional - not handled */
114 } CPLTag_t;
115
116
117 class ChunkList: public std::list<Chunk> {
118 public :
119     ChunkList();
120     ~ChunkList();
121 };
122
123 /*
124  * Chunk Class
125  */
126 int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){
127     string node;
128     int type;
129     string s_value;
130     static const string names[] = {"Path", "VolumeIndex", "Offset",
131                                "Length"};
132     if (p_type != XML_READER_STARTELEM)
133         return -1;
134     if( p_node != "Chunk")
135         return -1;
136     /* loop on Chunks Node */
137     while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
138         switch (type) {
139             case XML_READER_STARTELEM:
140             {
141                 ChunkTag_t chunk_tag = CHUNK_UNKNOWN;
142                 for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
143                     if( node == names[i-1]) {
144                         chunk_tag = i;
145                         if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value))
146                             return -1;
147                         switch (chunk_tag) {
148                             case CHUNK_PATH:
149                                 this->s_path = s_value;
150                                 break;
151                             case CHUNK_VOL_INDEX:
152                                 this->i_vol_index = atoi(s_value.c_str());
153                                 break;
154                             case CHUNK_OFFSET:
155                                 this->i_offset = atoi(s_value.c_str());
156                                 break;
157                             case CHUNK_LENGTH:
158                                 this->i_length = atoi(s_value.c_str());
159                                 break;
160                             case CHUNK_UNKNOWN:
161                             default:
162                                 break;
163                         }
164                         /* break the for loop as a tag is found*/
165                         break;
166                     }
167                 }
168                 if(chunk_tag == CHUNK_UNKNOWN)
169                     return -1;
170                 break;
171             }
172             case XML_READER_TEXT:
173                 s_value = node;
174                 if (unlikely(node.empty()))
175                     return -1;
176                 break;
177             case XML_READER_ENDELEM:
178                 /* Verify if we reach Chuk endelem  */
179                 if ( node == p_node) {
180                     /* verify path */
181                     if ( this->s_path.empty() ) {
182                         msg_Err(this->p_demux, "Chunk::Parse No path found");
183                         return -1;
184                     }
185                     if ( this->i_vol_index != 1 ) {
186                         msg_Err(this->p_demux, "Only one VOLINDEX suported. Patch welcome.");
187                         return -1;
188                     }
189                     /* end of chunk tag parse */
190                     return 0;
191                 }
192                 break;
193         }
194     }
195     return -1;
196 }
197 /*
198  * AssetMap Class
199  */
200
201 AssetMap::~AssetMap() { }
202
203 int AssetMap::Parse ( )
204 {
205     int type = 0;
206     int retval;
207     int reel_nbr = 0;
208     int index = 0;
209     int sum_duration_vid = 0;
210     int sum_duration_aud = 0;
211     string node;
212     char *psz_kdm_path;
213
214     CPL  *cpl;
215     Reel *reel;
216     PKL  *pkl;
217     AssetList *_p_asset_list = NULL;
218
219     vector<string> pklfiles;
220
221     /* init XML parser */
222     if( this->OpenXml() ) {
223         msg_Err( p_demux, "Failed to initialize Assetmap XML parser" );
224         return -1;
225     }
226
227     /* reading ASSETMAP file to get the asset_list */
228     msg_Dbg( p_demux, "reading ASSETMAP file..." );
229     while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
230         if ( (type == XML_READER_STARTELEM) && ( node =="AssetList")) {
231             _p_asset_list =  new (nothrow) AssetList();
232             if ( unlikely(_p_asset_list == NULL) ) {
233                 this->CloseXml();
234                 return -1;
235             }
236             p_dcp->p_asset_list = _p_asset_list;
237             if (this->ParseAssetList(p_xmlReader, node, type )) {
238                 this->CloseXml();
239                 return -1;
240             }
241             /* asset list found so break*/
242             break;
243         }
244     }
245
246
247     /* Look for PKLs path */
248     if ( (_p_asset_list == NULL) ||  (_p_asset_list->size() == 0) ) {
249         msg_Err( p_demux, "Asset list empty" );
250         this->CloseXml();
251         return -1;
252     }
253
254     for (AssetList::iterator iter = _p_asset_list->begin();
255             iter != _p_asset_list->end() ; ++iter) {
256         string s_filepath;
257         s_filepath = (*iter)->getPath();
258         if (s_filepath.empty()) {
259             msg_Err( p_demux, "No path element for asset" );
260             continue;
261         }
262         /* set an absolute file path */
263         s_filepath = p_dcp->path + s_filepath;
264
265         /* case of packing list */
266         if ((*iter)->isPackingList()) {
267             pklfiles.push_back( s_filepath );
268         }
269     }
270
271     /* TODO: case of only on PKL managed.
272      * Future work needed for managing severals
273      * the PKL vector will be used to select the required PKL  */
274     if( (pklfiles.size() == 0)  || (pklfiles[0].empty()) )
275     {
276         msg_Err( p_demux, "Could not find PKL file in ASSETMAP" );
277         this->CloseXml();
278         return -1;
279     }
280
281     /* Create the first PKL */
282     pkl = new (nothrow) PKL(p_demux, pklfiles[0], _p_asset_list, p_dcp->path);
283     if ( unlikely(pkl == NULL) ) {
284         this->CloseXml();
285         return -1;
286         }
287     if (pkl->Parse()) {
288         delete pkl;
289         this->CloseXml();
290         return -1;
291         }
292     p_dcp->pkls.push_back( pkl );
293
294     /* Now, CPL */
295     if ( pkl->FindCPLs() <= 0 ) {
296         msg_Err(p_demux, " No CPL found");
297         this->CloseXml();
298         return -1;
299     }
300     /* TODO: Only one CPL managed.
301      * Future work needed for managing severals
302      */
303
304     cpl = pkl->getCPL(0);
305     if( cpl == NULL ) {
306         msg_Err(p_demux, " No CPL found");
307         this->CloseXml();
308         return -1;
309     }
310     if ( cpl->Parse() ) {
311         this->CloseXml();
312         return -1;
313     }
314
315     /* KDM, if needed */
316     for( AssetList::iterator iter = _p_asset_list->begin(); iter != _p_asset_list->end(); ++iter )
317         if( ! (*iter)->getKeyId().empty() )
318         {
319             msg_Dbg( p_demux, "DCP is encrypted, searching KDM file...");
320             psz_kdm_path = var_InheritString( p_demux, "kdm" );
321             if( !psz_kdm_path || !*psz_kdm_path )
322             {
323                 msg_Err( p_demux, "cryptographic key IDs found in CPL and no path to KDM given");
324                 free( psz_kdm_path );
325                 this->CloseXml();
326                 return VLC_EGENERIC;
327             }
328             KDM p_kdm( p_demux, psz_kdm_path, p_dcp );
329             free( psz_kdm_path );
330             if( ( retval = p_kdm.Parse() ) )
331             {
332                 this->CloseXml();
333                 return retval;
334             }
335             break;
336         }
337
338     reel_nbr = cpl->getReelList().size();
339     for(index = 0; index != reel_nbr; ++index)
340     {
341         reel = cpl->getReel(index);
342
343         Asset *asset;
344         struct info_reel video;
345         struct info_reel audio;
346
347         /* Get picture */
348         asset = reel->getTrack(TRACK_PICTURE);
349         if (asset != NULL)
350         {
351             sum_duration_vid += asset->getDuration();
352             video.filename = p_dcp->path + asset->getPath();
353             video.i_entrypoint = asset->getEntryPoint();
354             video.i_duration = asset->getDuration();
355             video.i_correction = video.i_entrypoint - sum_duration_vid + video.i_duration;
356             video.i_absolute_end = sum_duration_vid;
357             video.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
358             p_dcp->video_reels.push_back(video);
359             msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
360             msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
361         }
362         /* Get audio */
363         asset = reel->getTrack(TRACK_SOUND);
364         if (asset != NULL)
365         {
366             /*if (!p_dcp->audio_reels.empty())
367             {
368                 sum_duration_aud = 0;
369                 for (int i = 0; i != p_dcp->audio_reels.size(); ++i)
370                 {
371                     sum_duration_aud += p_dcp->audio_reels(i).i_duration;
372                 }
373             }*/
374             sum_duration_aud += asset->getDuration();
375             audio.filename = p_dcp->path + asset->getPath();
376             audio.i_entrypoint = asset->getEntryPoint();
377             audio.i_duration = asset->getDuration();
378             audio.i_correction = audio.i_entrypoint - sum_duration_aud + audio.i_duration;
379             audio.i_absolute_end = sum_duration_aud;
380             audio.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
381             p_dcp->audio_reels.push_back(audio);
382             msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
383             msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
384         }
385     }
386     /* free memory */
387     this->CloseXml();
388     return VLC_SUCCESS;
389 }
390
391
392
393 /*
394  * Asset Class
395  */
396 Asset::~Asset() {}
397
398 int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
399 {
400     string node;
401     int type;
402     string s_value;
403     const string s_root_node = "Asset";
404
405     if (p_type != XML_READER_STARTELEM)
406         return -1;
407     if( p_node != s_root_node)
408         return -1;
409     /* loop on Assets Node */
410     while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
411         switch (type) {
412             case XML_READER_STARTELEM:
413                 {
414                     AssetTag_t _tag = ASSET_UNKNOWN;
415                     for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
416                         if( node == g_asset_names[i-1]) {
417                             _tag = i;
418                             switch(_tag) {
419                                 /* Case of complex nodes */
420                                 case ASSET_PACKING_LIST:
421                                     /* case of <PackinkList/> tag, bur not compliant with SMPTE-429-9 2007*/
422                                     if (xml_ReaderIsEmptyElement( p_xmlReader))
423                                         this->b_is_packing_list = true;
424                                     else if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
425                                         return -1;
426                                     if ( s_value == "true" )
427                                         this->b_is_packing_list = true;
428                                     break;
429                                 case ASSET_CHUNK_LIST:
430                                     if ( this->parseChunkList(p_xmlReader, node, type ) )
431                                         return -1;
432                                     this->s_path = this->chunk_vec[0].getPath();
433                                     break;
434                                 case ASSET_ID:
435                                     if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
436                                         return -1;
437                                     this->s_id = s_value;
438                                     break;
439                                 case ASSET_ANNOTATION_TEXT:
440                                     if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
441                                         return -1;
442                                     this->s_annotation = s_value;
443                                     break;
444                                 case ASSET_ORIGINAL_FILENAME:
445                                 case ASSET_HASH:
446                                 case ASSET_TYPE:
447                                 case ASSET_SIZE:
448                                     /* Asset tags not in AssetMap */
449                                     break;
450                                 default:
451                                     msg_Warn(this->p_demux, "Unknow ASSET_TAG: %i", _tag );
452                                     break;
453                             }
454                             /* break the for loop as a tag is found*/
455                             break;
456                         }
457                     }
458                     if( _tag == ASSET_UNKNOWN )
459                         return -1;
460                     break;
461                 }
462             case XML_READER_TEXT:
463                 return -1;
464             case XML_READER_ENDELEM:
465                 if ( node != s_root_node) {
466                     msg_Err(this->p_demux,
467                             "Something goes wrong in Asset parsing on Assetmap (node %s)", node.c_str());
468                     return -1;
469                 } else {
470                     /*Check Presence of Id and Chunklist */
471                     if ( this->s_id.empty() ) {
472                         msg_Err(this->p_demux, " No Id element found in Asset");
473                         return -1;
474                     }
475                     if ( this->s_path.empty() ) {
476                         msg_Err(this->p_demux, " No path element found in Asset");
477                         return -1;
478                     }
479                     return 0;
480                 }
481                 break;
482         }
483     }
484     return -1;
485 }
486
487
488
489 int Asset::ParsePKL( xml_reader_t *p_xmlReader)
490 {
491     string node;
492     int type;
493     string s_value;
494     const string s_root_node = "Asset";
495
496     while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
497         switch (type) {
498             case XML_READER_STARTELEM:
499                 {
500                     AssetTag_t _tag = ASSET_UNKNOWN;
501                     for(AssetTag_t i = ASSET_ID; i <= ASSET_ORIGINAL_FILENAME; i = AssetTag_t(i+1)) {
502                         if( node == g_asset_names[i-1]) {
503                             _tag = i;
504                             switch(_tag) {
505                                 case ASSET_ANNOTATION_TEXT:
506                                     if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
507                                         return -1;
508                                     if ( this->s_annotation.empty() )
509                                         this->s_annotation = s_value;
510                                     else
511                                         this->s_annotation = this->s_annotation + "--" + s_value;
512                                     break;
513                                 case ASSET_HASH:
514                                     if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
515                                         return -1;
516                                     this->s_hash = s_value;
517                                     break;
518                                 case ASSET_SIZE:
519                                     if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
520                                         return -1;
521                                     this->ui_size = atol(s_value.c_str());
522                                     break;
523                                 case ASSET_TYPE:
524                                     if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
525                                         return -1;
526                                     this->s_type = s_value;
527                                     break;
528                                 case ASSET_ORIGINAL_FILENAME:
529                                     if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
530                                         return -1;
531                                     this->s_original_filename = s_value;
532                                     break;
533                                 case ASSET_ID: /* already verified */
534                                 case ASSET_PACKING_LIST:
535                                 case ASSET_CHUNK_LIST:
536                                     /* Asset tags not in PKL */
537                                     break;
538                                 default:
539                                     msg_Warn(this->p_demux, "Unknow ASSET_TAG: %i", _tag );
540                                     break;
541                             }
542                             /* break the for loop as a tag is found*/
543                             break;
544                         }
545                     }
546                     if( _tag == ASSET_UNKNOWN )
547                         return -1;
548                 break;
549             }
550             case XML_READER_TEXT:
551                 return -1;
552             case XML_READER_ENDELEM:
553                 if ( node != s_root_node) {
554                     msg_Err(this->p_demux,
555                             "Something goes wrong in Asset parsing on PKL (node %s)", node.c_str());
556                     return -1;
557                 } else {
558                     /* Verify that mandatory attributes are filled */
559                     if (this->s_hash.empty()) {
560                         msg_Err(this->p_demux,"Asset Hash tag invalid");
561                         return -1;
562                     }
563                     if (this->ui_size == 0) {
564                         msg_Err(this->p_demux,"Asset Size tag invalid");
565                         return -1;
566                     }
567                     if (this->s_type.empty()) {
568                         msg_Err(this->p_demux,"Asset Type tag invalid");
569                         return -1;
570                     }
571                     return 0;
572                 }
573                 break;
574         }
575
576     }
577
578     return -1;
579 }
580
581 void Asset::Dump()
582 {
583     msg_Dbg(this->p_demux,"Id              = %s", this->s_id.c_str());
584     msg_Dbg(this->p_demux,"Path            = %s", this->s_path.c_str());
585     msg_Dbg(this->p_demux,"Is PKL          = %s", this->b_is_packing_list ? "True" : "False");
586     msg_Dbg(this->p_demux,"Hash            = %s", this->s_hash.c_str());
587     msg_Dbg(this->p_demux,"Size            = %i", this->ui_size);
588     msg_Dbg(this->p_demux,"Type            = %s", this->s_type.c_str());
589     msg_Dbg(this->p_demux,"OrignalFileName = %s", this->s_original_filename.c_str());
590     msg_Dbg(this->p_demux,"AnnotationText  = %s", this->s_annotation.c_str());
591 }
592
593 int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
594 {
595     string node;
596     int type;
597     string s_value;
598     std::vector<Chunk> chunk_vec;
599
600     if (p_type != XML_READER_STARTELEM)
601         return -1;
602     if( p_node != "ChunkList" )
603         return -1;
604     /* loop on Assets Node */
605     while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
606          switch (type) {
607             case XML_READER_STARTELEM:
608                 {
609                     Chunk chunk(this->p_demux);
610                     if (node != "Chunk" )
611                         return -1;
612                     if ( chunk.Parse(p_xmlReader, node, type) )
613                         return -1;
614                     chunk_vec.push_back(chunk);
615                     break;
616                 }
617             case XML_READER_ENDELEM:
618                 if ( node == p_node) {
619                     if (chunk_vec.size() != 1 ) {
620                         msg_Err(this->p_demux, "chunklist of size greater than one not supported");
621                         return -1;
622                         }
623                     this->chunk_vec = chunk_vec;
624                     return 0;
625                 }
626                 break;
627         }
628     }
629     return -1;
630 }
631
632 AESKey * Asset::getAESKeyById( AESKeyList* p_key_list, const string s_id )
633 {
634     /* return NULL if DCP is not encrypted */
635     if( !p_key_list || s_id.empty() )
636         return NULL;
637
638     for( AESKeyList::iterator index = p_key_list->begin(); index != p_key_list->end(); ++index )
639         if( (*index)->getKeyId() == s_id )
640             return *index;
641
642     return NULL;
643 }
644
645
646 int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type)
647 {
648     string node;
649     int type;
650     Asset *asset;
651
652     if (p_type != XML_READER_STARTELEM)
653         return -1;
654     if( p_node != "AssetList" )
655         return -1;
656     /* loop on AssetList nodes */
657     while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
658         switch (type) {
659             case XML_READER_STARTELEM:
660                 if (node != "Asset" )
661                     return -1;
662                 asset = new (nothrow) Asset(this->p_demux);
663                 if ( unlikely(asset == NULL) )
664                     return -1;
665                 if (asset->Parse(p_xmlReader, node, type)){
666                     delete asset;
667                     return -1;
668                 }
669                 p_dcp->p_asset_list->push_back(asset);
670                 break;
671
672             case XML_READER_ENDELEM:
673                 if (node == p_node )
674                     return 0;
675                 break;
676             default:
677             case XML_READER_TEXT:
678                 msg_Err(this->p_demux, "Error parsing AssetList in AssetMap");
679                 return -1;
680         }
681     }
682     return -1;
683 }
684
685 Asset * AssetMap::getAssetById(AssetList *asset_list, const string p_id)
686 {
687     AssetList::iterator index = asset_list->begin() ;
688     for (index = asset_list->begin(); index != asset_list->end(); ++index)
689         if ((*index)->getId() == p_id )
690             return *index;
691     return NULL;
692 }
693
694 /*
695  * XmlFile Class
696  */
697 XmlFile::~XmlFile() {}
698
699 int XmlFile::OpenXml()
700 {
701     char *psz_uri;
702
703     this->p_xml = xml_Create( this->p_demux );
704     if (! this->p_xml) {
705         return -1;
706     }
707     psz_uri = vlc_path2uri( this->s_path.c_str(), "file" );
708     this->p_stream = stream_UrlNew(this->p_demux, psz_uri );
709     free(psz_uri);
710     if( ! this->p_stream ) {
711         xml_Delete(this->p_xml );
712         return -1;
713     }
714
715     this->p_xmlReader = xml_ReaderCreate( this->p_xml, this->p_stream);
716     if( ! this->p_xmlReader ) {
717         stream_Delete( this->p_stream );
718         xml_Delete(this->p_xml );
719         return -1;
720     }
721     return 0;
722 }
723
724 int XmlFile::ReadNextNode( xml_reader_t *p_xmlReader, string& p_node )
725 {
726     const char * c_node;
727     int i;
728     i = xml_ReaderNextNode( p_xmlReader, &c_node );
729     p_node = c_node;
730     return i;
731 }
732
733 int XmlFile::ReadEndNode( xml_reader_t *p_xmlReader, string p_node, int p_type, string &s_value)
734 {
735     string node;
736
737     if ( xml_ReaderIsEmptyElement( p_xmlReader) )
738             return 0;
739
740     if (p_type != XML_READER_STARTELEM)
741         return -1;
742
743     if ( XmlFile::ReadNextNode( p_xmlReader, node ) == XML_READER_TEXT )
744     {
745         s_value = node;
746         if( ( XmlFile::ReadNextNode( p_xmlReader, node ) == XML_READER_ENDELEM ) &&
747                 node == p_node)
748             return 0;
749     }
750     return -1;
751 }
752
753 void XmlFile::CloseXml() {
754     if( this->p_stream )
755         stream_Delete( this->p_stream );
756     if( this->p_xmlReader )
757         xml_ReaderDelete( this->p_xmlReader );
758     if( this->p_xml )
759         xml_Delete( this->p_xml );
760
761 }
762
763 /*
764  * PKL Class
765  */
766
767 PKL::PKL(demux_t * p_demux, string s_path, AssetList *_asset_list, string s):
768     XmlFile(p_demux, s_path),
769     asset_list(_asset_list), s_dcp_path(s)
770 {
771     type = XML_PKL;
772 }
773
774 PKL::~PKL() {
775     vlc_delete_all(vec_cpl);
776 }
777
778 int PKL::Parse()
779 {
780     string node;
781     int type;
782     string s_value;
783     const string s_root_node = "PackingList";
784
785     static const string names[] = {
786         "Id",
787         "IssueDate",
788         "Issuer",
789         "Creator",
790         "AssetList",
791         "AnnotationText",
792         "IconId",
793         "GroupId",
794         "Signer",
795         "ds:Signature"
796     };
797
798     if (this->OpenXml())
799         return -1;
800
801     /* read 1st node  and verify that is a PKL*/
802     if (! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode(this->p_xmlReader, node) ) &&
803                 (node == s_root_node) ) ) {
804         msg_Err( this->p_demux, "Not a valid XML Packing List");
805         goto error;
806     }
807     while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
808         switch (type) {
809             case XML_READER_STARTELEM: {
810                 PKLTag_t _tag = PKL_UNKNOWN;
811                 for(PKLTag_t i = PKL_ID; i <= PKL_SIGNATURE; i = PKLTag_t(i+1)) {
812                     if( node == names[i-1]) {
813                         _tag = i;
814                         switch (_tag) {
815                             /* case for parsing non terminal nodes */
816                             case PKL_ASSET_LIST:
817                                 if ( this->ParseAssetList(node, type) )
818                                     goto error;
819                                 break;
820                             case PKL_SIGNER:
821                                 if ( this->ParseSigner(node, type) )
822                                     goto error;
823                                 break;
824                             case PKL_SIGNATURE:
825                                 if ( this->ParseSignature(node, type) )
826                                     goto error;
827                                 break;
828                             /* Parse simple/end nodes */
829                             case PKL_ID:
830                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
831                                     goto error;
832                                 this->s_id = s_value;
833                                 break;
834                             case PKL_ISSUE_DATE:
835                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
836                                     goto error;
837                                 this->s_issue_date = s_value;
838                                 break;
839                             case PKL_ISSUER:
840                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
841                                     goto error;
842                                 this->s_issuer = s_value;
843                                 break;
844                             case PKL_CREATOR:
845                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
846                                     goto error;
847                                 this->s_creator = s_value;
848                                 break;
849                             case PKL_ANNOTATION_TEXT:
850                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
851                                     goto error;
852                                 this->s_annotation = s_value;
853                                 break;
854                             case PKL_ICON_ID:
855                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
856                                     goto error;
857                                 this->s_icon_id = s_value;
858                                 break;
859                             case PKL_GROUP_ID:
860                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
861                                     goto error;
862                                 this->s_group_id = s_value;
863                                 break;
864                             default:
865                                 msg_Warn(this->p_demux, "Unknow PKG_TAG: %i", _tag );
866                                 break;
867                         }
868                         /* break the for loop as a tag is found*/
869                         break;
870                     }
871                 }
872                 if( _tag == PKL_UNKNOWN )
873                     goto error;
874                 break;
875             }
876             case XML_READER_TEXT:
877                 goto error;
878             case XML_READER_ENDELEM:
879                 if ( node != s_root_node) {
880                     msg_Err(this->p_demux,
881                         "Something goes wrong in PKL parsing (node %s)", node.c_str());
882                     goto error;
883                 }
884                 break;
885         }
886     }
887     /* TODO verify presence of mandatory fields*/
888
889     /* Close PKL XML*/
890     this->CloseXml();
891     return 0;
892 error:
893     msg_Err( this->p_demux, "PKL parsing failed");
894     this->CloseXml();
895     return -1;
896 }
897
898 int PKL::FindCPLs()
899 {
900     if ( this->vec_cpl.size() != 0 ) {
901         msg_Err(this->p_demux, "CPLs already checked");
902         return -1;
903     }
904
905     for (AssetList::iterator index = this->asset_list->begin();
906             index != this->asset_list->end(); ++index) {
907         Asset *asset = *index;
908         if ( asset->getType().find("text/xml") == string::npos) {
909             /* not an xml file */
910             continue;
911         }
912
913         CPL *cpl = new (nothrow) CPL(this->p_demux,
914                       this->s_dcp_path + asset->getPath(),
915                       this->asset_list);
916         if ( unlikely(cpl == NULL) )
917                     return -1;
918         if ( cpl->IsCPL() )
919             /* CPL Found */
920             this->vec_cpl.push_back(cpl);
921         else
922             delete cpl;
923     }
924     return this->vec_cpl.size();
925 }
926
927
928 int PKL::ParseAssetList(string p_node, int p_type) {
929     string node;
930     int type;
931
932     if (p_type != XML_READER_STARTELEM)
933         return -1;
934     if( p_node != "AssetList")
935         return -1;
936     while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
937         switch (type) {
938             case XML_READER_STARTELEM:
939                 if( node =="Asset") {
940                     if ( this->ParseAsset(node, type) )
941                         return -1;
942                 } else
943                     return -1;
944                 break;
945             case XML_READER_ENDELEM:
946                 if ( node == p_node) {
947                     /* parse of chunklist finished */
948                     goto end;
949                 }
950                 break;
951             }
952     }
953 end:
954     return 0;
955 }
956
957 int PKL::ParseAsset(string p_node, int p_type) {
958     string node;
959     int type;
960     string s_value;
961     Asset *asset = NULL;
962
963     if (p_type != XML_READER_STARTELEM)
964         return -1;
965     if( p_node != "Asset")
966         return -1;
967
968     /* 1st node shall be Id" */
969     if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
970         if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
971             return -1;
972     if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
973          if (type == XML_READER_TEXT) {
974             s_value = node;
975             if (unlikely(node.empty()))
976                 return -1;
977             }
978     if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
979         if (type == XML_READER_ENDELEM) {
980             asset = AssetMap::getAssetById(this->asset_list, s_value);
981             if (asset  == NULL)
982                 return -1;
983         }
984      if (asset == NULL)
985         return -1;
986      if ( asset->ParsePKL(this->p_xmlReader) )
987         return -1;
988     return 0;
989 }
990
991 int PKL::ParseSigner(string p_node, int p_type)
992 {
993     string node;
994     int type;
995
996     if (p_type != XML_READER_STARTELEM)
997         return -1;
998     if( p_node != "Signer")
999         return -1;
1000
1001     while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1002         /* TODO not implemented. Just parse until end of Signer node */
1003             if ((node == p_node) && (type = XML_READER_ENDELEM))
1004                 return 0;
1005     }
1006
1007     msg_Err(this->p_demux, "Parse of Signer finished bad");
1008     return -1;
1009 }
1010
1011 int PKL::ParseSignature(string p_node, int p_type)
1012 {
1013     string node;
1014     int type;
1015
1016     if (p_type != XML_READER_STARTELEM)
1017         return -1;
1018     if( p_node != "ds:Signature")
1019         return -1;
1020
1021     while (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1022         /* TODO not implemented. Just parse until end of Signature node */
1023             if ((node == p_node) && (type = XML_READER_ENDELEM))
1024                 return 0;
1025     }
1026     msg_Err(this->p_demux, "Parse of Signature finished bad");
1027     return -1;
1028 }
1029
1030 /*
1031  * Reel Class
1032  */
1033 int Reel::Parse(string p_node, int p_type) {
1034     string node;
1035     int type;
1036     string s_value;
1037
1038     if (p_type != XML_READER_STARTELEM)
1039         return -1;
1040     if( p_node != "Reel")
1041         return -1;
1042
1043     while (( type = XmlFile::ReadNextNode(this->p_xmlReader, node ) ) > 0 ) {
1044         switch (type) {
1045             case XML_READER_STARTELEM:
1046                 if (node =="Id") {
1047                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1048                         return -1;
1049                     this->s_id = s_value;
1050                 } else if (node == "AnnotationText") {
1051                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1052                         return -1;
1053                     this->s_annotation = s_value;
1054                 } else if ( node =="AssetList" ) {
1055                     if (this->ParseAssetList(node, type))
1056                         return -1;
1057                 } else {
1058                     /* unknown tag */
1059                     msg_Err(this->p_demux, "Reel::Parse, unknown tag:%s", node.c_str());
1060                     return -1;
1061                 }
1062                 break;
1063             case XML_READER_TEXT:
1064                 /* Error */
1065                 msg_Err(this->p_demux, "Reel parsing error");
1066                 return -1;
1067             case XML_READER_ENDELEM:
1068                 /* verify correctness of end node */
1069                 if ( node == p_node) {
1070                     /* TODO : verify Reel id */
1071                     return 0;
1072                 }
1073                 break;
1074         }
1075     }
1076     return -1;
1077 }
1078
1079
1080 Asset * Reel::getTrack(TrackType_t e_track)
1081 {
1082     switch (e_track) {
1083         case TRACK_PICTURE:
1084             return this->p_picture_track;
1085         case TRACK_SOUND:
1086             return this->p_sound_track;
1087         case TRACK_SUBTITLE:
1088             return this->p_subtitle_track;
1089         case TRACK_UNKNOWN:
1090         default:
1091             break;
1092     }
1093     return NULL;
1094 }
1095
1096 int Reel::ParseAssetList(string p_node, int p_type) {
1097     string node;
1098     int type;
1099     string s_value;
1100
1101     if (p_type != XML_READER_STARTELEM)
1102         return -1;
1103     if( p_node != "AssetList")
1104         return -1;
1105
1106     while (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 )  {
1107         switch (type) {
1108             case XML_READER_STARTELEM:
1109                 if (node =="MainPicture") {
1110                     if ( this->ParseAsset(node, type, TRACK_PICTURE) )
1111                         return -1;
1112                 } else if (node =="MainSound") {
1113                     if ( this->ParseAsset(node, type, TRACK_SOUND) )
1114                         return -1;
1115                 } else if (node =="MainSubtitle") {
1116                     if ( this->ParseAsset(node, type, TRACK_SUBTITLE) )
1117                         return -1;
1118                 } else {
1119                     /* unknown tag */
1120                     msg_Err(this->p_demux, "Reel::ParseAssetList, unknown tag:%s", node.c_str());
1121                     return -1;
1122                 }
1123                 break;
1124             case XML_READER_TEXT:
1125                 /* Parsing error */
1126                 msg_Err(this->p_demux, "AssetList parsing error");
1127                 return -1;
1128             case XML_READER_ENDELEM:
1129                 /* verify correctness of end node */
1130                 if ( node == p_node) {
1131                     /* TODO : verify id */
1132                     return 0;
1133                 }
1134                 break;
1135         }
1136     }
1137     return -1;
1138 }
1139
1140 int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
1141     string node;
1142     int type;
1143     string s_value;
1144     bool b_stop_parse = false;
1145     Asset *asset = NULL;
1146
1147     if (p_type != XML_READER_STARTELEM)
1148         return -1;
1149
1150     /* 1st node shall be Id */
1151     if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
1152         if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
1153             return -1;
1154
1155     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value) )
1156         return -1;
1157
1158     asset = AssetMap::getAssetById(this->p_asset_list, s_value);
1159     if (asset  == NULL)
1160         return -1;
1161
1162     while(  (! b_stop_parse) &&
1163             (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) ) {
1164         switch (type) {
1165             case XML_READER_STARTELEM:
1166                 if (node =="EditRate") {
1167                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1168                         return -1;
1169                 } else if (node == "AnnotationText") {
1170                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1171                         return -1;
1172                         asset->setAnnotation(s_value);
1173                 } else if (node == "IntrinsicDuration") {
1174                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1175                         return -1;
1176                         asset->setIntrinsicDuration(atoi(s_value.c_str()));
1177                 } else if (node == "EntryPoint") {
1178                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1179                         return -1;
1180                         asset->setEntryPoint(atoi(s_value.c_str()));
1181                 } else if (node == "Duration") {
1182                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1183                         return -1;
1184                         asset->setDuration(atoi(s_value.c_str()));
1185                 } else if (node == "KeyId") {
1186                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1187                         return -1;
1188                     asset->setKeyId( s_value );
1189                 } else if (node == "Hash") {
1190                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1191                         return -1;
1192                 } else if (node == "FrameRate") {
1193                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1194                         return -1;
1195                 } else if (node == "ScreenAspectRatio") {
1196                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1197                         return -1;
1198                 } else if (node == "Language") {
1199                     if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
1200                         return -1;
1201                 } else {
1202                     /* unknown tag */
1203                     msg_Err(this->p_demux, "Reel::ParseAsset unknown tag:%s", node.c_str());
1204                     return -1;
1205                 }
1206                 break;
1207             case XML_READER_TEXT:
1208                 /* impossible */
1209                 return -1;
1210                 break;
1211             case XML_READER_ENDELEM:
1212                 /* verify correctness of end node */
1213                 if ( node == p_node) {
1214                     /* TODO : verify id */
1215                     b_stop_parse = true;
1216                 }
1217         }
1218     }
1219     /* store by track */
1220     switch (e_track) {
1221         case TRACK_PICTURE:
1222             this->p_picture_track = asset;
1223             break;
1224         case TRACK_SOUND:
1225             this->p_sound_track = asset;
1226             break;
1227         case TRACK_SUBTITLE:
1228             this->p_subtitle_track = asset;
1229             break;
1230         case TRACK_UNKNOWN:
1231         default:
1232             break;
1233     }
1234     return 0;
1235 }
1236
1237 /*
1238  * CPL Class
1239  */
1240
1241 CPL::CPL(demux_t * p_demux, string s_path, AssetList *_asset_list)
1242     : XmlFile(p_demux, s_path), asset_list( _asset_list)
1243 {
1244     string node;
1245     int type;
1246
1247     this->type = XML_UNKNOWN;
1248
1249     if (this->OpenXml()) {
1250         msg_Err(this->p_demux, "Failed to open CPL XML file");
1251         return;
1252         }
1253
1254     /* read 1st node  and verify that is a CPL */
1255     if ( (type = XmlFile::ReadNextNode(p_xmlReader, node)) > 0) {
1256         if ( (type == XML_READER_STARTELEM) && (node == "CompositionPlaylist") ) {
1257             this->type = XML_CPL;
1258         }
1259     }
1260     /* close xml */
1261     this->CloseXml();
1262 };
1263
1264 CPL::~CPL() {
1265     vlc_delete_all(vec_reel);
1266 }
1267
1268 int CPL::Parse()
1269 {
1270     string node;
1271     int type;
1272     string s_value;
1273     const string s_root_node = "CompositionPlaylist";
1274
1275     static const string names[] = {
1276         "Id",
1277         "AnnotationText",
1278         "IconId",
1279         "IssueDate",
1280         "Issuer",
1281         "Creator",
1282         "ContentTitleText",
1283         "ContentKind",
1284         "ContentVersion",
1285         "RatingList",
1286         "ReelList",
1287         "Signer",
1288         "ds:Signature"
1289     };
1290
1291     if (this->OpenXml())
1292         return -1;
1293
1294     /* read 1st node  and verify that is a CPL*/
1295     if (! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode(this->p_xmlReader, node) ) &&
1296                 (node == s_root_node) ) ) {
1297         msg_Err( this->p_demux, "Not a valid XML Packing List");
1298         goto error;
1299     }
1300
1301     while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1302         switch (type) {
1303             case XML_READER_STARTELEM: {
1304                 CPLTag_t _tag = CPL_UNKNOWN;
1305                 for(CPLTag_t i = CPL_ID; i <= CPL_SIGNATURE; i = CPLTag_t(i+1)) {
1306                     if( node == names[i-1]) {
1307                         _tag = i;
1308                         switch (_tag) {
1309                             /* case for parsing non terminal nodes */
1310                             case CPL_REEL_LIST:
1311                                 if ( this->ParseReelList(node, type) )
1312                                     goto error;
1313                                 break;
1314                             case CPL_CONTENT_VERSION:
1315                             case CPL_SIGNER:
1316                             case CPL_SIGNATURE:
1317                             case CPL_RATING_LIST:
1318                                 if ( this->DummyParse(node,type) )
1319                                     goto error;
1320                                 break;
1321                                 /* Parse simple/end nodes */
1322                             case CPL_ID:
1323                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1324                                     goto error;
1325                                 this->s_id = s_value;
1326                                 break;
1327                             case CPL_ANNOTATION_TEXT:
1328                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1329                                     goto error;
1330                                 this->s_annotation = s_value;
1331                                 break;
1332                             case CPL_ICON_ID:
1333                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1334                                     goto error;
1335                                 this->s_icon_id = s_value;
1336                                 break;
1337                             case CPL_ISSUE_DATE:
1338                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1339                                     goto error;
1340                                 this->s_issue_date= s_value;
1341                                 break;
1342                             case CPL_ISSUER:
1343                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1344                                     goto error;
1345                                 this->s_issuer = s_value;
1346                                 break;
1347                             case CPL_CREATOR:
1348                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1349                                     goto error;
1350                                 this->s_creator = s_value;
1351                                 break;
1352                             case CPL_CONTENT_TITLE:
1353                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1354                                     goto error;
1355                                 this->s_content_title = s_value;
1356                                 break;
1357                             case CPL_CONTENT_KIND:
1358                                 if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
1359                                     goto error;
1360                                 this->s_content_kind = s_value;
1361                                 break;
1362                             default:
1363                                 msg_Warn(this->p_demux, "Unknow CPL_TAG: %i", _tag );
1364                                 break;
1365                         }
1366
1367                         /* break the for loop as a tag is found*/
1368                         break;
1369                     }
1370                 }
1371                 if( _tag == CPL_UNKNOWN )
1372                     goto error;
1373                 break;
1374             }
1375             case XML_READER_TEXT:
1376                goto error;
1377             case XML_READER_ENDELEM:
1378                if ( node != s_root_node) {
1379                    msg_Err(this->p_demux,
1380                            "Something goes wrong in CKL parsing (node %s)", node.c_str());
1381                    goto error;
1382                }
1383                break;
1384         }
1385     }
1386
1387     /* TODO verify presence of mandatory fields*/
1388
1389     /* Close CPL XML*/
1390     this->CloseXml();
1391     return 0;
1392 error:
1393     this->CloseXml();
1394     return -1;
1395 }
1396
1397 int CPL::ParseReelList(string p_node, int p_type) {
1398     string node;
1399     int type;
1400
1401     if (p_type != XML_READER_STARTELEM)
1402         return -1;
1403     if( p_node != "ReelList")
1404         return -1;
1405     while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1406         switch (type) {
1407             case XML_READER_STARTELEM: {
1408                 Reel *p_reel = new (nothrow) Reel( this->p_demux, this->asset_list, this->p_xmlReader);
1409                 if ( unlikely(p_reel == NULL) )
1410                     return -1;
1411                 if( node =="Reel") {
1412                     if ( p_reel->Parse(node, type) ) {
1413                         delete p_reel;
1414                         return -1;
1415                     }
1416                 } else {
1417                     delete p_reel;
1418                     return -1;
1419                 }
1420                 this->vec_reel.push_back(p_reel);
1421
1422                 break;
1423             }
1424             case XML_READER_TEXT:
1425                 /* impossible */
1426                 break;
1427             case XML_READER_ENDELEM:
1428                 if ( node == p_node)
1429                     return 0;
1430                 break;
1431             }
1432     }
1433     return -1;
1434 }
1435
1436
1437 int CPL::DummyParse(string p_node, int p_type)
1438 {
1439     string node;
1440     int type;
1441
1442     if (p_type != XML_READER_STARTELEM)
1443         return -1;
1444
1445     if (xml_ReaderIsEmptyElement( this->p_xmlReader))
1446         return 0;
1447
1448     while (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
1449         /* TODO not implemented. Just pase until end of input node */
1450         if ((node == p_node) && (type = XML_READER_ENDELEM))
1451             return 0;
1452     }
1453
1454     return -1;
1455 }