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