1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /***************************************************************************
4  *            brasero-project-parse.c
5  *
6  *  dim nov 27 14:58:13 2008
7  *  Copyright  2005-2008  Rouquier Philippe
8  *  brasero-app@wanadoo.fr
9  ***************************************************************************/
10 
11 /*
12  *  Brasero is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  Brasero is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU Library General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to:
24  * 	The Free Software Foundation, Inc.,
25  * 	51 Franklin Street, Fifth Floor
26  * 	Boston, MA  02110-1301, USA.
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32 
33 #include <glib.h>
34 #include <glib/gstdio.h>
35 #include <glib/gi18n.h>
36 
37 #include <libxml/xmlerror.h>
38 #include <libxml/xmlwriter.h>
39 #include <libxml/parser.h>
40 #include <libxml/xmlstring.h>
41 #include <libxml/uri.h>
42 
43 #ifdef BUILD_PLAYLIST
44 #include <totem-pl-parser.h>
45 #endif
46 
47 #include "brasero-project-parse.h"
48 #include "brasero-app.h"
49 
50 #include "brasero-units.h"
51 #include "brasero-track-stream-cfg.h"
52 #include "brasero-track-data-cfg.h"
53 #include "brasero-session.h"
54 #include "brasero-tags.h"
55 
56 #define BRASERO_PROJECT_VERSION "0.2"
57 
58 static void
brasero_project_invalid_project_dialog(const char * reason)59 brasero_project_invalid_project_dialog (const char *reason)
60 {
61 	brasero_app_alert (brasero_app_get_default (),
62 			   _("Error while loading the project."),
63 			   reason,
64 			   GTK_MESSAGE_ERROR);
65 }
66 
67 static GSList *
_read_graft_point(xmlDocPtr project,xmlNodePtr graft,GSList * grafts)68 _read_graft_point (xmlDocPtr project,
69 		   xmlNodePtr graft,
70 		   GSList *grafts)
71 {
72 	BraseroGraftPt *retval;
73 
74 	retval = g_new0 (BraseroGraftPt, 1);
75         grafts = g_slist_prepend (grafts, retval);
76 	while (graft) {
77 		if (!xmlStrcmp (graft->name, (const xmlChar *) "uri")) {
78 			xmlChar *uri;
79 
80 			if (retval->uri)
81 				goto error;
82 
83 			uri = xmlNodeListGetString (project,
84 						    graft->xmlChildrenNode,
85 						    1);
86 			retval->uri = g_uri_unescape_string ((char *)uri, NULL);
87 			g_free (uri);
88 			if (!retval->uri)
89 				goto error;
90 		}
91 		else if (!xmlStrcmp (graft->name, (const xmlChar *) "path")) {
92 			if (retval->path)
93 				goto error;
94 
95 			retval->path = (char *) xmlNodeListGetString (project,
96 								      graft->xmlChildrenNode,
97 								      1);
98 			if (!retval->path)
99 				goto error;
100 		}
101 		else if (graft->type == XML_ELEMENT_NODE)
102 			goto error;
103 
104 		graft = graft->next;
105 	}
106 
107 	return grafts;
108 
109 error:
110 
111         g_slist_foreach (grafts, (GFunc) brasero_graft_point_free, NULL);
112         g_slist_free (grafts);
113 
114 	return NULL;
115 }
116 
117 static BraseroTrack *
_read_data_track(xmlDocPtr project,xmlNodePtr item)118 _read_data_track (xmlDocPtr project,
119 		  xmlNodePtr item)
120 {
121 	BraseroTrackDataCfg *track;
122         GSList *grafts= NULL;
123         GSList *excluded = NULL;
124 
125 	track = brasero_track_data_cfg_new ();
126 
127 	while (item) {
128 		if (!xmlStrcmp (item->name, (const xmlChar *) "graft")) {
129 			if (!(grafts = _read_graft_point (project, item->xmlChildrenNode, grafts)))
130 				goto error;
131 		}
132 		else if (!xmlStrcmp (item->name, (const xmlChar *) "icon")) {
133 			xmlChar *icon_path;
134 
135 			icon_path = xmlNodeListGetString (project,
136 							  item->xmlChildrenNode,
137 							  1);
138 			if (!icon_path)
139 				goto error;
140 
141 			brasero_track_data_cfg_set_icon (track, (gchar *) icon_path, NULL);
142                         g_free (icon_path);
143 		}
144 		else if (!xmlStrcmp (item->name, (const xmlChar *) "restored")) {
145 			xmlChar *restored;
146 
147 			restored = xmlNodeListGetString (project,
148 							 item->xmlChildrenNode,
149 							 1);
150 			if (!restored)
151 				goto error;
152 
153                         brasero_track_data_cfg_dont_filter_uri (track, (gchar *) restored);
154                         g_free (restored);
155 		}
156 		else if (!xmlStrcmp (item->name, (const xmlChar *) "excluded")) {
157 			xmlChar *excluded_uri;
158 
159 			excluded_uri = xmlNodeListGetString (project,
160 							     item->xmlChildrenNode,
161 							     1);
162 			if (!excluded_uri)
163 				goto error;
164 
165 			excluded = g_slist_prepend (excluded, xmlURIUnescapeString ((char*) excluded_uri, 0, NULL));
166 			g_free (excluded_uri);
167 		}
168 		else if (item->type == XML_ELEMENT_NODE)
169 			goto error;
170 
171 		item = item->next;
172 	}
173 
174         grafts = g_slist_reverse (grafts);
175         excluded = g_slist_reverse (excluded);
176         brasero_track_data_set_source (BRASERO_TRACK_DATA (track),
177                                                            grafts,
178                                                            excluded);
179 	return BRASERO_TRACK (track);
180 
181 error:
182 
183         g_slist_foreach (grafts, (GFunc) brasero_graft_point_free, NULL);
184         g_slist_free (grafts);
185 
186         g_slist_foreach (excluded, (GFunc) g_free, NULL);
187         g_slist_free (excluded);
188 
189 	g_object_unref (track);
190 
191 	return NULL;
192 }
193 
194 static BraseroTrack *
_read_audio_track(xmlDocPtr project,xmlNodePtr uris,gboolean is_video)195 _read_audio_track (xmlDocPtr project,
196 		   xmlNodePtr uris,
197                    gboolean is_video)
198 {
199 	BraseroTrackStreamCfg *track;
200 
201 	track = brasero_track_stream_cfg_new ();
202 
203 	while (uris) {
204 		if (!xmlStrcmp (uris->name, (const xmlChar *) "uri")) {
205 			xmlChar *uri;
206                         gchar *unescaped_uri;
207 
208 			uri = xmlNodeListGetString (project,
209 						    uris->xmlChildrenNode,
210 						    1);
211 			if (!uri)
212 				goto error;
213 
214                         unescaped_uri = g_uri_unescape_string ((char *) uri, NULL);
215                         g_free (uri);
216 
217 			/* Note: this must come before brasero_track_stream_set_boundaries ()
218 			 * or we will reset the end point to 0 */
219 			brasero_track_stream_set_source (BRASERO_TRACK_STREAM (track), unescaped_uri);
220 
221 			/* For the moment pretend it is a video file. Since it is BraseroTrackStreamCfg, that
222 			 * will be set properly afterwards. */
223 			if (is_video)
224 				brasero_track_stream_set_format (BRASERO_TRACK_STREAM (track),
225 				                                 BRASERO_VIDEO_FORMAT_UNDEFINED);
226 
227                         g_free (unescaped_uri);
228 		}
229 		else if (!xmlStrcmp (uris->name, (const xmlChar *) "silence")) {
230 			gchar *silence;
231 
232 			/* impossible to have two gaps in a row */
233 			if (brasero_track_stream_get_gap (BRASERO_TRACK_STREAM (track)) > 0)
234 				goto error;
235 
236 			silence = (gchar *) xmlNodeListGetString (project,
237 								  uris->xmlChildrenNode,
238 								  1);
239 			if (!silence)
240 				goto error;
241 
242                         brasero_track_stream_set_boundaries (BRASERO_TRACK_STREAM (track),
243                                                              -1,
244                                                              -1,
245                                                              g_ascii_strtoull (silence, NULL, 10));
246 			g_free (silence);
247 		}
248 		else if (!xmlStrcmp (uris->name, (const xmlChar *) "start")) {
249 			gchar *start;
250 
251 			start = (gchar *) xmlNodeListGetString (project,
252 								uris->xmlChildrenNode,
253 								1);
254 			if (!start)
255 				goto error;
256 
257                         brasero_track_stream_set_boundaries (BRASERO_TRACK_STREAM (track),
258                                                              g_ascii_strtoull (start, NULL, 10),
259                                                              -1,
260                                                              -1);
261 			g_free (start);
262 		}
263 		else if (!xmlStrcmp (uris->name, (const xmlChar *) "end")) {
264 			gchar *end;
265 
266 			end = (gchar *) xmlNodeListGetString (project,
267 							      uris->xmlChildrenNode,
268 							      1);
269 			if (!end)
270 				goto error;
271 
272                         brasero_track_stream_set_boundaries (BRASERO_TRACK_STREAM (track),
273                                                              -1,
274                                                              g_ascii_strtoull (end, NULL, 10),
275                                                              -1);
276 			g_free (end);
277 		}
278 		else if (!xmlStrcmp (uris->name, (const xmlChar *) "title")) {
279 			xmlChar *title;
280 			gchar *unescaped_title;
281 
282 			title = xmlNodeListGetString (project,
283 						      uris->xmlChildrenNode,
284 						      1);
285 			if (!title)
286 				goto error;
287 
288                         unescaped_title = g_uri_unescape_string ((char *) title, NULL);
289                         g_free (title);
290 
291                         brasero_track_tag_add_string (BRASERO_TRACK (track),
292                                                       BRASERO_TRACK_STREAM_TITLE_TAG,
293                                                       unescaped_title);
294         		g_free (unescaped_title);
295 		}
296 		else if (!xmlStrcmp (uris->name, (const xmlChar *) "artist")) {
297 			xmlChar *artist;
298                         gchar *unescaped_artist;
299 
300 			artist = xmlNodeListGetString (project,
301 						      uris->xmlChildrenNode,
302 						      1);
303 			if (!artist)
304 				goto error;
305 
306 			unescaped_artist = g_uri_unescape_string ((char *) artist, NULL);
307 			g_free (artist);
308 
309                         brasero_track_tag_add_string (BRASERO_TRACK (track),
310                                                       BRASERO_TRACK_STREAM_ARTIST_TAG,
311                                                       unescaped_artist);
312         		g_free (unescaped_artist);
313 		}
314 		else if (!xmlStrcmp (uris->name, (const xmlChar *) "composer")) {
315 			xmlChar *composer;
316                         gchar *unescaped_composer;
317 
318 			composer = xmlNodeListGetString (project,
319 							 uris->xmlChildrenNode,
320 							 1);
321 			if (!composer)
322 				goto error;
323 
324 			unescaped_composer = g_uri_unescape_string ((char *) composer, NULL);
325 			g_free (composer);
326 
327                         brasero_track_tag_add_string (BRASERO_TRACK (track),
328                                                       BRASERO_TRACK_STREAM_COMPOSER_TAG,
329                                                       unescaped_composer);
330         		g_free (unescaped_composer);
331 		}
332 		else if (!xmlStrcmp (uris->name, (const xmlChar *) "isrc")) {
333 			xmlChar *isrc;
334                         gchar *unescaped_isrc;
335 
336 			isrc = xmlNodeListGetString (project,
337 						     uris->xmlChildrenNode,
338 						     1);
339 			if (!isrc)
340 				goto error;
341 
342 			unescaped_isrc = g_uri_unescape_string ((char *) isrc, NULL);
343 			g_free (isrc);
344 
345                         brasero_track_tag_add_string (BRASERO_TRACK (track),
346                                                       BRASERO_TRACK_STREAM_ISRC_TAG,
347                                                       unescaped_isrc);
348         		g_free (unescaped_isrc);
349 		}
350 		else if (uris->type == XML_ELEMENT_NODE)
351 			goto error;
352 
353 		uris = uris->next;
354 	}
355 
356 	return BRASERO_TRACK (track);
357 
358 error:
359 
360 	g_object_unref (track);
361 
362 	return NULL;
363 }
364 
365 static gboolean
_get_tracks(xmlDocPtr project,xmlNodePtr track_node,BraseroBurnSession * session)366 _get_tracks (xmlDocPtr project,
367 	     xmlNodePtr track_node,
368 	     BraseroBurnSession *session)
369 {
370 	GSList *tracks = NULL;
371 	GSList *iter;
372 
373 	track_node = track_node->xmlChildrenNode;
374 
375 	while (track_node) {
376 		BraseroTrack *newtrack;
377 
378 		if (!xmlStrcmp (track_node->name, (const xmlChar *) "audio")) {
379 			newtrack = _read_audio_track (project, track_node->xmlChildrenNode, FALSE);
380 			if (!newtrack)
381 				goto error;
382 
383 			tracks = g_slist_append (tracks, newtrack);
384 		}
385 		else if (!xmlStrcmp (track_node->name, (const xmlChar *) "data")) {
386 			newtrack = _read_data_track (project, track_node->xmlChildrenNode);
387 
388 			if (!newtrack)
389 				goto error;
390 
391 			tracks = g_slist_append (tracks, newtrack);
392 		}
393 		else if (!xmlStrcmp (track_node->name, (const xmlChar *) "video")) {
394 			newtrack = _read_audio_track (project, track_node->xmlChildrenNode, TRUE);
395 
396 			if (!newtrack)
397 				goto error;
398 
399 			tracks = g_slist_append (tracks, newtrack);
400 		}
401 		else if (track_node->type == XML_ELEMENT_NODE)
402 			goto error;
403 
404 		track_node = track_node->next;
405 	}
406 
407 	if (!tracks)
408 		goto error;
409 
410 	for (iter = tracks; iter; iter = iter->next) {
411 		BraseroTrack *newtrack;
412 
413 		newtrack = iter->data;
414 		brasero_burn_session_add_track (session, newtrack, NULL);
415 		g_object_unref (newtrack);
416 	}
417 
418 	g_slist_free (tracks);
419 
420 	return TRUE;
421 
422 error :
423 
424 	if (tracks) {
425 		g_slist_foreach (tracks, (GFunc) g_object_unref, NULL);
426 		g_slist_free (tracks);
427 	}
428 
429 	return FALSE;
430 }
431 
432 gboolean
brasero_project_open_project_xml(const gchar * uri,BraseroBurnSession * session,gboolean warn_user)433 brasero_project_open_project_xml (const gchar *uri,
434 				  BraseroBurnSession *session,
435 				  gboolean warn_user)
436 {
437 	xmlNodePtr track_node = NULL;
438 	gchar *label = NULL;
439 	gchar *cover = NULL;
440 	xmlDocPtr project;
441 	xmlNodePtr item;
442 	gboolean retval;
443 	GFile *file;
444 	gchar *path;
445 
446 	file = g_file_new_for_commandline_arg (uri);
447 	path = g_file_get_path (file);
448 	g_object_unref (file);
449 	if (!path)
450 		return FALSE;
451 
452 	/* start parsing xml doc */
453 	project = xmlParseFile (path);
454     	g_free (path);
455 
456 	if (!project) {
457 	    	if (warn_user)
458 			brasero_project_invalid_project_dialog (_("The project could not be opened"));
459 
460 		return FALSE;
461 	}
462 
463 	/* parses the "header" */
464 	item = xmlDocGetRootElement (project);
465 	if (!item) {
466 	    	if (warn_user)
467 			brasero_project_invalid_project_dialog (_("The file is empty"));
468 
469 		xmlFreeDoc (project);
470 		return FALSE;
471 	}
472 
473 	if (xmlStrcmp (item->name, (const xmlChar *) "braseroproject")
474 	||  item->next)
475 		goto error;
476 
477 	item = item->children;
478 	while (item) {
479 		if (!xmlStrcmp (item->name, (const xmlChar *) "version")) {
480 			/* simply ignore it */
481 		}
482 		else if (!xmlStrcmp (item->name, (const xmlChar *) "label")) {
483 			label = (gchar *) xmlNodeListGetString (project,
484 								item->xmlChildrenNode,
485 								1);
486 			if (!(label))
487 				goto error;
488 		}
489 		else if (!xmlStrcmp (item->name, (const xmlChar *) "cover")) {
490 			xmlChar *escaped;
491 
492 			escaped = xmlNodeListGetString (project,
493 							item->xmlChildrenNode,
494 							1);
495 			if (!escaped)
496 				goto error;
497 
498 			cover = g_uri_unescape_string ((char *) escaped, NULL);
499 			g_free (escaped);
500 		}
501 		else if (!xmlStrcmp (item->name, (const xmlChar *) "track")) {
502 			if (track_node)
503 				goto error;
504 
505 			track_node = item;
506 		}
507 		else if (item->type == XML_ELEMENT_NODE)
508 			goto error;
509 
510 		item = item->next;
511 	}
512 
513 	retval = _get_tracks (project, track_node, session);
514 	if (!retval)
515 		goto error;
516 
517 	xmlFreeDoc (project);
518 
519         brasero_burn_session_set_label (session, label);
520         g_free (label);
521 
522         if (cover) {
523                 GValue *value;
524 
525                 value = g_new0 (GValue, 1);
526                 g_value_init (value, G_TYPE_STRING);
527                 g_value_set_string (value, cover);
528                 brasero_burn_session_tag_add (session,
529                                                BRASERO_COVER_URI,
530                                                value);
531 
532                 g_free (cover);
533         }
534 
535         return retval;
536 
537 error:
538 
539 	if (cover)
540 		g_free (cover);
541 	if (label)
542 		g_free (label);
543 
544 	xmlFreeDoc (project);
545     	if (warn_user)
546 		brasero_project_invalid_project_dialog (_("It does not seem to be a valid Brasero project"));
547 
548 	return FALSE;
549 }
550 
551 #ifdef BUILD_PLAYLIST
552 
553 static void
brasero_project_playlist_playlist_started(TotemPlParser * parser,const gchar * uri,GHashTable * metadata,gpointer user_data)554 brasero_project_playlist_playlist_started (TotemPlParser *parser,
555 					   const gchar *uri,
556 					   GHashTable *metadata,
557 					   gpointer user_data)
558 {
559         BraseroBurnSession *session = user_data;
560 
561         brasero_burn_session_set_label (session, g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE));
562 }
563 
564 static void
brasero_project_playlist_entry_parsed(TotemPlParser * parser,const gchar * uri,GHashTable * metadata,gpointer user_data)565 brasero_project_playlist_entry_parsed (TotemPlParser *parser,
566 				       const gchar *uri,
567 				       GHashTable *metadata,
568 				       gpointer user_data)
569 {
570 	BraseroBurnSession *session = user_data;
571         BraseroTrackStreamCfg *track;
572 
573         track = brasero_track_stream_cfg_new ();
574         brasero_track_stream_set_source (BRASERO_TRACK_STREAM (track), uri);
575         brasero_burn_session_add_track (session, BRASERO_TRACK (track), NULL);
576 }
577 
578 gboolean
brasero_project_open_audio_playlist_project(const gchar * uri,BraseroBurnSession * session,gboolean warn_user)579 brasero_project_open_audio_playlist_project (const gchar *uri,
580 					     BraseroBurnSession *session,
581 					     gboolean warn_user)
582 {
583 	TotemPlParser *parser;
584 	TotemPlParserResult result;
585 	GFile *file;
586 	char *_uri;
587 
588 	file = g_file_new_for_commandline_arg (uri);
589 	_uri = g_file_get_uri (file);
590 	g_object_unref (file);
591 
592 	parser = totem_pl_parser_new ();
593 	g_object_set (parser,
594 		      "recurse", FALSE,
595 		      "disable-unsafe", TRUE,
596 		      NULL);
597 
598 	g_signal_connect (parser,
599 			  "playlist-started",
600 			  G_CALLBACK (brasero_project_playlist_playlist_started),
601 			  session);
602 
603 	g_signal_connect (parser,
604 			  "entry-parsed",
605 			  G_CALLBACK (brasero_project_playlist_entry_parsed),
606 			  session);
607 
608 	result = totem_pl_parser_parse (parser, _uri, FALSE);
609 	if (result != TOTEM_PL_PARSER_RESULT_SUCCESS) {
610 		if (warn_user)
611 			brasero_project_invalid_project_dialog (_("It does not seem to be a valid Brasero project"));
612 	}
613 
614 	g_free (_uri);
615 	g_object_unref (parser);
616 
617 	return (result == TOTEM_PL_PARSER_RESULT_SUCCESS);
618 }
619 
620 #endif
621 
622 /**
623  * Project saving
624  */
625 
626 static gboolean
_save_audio_track_xml(xmlTextWriter * project,BraseroTrackStream * track)627 _save_audio_track_xml (xmlTextWriter *project,
628 		       BraseroTrackStream *track)
629 {
630 	xmlChar *escaped;
631 	gchar *start;
632 	gint success;
633 	gchar *uri;
634 	gchar *end;
635 
636 	uri = brasero_track_stream_get_source (track, TRUE);
637 	escaped = (unsigned char *) g_uri_escape_string (uri, NULL, FALSE);
638 	g_free (uri);
639 
640 	success = xmlTextWriterWriteElement (project,
641 					    (xmlChar *) "uri",
642 					     escaped);
643 	g_free (escaped);
644 
645 	if (success == -1)
646 		return FALSE;
647 
648 	if (brasero_track_stream_get_gap (track) > 0) {
649 		gchar *silence;
650 
651 		silence = g_strdup_printf ("%"G_GINT64_FORMAT, brasero_track_stream_get_gap (track));
652 		success = xmlTextWriterWriteElement (project,
653 						     (xmlChar *) "silence",
654 						     (xmlChar *) silence);
655 
656 		g_free (silence);
657 		if (success == -1)
658 			return FALSE;
659 	}
660 
661 	if (brasero_track_stream_get_end (track) > 0) {
662 		/* start of the song */
663 		start = g_strdup_printf ("%"G_GINT64_FORMAT, brasero_track_stream_get_start (track));
664 		success = xmlTextWriterWriteElement (project,
665 						     (xmlChar *) "start",
666 						     (xmlChar *) start);
667 
668 		g_free (start);
669 		if (success == -1)
670 			return FALSE;
671 
672 		/* end of the song */
673 		end = g_strdup_printf ("%"G_GINT64_FORMAT, brasero_track_stream_get_end (track));
674 		success = xmlTextWriterWriteElement (project,
675 						     (xmlChar *) "end",
676 						     (xmlChar *) end);
677 
678 		g_free (end);
679 		if (success == -1)
680 			return FALSE;
681 	}
682 
683 	if (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_TITLE_TAG)) {
684 		escaped = (unsigned char *) g_uri_escape_string (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_TITLE_TAG), NULL, FALSE);
685 		success = xmlTextWriterWriteElement (project,
686 						    (xmlChar *) "title",
687 						     escaped);
688 		g_free (escaped);
689 
690 		if (success == -1)
691 			return FALSE;
692 	}
693 
694 	if (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_ARTIST_TAG)) {
695 		escaped = (unsigned char *) g_uri_escape_string (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_ARTIST_TAG), NULL, FALSE);
696 		success = xmlTextWriterWriteElement (project,
697 						    (xmlChar *) "artist",
698 						     escaped);
699 		g_free (escaped);
700 
701 		if (success == -1)
702 			return FALSE;
703 	}
704 
705 	if (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_COMPOSER_TAG)) {
706 		escaped = (unsigned char *) g_uri_escape_string (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_COMPOSER_TAG), NULL, FALSE);
707 		success = xmlTextWriterWriteElement (project,
708 						    (xmlChar *) "composer",
709 						     escaped);
710 		g_free (escaped);
711 		if (success == -1)
712 			return FALSE;
713 	}
714 
715 	if (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_ISRC_TAG)) {
716 		escaped = (unsigned char *) g_uri_escape_string (brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_ISRC_TAG), NULL, FALSE);
717 		success = xmlTextWriterWriteElement (project,
718 						     (xmlChar *) "isrc",
719 						     escaped);
720 
721 		g_free (escaped);
722 		if (success == -1)
723 			return FALSE;
724 	}
725 
726 	return TRUE;
727 }
728 
729 static gboolean
_save_data_track_xml(xmlTextWriter * project,BraseroBurnSession * session)730 _save_data_track_xml (xmlTextWriter *project,
731 		      BraseroBurnSession *session)
732 {
733 	gchar *uri;
734 	gint success;
735 	GSList *iter;
736 	GSList *tracks;
737 	GSList *grafts;
738 	gchar *filename;
739 	BraseroTrackDataCfg *track;
740 
741 	tracks = brasero_burn_session_get_tracks (session);
742 	track = BRASERO_TRACK_DATA_CFG (tracks->data);
743 
744 	filename = brasero_track_data_cfg_get_icon_path (track);
745 	if (filename) {
746 		/* Write the icon if any */
747 		success = xmlTextWriterWriteElement (project, (xmlChar *) "icon", (xmlChar *) filename);
748 		g_free (filename);
749 		if (success < 0)
750 			return FALSE;
751 	}
752 
753 	grafts = brasero_track_data_get_grafts (BRASERO_TRACK_DATA (track));
754 	for (; grafts; grafts = grafts->next) {
755 		BraseroGraftPt *graft;
756 
757 		graft = grafts->data;
758 
759 		success = xmlTextWriterStartElement (project, (xmlChar *) "graft");
760 		if (success < 0)
761 			return FALSE;
762 
763 		success = xmlTextWriterWriteElement (project, (xmlChar *) "path", (xmlChar *) graft->path);
764 		if (success < 0)
765 			return FALSE;
766 
767 		if (graft->uri) {
768 			xmlChar *escaped;
769 
770 			escaped = (unsigned char *) g_uri_escape_string (graft->uri, NULL, FALSE);
771 			success = xmlTextWriterWriteElement (project, (xmlChar *) "uri", escaped);
772 			g_free (escaped);
773 			if (success < 0)
774 				return FALSE;
775 		}
776 
777 		success = xmlTextWriterEndElement (project); /* graft */
778 		if (success < 0)
779 			return FALSE;
780 	}
781 
782 	/* save excluded uris */
783 	iter = brasero_track_data_get_excluded_list (BRASERO_TRACK_DATA (track));
784 	for (; iter; iter = iter->next) {
785 		xmlChar *escaped;
786 
787 		escaped = xmlURIEscapeStr ((xmlChar *) iter->data, NULL);
788 		success = xmlTextWriterWriteElement (project, (xmlChar *) "excluded", (xmlChar *) escaped);
789 		g_free (escaped);
790 		if (success < 0)
791 			return FALSE;
792 	}
793 
794 	/* save restored uris */
795 	iter = brasero_track_data_cfg_get_restored_list (track);
796 	for (; iter; iter = iter->next) {
797 		uri = iter->data;
798 		success = xmlTextWriterWriteElement (project, (xmlChar *) "restored", (xmlChar *) uri);
799 		if (success < 0)
800 			return FALSE;
801 	}
802 
803 	/* NOTE: we don't write symlinks and unreadable they are useless */
804 	return TRUE;
805 }
806 
807 gboolean
brasero_project_save_project_xml(BraseroBurnSession * session,const gchar * uri)808 brasero_project_save_project_xml (BraseroBurnSession *session,
809 				  const gchar *uri)
810 {
811 	BraseroTrackType *track_type = NULL;
812 	xmlTextWriter *project;
813 	gboolean retval;
814 	GSList *tracks;
815 	GValue *value;
816 	gint success;
817 	gchar *path;
818 
819 	path = g_filename_from_uri (uri, NULL, NULL);
820 	if (!path)
821 		return FALSE;
822 
823 	project = xmlNewTextWriterFilename (path, 0);
824 	if (!project) {
825 		g_free (path);
826 		return FALSE;
827 	}
828 
829 	xmlTextWriterSetIndent (project, 1);
830 	xmlTextWriterSetIndentString (project, (xmlChar *) "\t");
831 
832 	success = xmlTextWriterStartDocument (project,
833 					      NULL,
834 					      "UTF-8",
835 					      NULL);
836 	if (success < 0)
837 		goto error;
838 
839 	success = xmlTextWriterStartElement (project, (xmlChar *) "braseroproject");
840 	if (success < 0)
841 		goto error;
842 
843 	/* write the name of the version */
844 	success = xmlTextWriterWriteElement (project,
845 					     (xmlChar *) "version",
846 					     (xmlChar *) BRASERO_PROJECT_VERSION);
847 	if (success < 0)
848 		goto error;
849 
850 	if (brasero_burn_session_get_label (session)) {
851 		success = xmlTextWriterWriteElement (project,
852 						     (xmlChar *) "label",
853 						     (xmlChar *) brasero_burn_session_get_label (session));
854 
855 		if (success < 0)
856 			goto error;
857 	}
858 
859 	value = NULL;
860 	brasero_burn_session_tag_lookup (session,
861 					 BRASERO_COVER_URI,
862 					 &value);
863 	if (value) {
864 		gchar *escaped;
865 
866 		escaped = g_uri_escape_string (g_value_get_string (value), NULL, FALSE);
867 		success = xmlTextWriterWriteElement (project,
868 						     (xmlChar *) "cover",
869 						     (xmlChar *) escaped);
870 		g_free (escaped);
871 
872 		if (success < 0)
873 			goto error;
874 	}
875 
876 	success = xmlTextWriterStartElement (project, (xmlChar *) "track");
877 	if (success < 0)
878 		goto error;
879 
880 	track_type = brasero_track_type_new ();
881 	tracks = brasero_burn_session_get_tracks (session);
882 
883 	for (; tracks; tracks = tracks->next) {
884 		BraseroTrack *track;
885 
886 		track = tracks->data;
887 
888 		brasero_track_get_track_type (track, track_type);
889 		if (brasero_track_type_get_has_stream (track_type)) {
890 			if (BRASERO_STREAM_FORMAT_HAS_VIDEO (brasero_track_type_get_stream_format (track_type)))
891 				success = xmlTextWriterStartElement (project, (xmlChar *) "video");
892 			else
893 				success = xmlTextWriterStartElement (project, (xmlChar *) "audio");
894 
895 			if (success < 0)
896 				goto error;
897 
898 			retval = _save_audio_track_xml (project, BRASERO_TRACK_STREAM (track));
899 			if (!retval)
900 				goto error;
901 
902 			success = xmlTextWriterEndElement (project); /* audio/video */
903 			if (success < 0)
904 				goto error;
905 		}
906 		else if (brasero_track_type_get_has_data (track_type)) {
907 			success = xmlTextWriterStartElement (project, (xmlChar *) "data");
908 			if (success < 0)
909 				goto error;
910 
911 			retval = _save_data_track_xml (project, session);
912 			if (!retval)
913 				goto error;
914 
915 			success = xmlTextWriterEndElement (project); /* data */
916 			if (success < 0)
917 				goto error;
918 		}
919 		else
920 			retval = FALSE;
921 	}
922 
923 	success = xmlTextWriterEndElement (project); /* track */
924 	if (success < 0)
925 		goto error;
926 
927 	brasero_track_type_free (track_type);
928 
929 	success = xmlTextWriterEndElement (project); /* braseroproject */
930 	if (success < 0)
931 		goto error;
932 
933 	xmlTextWriterEndDocument (project);
934 	xmlFreeTextWriter (project);
935 	g_free (path);
936 	return TRUE;
937 
938 error:
939 
940 	if (track_type)
941 		brasero_track_type_free (track_type);
942 
943 	xmlTextWriterEndDocument (project);
944 	xmlFreeTextWriter (project);
945 
946 	g_remove (path);
947 	g_free (path);
948 
949 	return FALSE;
950 }
951 
952 gboolean
brasero_project_save_audio_project_plain_text(BraseroBurnSession * session,const gchar * uri)953 brasero_project_save_audio_project_plain_text (BraseroBurnSession *session,
954 					       const gchar *uri)
955 {
956 	const gchar *title;
957 	guint written;
958 	GSList *iter;
959 	gchar *path;
960 	FILE *file;
961 
962     	path = g_filename_from_uri (uri, NULL, NULL);
963     	if (!path)
964 		return FALSE;
965 
966 	file = fopen (path, "w+");
967 	g_free (path);
968 	if (!file)
969 		return FALSE;
970 
971 	/* write title */
972 	title = brasero_burn_session_get_label (session);
973 	written = fwrite (title, strlen (title), 1, file);
974 	if (written != 1)
975 		goto error;
976 
977 	written = fwrite ("\n", 1, 1, file);
978 	if (written != 1)
979 		goto error;
980 
981 	iter = brasero_burn_session_get_tracks (session);
982 	for (; iter; iter = iter->next) {
983 		BraseroTrackStream *track;
984 		const gchar *text;
985 		gchar *time;
986 		guint64 len;
987 		gchar *uri;
988 
989 		track = iter->data;
990 
991 		text = brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_TITLE_TAG);
992 		written = fwrite (title, 1, strlen (title), file);
993 		if (written != strlen (title))
994 			goto error;
995 
996 		len = 0;
997 		brasero_track_stream_get_length (track, &len);
998 		time = brasero_units_get_time_string (len, TRUE, FALSE);
999 		if (time) {
1000 			written = fwrite ("\t", 1, 1, file);
1001 			if (written != 1)
1002 				goto error;
1003 
1004 			written = fwrite (time, 1, strlen (time), file);
1005 			if (written != strlen (time)) {
1006 				g_free (time);
1007 				goto error;
1008 			}
1009 			g_free (time);
1010 		}
1011 
1012 		text = brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_ARTIST_TAG);
1013 		if (text) {
1014 			gchar *string;
1015 
1016 			written = fwrite ("\t", 1, 1, file);
1017 			if (written != 1)
1018 				goto error;
1019 
1020 			/* Translators: %s is an artist */
1021 			string = g_strdup_printf (" by %s", text);
1022 			written = fwrite (string, 1, strlen (string), file);
1023 			if (written != strlen (string)) {
1024 				g_free (string);
1025 				goto error;
1026 			}
1027 			g_free (string);
1028 		}
1029 
1030 		written = fwrite ("\n(", 1, 2, file);
1031 		if (written != 2)
1032 			goto error;
1033 
1034 		uri = brasero_track_stream_get_source (track, TRUE);
1035 		written = fwrite (uri, 1, strlen (uri), file);
1036 		if (written != strlen (uri)) {
1037 			g_free (uri);
1038 			goto error;
1039 		}
1040 
1041 		g_free (uri);
1042 
1043 		written = fwrite (")", 1, 1, file);
1044 		if (written != 1)
1045 			goto error;
1046 
1047 		written = fwrite ("\n\n", 1, 2, file);
1048 		if (written != 2)
1049 			goto error;
1050 	}
1051 
1052 	fclose (file);
1053 	return TRUE;
1054 
1055 error:
1056 
1057 	fclose (file);
1058 
1059 	return FALSE;
1060 }
1061 
1062 #ifdef BUILD_PLAYLIST
1063 
1064 gboolean
brasero_project_save_audio_project_playlist(BraseroBurnSession * session,const gchar * uri,BraseroProjectSave type)1065 brasero_project_save_audio_project_playlist (BraseroBurnSession *session,
1066 					     const gchar *uri,
1067 					     BraseroProjectSave type)
1068 {
1069 	TotemPlParserType pl_type;
1070 	TotemPlParser *parser;
1071 	TotemPlPlaylist *playlist;
1072 	TotemPlPlaylistIter pl_iter;
1073 	gboolean result;
1074 	GFile *file;
1075 	GSList *iter;
1076 
1077 	file = g_file_new_for_uri (uri);
1078 	parser = totem_pl_parser_new ();
1079 	playlist = totem_pl_playlist_new ();
1080 
1081 	/* populate playlist */
1082 	iter = brasero_burn_session_get_tracks (session);
1083 	for (; iter; iter = iter->next) {
1084 		BraseroTrackStream *track;
1085 		const gchar *title;
1086 		gchar *uri;
1087 
1088 		track = iter->data;
1089 
1090 		uri = brasero_track_stream_get_source (track, TRUE);
1091 		title = brasero_track_tag_lookup_string (BRASERO_TRACK (track), BRASERO_TRACK_STREAM_TITLE_TAG);
1092 
1093 		totem_pl_playlist_append (playlist, &pl_iter);
1094 		totem_pl_playlist_set (playlist, &pl_iter,
1095 				       TOTEM_PL_PARSER_FIELD_URI, uri,
1096 				       TOTEM_PL_PARSER_FIELD_TITLE, title,
1097 				       NULL);
1098 		g_free (uri);
1099 	}
1100 
1101 	switch (type) {
1102 		case BRASERO_PROJECT_SAVE_PLAYLIST_M3U:
1103 			pl_type = TOTEM_PL_PARSER_M3U;
1104 			break;
1105 		case BRASERO_PROJECT_SAVE_PLAYLIST_XSPF:
1106 			pl_type = TOTEM_PL_PARSER_XSPF;
1107 			break;
1108 		case BRASERO_PROJECT_SAVE_PLAYLIST_IRIVER_PLA:
1109 			pl_type = TOTEM_PL_PARSER_IRIVER_PLA;
1110 			break;
1111 
1112 		case BRASERO_PROJECT_SAVE_PLAYLIST_PLS:
1113 		default:
1114 			pl_type = TOTEM_PL_PARSER_PLS;
1115 			break;
1116 	}
1117 
1118 	result = totem_pl_parser_save (parser, playlist, file,
1119 				       brasero_burn_session_get_label (session),
1120 				       pl_type, NULL);
1121 
1122 	g_object_unref (playlist);
1123 	g_object_unref (parser);
1124 	g_object_unref (file);
1125 
1126 	return result;
1127 }
1128 
1129 #endif
1130