1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-open-pkg-utils.c: Utilities for handling Open Package zip files
4  * 			from MS Office 2007 or XPS.
5  *
6  * Copyright (C) 2006-2007 Jody Goldberg (jody@gnome.org)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2.1 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 
23 #include <gsf-config.h>
24 #include <gsf/gsf-open-pkg-utils.h>
25 #include <gsf/gsf.h>
26 
27 #include <glib/gi18n-lib.h>
28 
29 #include <string.h>
30 
31 #undef G_LOG_DOMAIN
32 #define G_LOG_DOMAIN "libgsf:open_pkg"
33 
34 enum {
35 	OPEN_PKG_NS_REL
36 };
37 
38 static GsfXMLInNS const open_pkg_ns[] = {
39 	GSF_XML_IN_NS (OPEN_PKG_NS_REL,   "http://schemas.openxmlformats.org/package/2006/relationships"),
40 	GSF_XML_IN_NS_END
41 };
42 
43 struct _GsfOpenPkgRel {
44 	char *id, *type, *target;
45 	gboolean is_extern;
46 };
47 
48 struct _GsfOpenPkgRels {
49 	GHashTable	*by_id;
50 	GHashTable	*by_type;
51 };
52 
53 static void
gsf_open_pkg_rels_free(GsfOpenPkgRels * rels)54 gsf_open_pkg_rels_free (GsfOpenPkgRels *rels)
55 {
56 	g_hash_table_destroy (rels->by_id);
57 	g_hash_table_destroy (rels->by_type);
58 	g_free (rels);
59 }
60 
61 static void
gsf_open_pkg_rel_free(GsfOpenPkgRel * rel)62 gsf_open_pkg_rel_free (GsfOpenPkgRel *rel)
63 {
64 	g_free (rel->id);	rel->id = NULL;
65 	g_free (rel->type);	rel->type = NULL;
66 	g_free (rel->target);	rel->target = NULL;
67 	g_free (rel);
68 }
69 
70 static void
open_pkg_rel_begin(GsfXMLIn * xin,xmlChar const ** attrs)71 open_pkg_rel_begin (GsfXMLIn *xin, xmlChar const **attrs)
72 {
73 	GsfOpenPkgRels *rels = xin->user_state;
74 	GsfOpenPkgRel *rel, *orel;
75 	xmlChar const *id = NULL;
76 	xmlChar const *type = NULL;
77 	xmlChar const *target = NULL;
78 	gboolean is_extern = FALSE;
79 
80 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
81 		if (0 == strcmp (attrs[0], "Id"))
82 			id = attrs[1];
83 		else if (0 == strcmp (attrs[0], "Type"))
84 			type = attrs[1];
85 		else if (0 == strcmp (attrs[0], "Target"))
86 			target = attrs[1];
87 		else if (0 == strcmp (attrs[0], "TargetMode"))
88 			is_extern = 0 == strcmp (attrs[1], "External");
89 
90 	if (id == NULL) {
91 		g_warning ("Broken relation: missing id");
92 		id = "?";
93 	}
94 	if (type == NULL) {
95 		g_warning ("Broken relation: missing type");
96 		type = "?";
97 	}
98 	if (target == NULL) {
99 		g_warning ("Broken relation: missing target");
100 		target = "?";
101 	}
102 
103 	rel = g_new0 (GsfOpenPkgRel, 1);
104 	rel->id		= g_strdup (id);
105 	rel->type	= g_strdup (type);
106 	rel->target	= g_strdup (target);
107 	rel->is_extern	= is_extern;
108 
109 	/* Make sure we don't point to a freed rel in the type hash.  */
110 	orel = g_hash_table_lookup (rels->by_id, id);
111 	if (orel)
112 		g_hash_table_remove (rels->by_type, orel->type);
113 	g_hash_table_replace (rels->by_type, rel->type, rel);
114 
115 	/* This will free a duplicate rel, so do this last.  */
116 	g_hash_table_replace (rels->by_id, rel->id, rel);
117 }
118 
119 static GsfXMLInNode const open_pkg_rel_dtd[] = {
120 GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
121 GSF_XML_IN_NODE_FULL (START, RELS, OPEN_PKG_NS_REL, "Relationships", GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
122   GSF_XML_IN_NODE (RELS, REL, OPEN_PKG_NS_REL, "Relationship", GSF_XML_NO_CONTENT, open_pkg_rel_begin, NULL),
123 
124 GSF_XML_IN_NODE_END
125 };
126 
127 /**
128  * gsf_open_pkg_rel_is_extern:
129  * @rel: #GsfOpenPkgRel
130  *
131  * Returns: %TRUE if @rel has mode 'External'
132  **/
133 gboolean
gsf_open_pkg_rel_is_extern(GsfOpenPkgRel const * rel)134 gsf_open_pkg_rel_is_extern (GsfOpenPkgRel const *rel)
135 {
136 	g_return_val_if_fail (rel != NULL, FALSE);
137 	return rel->is_extern;
138 }
139 
140 /**
141  * gsf_open_pkg_rel_get_target:
142  * @rel: #GsfOpenPkgRel
143  *
144  * Returns: (transfer none): The target of @rel.
145  **/
146 char const *
gsf_open_pkg_rel_get_target(GsfOpenPkgRel const * rel)147 gsf_open_pkg_rel_get_target (GsfOpenPkgRel const *rel)
148 {
149 	g_return_val_if_fail (rel != NULL, NULL);
150 	return rel->target;
151 }
152 
153 /**
154  * gsf_open_pkg_rel_get_type:
155  * @rel: #GsfOpenPkgRel
156  *
157  * Returns: (transfer none): The type of @rel.
158  **/
159 char const *
gsf_open_pkg_rel_get_type(GsfOpenPkgRel const * rel)160 gsf_open_pkg_rel_get_type (GsfOpenPkgRel const *rel)
161 {
162 	g_return_val_if_fail (rel != NULL, NULL);
163 	return rel->type;
164 }
165 
166 static GsfOpenPkgRels *
gsf_open_pkg_get_rels(GsfInput * opkg)167 gsf_open_pkg_get_rels (GsfInput *opkg)
168 {
169 	GsfOpenPkgRels *rels = NULL;
170 
171 	g_return_val_if_fail (opkg != NULL, NULL);
172 
173 	if (NULL == (rels = g_object_get_data (G_OBJECT (opkg), "OpenPkgRels"))) {
174 		char const *part_name = gsf_input_name (opkg);
175 		GsfXMLInDoc *rel_doc;
176 		GsfInput *rel_stream;
177 
178 		if (NULL != part_name) {
179 			GsfInfile *container = gsf_input_container (opkg);
180 			char *rel_name;
181 
182 			g_return_val_if_fail (container != NULL, NULL);
183 
184 			rel_name = g_strconcat (part_name, ".rels", NULL);
185 			rel_stream = gsf_infile_child_by_vname (container, "_rels", rel_name, NULL);
186 			g_free (rel_name);
187 		} else /* the root */
188 			rel_stream = gsf_infile_child_by_vname (GSF_INFILE (opkg), "_rels", ".rels", NULL);
189 
190 		if (NULL != rel_stream) {
191 			rels = g_new (GsfOpenPkgRels, 1);
192 			rels->by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
193 				NULL, (GDestroyNotify)gsf_open_pkg_rel_free);
194 			rels->by_type = g_hash_table_new (g_str_hash, g_str_equal);
195 
196 			rel_doc = gsf_xml_in_doc_new (open_pkg_rel_dtd, open_pkg_ns);
197 			(void) gsf_xml_in_doc_parse (rel_doc, rel_stream, rels);
198 
199 			gsf_xml_in_doc_free (rel_doc);
200 			g_object_unref (rel_stream);
201 		}
202 
203 		g_object_set_data_full (G_OBJECT (opkg), "OpenPkgRels", rels,
204 			(GDestroyNotify) gsf_open_pkg_rels_free);
205 	}
206 
207 	return rels;
208 }
209 
210 /**
211  * gsf_open_pkg_open_rel:
212  * @opkg: #GsfInput
213  * @rel: #GsfOpenPkgRel
214  * @err: #GError.
215  *
216  * Returns: (transfer full): a new #GsfInput which the called needs to unref, or %NULL and sets @err
217  **/
218 GsfInput *
gsf_open_pkg_open_rel(GsfInput * opkg,GsfOpenPkgRel const * rel,G_GNUC_UNUSED GError ** err)219 gsf_open_pkg_open_rel (GsfInput *opkg, GsfOpenPkgRel const *rel,
220 		       G_GNUC_UNUSED GError **err /* just in case we need it one day */ )
221 {
222 	GsfInput *res = NULL;
223 	GsfInfile *parent, *prev_parent;
224 	gchar **elems;
225 	unsigned i;
226 	const char *target;
227 
228 	g_return_val_if_fail (rel != NULL, NULL);
229 	g_return_val_if_fail (opkg != NULL, NULL);
230 
231 	/* References from the root use children of opkg
232 	 * References from a child are relative to siblings of opkg */
233 	parent = gsf_input_name (opkg)
234 		? gsf_input_container (opkg)
235 		: GSF_INFILE (opkg);
236 
237 	target = rel->target;
238 	if (target[0] == '/') {
239 		target++;
240 		/* Handle absolute references by going as far up as we can.  */
241 		while (1) {
242 			GsfInfile *next_parent = gsf_input_container (GSF_INPUT (parent));
243 			if (next_parent &&
244 			    G_OBJECT_TYPE (next_parent) == G_OBJECT_TYPE (parent))
245 				parent = next_parent;
246 			else
247 				break;
248 		}
249 	}
250 
251 	g_object_ref (parent);
252 	elems = g_strsplit (rel->target, "/", 0);
253 	for (i = 0 ; elems[i] && NULL != parent ; i++) {
254 		if (0 == strcmp (elems[i], ".") || '\0' == *elems[i])
255 			continue; /* ignore '.' and empty */
256 
257 		prev_parent = parent;
258 		if (0 == strcmp (elems[i], "..")) {
259 			parent = gsf_input_container (GSF_INPUT (parent));
260 			res = NULL;	/* only return newly created children */
261 			if (NULL != parent) {
262 				/* check for attempt to gain access outside the zip file */
263 				if (G_OBJECT_TYPE (parent) == G_OBJECT_TYPE (prev_parent))
264 					g_object_ref (parent);
265 				else {
266 					g_warning ("Broken file: relation access outside container\n");
267 					parent = NULL;
268 				}
269 			}
270 		} else {
271 			res = gsf_infile_child_by_name (parent, elems[i]);
272 			if (NULL != elems[i+1]) {
273 				g_return_val_if_fail (GSF_IS_INFILE (res), NULL);
274 				parent = GSF_INFILE (res);
275 			}
276 		}
277 		g_object_unref (prev_parent);
278 	}
279 	g_strfreev (elems);
280 
281 	return res;
282 }
283 
284 /**
285  * gsf_open_pkg_lookup_rel_by_type: (skip)
286  * @opkg: #GsfInput
287  * @type: target
288  *
289  * New in 1.14.6
290  *
291  * Finds _a_ relation of @opkg with @type (no order is guaranteed)
292  *
293  * NOTE: skipping because gsf_open_pkg_rel_get_type() does not return a GType.
294  *
295  * Returns: (transfer none): A #GsfOpenPkgRel or %NULL
296  **/
297 GsfOpenPkgRel *
gsf_open_pkg_lookup_rel_by_type(GsfInput * opkg,char const * type)298 gsf_open_pkg_lookup_rel_by_type (GsfInput *opkg, char const *type)
299 {
300 	GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
301 	return rels ? g_hash_table_lookup (rels->by_type, type) : NULL;
302 }
303 
304 /**
305  * gsf_open_pkg_lookup_rel_by_id: (skip)
306  * @opkg: #GsfInput
307  * @id: identifier.
308  *
309  * New in 1.14.6
310  *
311  * Finds @opkg's relation with @id
312  *
313  * NOTE: skipping because gsf_open_pkg_rel_get_type() does not return a GType.
314  *
315  * Returns: (transfer none): A #GsfOpenPkgRel or %NULL
316  **/
317 GsfOpenPkgRel *
gsf_open_pkg_lookup_rel_by_id(GsfInput * opkg,char const * id)318 gsf_open_pkg_lookup_rel_by_id (GsfInput *opkg, char const *id)
319 {
320 	GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
321 	return rels ? g_hash_table_lookup (rels->by_id, id) : NULL;
322 }
323 
324 struct pkg_iter_data {
325 	GsfInput *opkg;
326 	GsfOpenPkgIter func;
327 	gpointer user_data;
328 };
329 
330 static void
cb_foreach_rel(G_GNUC_UNUSED gpointer id,GsfOpenPkgRel * rel,struct pkg_iter_data * dat)331 cb_foreach_rel (G_GNUC_UNUSED gpointer id,
332 		GsfOpenPkgRel *rel,
333 		struct pkg_iter_data *dat)
334 {
335 	(*dat->func) (dat->opkg, rel, dat->user_data);
336 }
337 
338 /**
339  * gsf_open_pkg_foreach_rel:
340  * @opkg: #GsfInput
341  * @func: (scope call): #GsfOpenPkgIter
342  * @user_data: gpointer
343  *
344  * New in 1.14.9
345  *
346  * Walks each relationship associated with @opkg and calls @func with @user_data.
347  **/
348 void
gsf_open_pkg_foreach_rel(GsfInput * opkg,GsfOpenPkgIter func,gpointer user_data)349 gsf_open_pkg_foreach_rel (GsfInput *opkg,
350 			  GsfOpenPkgIter func,
351 			  gpointer       user_data)
352 {
353 	GsfOpenPkgRels *rels = gsf_open_pkg_get_rels (opkg);
354 	struct pkg_iter_data dat;
355 
356 	if (NULL != rels) {
357 		dat.opkg = opkg;
358 		dat.func = func;
359 		dat.user_data = user_data;
360 		g_hash_table_foreach (rels->by_id, (GHFunc)&cb_foreach_rel, &dat);
361 	}
362 }
363 
364 /**
365  * gsf_open_pkg_open_rel_by_id:
366  * @opkg: #GsfInput
367  * @id: target id
368  * @err: optionally %NULL
369  *
370  * New in 1.14.7
371  *
372  * Open @opkg's relation @id
373  *
374  * Returns: (transfer full): A new GsfInput or %NULL, and sets @err if possible.
375  **/
376 GsfInput *
gsf_open_pkg_open_rel_by_id(GsfInput * opkg,char const * id,GError ** err)377 gsf_open_pkg_open_rel_by_id (GsfInput *opkg, char const *id, GError **err)
378 {
379 	GsfOpenPkgRel *rel = gsf_open_pkg_lookup_rel_by_id (opkg, id);
380 
381 	if (rel)
382 		return gsf_open_pkg_open_rel (opkg, rel, err);
383 
384 	if (err)
385 		*err = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
386 			_("Unable to find part id='%s' for '%s'"),
387 			id, gsf_input_name (opkg) );
388 	return NULL;
389 }
390 
391 /**
392  * gsf_open_pkg_open_rel_by_type:
393  * @opkg: #GsfInput
394  * @type: target type
395  * @err: optionally %NULL
396  *
397  * New in 1.14.9
398  *
399  * Open one of @opkg's relationships with type=@type.
400  *
401  * Returns: (transfer full): A new GsfInput or %NULL, and sets @err if possible.
402  **/
403 GsfInput *
gsf_open_pkg_open_rel_by_type(GsfInput * opkg,char const * type,GError ** err)404 gsf_open_pkg_open_rel_by_type (GsfInput *opkg, char const *type, GError **err)
405 {
406 	GsfOpenPkgRel *rel = gsf_open_pkg_lookup_rel_by_type (opkg, type);
407 
408 	if (rel)
409 		return gsf_open_pkg_open_rel (opkg, rel, err);
410 
411 	if (err)
412 		*err = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
413 			_("Unable to find part with type='%s' for '%s'"),
414 			type, gsf_input_name (opkg) );
415 	return NULL;
416 }
417 
418 /**
419  * gsf_open_pkg_parse_rel_by_id:
420  * @xin: #GsfXMLIn
421  * @id: target id
422  * @dtd: #GsfXMLInNode
423  * @ns: #GsfXMLInNS
424  *
425  * Convenience function to parse a related part.
426  *
427  * Returns: (transfer full): %NULL on success or a #GError on failure.
428  **/
429 GError *
gsf_open_pkg_parse_rel_by_id(GsfXMLIn * xin,char const * id,GsfXMLInNode const * dtd,GsfXMLInNS const * ns)430 gsf_open_pkg_parse_rel_by_id (GsfXMLIn *xin, char const *id,
431 			      GsfXMLInNode const *dtd,
432 			      GsfXMLInNS const *ns)
433 {
434 	GError *res = NULL;
435 	GsfInput *cur_stream, *part_stream;
436 
437 	g_return_val_if_fail (xin != NULL, NULL);
438 
439 	cur_stream = gsf_xml_in_get_input (xin);
440 
441 	if (NULL == id)
442 		return g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
443 			_("Missing id for part in '%s'"),
444 			gsf_input_name (cur_stream) );
445 
446 	part_stream = gsf_open_pkg_open_rel_by_id (cur_stream, id, &res);
447 	if (NULL != part_stream) {
448 		GsfXMLInDoc *doc = gsf_xml_in_doc_new (dtd, ns);
449 
450 		if (!gsf_xml_in_doc_parse (doc, part_stream, xin->user_state))
451 			res = g_error_new (gsf_input_error_id(), gsf_open_pkg_error_id (),
452 				_("Part '%s' in '%s' from '%s' is corrupt!"),
453 				id,
454 				gsf_input_name (part_stream),
455 				gsf_input_name (cur_stream) );
456 		gsf_xml_in_doc_free (doc);
457 
458 		g_object_unref (part_stream);
459 	}
460 	return res;
461 }
462 
463 /* DEPRECATED in 1.14.6 */
464 /**
465  * gsf_open_pkg_get_rel_by_type: (skip)
466  * @opkg:
467  * @type:
468  *
469  * Deprecated: 1.14.6
470  */
gsf_open_pkg_get_rel_by_type(GsfInput * opkg,char const * type)471 GsfInput *gsf_open_pkg_get_rel_by_type (GsfInput *opkg, char const *type) { return gsf_open_pkg_open_rel_by_type (opkg, type, NULL); }
472 /**
473  * gsf_open_pkg_get_rel_by_id: (skip)
474  * @opkg:
475  * @id:
476  *
477  * Deprecated: 1.14.6
478  */
gsf_open_pkg_get_rel_by_id(GsfInput * opkg,char const * id)479 GsfInput *gsf_open_pkg_get_rel_by_id   (GsfInput *opkg, char const *id)   { return gsf_open_pkg_open_rel_by_id (opkg, id, NULL); }
480 
481 /*************************************************************/
482 
483 struct _GsfOutfileOpenPkg {
484 	GsfOutfile  parent;
485 
486 	GsfOutput  *sink;
487 	gboolean    is_dir;
488 	char	   *content_type;
489 	GSList	   *children;
490 	GSList	   *relations;
491 };
492 
493 typedef GsfOutfileClass GsfOutfileOpenPkgClass;
494 
495 enum {
496 	PROP_0,
497 	PROP_SINK,
498 	PROP_CONTENT_TYPE,
499 	PROP_IS_DIR,
500 	PROP_IS_ROOT
501 };
502 
503 static GObjectClass *parent_class;
504 
505 static void
gsf_outfile_open_pkg_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)506 gsf_outfile_open_pkg_get_property (GObject     *object,
507 				   guint        property_id,
508 				   GValue      *value,
509 				   GParamSpec  *pspec)
510 {
511 	GsfOutfileOpenPkg *open_pkg = (GsfOutfileOpenPkg *)object;
512 
513 	switch (property_id) {
514 	case PROP_SINK:
515 		g_value_set_object (value, open_pkg->sink);
516 		break;
517 	case PROP_CONTENT_TYPE:
518 		g_value_set_string (value, open_pkg->content_type);
519 		break;
520 	case PROP_IS_DIR:
521 		g_value_set_boolean (value, open_pkg->is_dir);
522 		break;
523 	default:
524 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
525 		break;
526 	}
527 }
528 
529 static void
gsf_outfile_open_pkg_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)530 gsf_outfile_open_pkg_set_property (GObject      *object,
531 				   guint         property_id,
532 				   GValue const *value,
533 				   GParamSpec   *pspec)
534 {
535 	GsfOutfileOpenPkg *open_pkg = (GsfOutfileOpenPkg *)object;
536 
537 	switch (property_id) {
538 	case PROP_SINK:
539 		gsf_outfile_open_pkg_set_sink (open_pkg, g_value_get_object (value));
540 		break;
541 	case PROP_CONTENT_TYPE:
542 		gsf_outfile_open_pkg_set_content_type (open_pkg, g_value_get_string (value));
543 		break;
544 	case PROP_IS_DIR:
545 		open_pkg->is_dir = g_value_get_boolean (value);
546 		break;
547 	default:
548 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
549 		break;
550 	}
551 }
552 
553 static void
gsf_outfile_open_pkg_init(GObject * obj)554 gsf_outfile_open_pkg_init (GObject *obj)
555 {
556 	GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (obj);
557 
558 	open_pkg->sink = NULL;
559 	open_pkg->content_type = NULL;
560 	open_pkg->is_dir = FALSE;
561 	open_pkg->children = NULL;
562 	open_pkg->relations = NULL;
563 }
564 
565 static void
gsf_outfile_open_pkg_finalize(GObject * obj)566 gsf_outfile_open_pkg_finalize (GObject *obj)
567 {
568 	GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (obj);
569 
570 	if (open_pkg->sink != NULL) {
571 		g_object_unref (open_pkg->sink);
572 		open_pkg->sink = NULL;
573 	}
574 	g_free (open_pkg->content_type);
575 	open_pkg->content_type = NULL;
576 
577 	g_slist_free_full (open_pkg->children, g_object_unref);
578 	open_pkg->children = NULL;
579 
580 	parent_class->finalize (obj);
581 }
582 
583 static gboolean
gsf_outfile_open_pkg_write(GsfOutput * output,size_t num_bytes,guint8 const * data)584 gsf_outfile_open_pkg_write (GsfOutput *output, size_t num_bytes, guint8 const *data)
585 {
586 	GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
587 	return gsf_output_write (open_pkg->sink, num_bytes, data);
588 }
589 static gboolean
gsf_outfile_open_pkg_seek(GsfOutput * output,gsf_off_t offset,GSeekType whence)590 gsf_outfile_open_pkg_seek (GsfOutput *output, gsf_off_t offset, GSeekType whence)
591 {
592 	GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
593 	return gsf_output_seek (open_pkg->sink, offset, whence);
594 }
595 static GsfOutput *
gsf_outfile_open_pkg_new_child(GsfOutfile * parent,char const * name,gboolean is_dir,char const * first_property_name,va_list args)596 gsf_outfile_open_pkg_new_child (GsfOutfile *parent,
597 				char const *name, gboolean is_dir,
598 				char const *first_property_name, va_list args)
599 {
600 	GsfOutfileOpenPkg *child, *open_pkg = GSF_OUTFILE_OPEN_PKG (parent);
601 	GsfOutput *sink;
602 
603 	if (!open_pkg->is_dir)
604 		return NULL;
605 
606 	child = (GsfOutfileOpenPkg *)g_object_new_valist (
607 		GSF_OUTFILE_OPEN_PKG_TYPE, first_property_name, args);
608 	gsf_output_set_name (GSF_OUTPUT (child), name);
609 	gsf_output_set_container (GSF_OUTPUT (child), parent);
610 	child->is_dir = is_dir;
611 
612 	sink = gsf_outfile_new_child (GSF_OUTFILE (open_pkg->sink), name, is_dir);
613 	gsf_outfile_open_pkg_set_sink (child, sink);
614 	g_object_unref (sink);
615 
616 	/*
617 	 * Holding a ref here is not ideal.  It means we won't release any of the
618 	 * children until the package is closed.
619 	 */
620 	open_pkg->children = g_slist_prepend (open_pkg->children, g_object_ref (child));
621 
622 	return GSF_OUTPUT (child);
623 }
624 
625 static void
gsf_open_pkg_write_content_default(GsfXMLOut * xml,char const * ext,char const * type)626 gsf_open_pkg_write_content_default (GsfXMLOut *xml, char const *ext, char const *type)
627 {
628 	gsf_xml_out_start_element (xml, "Default");
629 	gsf_xml_out_add_cstr (xml, "Extension", ext);
630 	gsf_xml_out_add_cstr (xml, "ContentType", type);
631 	gsf_xml_out_end_element (xml); /* </Default> */
632 }
633 static void
gsf_open_pkg_write_content_override(GsfOutfileOpenPkg * open_pkg,char const * base,GsfXMLOut * xml)634 gsf_open_pkg_write_content_override (GsfOutfileOpenPkg *open_pkg,
635 				     char const *base,
636 				     GsfXMLOut *xml)
637 {
638 	GSList *ptr;
639 
640 	for (ptr = open_pkg->children ; ptr != NULL ; ptr = ptr->next) {
641 		GsfOutfileOpenPkg *child = ptr->data;
642 		char *path;
643 		if (child->is_dir) {
644 			path = g_strconcat (base, gsf_output_name (GSF_OUTPUT (child)), "/", NULL);
645 			gsf_open_pkg_write_content_override (child, path, xml);
646 		} else {
647 			path = g_strconcat (base, gsf_output_name (GSF_OUTPUT (child)), NULL);
648 			/* rels files do need content types, the defaults handle them */
649 			if (NULL != child->content_type) {
650 				gsf_xml_out_start_element (xml, "Override");
651 				gsf_xml_out_add_cstr (xml, "PartName", path);
652 				gsf_xml_out_add_cstr (xml, "ContentType", child->content_type);
653 				gsf_xml_out_end_element (xml); /* </Override> */
654 			}
655 		}
656 		g_free (path);
657 	}
658 
659 	/* Dispose of children here to break link cycles.  */
660 	g_slist_free_full (open_pkg->children, g_object_unref);
661 	open_pkg->children = NULL;
662 }
663 
664 static gboolean
gsf_outfile_open_pkg_close(GsfOutput * output)665 gsf_outfile_open_pkg_close (GsfOutput *output)
666 {
667 	GsfOutfileOpenPkg *open_pkg = GSF_OUTFILE_OPEN_PKG (output);
668 	GsfOutput *dir;
669 	gboolean res = FALSE;
670 	char *rels_name;
671 
672 	if (NULL == open_pkg->sink || gsf_output_is_closed (open_pkg->sink))
673 		return TRUE;
674 
675 	/* Generate [Content_types].xml when we close the root dir */
676 	if (NULL == gsf_output_name (output)) {
677 		GsfOutput *out = gsf_outfile_new_child (GSF_OUTFILE (open_pkg->sink),
678 			"[Content_Types].xml", FALSE);
679 		GsfXMLOut *xml = gsf_xml_out_new (out);
680 
681 		gsf_xml_out_start_element (xml, "Types");
682 		gsf_xml_out_add_cstr_unchecked (xml, "xmlns",
683 			"http://schemas.openxmlformats.org/package/2006/content-types");
684 		gsf_open_pkg_write_content_default (xml, "rels",
685 			"application/vnd.openxmlformats-package.relationships+xml");
686 		gsf_open_pkg_write_content_default (xml, "xlbin",
687 			"application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings");
688 		gsf_open_pkg_write_content_default (xml, "xml",
689 			"application/xml");
690 		gsf_open_pkg_write_content_default (xml, "vml",
691 			"application/vnd.openxmlformats-officedocument.vmlDrawing");
692 		gsf_open_pkg_write_content_override (open_pkg, "/", xml);
693 		gsf_xml_out_end_element (xml); /* </Types> */
694 		g_object_unref (xml);
695 
696 		gsf_output_close (out);
697 		g_object_unref (out);
698 
699 		dir = open_pkg->sink;
700 		rels_name = g_strdup (".rels");
701 	} else {
702 		res = gsf_output_close (open_pkg->sink);
703 
704 		dir = (GsfOutput *)gsf_output_container (open_pkg->sink);
705 		rels_name = g_strconcat (gsf_output_name (output), ".rels", NULL);
706 	}
707 
708 	if (NULL != open_pkg->relations) {
709 		GsfOutput *rels;
710 		GsfXMLOut *xml;
711 		GsfOpenPkgRel *rel;
712 		GSList *ptr;
713 
714 		dir = gsf_outfile_new_child (GSF_OUTFILE (dir), "_rels", TRUE);
715 		rels = gsf_outfile_new_child (GSF_OUTFILE (dir), rels_name, FALSE);
716 		xml = gsf_xml_out_new (rels);
717 
718 		gsf_xml_out_start_element (xml, "Relationships");
719 		gsf_xml_out_add_cstr_unchecked (xml, "xmlns",
720 			"http://schemas.openxmlformats.org/package/2006/relationships");
721 
722 		for (ptr = open_pkg->relations ; ptr != NULL ; ptr = ptr->next) {
723 			rel = ptr->data;
724 			gsf_xml_out_start_element (xml, "Relationship");
725 			gsf_xml_out_add_cstr (xml, "Id", rel->id);
726 			gsf_xml_out_add_cstr (xml, "Type", rel->type);
727 			gsf_xml_out_add_cstr (xml, "Target", rel->target);
728 			if (rel->is_extern)
729 				gsf_xml_out_add_cstr_unchecked (xml, "TargetMode", "External");
730 			gsf_xml_out_end_element (xml); /* </Relationship> */
731 
732 			g_free (rel->id);
733 			g_free (rel->type);
734 			g_free (rel->target);
735 			g_free (rel);
736 		}
737 		g_slist_free (open_pkg->relations);
738 
739 		gsf_xml_out_end_element (xml); /* </Relationships> */
740 		g_object_unref (xml);
741 		gsf_output_close (rels);
742 		g_object_unref (rels);
743 		g_object_unref (dir);
744 	}
745 	g_free (rels_name);
746 
747 	/* close the container */
748 	if (NULL == gsf_output_name (output))
749 		return gsf_output_close (open_pkg->sink);
750 
751 	return res;
752 }
753 
754 static void
gsf_outfile_open_pkg_class_init(GObjectClass * gobject_class)755 gsf_outfile_open_pkg_class_init (GObjectClass *gobject_class)
756 {
757 	GsfOutputClass  *output_class  = GSF_OUTPUT_CLASS (gobject_class);
758 	GsfOutfileClass *outfile_class = GSF_OUTFILE_CLASS (gobject_class);
759 
760 	gobject_class->finalize		= gsf_outfile_open_pkg_finalize;
761 	gobject_class->get_property     = gsf_outfile_open_pkg_get_property;
762 	gobject_class->set_property     = gsf_outfile_open_pkg_set_property;
763 
764 	output_class->Write		= gsf_outfile_open_pkg_write;
765 	output_class->Seek		= gsf_outfile_open_pkg_seek;
766 	output_class->Close		= gsf_outfile_open_pkg_close;
767 	outfile_class->new_child	= gsf_outfile_open_pkg_new_child;
768 
769 	parent_class = g_type_class_peek_parent (gobject_class);
770 
771 	g_object_class_install_property (gobject_class, PROP_SINK,
772 		 g_param_spec_object ("sink",
773 				      _("Sink"),
774 				      _("The GsfOutput that stores the Open Package content"),
775 				      GSF_OUTFILE_TYPE,
776 				      G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
777 
778 	g_object_class_install_property (gobject_class, PROP_CONTENT_TYPE,
779 		 g_param_spec_string ("content-type",
780 				      _("Content type"),
781 				      _("The content type stored in the root [Content_Types].xml file"),
782 				      "",
783 				      G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
784 
785 	g_object_class_install_property (gobject_class, PROP_IS_DIR,
786 		 g_param_spec_boolean ("is-dir",
787 				       _("Is Directory"),
788 				       _("Can the outfile have children"),
789 				       FALSE,
790 				       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
791 }
792 
GSF_CLASS(GsfOutfileOpenPkg,gsf_outfile_open_pkg,gsf_outfile_open_pkg_class_init,gsf_outfile_open_pkg_init,GSF_OUTFILE_TYPE)793 GSF_CLASS (GsfOutfileOpenPkg, gsf_outfile_open_pkg,
794 	   gsf_outfile_open_pkg_class_init, gsf_outfile_open_pkg_init,
795 	   GSF_OUTFILE_TYPE)
796 
797 /**
798  * gsf_outfile_open_pkg_new:
799  * @sink: #GsfOutfile
800  *
801  * Convenience routine to create a GsfOutfileOpenPkg inside @sink.
802  *
803  * Returns: a GsfOutfile that the caller is responsible for.
804  **/
805 GsfOutfile *
806 gsf_outfile_open_pkg_new (GsfOutfile *sink)
807 {
808 	return g_object_new (GSF_OUTFILE_OPEN_PKG_TYPE,
809 		"sink", sink,	"is-dir", TRUE,
810 		NULL);
811 }
812 
813 /**
814  * gsf_outfile_open_pkg_set_sink:
815  * @open_pkg: #GsfOutfileOpenPkg
816  * @sink: #GsfOutput
817  *
818  * Assigns a GsfOutput (@sink) to store the package into.
819  **/
820 void
gsf_outfile_open_pkg_set_sink(GsfOutfileOpenPkg * open_pkg,GsfOutput * sink)821 gsf_outfile_open_pkg_set_sink (GsfOutfileOpenPkg *open_pkg, GsfOutput *sink)
822 {
823 	if (sink)
824 		g_object_ref (sink);
825 	if (open_pkg->sink)
826 		g_object_unref (open_pkg->sink);
827 	open_pkg->sink = sink;
828 }
829 
830 /**
831  * gsf_outfile_open_pkg_set_content_type:
832  * @open_pkg: #GsfOutfileOpenPkg
833  * @content_type:
834  *
835  **/
836 void
gsf_outfile_open_pkg_set_content_type(GsfOutfileOpenPkg * open_pkg,char const * content_type)837 gsf_outfile_open_pkg_set_content_type (GsfOutfileOpenPkg *open_pkg,
838 				       char const *content_type)
839 {
840 	if (open_pkg->content_type != content_type) {
841 		g_free (open_pkg->content_type);
842 		open_pkg->content_type = g_strdup (content_type);
843 	}
844 }
845 
846 static char const *
gsf_outfile_open_pkg_create_rel(GsfOutfileOpenPkg * parent,char * target,char const * type,gboolean is_extern)847 gsf_outfile_open_pkg_create_rel (GsfOutfileOpenPkg *parent,
848 				 char *target,
849 				 char const *type,
850 				 gboolean is_extern)
851 {
852 	GsfOpenPkgRel *rel = g_new0 (GsfOpenPkgRel, 1);
853 	rel->target = target;
854 	rel->type   = g_strdup (type);
855 	rel->id     = g_strdup_printf ("rId%u", g_slist_length (parent->relations) + 1);
856 	rel->is_extern = is_extern;
857 	parent->relations = g_slist_prepend (parent->relations, rel);
858 	return rel->id;
859 }
860 
861 /**
862  * gsf_outfile_open_pkg_relate:
863  * @child: #GsfOutfileOpenPkg
864  * @parent: #GsfOutfileOpenPkg
865  * @type: target type
866  *
867  * Create a relationship between @child and @parent of @type.
868  *
869  * Returns: (transfer none): the relID which the caller does not own
870  * 	but will live as long as @parent.
871  **/
872 char const *
gsf_outfile_open_pkg_relate(GsfOutfileOpenPkg * child,GsfOutfileOpenPkg * parent,char const * type)873 gsf_outfile_open_pkg_relate (GsfOutfileOpenPkg *child,
874 			     GsfOutfileOpenPkg *parent,
875 			     char const *type)
876 {
877 	GString *path;
878 	int up = -1;
879 	GsfOutfile *child_dir, *parent_dir;
880 
881 	/* Calculate the path from @child to @parent */
882 	parent_dir = parent->is_dir ? GSF_OUTFILE (parent)
883 		: gsf_output_container (GSF_OUTPUT (parent));
884 	do {
885 		up++;
886 		child_dir  = GSF_OUTFILE (child);
887 		while (NULL != (child_dir = gsf_output_container (GSF_OUTPUT (child_dir))))
888 		       if (child_dir == parent_dir)
889 			       goto found; /* break out of both loops */
890 	} while (NULL != (parent_dir = gsf_output_container (GSF_OUTPUT (parent_dir))));
891 
892 found:
893 	/* yes prepend is slow, this will never be preformance critical */
894 	path = g_string_new (gsf_output_name (GSF_OUTPUT (child)));
895 	child_dir  = GSF_OUTFILE (child);
896 	while (NULL != (child_dir = gsf_output_container (GSF_OUTPUT (child_dir))) &&
897 	       NULL != gsf_output_name (GSF_OUTPUT (child_dir)) &&
898 	       child_dir != parent_dir) {
899 		g_string_prepend_c (path, '/');
900 		g_string_prepend (path, gsf_output_name (GSF_OUTPUT (child_dir)));
901 	}
902 	while (up--)
903 		g_string_prepend (path, "../");
904 
905 	return gsf_outfile_open_pkg_create_rel (parent,
906 		g_string_free (path, FALSE), type, FALSE);
907 }
908 
909 /**
910  * gsf_outfile_open_pkg_add_rel:
911  * @dir: #GsfOutfile
912  * @name: target name
913  * @content_type: non-%NULL content type
914  * @parent: #GsfOutfile
915  * @type: target type
916  *
917  * A convenience wrapper to create a child in @dir of @content_type then create
918  * a @type relation to @parent
919  *
920  * Returns: (transfer full): the new part.
921  **/
922 GsfOutput *
gsf_outfile_open_pkg_add_rel(GsfOutfile * dir,char const * name,char const * content_type,GsfOutfile * parent,char const * type)923 gsf_outfile_open_pkg_add_rel (GsfOutfile *dir,
924 			      char const *name,
925 			      char const *content_type,
926 			      GsfOutfile *parent,
927 			      char const *type)
928 {
929 	GsfOutput *part = gsf_outfile_new_child_full (dir, name, FALSE,
930 		"content-type", content_type,
931 		NULL);
932 	(void) gsf_outfile_open_pkg_relate (GSF_OUTFILE_OPEN_PKG (part),
933 		GSF_OUTFILE_OPEN_PKG (parent), type);
934 	return part;
935 }
936 
937 /**
938  * gsf_outfile_open_pkg_add_extern_rel:
939  * @parent: #GsfOutfileOpenPkg
940  * @target: target type
941  * @content_type: target content
942  *
943  * Add an external relation to @parent.
944  *
945  * Returns: (transfer none): The id of the relation.  The string is
946  * managed by the parent and should not be changed or freed by the
947  * caller.
948  **/
949 char const *
gsf_outfile_open_pkg_add_extern_rel(GsfOutfileOpenPkg * parent,char const * target,char const * content_type)950 gsf_outfile_open_pkg_add_extern_rel (GsfOutfileOpenPkg *parent,
951 				     char const *target,
952 				     char const *content_type)
953 {
954 	return gsf_outfile_open_pkg_create_rel (parent,
955 		g_strdup (target), content_type, TRUE);
956 }
957 
958 gint
gsf_open_pkg_error_id(void)959 gsf_open_pkg_error_id (void)
960 {
961 	return 42;	/* something arbitrary */
962 }
963 
964