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