1/*
2 * Copyright (c) 2005-2008 Rob Braun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Rob Braun nor the names of his contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * 03-Apr-2005
31 * DRI: Rob Braun <bbraun@synack.net>
32 */
33/*
34 * Portions Copyright 2006, Apple Computer, Inc.
35 * Christopher Ryan <ryanc@apple.com>
36*/
37
38#define _FILE_OFFSET_BITS 64
39
40#include "config.h"
41#include <stdlib.h>
42#include <stdio.h>
43#include <string.h>
44#include <assert.h>
45#include <libgen.h>
46#include <libxml/xmlwriter.h>
47#include <libxml/xmlreader.h>
48#include <libxml/xmlstring.h>
49
50#ifndef HAVE_ASPRINTF
51#include "asprintf.h"
52#endif
53#include "xar.h"
54#include "filetree.h"
55#include "archive.h"
56#include "b64.h"
57#include "ea.h"
58
59/* Overview:
60 * xar_file_t's exist within a xar_archive_t.  xar_prop_t's exist
61 * within xar_file_t's and xar_attr_t's exist within xar_prop_t's
62 * and xar_file_t's.
63 * Basically, a xar_file_t is a container for xar_prop_t's.
64 * xar_attr_t's are things like: <foo bar=5>blah</foo>
65 * In this example, foo is the key of a xar_prop_t, and blah is
66 * the value.  bar is the key of a xar_attr_t which is part of
67 * foo's xar_prop_t, and 5 is bar's value.
68 * xar_file_t's have xar_attr_t's for the case of:
69 * <file id=42>
70 * The file has an attribute of "id" with a value of "42".
71 */
72
73struct __xar_iter_t {
74	const void *iter;
75	char *path;
76	void *node;
77	int nochild;
78};
79
80/* Convenience macros for dereferencing the structs */
81#define XAR_ITER(x) ((struct __xar_iter_t *)(x))
82
83/* xar_attr_prop
84 * Returns: a newly allocated and initialized property attribute.
85 * It is the caller's responsibility to associate the attribute
86 * with either a file or a property.
87 */
88xar_attr_t xar_attr_new(void) {
89	xar_attr_t ret;
90
91	ret = malloc(sizeof(struct __xar_attr_t));
92	if(!ret) return NULL;
93
94	XAR_ATTR(ret)->key = NULL;
95	XAR_ATTR(ret)->value = NULL;
96	XAR_ATTR(ret)->next = NULL;
97	XAR_ATTR(ret)->ns = NULL;
98	return ret;
99}
100
101int32_t xar_attr_pset(xar_file_t f, xar_prop_t p, const char *key, const char *value) {
102	xar_attr_t a, i;
103	if( !p ) {
104		a = XAR_FILE(f)->attrs;
105	} else {
106		a = XAR_PROP(p)->attrs;
107	}
108
109	if( !a ) {
110		a = xar_attr_new();
111		if(!p)
112			XAR_FILE(f)->attrs = a;
113		else
114			XAR_PROP(p)->attrs = a;
115		XAR_ATTR(a)->key = strdup(key);
116		XAR_ATTR(a)->value = strdup(value);
117		return 0;
118	}
119
120	for(i = a; i && XAR_ATTR(i)->next; i = XAR_ATTR(i)->next) {
121		if(strcmp(XAR_ATTR(i)->key, key)==0) {
122			free((char*)XAR_ATTR(i)->value);
123			XAR_ATTR(i)->value = strdup(value);
124			return 0;
125		}
126	}
127	a = xar_attr_new();
128	if(!p) {
129		XAR_ATTR(a)->next = XAR_ATTR(XAR_FILE(f)->attrs);
130		XAR_FILE(f)->attrs = a;
131	} else {
132		XAR_ATTR(a)->next = XAR_ATTR(XAR_PROP(p)->attrs);
133		XAR_PROP(p)->attrs = a;
134	}
135	XAR_ATTR(a)->key = strdup(key);
136	XAR_ATTR(a)->value = strdup(value);
137	return 0;
138}
139
140/* xar_attr_set
141 * f: the file the attribute is associated with
142 * prop: The property key the attribute is associated with.  This can
143 *       be NULL to signify the attribute should be set for the file,
144 *       rather than the property.
145 * key: The name of the attribute to set.
146 * value: The value of the attribute.
147 * Returns: 0 on success, -1 on failure.
148 * Summary: Basically, sets an attribute.  The only tricky part is
149 * it can set an attribute on a property or a file.
150 */
151int32_t xar_attr_set(xar_file_t f, const char *prop, const char *key, const char *value) {
152	if( !prop ) {
153		return xar_attr_pset(f, NULL, key, value);
154	} else {
155		xar_prop_t p = NULL;
156		p = xar_prop_find(XAR_FILE(f)->props, prop);
157		if( !p ) return -1;
158		return xar_attr_pset(f, p, key, value);
159	}
160}
161
162const char *xar_attr_pget(xar_file_t f, xar_prop_t p, const char *key) {
163	xar_attr_t a, i;
164
165	if( !p )
166		a = XAR_FILE(f)->attrs;
167	else
168		a = XAR_PROP(p)->attrs;
169
170	if( !a ) return NULL;
171
172	for(i = a; i && XAR_ATTR(i)->next; i = XAR_ATTR(i)->next) {
173		if(strcmp(XAR_ATTR(i)->key, key)==0) {
174			return XAR_ATTR(i)->value;
175		}
176	}
177	if( i && (strcmp(XAR_ATTR(i)->key, key)==0))
178		return XAR_ATTR(i)->value;
179	return NULL;
180}
181
182/* xar_attr_get
183 * f: file to find the associated attribute in
184 * prop: name of the property the attribute is of.  May be NULL to specify
185 *       the file's attributes.
186 * key: name of the attribute to search for.
187 * Returns: a reference to the value of the attribute.
188 */
189const char *xar_attr_get(xar_file_t f, const char *prop, const char *key) {
190	if( !prop )
191		return xar_attr_pget(f, NULL, key);
192	else {
193		xar_prop_t p = NULL;
194		p = xar_prop_find(XAR_FILE(f)->props, prop);
195		if( !p ) return NULL;
196		return xar_attr_pget(f, p, key);
197	}
198}
199
200/* xar_attr_free
201 * a: attribute to free
202 * Summary: frees the attribute structure and everything inside it.
203 * It is the caller's responsibility to ensure the linked list gets
204 * updated.  This will *not* do anything to ensure the consistency
205 * of the attribute linked list.
206 */
207void xar_attr_free(xar_attr_t a) {
208	if(!a) return;
209	free((char*)XAR_ATTR(a)->key);
210	free((char*)XAR_ATTR(a)->value);
211	free(XAR_ATTR(a));
212	return;
213}
214
215/* xar_attr_first
216 * f: file to associate the iterator with
217 * prop: the name of the property within the file to associate the iterator with
218 * i: an iterator as returned by xar_iter_new
219 * Returns: a pointer to the value of the first attribute associated with the
220 * property 'prop' associated with the file 'f'
221 * Summary: This MUST be called prior to calling xar_attr_next,
222 * to iterate over the attributes of property key 'prop'.
223 */
224const char *xar_attr_first(xar_file_t f, const char *prop, xar_iter_t i) {
225	xar_prop_t p = NULL;
226	xar_attr_t a;
227
228	if( !prop )
229		a = XAR_FILE(f)->attrs;
230	else {
231		p = xar_prop_find(XAR_FILE(f)->props, prop);
232		if( !p ) return NULL;
233		a = XAR_PROP(p)->attrs;
234	}
235
236	if( !a ) return NULL;
237
238	XAR_ITER(i)->iter = a;
239	free(XAR_ITER(i)->node);
240	XAR_ITER(i)->node = strdup(XAR_ATTR(a)->key);
241	return XAR_ITER(i)->node;
242}
243
244/* xar_attr_next
245 * i: iterator allocated by xar_iter_new, and initialized with xar_attr_first
246 * Returns: a pointer to the key of the next attribute associated with
247 * the iterator.  NULL will be returned when there are no more attributes
248 * to find.
249 */
250const char *xar_attr_next(xar_iter_t i) {
251	xar_attr_t a = XAR_ITER(i)->iter;
252
253	if( XAR_ATTR(a)->next == NULL )
254		return NULL;
255
256	XAR_ITER(i)->iter = XAR_ATTR(a)->next;
257	free(XAR_ITER(i)->node);
258	XAR_ITER(i)->node = strdup(XAR_ATTR(XAR_ITER(i)->iter)->key);
259	return XAR_ITER(i)->node;
260}
261
262/* xar_iter_new
263 * Returns a newly allocated iterator for use on files, properties, or
264 * attributes.
265 */
266xar_iter_t xar_iter_new(void) {
267	xar_iter_t ret = malloc(sizeof(struct __xar_iter_t));
268	if(!ret) return NULL;
269
270	XAR_ITER(ret)->iter = NULL;
271	XAR_ITER(ret)->path = NULL;
272	XAR_ITER(ret)->node = NULL;
273	XAR_ITER(ret)->nochild = 0;
274	return ret;
275}
276
277/* xar_iter_free
278 * Frees memory associated with the specified iterator
279 */
280void xar_iter_free(xar_iter_t i) {
281	free(XAR_ITER(i)->node);
282	if( XAR_ITER(i)->path )
283		free(XAR_ITER(i)->path);
284	free(XAR_ITER(i));
285}
286
287const char *xar_prop_getkey(xar_prop_t p) {
288	return XAR_PROP(p)->key;
289}
290const char *xar_prop_getvalue(xar_prop_t p) {
291	return XAR_PROP(p)->value;
292}
293int32_t xar_prop_setkey(xar_prop_t p, const char *key) {
294	free((char *)XAR_PROP(p)->key);
295	if(key)
296		XAR_PROP(p)->key = strdup(key);
297	return 0;
298}
299int32_t xar_prop_setvalue(xar_prop_t p, const char *value) {
300	free((char *)XAR_PROP(p)->value);
301	if(value)
302		XAR_PROP(p)->value = strdup(value);
303	return 0;
304}
305
306/* xar_prop_pfirst
307 * f: file to retrieve the first property from
308 * Returns: a xar_prop_t corresponding to the first xar_prop_t associated with f
309 * NULL if there are no properties associated with the file.
310 */
311xar_prop_t xar_prop_pfirst(xar_file_t f) {
312	return XAR_FILE(f)->props;
313}
314
315/* xar_prop_pnext
316 * p: previous property used to retrieve the next
317 * Returns: a xar_prop_t if there is a next, NULL otherwise
318 */
319xar_prop_t xar_prop_pnext(xar_prop_t p) {
320	return XAR_PROP(p)->next;
321}
322
323/* xar_prop_first
324 * f: file to associate the iterator with
325 * i: an iterator as returned by xar_iter_new
326 * Returns: a pointer to the value of the first property associated with
327 * the file 'f'.
328 * Summary: This MUST be called first prior to calling xar_prop_next,
329 * to iterate over properties of file 'f'.  This has the side effect of
330 * associating the iterator with the file's properties, which is needed
331 * before xar_prop_next.
332 */
333const char *xar_prop_first(xar_file_t f, xar_iter_t i) {
334	XAR_ITER(i)->iter = XAR_FILE(f)->props;
335	free(XAR_ITER(i)->node);
336	XAR_ITER(i)->node = strdup(XAR_PROP(XAR_ITER(i)->iter)->key);
337	return XAR_ITER(i)->node;
338}
339
340/* xar_prop_next
341 * i: iterator allocated by xar_iter_new, and initialized with xar_prop_first
342 * Returns: a pointer to the value of the next property associated with
343 * the iterator.  NULL will be returned when there are no more properties
344 * to find.  If a property has a NULL value, the string "" will be returned.
345 * This will recurse down child properties, flattening the namespace and
346 * adding separators.  For instance a1->b1->c1, a1 will first be returned,
347 * the subsequent call will return "a1/b1", and the next call will return
348 * "a1/b1/c1", etc.
349 */
350const char *xar_prop_next(xar_iter_t i) {
351	xar_prop_t p = XAR_ITER(i)->iter;
352	if( !(XAR_ITER(i)->nochild) && XAR_PROP(p)->children ) {
353		char *tmp = XAR_ITER(i)->path;
354		if( tmp ) {
355			asprintf(&XAR_ITER(i)->path, "%s/%s", tmp, XAR_PROP(p)->key);
356			free(tmp);
357		} else
358			XAR_ITER(i)->path = strdup(XAR_PROP(p)->key);
359		XAR_ITER(i)->iter = p = XAR_PROP(p)->children;
360		goto SUCCESS;
361	}
362	XAR_ITER(i)->nochild = 0;
363
364	if( XAR_PROP(p)->next ) {
365		XAR_ITER(i)->iter = p = XAR_PROP(p)->next;
366		goto SUCCESS;
367	}
368
369	if( XAR_PROP(p)->parent ) {
370		char *tmp1, *tmp2;
371		char *dname;
372
373		if( strstr(XAR_ITER(i)->path, "/") ) {
374		tmp1 = tmp2 = XAR_ITER(i)->path;
375		dname = dirname(tmp2);
376		XAR_ITER(i)->path = strdup(dname);
377		free(tmp1);
378		} else {
379			free(XAR_ITER(i)->path);
380			XAR_ITER(i)->path = NULL;
381		}
382
383		XAR_ITER(i)->iter = p = XAR_PROP(p)->parent;
384		XAR_ITER(i)->nochild = 1;
385		return xar_prop_next(i);
386	}
387
388	return NULL;
389SUCCESS:
390	free(XAR_ITER(i)->node);
391	if( XAR_ITER(i)->path )
392		asprintf((char **)&XAR_ITER(i)->node, "%s/%s", XAR_ITER(i)->path, XAR_PROP(p)->key);
393	else {
394		if(XAR_PROP(p)->key == NULL)
395			XAR_ITER(i)->node = strdup("");
396		else
397			XAR_ITER(i)->node = strdup(XAR_PROP(p)->key);
398	}
399	return XAR_ITER(i)->node;
400}
401
402/* xar_prop_new
403 * f: file to associate the new file with.  May not be NULL
404 * parent: the parent property of the new property.  May be NULL
405 * Returns: a newly allocated and initialized property.
406 * Summary: in addition to allocating the new property, it
407 * will be inserted into the parent node's list of children,
408 * and/or added to the file's list of properties, as appropriate.
409 */
410xar_prop_t xar_prop_new(xar_file_t f, xar_prop_t parent) {
411	xar_prop_t p;
412
413	p = malloc(sizeof(struct __xar_prop_t));
414	if( !p ) return NULL;
415
416	XAR_PROP(p)->key = NULL;
417	XAR_PROP(p)->value = NULL;
418	XAR_PROP(p)->children = NULL;
419	XAR_PROP(p)->next = NULL;
420	XAR_PROP(p)->attrs = NULL;
421	XAR_PROP(p)->parent = parent;
422	XAR_PROP(p)->file = f;
423	XAR_PROP(p)->prefix = XAR_FILE(f)->prefix;
424	XAR_PROP(p)->ns = NULL;
425	if(parent) {
426		if( !XAR_PROP(parent)->children ) {
427			XAR_PROP(parent)->children = p;
428		} else {
429			XAR_PROP(p)->next = XAR_PROP(parent)->children;
430			XAR_PROP(parent)->children = p;
431		}
432	} else {
433		if( XAR_FILE(f)->props == NULL ) {
434			XAR_FILE(f)->props = p;
435		} else {
436			XAR_PROP(p)->next = XAR_FILE(f)->props;
437			XAR_FILE(f)->props = p;
438		}
439	}
440
441	return p;
442}
443
444/* xar_prop_find
445 * p: property to check
446 * key: name of property to find.
447 * Returns: reference to the property with the specified key
448 * Summary: A node's name may be specified by a path, such as
449 * "a1/b1/c1", and child nodes will be searched for each
450 * "/" separator.
451 */
452xar_prop_t xar_prop_find(xar_prop_t p, const char *key) {
453	xar_prop_t i, ret;
454	char *tmp1, *tmp2, *tmp3;
455
456	if( !p ) return NULL;
457	tmp2 = tmp1 = strdup(key);
458	tmp3 = strsep(&tmp2, "/");
459	i = p;
460	do {
461		if( strcmp(tmp3, XAR_PROP(i)->key) == 0 ) {
462			if( tmp2 == NULL ) {
463				free(tmp1);
464				return i;
465			}
466			ret = xar_prop_find(XAR_PROP(i)->children, tmp2);
467			free(tmp1);
468			return ret;
469		}
470		i = XAR_PROP(i)->next;
471	} while(i);
472	free(tmp1);
473	return NULL;
474}
475
476/* xar_prop_set_r
477 * p: property to recurse down and set the property of
478 * key: key of the property to set
479 * value: desired value of the property
480 * Returns: 0 on sucess, -1 on failure.
481 * Summary: This is an internal helper function for xar_prop_set() which
482 * does the recursion down the property tree.
483 */
484static xar_prop_t xar_prop_set_r(xar_file_t f, xar_prop_t p, const char *key, const char *value, int overwrite) {
485	xar_prop_t i, ret, ret2, start;
486	char *tmp1, *tmp2, *tmp3;
487
488	tmp2 = tmp1 = strdup(key);
489	tmp3 = strsep(&tmp2, "/");
490
491	if( !p ) {
492		start = XAR_FILE(f)->props;
493	} else {
494		start = XAR_PROP(p)->children;
495	}
496
497	for( i = start; i; i = XAR_PROP(i)->next ) {
498		if( strcmp(tmp3, XAR_PROP(i)->key) == 0 ) {
499			if( !tmp2 ) {
500				if( overwrite ) {
501					xar_prop_setvalue(i, value);
502					free(tmp1);
503					return i;
504				} else {
505					ret = xar_prop_new(f, p);
506					if( !ret ) {
507						free(tmp1);
508						return ret;
509					}
510					xar_prop_setvalue(ret, value);
511					xar_prop_setkey(ret, tmp3);
512					free(tmp1);
513					return ret;
514				}
515			}
516
517			ret2 = xar_prop_set_r(f, i, tmp2, value, overwrite);
518			free(tmp1);
519			return ret2;
520		}
521	}
522
523	ret = xar_prop_new(f, p);
524	if( !ret ) {
525		free(tmp1);
526		return ret;
527	}
528
529	if( !tmp2 ) {
530		xar_prop_setvalue(ret, value);
531		xar_prop_setkey(ret, tmp3);
532		free(tmp1);
533		return ret;
534	}
535
536	xar_prop_setkey(ret, tmp3);
537	xar_prop_setvalue(ret, NULL);
538
539	ret2 = xar_prop_set_r(f, ret, tmp2, value, overwrite);
540	free(tmp1);
541	return ret2;
542}
543
544/* xar_prop_set
545 * f: file to set the property on
546 * key: key of the property to set
547 * value: desired value of the property
548 * Returns: 0 on success, -1 on failure
549 * Summary: If the property already exists, its value is overwritten
550 * by 'value'.  If the property does not exist, it is created.
551 * Copies of key and value are kept in the tree.  The caller may do
552 * what they wish with these values after the call returns.
553 * References to these copies will be returned by iterating over
554 * the properties, or by calling xar_prop_get().
555 * These copies will be released when the property is released.
556 *
557 * Note that you *CANNOT* have a node with a value and children.
558 * This implementation will let you, but the serialization to xml
559 * will not be what you're hoping for.
560 */
561int32_t xar_prop_set(xar_file_t f, const char *key, const char *value) {
562	if( xar_prop_set_r(f, NULL, key, value, 1) )
563		return 0;
564	return -1;
565}
566
567/* xar_prop_pset
568 * Same as xar_prop_set, except it takes a xar_prop_t which will be
569 * treated as the root property.
570 * Returns a xar_prop_t that was created or set.  Returns NULL if error.
571 */
572xar_prop_t xar_prop_pset(xar_file_t f, xar_prop_t p, const char *key, const char *value) {
573	return xar_prop_set_r(f, p, key, value, 1);
574}
575
576/* xar_prop_create
577 * Identical to xar_prop_set, except it will not overwrite an existing
578 * property, it will create another one.
579 */
580int32_t xar_prop_create(xar_file_t f, const char *key, const char *value) {
581	if( xar_prop_set_r(f, NULL, key, value, 0) )
582		return 0;
583	return -1;
584}
585
586/* xar_prop_get
587 * f: file to look for the property in
588 * key: name of property to find.
589 * value: on return, *value will point to the value of the property
590 * value may be NULL, in which case, only the existence of the property
591 * is tested.
592 * Returns: 0 for success, -1 on failure
593 * Summary: A node's name may be specified by a path, such as
594 * "a1/b1/c1", and child nodes will be searched for each
595 * "/" separator.
596 */
597int32_t xar_prop_get(xar_file_t f, const char *key, const char **value) {
598	xar_prop_t r = xar_prop_find(XAR_FILE(f)->props, key);
599	if( !r ) {
600		if(value)
601			*value = NULL;
602		return -1;
603	}
604	if(value)
605		*value = XAR_PROP(r)->value;
606	return 0;
607}
608
609xar_prop_t xar_prop_pget(xar_prop_t p, const char *key) {
610	char *tmp;
611	const char *k;
612	xar_prop_t ret;
613	k = XAR_PROP(p)->key;
614	asprintf(&tmp, "%s/%s", k, key);
615	ret = xar_prop_find(p, tmp);
616	free(tmp);
617	return ret;
618}
619
620/* xar_prop_replicate_r
621* f: file to attach property
622* p: property (list) to iterate and add
623* parent: parent property
624* Summary: Recursivley adds property list (p) to file (f) and parent (parent).
625*/
626
627void xar_prop_replicate_r(xar_file_t f, xar_prop_t p, xar_prop_t parent )
628{
629	xar_prop_t property = p;
630
631	/* look through properties */
632	for( property = p; property; property = property->next ){
633		xar_prop_t	newprop = xar_prop_new( f, parent );
634
635		/* copy the key value for the property */
636		XAR_PROP(newprop)->key = strdup(property->key);
637		if(property->value)
638			XAR_PROP(newprop)->value = strdup(property->value);
639
640		/* loop through the attributes and copy them */
641		xar_attr_t a = NULL;
642		xar_attr_t last = NULL;
643
644		/* copy attributes for file */
645		for(a = property->attrs; a; a = a->next) {
646			if( NULL == newprop->attrs ){
647				last = xar_attr_new();
648				XAR_PROP(newprop)->attrs = last;
649			}else{
650				XAR_ATTR(last)->next = xar_attr_new();
651				last = XAR_ATTR(last)->next;
652			}
653
654			XAR_ATTR(last)->key = strdup(a->key);
655			if(a->value)
656				XAR_ATTR(last)->value = strdup(a->value);
657		}
658
659		/* loop through the children properties and recursively add them */
660		xar_prop_replicate_r(f, property->children, newprop );
661	}
662
663}
664
665/* xar_prop_free
666 * p: property to free
667 * Summary: frees the specified property and all its children.
668 * Stored copies of the key and value will be released, as will
669 * all attributes.
670 */
671void xar_prop_free(xar_prop_t p) {
672	xar_prop_t i;
673	xar_attr_t a;
674	while( XAR_PROP(p)->children ) {
675		i = XAR_PROP(p)->children;
676		XAR_PROP(p)->children = XAR_PROP(i)->next;
677		xar_prop_free(i);
678	}
679	while(XAR_PROP(p)->attrs) {
680		a = XAR_PROP(p)->attrs;
681		XAR_PROP(p)->attrs = XAR_ATTR(a)->next;
682		xar_attr_free(a);
683	}
684	free((char*)XAR_PROP(p)->key);
685	free((char*)XAR_PROP(p)->value);
686	free(XAR_PROP(p));
687}
688
689void xar_prop_punset(xar_file_t f, xar_prop_t p) {
690	xar_prop_t i;
691	if( !p ) {
692		return;
693	}
694	if( XAR_PROP(p)->parent ) {
695		i = XAR_PROP(p)->parent->children;
696		if( i == p ) {
697			XAR_PROP(XAR_PROP(p)->parent)->children = XAR_PROP(p)->next;
698			xar_prop_free(p);
699			return;
700		}
701	} else {
702		i = XAR_FILE(f)->props;
703		if( i == p ) {
704			XAR_FILE(f)->props = XAR_PROP(p)->next;
705			xar_prop_free(p);
706			return;
707		}
708	}
709
710	while( i && (XAR_PROP(i)->next != XAR_PROP(p)) ) {
711		i = XAR_PROP(i)->next;
712	}
713	if( i && (XAR_PROP(i)->next == XAR_PROP(p)) ) {
714		XAR_PROP(i)->next = XAR_PROP(p)->next;
715		xar_prop_free(p);
716	}
717	return;
718}
719
720void xar_prop_unset(xar_file_t f, const char *key) {
721	xar_prop_t r = xar_prop_find(XAR_FILE(f)->props, key);
722
723	xar_prop_punset(f, r);
724	return;
725}
726
727/* xar_file_new
728 * f: parent file of the file to be created.  May be NULL
729 * Returns: a newly allocated file structure.
730 */
731xar_file_t xar_file_new(xar_file_t f) {
732	xar_file_t ret, i;
733
734	ret = calloc(1, sizeof(struct __xar_file_t));
735	if(!ret) return NULL;
736
737	XAR_FILE(ret)->parent = f;
738	XAR_FILE(ret)->next = NULL;
739	XAR_FILE(ret)->children = NULL;
740	XAR_FILE(ret)->props = NULL;
741	XAR_FILE(ret)->attrs = NULL;
742	XAR_FILE(ret)->prefix = NULL;
743	XAR_FILE(ret)->ns = NULL;
744	XAR_FILE(ret)->fspath = NULL;
745	XAR_FILE(ret)->eas = NULL;
746	XAR_FILE(ret)->nexteaid = 0;
747	if( f ) {
748		if( !XAR_FILE(f)->children ) {
749			XAR_FILE(f)->children = ret;
750		} else {
751			for(i = XAR_FILE(f)->children; XAR_FILE(i)->next; i = XAR_FILE(i)->next);
752			XAR_FILE(i)->next = ret;
753		}
754	}
755
756	return ret;
757}
758
759xar_file_t xar_file_replicate(xar_file_t original, xar_file_t newparent)
760{
761	xar_file_t ret = xar_file_new(newparent);
762	xar_attr_t a;
763
764	/* copy attributes for file */
765	for(a = XAR_FILE(original)->attrs; a; a = XAR_ATTR(a)->next) {
766		/* skip the id attribute */
767		if( 0 == strcmp(a->key, "id" ) )
768			continue;
769
770		xar_attr_set(ret, NULL , a->key, a->value );
771	}
772
773	/* recursively copy properties */
774	xar_prop_replicate_r(ret, XAR_FILE(original)->props, NULL);
775
776	return ret;
777}
778
779/* xar_file_free
780 * f: file to free
781 * Summary: frees the specified file and all children,
782 * properties, and attributes associated with the file.
783 */
784void xar_file_free(xar_file_t f) {
785	xar_file_t i;
786	xar_prop_t n;
787	xar_attr_t a;
788	while(XAR_FILE(f)->children) {
789		i = XAR_FILE(f)->children;
790		XAR_FILE(f)->children = XAR_FILE(i)->next;
791		xar_file_free(i);
792	}
793	while(XAR_FILE(f)->props) {
794		n = XAR_FILE(f)->props;
795		XAR_FILE(f)->props = XAR_PROP(n)->next;
796		xar_prop_free(n);
797	}
798	while(XAR_FILE(f)->attrs) {
799		a = XAR_FILE(f)->attrs;
800		XAR_FILE(f)->attrs = XAR_ATTR(a)->next;
801		xar_attr_free(a);
802	}
803	free((char *)XAR_FILE(f)->fspath);
804	free(XAR_FILE(f));
805}
806
807/* xar_file_first
808 * x: archive to associate the iterator with
809 * i: an iterator as returned by xar_iter_new
810 * Returns: a pointer to the name of the first file associated with
811 * the archive 'x'.
812 * Summary: This MUST be called first prior to calling xar_file_next,
813 * to iterate over files of archive 'x'.  This has the side effect of
814 * associating the iterator with the archive's files, which is needed
815 * before xar_file_next.
816 */
817xar_file_t xar_file_first(xar_t x, xar_iter_t i) {
818	XAR_ITER(i)->iter = XAR(x)->files;
819	free(XAR_ITER(i)->node);
820	return XAR_ITER(i)->iter;
821}
822
823/* xar_file_next
824 * i: iterator allocated by xar_iter_new, and initialized with xar_file_first
825 * Returns: a pointer to the name of the next file associated with
826 * the iterator.  NULL will be returned when there are no more files
827 * to find.
828 * This will recurse down child files (directories), flattening the
829 * namespace and adding separators.  For instance a1->b1->c1, a1 will
830 * first be returned, the subsequent call will return "a1/b1", and the
831 * next call will return "a1/b1/c1", etc.
832 */
833xar_file_t xar_file_next(xar_iter_t i) {
834	xar_file_t f = XAR_ITER(i)->iter;
835	const char *name;
836	if( !(XAR_ITER(i)->nochild) && XAR_FILE(f)->children ) {
837		char *tmp = XAR_ITER(i)->path;
838		xar_prop_get(f, "name", &name);
839		if( tmp ) {
840			asprintf(&XAR_ITER(i)->path, "%s/%s", tmp, name);
841			free(tmp);
842		} else
843			XAR_ITER(i)->path = strdup(name);
844		XAR_ITER(i)->iter = f = XAR_FILE(f)->children;
845		goto FSUCCESS;
846	}
847	XAR_ITER(i)->nochild = 0;
848
849	if( XAR_FILE(f)->next ) {
850		XAR_ITER(i)->iter = f = XAR_FILE(f)->next;
851		goto FSUCCESS;
852	}
853
854	if( XAR_FILE(f)->parent ) {
855		char *tmp1, *tmp2;
856		char *dname;
857
858		if( strstr(XAR_ITER(i)->path, "/") ) {
859			tmp1 = tmp2 = XAR_ITER(i)->path;
860			dname = dirname(tmp2);
861			XAR_ITER(i)->path = strdup(dname);
862			free(tmp1);
863		} else {
864			free(XAR_ITER(i)->path);
865			XAR_ITER(i)->path = NULL;
866		}
867
868		XAR_ITER(i)->iter = f = XAR_FILE(f)->parent;
869		XAR_ITER(i)->nochild = 1;
870		return xar_file_next(i);
871	}
872
873	return NULL;
874FSUCCESS:
875	xar_prop_get(f, "name", &name);
876	XAR_ITER(i)->iter = (void *)f;
877
878	return XAR_ITER(i)->iter;
879}
880
881/* xar_file_find
882 * f: file subtree to look under
883 * path: path to file to find
884 * Returns the file_t describing the file, or NULL if not found.
885 */
886xar_file_t xar_file_find(xar_file_t f, const char *path) {
887	xar_file_t i, ret;
888	char *tmp1, *tmp2, *tmp3;
889
890	if( !f ) return NULL;
891	tmp2 = tmp1 = strdup(path);
892	tmp3 = strsep(&tmp2, "/");
893	i = f;
894	do {
895		const char *name;
896		xar_prop_get(i, "name", &name);
897		if( name == NULL ) continue;
898		if( strcmp(tmp3, name) == 0 ) {
899			if( tmp2 == NULL ) {
900				free(tmp1);
901				return i;
902			}
903			ret = xar_file_find(XAR_FILE(i)->children, tmp2);
904			free(tmp1);
905			return ret;
906		}
907		i = XAR_FILE(i)->next;
908	} while(i);
909	free(tmp1);
910	return NULL;
911}
912
913
914/* xar_prop_serialize
915 * p: property to serialize
916 * writer: the xmlTextWriterPtr allocated by xmlNewTextWriter*()
917 * Summary: recursively serializes the property passed to it, including
918 * children, siblings, attributes, etc.
919 */
920void xar_prop_serialize(xar_prop_t p, xmlTextWriterPtr writer) {
921	xar_prop_t i;
922	xar_attr_t a;
923
924	if( !p )
925		return;
926	i = p;
927	do {
928		if( XAR_PROP(i)->prefix || XAR_PROP(i)->ns )
929			xmlTextWriterStartElementNS(writer, BAD_CAST(XAR_PROP(i)->prefix), BAD_CAST(XAR_PROP(i)->key), NULL);
930		else
931			xmlTextWriterStartElement(writer, BAD_CAST(XAR_PROP(i)->key));
932		for(a = XAR_PROP(i)->attrs; a; a = XAR_ATTR(a)->next) {
933			xmlTextWriterWriteAttributeNS(writer, BAD_CAST(XAR_ATTR(a)->ns), BAD_CAST(XAR_ATTR(a)->key), NULL, BAD_CAST(XAR_ATTR(a)->value));
934		}
935		if( XAR_PROP(i)->value ) {
936			if( strcmp(XAR_PROP(i)->key, "name") == 0 ) {
937				unsigned char *tmp;
938				int outlen = strlen(XAR_PROP(i)->value);
939				int inlen, len;
940
941				inlen = len = outlen;
942
943				tmp = malloc(len);
944				assert(tmp);
945				if( UTF8Toisolat1(tmp, &len, BAD_CAST(XAR_PROP(i)->value), &inlen) < 0 ) {
946					xmlTextWriterWriteAttribute(writer, BAD_CAST("enctype"), BAD_CAST("base64"));
947					xmlTextWriterWriteBase64(writer, XAR_PROP(i)->value, 0, strlen(XAR_PROP(i)->value));
948				} else
949					xmlTextWriterWriteString(writer, BAD_CAST(XAR_PROP(i)->value));
950				free(tmp);
951			} else
952				xmlTextWriterWriteString(writer, BAD_CAST(XAR_PROP(i)->value));
953		}
954
955		if( XAR_PROP(i)->children ) {
956			xar_prop_serialize(XAR_PROP(i)->children, writer);
957		}
958		xmlTextWriterEndElement(writer);
959
960		i = XAR_PROP(i)->next;
961	} while(i);
962}
963
964/* xar_file_serialize
965 * f: file to serialize
966 * writer: the xmlTextWriterPtr allocated by xmlNewTextWriter*()
967 * Summary: recursively serializes the file passed to it, including
968 * children, siblings, properties, attributes, etc.
969 */
970void xar_file_serialize(xar_file_t f, xmlTextWriterPtr writer) {
971	xar_file_t i;
972	xar_attr_t a;
973
974	i = f;
975	do {
976		xmlTextWriterStartElement(writer, BAD_CAST("file"));
977		for(a = XAR_FILE(i)->attrs; a; a = XAR_ATTR(a)->next) {
978			xmlTextWriterWriteAttribute(writer, BAD_CAST(XAR_ATTR(a)->key), BAD_CAST(XAR_ATTR(a)->value));
979		}
980		xar_prop_serialize(XAR_FILE(i)->props, writer);
981		if( XAR_FILE(i)->children )
982			xar_file_serialize(XAR_FILE(i)->children, writer);
983		xmlTextWriterEndElement(writer);
984		i = XAR_FILE(i)->next;
985	} while(i);
986	return;
987}
988
989/* xar_prop_unserialize
990 * f: file the property is to belong to
991 * p: parent property, may be NULL
992 * reader: xmlTextReaderPtr already allocated
993 */
994int32_t xar_prop_unserialize(xar_file_t f, xar_prop_t parent, xmlTextReaderPtr reader) {
995	const char *name, *value, *ns;
996	int type, i, isempty = 0;
997	int isname = 0, isencoded = 0;
998	xar_prop_t p;
999
1000	p = xar_prop_new(f, parent);
1001	if( xmlTextReaderIsEmptyElement(reader) )
1002		isempty = 1;
1003	i = xmlTextReaderAttributeCount(reader);
1004	name = (const char *)xmlTextReaderConstLocalName(reader);
1005	XAR_PROP(p)->key = strdup(name);
1006	ns = (const char *)xmlTextReaderConstPrefix(reader);
1007	if( ns ) XAR_PROP(p)->prefix = strdup(ns);
1008	if( strcmp(name, "name") == 0 )
1009		isname = 1;
1010	if( i > 0 ) {
1011		for(i = xmlTextReaderMoveToFirstAttribute(reader); i == 1; i = xmlTextReaderMoveToNextAttribute(reader)) {
1012			xar_attr_t a;
1013			const char *name = (const char *)xmlTextReaderConstLocalName(reader);
1014			const char *value = (const char *)xmlTextReaderConstValue(reader);
1015			const char *ns = (const char *)xmlTextReaderConstPrefix(reader);
1016			if( isname && (strcmp(name, "enctype") == 0) && (strcmp(value, "base64") == 0) ) {
1017				isencoded = 1;
1018			} else {
1019				a = xar_attr_new();
1020				XAR_ATTR(a)->key = strdup(name);
1021				XAR_ATTR(a)->value = strdup(value);
1022				if(ns) XAR_ATTR(a)->ns = strdup(ns);
1023				XAR_ATTR(a)->next = XAR_PROP(p)->attrs;
1024				XAR_PROP(p)->attrs = a;
1025			}
1026		}
1027	}
1028	if( isempty )
1029		return 0;
1030	while( xmlTextReaderRead(reader) == 1) {
1031		type = xmlTextReaderNodeType(reader);
1032		switch(type) {
1033		case XML_READER_TYPE_ELEMENT:
1034			xar_prop_unserialize(f, p, reader);
1035			break;
1036		case XML_READER_TYPE_TEXT:
1037			value = (const char *)xmlTextReaderConstValue(reader);
1038			free((char*)XAR_PROP(p)->value);
1039			if( isencoded )
1040				XAR_PROP(p)->value = (const char *)xar_from_base64(BAD_CAST(value), strlen(value), NULL);
1041			else
1042				XAR_PROP(p)->value = strdup(value);
1043			if( isname ) {
1044				if( XAR_FILE(f)->parent ) {
1045
1046					if (XAR_FILE(f)->fspath) {		/* It's possible that a XAR header may contain multiple name entries. Make sure we don't smash the old one. */
1047						free((void*)XAR_FILE(f)->fspath);
1048						XAR_FILE(f)->fspath = NULL;
1049					}
1050
1051					asprintf((char **)&XAR_FILE(f)->fspath, "%s/%s", XAR_FILE(XAR_FILE(f)->parent)->fspath, XAR_PROP(p)->value);
1052				} else {
1053
1054					if (XAR_FILE(f)->fspath) {		/* It's possible that a XAR header may contain multiple name entries. Make sure we don't smash the old one. */
1055						free((void*)XAR_FILE(f)->fspath);
1056						XAR_FILE(f)->fspath = NULL;
1057					}
1058
1059					XAR_FILE(f)->fspath = strdup(XAR_PROP(p)->value);
1060				}
1061			}
1062			break;
1063		case XML_READER_TYPE_END_ELEMENT:
1064			return 0;
1065			break;
1066		}
1067	}
1068
1069	/* XXX: Should never be reached */
1070	return 0;
1071}
1072
1073/* xar_file_unserialize
1074 * x: archive we're unserializing to
1075 * parent: The parent file of the file to be unserialized.  May be NULL
1076 * reader: The xmlTextReaderPtr we are reading the xml from.
1077 * Summary: Takes a <file> node, and adds all attributes, child properties,
1078 * and child files.
1079 */
1080xar_file_t xar_file_unserialize(xar_t x, xar_file_t parent, xmlTextReaderPtr reader) {
1081	xar_file_t ret;
1082	const char *name;
1083	int type, i;
1084
1085	ret = xar_file_new(parent);
1086
1087	i = xmlTextReaderAttributeCount(reader);
1088	if( i > 0 ) {
1089		for(i = xmlTextReaderMoveToFirstAttribute(reader); i == 1; i = xmlTextReaderMoveToNextAttribute(reader)) {
1090			xar_attr_t a;
1091			const char *name = (const char *)xmlTextReaderConstLocalName(reader);
1092			const char *value = (const char *)xmlTextReaderConstValue(reader);
1093			a = xar_attr_new();
1094			XAR_ATTR(a)->key = strdup(name);
1095			XAR_ATTR(a)->value = strdup(value);
1096			XAR_ATTR(a)->next = XAR_FILE(ret)->attrs;
1097			XAR_FILE(ret)->attrs = a;
1098		}
1099	}
1100
1101	// recursively unserialize each element nested within this <file>
1102	while( xmlTextReaderRead(reader) == 1 ) {
1103		type = xmlTextReaderNodeType(reader);
1104		name = (const char *)xmlTextReaderConstLocalName(reader);
1105		if( (type == XML_READER_TYPE_END_ELEMENT) && (strcmp(name, "file")==0) ) {
1106			const char *opt;
1107			xar_prop_get(ret, "type", &opt);
1108			if( opt && (strcmp(opt, "hardlink") == 0) ) {
1109				opt = xar_attr_get(ret, "type", "link");
1110				if( opt && (strcmp(opt, "original") == 0) ) {
1111					opt = xar_attr_get(ret, NULL, "id");
1112					xmlHashAddEntry(XAR(x)->link_hash, BAD_CAST(opt), XAR_FILE(ret));
1113				}
1114			}
1115			return ret;
1116		}
1117
1118		if( type == XML_READER_TYPE_ELEMENT ) {
1119			if( strcmp(name, "file")==0 ) {
1120				xar_file_unserialize(x, ret, reader);
1121			} else
1122				xar_prop_unserialize(ret, NULL, reader);
1123		}
1124	}
1125
1126	/* XXX Should never be reached */
1127	return ret;
1128}
1129
1130