1/* 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "webdavd.h" 25 26#include <CoreFoundation/CoreFoundation.h> 27#include <sys/dirent.h> 28#include <unistd.h> 29#include <stdlib.h> 30#include <libxml/parser.h> 31#include <libxml/xmlmemory.h> 32#include "webdav_parse.h" 33#include "webdav_cache.h" 34#include "webdav_network.h" 35#include "LogMessage.h" 36 37extern long long 38strtoq(const char *, char **, int); 39 40/*****************************************************************************/ 41 42/* local function prototypes */ 43 44static int from_base64(const char *base64str, unsigned char *outBuffer, size_t *lengthptr); 45static void parser_file_count_create(void *ctx, 46 const xmlChar *localname, 47 const xmlChar *prefix, 48 const xmlChar *URI, 49 int nb_namespaces, 50 const xmlChar **namespaces, 51 int nb_attributes, 52 int nb_defaulted, 53 const xmlChar **attributes); 54static void parser_stat_create(void *ctx, 55 const xmlChar *localname, 56 const xmlChar *prefix, 57 const xmlChar *URI, 58 int nb_namespaces, 59 const xmlChar **namespaces, 60 int nb_attributes, 61 int nb_defaulted, 62 const xmlChar **attributes); 63static void parser_statfs_create(void *ctx, 64 const xmlChar *localname, 65 const xmlChar *prefix, 66 const xmlChar *URI, 67 int nb_namespaces, 68 const xmlChar **namespaces, 69 int nb_attributes, 70 int nb_defaulted, 71 const xmlChar **attributes); 72static void parser_lock_create(void *ctx, 73 const xmlChar *localname, 74 const xmlChar *prefix, 75 const xmlChar *URI, 76 int nb_namespaces, 77 const xmlChar **namespaces, 78 int nb_attributes, 79 int nb_defaulted, 80 const xmlChar **attributes); 81void parser_opendir_create (void *ctx, 82 const xmlChar *localname, 83 const xmlChar *prefix, 84 const xmlChar *URI, 85 int nb_namespaces, 86 const xmlChar **namespaces, 87 int nb_attributes, 88 int nb_defaulted, 89 const xmlChar **attributes); 90static void parser_lock_add(void *ctx, const xmlChar *localname, int length); 91static void parser_stat_add(void *ctx, const xmlChar *localname, int length); 92static void parser_statfs_add(void *ctx, const xmlChar *localname, int length); 93void parser_opendir_add(void *ctx, const xmlChar *localname, int length); 94void parse_stat_end(void *ctx, 95 const xmlChar *localname, 96 const xmlChar *prefix, 97 const xmlChar *URI); 98void parser_statfs_end(void *ctx, 99 const xmlChar *localname, 100 const xmlChar *prefix, 101 const xmlChar *URI); 102void parser_lock_end(void *ctx, 103 const xmlChar *localname, 104 const xmlChar *prefix, 105 const xmlChar *URI); 106void parser_opendir_end(void *ctx, 107 const xmlChar *localname, 108 const xmlChar *prefix, 109 const xmlChar *URI); 110void parser_file_count_end(void *ctx, 111 const xmlChar *localname, 112 const xmlChar *prefix, 113 const xmlChar *URI); 114void parser_cachevalidators_end(void *ctx, 115 const xmlChar *localname, 116 const xmlChar *prefix, 117 const xmlChar *URI); 118void parser_multistatus_end(void *ctx, 119 const xmlChar *localname, 120 const xmlChar *prefix, 121 const xmlChar *URI); 122/*****************************************************************************/ 123/* The from_base64 function decodes a base64 encoded c-string into outBuffer. 124 * The outBuffer's size is *lengthptr. The actual number of bytes decoded into 125 * outBuffer is also returned in *lengthptr. If outBuffer is large enough to 126 * decode the base64 string and if the base64 encoding is valid, from_base64() 127 * returns 0; otherwise -1 is returned. Note that outBuffer is just an array of 128 * bytes... it is not a c-string. 129 */ 130static int from_base64(const char *base64str, unsigned char *outBuffer, size_t *lengthptr) 131{ 132 char decodedChar; 133 unsigned long base64Length; 134 unsigned char *eightBitByte; 135 unsigned char sixBitEncoding[4]; 136 unsigned short encodingIndex; 137 int endOfData; 138 const char *equalPtr; 139 const char *base64CharPtr; 140 const char *base64EndPtr; 141 142 /* Determine the length of the base64 input string. 143 * This also catches illegal '=' characters within a base64 string. 144 */ 145 146 base64Length = 0; 147 148 /* is there an '=' character? */ 149 equalPtr = strchr(base64str, '='); 150 if ( equalPtr != NULL ) 151 { 152 /* yes -- then it must be the last character of an octet, or 153 * it must be the next to last character of an octet followed 154 * by another '=' character */ 155 switch ( (equalPtr - base64str) % 4 ) 156 { 157 case 0: 158 case 1: 159 /* invalid encoding */ 160 goto error_exit; 161 break; 162 163 case 2: 164 if ( equalPtr[1] != '=' ) 165 { 166 /* invalid encoding */ 167 goto error_exit; 168 } 169 base64Length = (equalPtr - base64str) + 2; 170 *lengthptr += 2; /* adjust for padding */ 171 break; 172 173 case 3: 174 base64Length = (equalPtr - base64str) + 1; 175 *lengthptr += 1; /* adjust for padding */ 176 break; 177 } 178 } 179 else 180 { 181 base64Length = strlen(base64str); 182 } 183 184 /* Make sure outBuffer is big enough */ 185 if ( *lengthptr < ((base64Length / 4) * 3) ) 186 { 187 /* outBuffer is too small */ 188 goto error_exit; 189 } 190 191 /* Make sure length is a multiple of 4 */ 192 if ( (base64Length % 4) != 0 ) 193 { 194 /* invalid encoding */ 195 goto error_exit; 196 } 197 198 /* OK -- */ 199 eightBitByte = outBuffer; 200 encodingIndex = 0; 201 endOfData = FALSE; 202 base64EndPtr = (char *)((unsigned long)base64str + base64Length); 203 base64CharPtr = base64str; 204 while ( base64CharPtr < base64EndPtr ) 205 { 206 decodedChar = *base64CharPtr++; 207 208 if ( (decodedChar >= 'A') && (decodedChar <= 'Z') ) 209 { 210 decodedChar = decodedChar - 'A'; 211 } 212 else if ( (decodedChar >= 'a') && (decodedChar <= 'z') ) 213 { 214 decodedChar = decodedChar - 'a' + 26; 215 } 216 else if ( (decodedChar >= '0') && (decodedChar <= '9') ) 217 { 218 decodedChar = decodedChar - '0' + 52; 219 } 220 else if ( decodedChar == '+' ) 221 { 222 decodedChar = 62; 223 } 224 else if ( decodedChar == '/' ) 225 { 226 decodedChar = 63; 227 } 228 else if ( decodedChar == '=' ) /* end of base64 encoding */ 229 { 230 endOfData = TRUE; 231 } 232 else 233 { 234 /* invalid character */ 235 goto error_exit; 236 } 237 238 if ( endOfData ) 239 { 240 /* make sure there's no more looping */ 241 base64CharPtr = base64EndPtr; 242 } 243 else 244 { 245 sixBitEncoding[encodingIndex] = (unsigned char)decodedChar; 246 ++encodingIndex; 247 } 248 249 if ( (encodingIndex == 4) || endOfData) 250 { 251 /* convert four 6-bit characters into three 8-bit bytes */ 252 253 /* always get first byte */ 254 *eightBitByte++ = 255 (sixBitEncoding[0] << 2) | ((sixBitEncoding[1] & 0x30) >> 4); 256 if ( encodingIndex >= 3 ) 257 { 258 /* get second byte only if encodingIndex is 3 or 4 */ 259 *eightBitByte++ = 260 ((sixBitEncoding[1] & 0x0F) << 4) | ((sixBitEncoding[2] & 0x3C) >> 2); 261 if ( encodingIndex == 4 ) 262 { 263 /* get third byte only if encodingIndex is 4 */ 264 *eightBitByte++ = 265 ((sixBitEncoding[2] & 0x03) << 6) | (sixBitEncoding[3] & 0x3F); 266 } 267 } 268 269 /* reset encodingIndex */ 270 encodingIndex = 0; 271 } 272 } 273 274 /* return the number of bytes in outBuffer and no error */ 275 *lengthptr = eightBitByte - outBuffer; 276 return ( 0 ); 277 278error_exit: 279 /* return 0 bytes in outBuffer and an error */ 280 *lengthptr = 0; 281 return ( -1 ); 282} 283/*****************************************************************************/ 284 285void parse_stat_end(void *ctx, 286 const xmlChar *localname, 287 const xmlChar *prefix, 288 const xmlChar *URI) 289{ 290 #pragma unused(localname,prefix,URI) 291 struct webdav_stat_attr* text_ptr = (struct webdav_stat_attr*)ctx; 292 text_ptr->start = false; 293} 294/*****************************************************************************/ 295 296void parser_file_count_end(void *ctx, 297 const xmlChar *localname, 298 const xmlChar *prefix, 299 const xmlChar *URI) 300{ 301 #pragma unused(ctx,localname,prefix,URI) 302} 303/*****************************************************************************/ 304 305void parser_statfs_end(void *ctx, 306 const xmlChar *localname, 307 const xmlChar *prefix, 308 const xmlChar *URI) 309{ 310 #pragma unused(localname,prefix,URI) 311 struct webdav_quotas* text_ptr = (struct webdav_quotas*)ctx; 312 text_ptr->start = false; 313} 314/*****************************************************************************/ 315 316void parser_lock_end(void *ctx, 317 const xmlChar *localname, 318 const xmlChar *prefix, 319 const xmlChar *URI) 320{ 321 #pragma unused(localname,prefix,URI) 322 webdav_parse_lock_struct_t *lock_struct = (webdav_parse_lock_struct_t *)ctx; 323 lock_struct->start = false; 324} 325/*****************************************************************************/ 326 327void parser_opendir_end(void *ctx, 328 const xmlChar *localname, 329 const xmlChar *prefix, 330 const xmlChar *URI) 331{ 332 #pragma unused(localname,prefix,URI) 333 webdav_parse_opendir_struct_t * struct_ptr = (webdav_parse_opendir_struct_t *)ctx; 334 struct_ptr->start = false; 335} 336/*****************************************************************************/ 337 338void parser_cachevalidators_end(void *ctx, 339 const xmlChar *localname, 340 const xmlChar *prefix, 341 const xmlChar *URI) 342{ 343 #pragma unused(localname,prefix,URI) 344 struct webdav_parse_cachevalidators_struct *cachevalidators_struct = (struct webdav_parse_cachevalidators_struct *)ctx; 345 cachevalidators_struct->start = false; 346} 347/*****************************************************************************/ 348 349void parser_multistatus_end(void *ctx, 350 const xmlChar *localname, 351 const xmlChar *prefix, 352 const xmlChar *URI) 353{ 354 #pragma unused(localname,prefix,URI) 355 webdav_parse_multistatus_list_t * struct_ptr = (webdav_parse_multistatus_list_t *)ctx; 356 struct_ptr->start = false; 357} 358/*****************************************************************************/ 359static webdav_parse_opendir_element_t *create_opendir_element(void) 360{ 361 webdav_parse_opendir_element_t *element_ptr; 362 363 element_ptr = malloc(sizeof(webdav_parse_opendir_element_t)); 364 if (!element_ptr) 365 return (NULL); 366 367 bzero(element_ptr, sizeof(webdav_parse_opendir_element_t)); 368 element_ptr->dir_data.d_type = DT_REG; 369 element_ptr->seen_href = FALSE; 370 element_ptr->seen_response_end = FALSE; 371 element_ptr->dir_data.d_namlen = 0; 372 element_ptr->dir_data.d_name_URI_length = 0; 373 element_ptr->dir_data.d_reclen = sizeof(struct webdav_dirent); 374 element_ptr->next = NULL; 375 return (element_ptr); 376} 377/*****************************************************************************/ 378void parser_opendir_create (void *ctx, 379 const xmlChar *localname, 380 const xmlChar *prefix, 381 const xmlChar *URI, 382 int nb_namespaces, 383 const xmlChar **namespaces, 384 int nb_attributes, 385 int nb_defaulted, 386 const xmlChar **attributes) 387{ 388 #pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes) 389 webdav_parse_opendir_element_t * element_ptr = NULL; 390 webdav_parse_opendir_element_t * list_ptr; 391 webdav_parse_opendir_struct_t * struct_ptr = (webdav_parse_opendir_struct_t *)ctx; 392 CFStringRef nodeString; 393 struct_ptr->start=true; 394 nodeString = CFStringCreateWithCString (kCFAllocatorDefault, 395 (const char *)localname, 396 kCFStringEncodingUTF8 397 ); 398 /* See if this is the resource type. If it is, malloc a webdav_parse_opendir_element_t element and add it to the list.*/ 399 400 if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 401 { 402 element_ptr = struct_ptr->tail; 403 404 if ( (element_ptr != NULL) && (element_ptr->seen_href == FALSE)) 405 { 406 // <rdar://problem/4173444> 407 // This is a placeholder (<D:propstat> & children came before the <D:href>) 408 element_ptr->seen_href = TRUE; 409 struct_ptr->id = WEBDAV_OPENDIR_ELEMENT; 410 struct_ptr->data_ptr = (void *)element_ptr; 411 } 412 else 413 { 414 // Create the new href element 415 element_ptr = create_opendir_element(); 416 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 417 418 element_ptr->seen_href = TRUE; 419 420 if (struct_ptr->head == NULL) 421 { 422 struct_ptr->head = element_ptr; 423 } 424 else 425 { 426 list_ptr = struct_ptr->tail; 427 list_ptr->next = element_ptr; 428 } 429 struct_ptr->tail = element_ptr; 430 431 struct_ptr->id = WEBDAV_OPENDIR_ELEMENT; 432 struct_ptr->data_ptr = (void *)element_ptr; 433 } 434 } /* end if href */ 435 else if (((CFStringCompare(nodeString, CFSTR("collection"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 436 { 437 /* If we have a collection property, we should normally have an 438 * element ptr in the context already. But this is not always the 439 * case (see <rdar://problem/4173444>). 440 */ 441 element_ptr = struct_ptr->tail; 442 443 // ignore the last <D:href> if we've already seen <D:/response> 444 if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE)) 445 element_ptr = NULL; 446 447 if (element_ptr == NULL) 448 { 449 // <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload 450 // 451 // The <D:href> element might appear after the <D:propstat>. To handle this 452 // case we simply create a placeholder opendir element. 453 element_ptr = create_opendir_element(); 454 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 455 456 if (struct_ptr->head == NULL) 457 { 458 struct_ptr->head = element_ptr; 459 } 460 else 461 { 462 list_ptr = struct_ptr->tail; 463 list_ptr->next = element_ptr; 464 } 465 struct_ptr->tail = element_ptr; 466 } 467 468 element_ptr->dir_data.d_type = DT_DIR; 469 470 /* Not interested in child of collection element. We can 471 * and should free the return_ptr in this case. 472 */ 473 struct_ptr->id = WEBDAV_OPENDIR_IGNORE; 474 struct_ptr->data_ptr = NULL; 475 } /* end if collection */ 476 else if (((CFStringCompare(nodeString, CFSTR("getcontentlength"), kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 477 { 478 /* If we have size then mark up the element pointer so that the 479 * child will know to parse the upcoming text size and store it. 480 */ 481 element_ptr = struct_ptr->tail; 482 483 // ignore the last <D:href> if we've already seen <D:/response> 484 if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE)) 485 element_ptr = NULL; 486 487 if (element_ptr == NULL) 488 { 489 // <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload 490 // 491 // The <D:href> element might appear after the <D:propstat>. To handle this 492 // case we simply create a placeholder opendir element. 493 element_ptr = create_opendir_element(); 494 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 495 496 if (struct_ptr->head == NULL) 497 { 498 struct_ptr->head = element_ptr; 499 } 500 else 501 { 502 list_ptr = struct_ptr->tail; 503 list_ptr->next = element_ptr; 504 } 505 struct_ptr->tail = element_ptr; 506 } 507 508 struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_LENGTH; 509 struct_ptr->data_ptr = (void *)element_ptr; 510 } /* end if length */ 511 else if (((CFStringCompare(nodeString, CFSTR("getlastmodified"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 512 { 513 /* If we have size then mark up the element pointer so that the 514 * child will know to parse the upcoming text size and store it. 515 */ 516 element_ptr = struct_ptr->tail; 517 518 // ignore the last <D:href> if we've already seen <D:/response> 519 if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE)) 520 element_ptr = NULL; 521 522 if (element_ptr == NULL) 523 { 524 // <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload 525 // 526 // The <D:href> element might appear after the <D:propstat>. To handle this 527 // case we simply create a placeholder opendir element. 528 element_ptr = create_opendir_element(); 529 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 530 531 if (struct_ptr->head == NULL) 532 { 533 struct_ptr->head = element_ptr; 534 } 535 else 536 { 537 list_ptr = struct_ptr->tail; 538 list_ptr->next = element_ptr; 539 } 540 struct_ptr->tail = element_ptr; 541 } 542 543 struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_MODDATE; 544 struct_ptr->data_ptr = (void *)element_ptr; 545 } /* end if modified */ 546 else if (((CFStringCompare(nodeString, CFSTR("creationdate"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 547 { 548 /* If we have size then mark up the element pointer so that the 549 * child will know to parse the upcoming text size and store it. 550 */ 551 element_ptr = struct_ptr->tail; 552 553 // ignore the last <D:href> if we've already seen <D:/response> 554 if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE)) 555 element_ptr = NULL; 556 557 if (element_ptr == NULL) 558 { 559 // <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload 560 // 561 // The <D:href> element might appear after the <D:propstat>. To handle this 562 // case we simply create a placeholder opendir element. 563 element_ptr = create_opendir_element(); 564 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 565 566 if (struct_ptr->head == NULL) 567 { 568 struct_ptr->head = element_ptr; 569 } 570 else 571 { 572 list_ptr = struct_ptr->tail; 573 list_ptr->next = element_ptr; 574 } 575 struct_ptr->tail = element_ptr; 576 } 577 578 struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_CREATEDATE; 579 struct_ptr->data_ptr = (void *)element_ptr; 580 } /* end if createdate */ 581 else if (((CFStringCompare(nodeString, CFSTR("appledoubleheader"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 582 { 583 /* If we have size then mark up the element pointer so that the 584 * child will know to parse the upcoming text size and store it. 585 */ 586 syslog(LOG_ERR, "in the create opendir appledoubleheaderfield\n"); 587 element_ptr = struct_ptr->tail; 588 589 // ignore the last <D:href> if we've already seen <D:/response> 590 if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE)) 591 element_ptr = NULL; 592 593 if (element_ptr == NULL) 594 { 595 // <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload 596 // 597 // The <D:href> element might appear after the <D:propstat>. To handle this 598 // case we simply create a placeholder opendir element. 599 element_ptr = create_opendir_element(); 600 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 601 602 if (struct_ptr->head == NULL) 603 { 604 struct_ptr->head = element_ptr; 605 } 606 else 607 { 608 list_ptr = struct_ptr->tail; 609 list_ptr->next = element_ptr; 610 } 611 struct_ptr->tail = element_ptr; 612 } 613 614 struct_ptr->id = WEBDAV_OPENDIR_APPLEDOUBLEHEADER; 615 struct_ptr->data_ptr = (void *)element_ptr; 616 } /* end if appledoubleheader */ 617 else if (((CFStringCompare(nodeString, CFSTR("response"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 618 { 619 struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_RESPONSE; 620 struct_ptr->data_ptr = (void *)NULL; 621 } 622 else { 623 struct_ptr->id = WEBDAV_OPENDIR_IGNORE; 624 struct_ptr->data_ptr = (void *)NULL; 625 } 626 627malloc_element_ptr: 628 syslog(LOG_DEBUG,"malloc failed\n"); 629 630 631} 632/*****************************************************************************/ 633static webdav_parse_multistatus_element_t * 634create_multistatus_element(void) 635{ 636 webdav_parse_multistatus_element_t *element_ptr; 637 638 element_ptr = malloc(sizeof(webdav_parse_multistatus_element_t)); 639 if (!element_ptr) 640 return (NULL); 641 642 bzero(element_ptr, sizeof(webdav_parse_multistatus_element_t)); 643 element_ptr->statusCode = WEBDAV_MULTISTATUS_INVALID_STATUS; 644 element_ptr->name_len = 0; 645 element_ptr->name[0] = 0; 646 element_ptr->seen_href = FALSE; 647 element_ptr->seen_response_end = FALSE; 648 element_ptr->next = NULL; 649 return (element_ptr); 650} 651/*****************************************************************************/ 652 653static void parser_file_count_create(void *ctx, 654 const xmlChar *localname, 655 const xmlChar *prefix, 656 const xmlChar *URI, 657 int nb_namespaces, 658 const xmlChar **namespaces, 659 int nb_attributes, 660 int nb_defaulted, 661 const xmlChar **attributes) 662{ 663 #pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes) 664 CFStringRef nodeString; 665 nodeString = CFStringCreateWithCString (kCFAllocatorDefault, 666 (const char *)localname, 667 kCFStringEncodingUTF8 668 ); 669 if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 670 { 671 ++(*((int *)ctx)); 672 } 673 if(nodeString) 674 CFRelease(nodeString); 675} 676/*****************************************************************************/ 677 678static void parser_stat_create(void *ctx, 679 const xmlChar *localname, 680 const xmlChar *prefix, 681 const xmlChar *URI, 682 int nb_namespaces, 683 const xmlChar **namespaces, 684 int nb_attributes, 685 int nb_defaulted, 686 const xmlChar **attributes) 687{ 688 #pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes) 689 struct webdav_stat_attr* text_ptr = (struct webdav_stat_attr*)ctx; 690 text_ptr->data = (void *)WEBDAV_STATFS_IGNORE; 691 text_ptr->start = true; 692 CFStringRef nodeString; 693 nodeString = CFStringCreateWithCString (kCFAllocatorDefault, 694 (const char *)localname, 695 kCFStringEncodingUTF8 696 ); 697 /* See if this is a type we are interested in If it is, we'll return 698 the appropriate constant */ 699 700 if (((CFStringCompare(nodeString, CFSTR("getcontentlength"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 701 { 702 text_ptr->data = (void *)WEBDAV_STAT_LENGTH; 703 } 704 else 705 { 706 if (((CFStringCompare(nodeString, CFSTR("getlastmodified"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 707 { 708 text_ptr->data = (void *)WEBDAV_STAT_MODDATE; 709 } 710 else if (((CFStringCompare(nodeString, CFSTR("creationdate"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 711 { 712 text_ptr->data = (void *)WEBDAV_STAT_CREATEDATE; 713 } 714 else 715 { 716 if (((CFStringCompare(nodeString, CFSTR("collection"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 717 { 718 /* It's a collection so set the type as VDIR */ 719 ((struct stat *)ctx)->st_mode = S_IFDIR; 720 } /* end if collection */ 721 } /* end of if-else mod date */ 722 } /* end if-else length*/ 723 if(nodeString) 724 CFRelease(nodeString); 725} 726/*****************************************************************************/ 727 728static void parser_statfs_create(void *ctx, 729 const xmlChar *localname, 730 const xmlChar *prefix, 731 const xmlChar *URI, 732 int nb_namespaces, 733 const xmlChar **namespaces, 734 int nb_attributes, 735 int nb_defaulted, 736 const xmlChar **attributes) 737{ 738 #pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes) 739 struct webdav_quotas* text_ptr = (struct webdav_quotas*)ctx; 740 text_ptr->data = (void *)WEBDAV_STATFS_IGNORE; 741 text_ptr->start = true; 742 CFStringRef nodeString; 743 nodeString = CFStringCreateWithCString (kCFAllocatorDefault, 744 (const char *)localname, 745 kCFStringEncodingUTF8 746 ); 747 /* See if this is a type we are interested in If it is, we'll return 748 the appropriate constant */ 749 750 /* handle the "quota-available-bytes" and "quota-used-bytes" properties in the "DAV:" namespace */ 751 if (((CFStringCompare(nodeString, CFSTR("quota-available-bytes"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 752 { 753 text_ptr->data = (void *)WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES; 754 } 755 else if (((CFStringCompare(nodeString, CFSTR("quota-used-bytes"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 756 { 757 text_ptr->data = (void *)WEBDAV_STATFS_QUOTA_USED_BYTES; 758 } 759 /* handle the deprecated "quota" and "quotaused" properties in the "DAV:" namespace */ 760 else if (((CFStringCompare(nodeString, CFSTR("quota"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 761 { 762 text_ptr->data = (void *)WEBDAV_STATFS_QUOTA; 763 } 764 else if (((CFStringCompare(nodeString, CFSTR("quotaused"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 765 { 766 text_ptr->data = (void *)WEBDAV_STATFS_QUOTAUSED; 767 } 768 769 if(nodeString) 770 CFRelease(nodeString); 771} 772 773/*****************************************************************************/ 774 775static void parser_lock_create(void *ctx, 776 const xmlChar *localname, 777 const xmlChar *prefix, 778 const xmlChar *URI, 779 int nb_namespaces, 780 const xmlChar **namespaces, 781 int nb_attributes, 782 int nb_defaulted, 783 const xmlChar **attributes) 784{ 785 #pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes) 786 webdav_parse_lock_struct_t *lock_struct = (webdav_parse_lock_struct_t *)ctx; 787 CFStringRef nodeString = NULL; 788 nodeString = CFStringCreateWithCString (kCFAllocatorDefault, 789 (const char *)localname, 790 kCFStringEncodingUTF8 791 ); 792 /* See if this is a type we are interested in If it is, we'll return 793 the appropriate constant */ 794 if (((CFStringCompare(nodeString, CFSTR("locktoken"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 795 { 796 lock_struct->context = WEBDAV_LOCK_TOKEN; 797 } 798 else 799 { 800 if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 801 { 802 if (lock_struct->context == WEBDAV_LOCK_TOKEN) 803 { 804 lock_struct->context = WEBDAV_LOCK_HREF; 805 } 806 else 807 { 808 lock_struct->context = 0; 809 } 810 } 811 } /* end if-else locktoken*/ 812 if(nodeString) 813 CFRelease(nodeString); 814} 815/*****************************************************************************/ 816static void parser_cachevalidators_create(void *ctx, 817 const xmlChar *localname, 818 const xmlChar *prefix, 819 const xmlChar *URI, 820 int nb_namespaces, 821 const xmlChar **namespaces, 822 int nb_attributes, 823 int nb_defaulted, 824 const xmlChar **attributes) 825{ 826 #pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes) 827 struct webdav_parse_cachevalidators_struct* text_ptr = (struct webdav_parse_cachevalidators_struct*)ctx; 828 CFStringRef nodeString = NULL; 829 text_ptr->start = true; 830 nodeString = CFStringCreateWithCString (kCFAllocatorDefault, 831 (const char *)localname, 832 kCFStringEncodingUTF8 833 ); 834 /* See if this is a type we are interested in If it is, we'll return 835 the appropriate constant */ 836 if (((CFStringCompare(nodeString, CFSTR("getlastmodified"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 837 { 838 text_ptr->data = (void *)WEBDAV_CACHEVALIDATORS_MODDATE; 839 } 840 else if (((CFStringCompare(nodeString, CFSTR("getetag"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 841 { 842 text_ptr->data = (void *)WEBDAV_CACHEVALIDATORS_ETAG; 843 } 844 if(nodeString) 845 CFRelease(nodeString); 846} 847 848/*****************************************************************************/ 849 850static void parser_multistatus_create(void *ctx, 851 const xmlChar *localname, 852 const xmlChar *prefix, 853 const xmlChar *URI, 854 int nb_namespaces, 855 const xmlChar **namespaces, 856 int nb_attributes, 857 int nb_defaulted, 858 const xmlChar **attributes) 859{ 860 #pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes) 861 webdav_parse_multistatus_element_t * element_ptr = NULL; 862 webdav_parse_multistatus_element_t * list_ptr = NULL; 863 webdav_parse_multistatus_list_t * struct_ptr = (webdav_parse_multistatus_list_t *)ctx; 864 CFStringRef nodeString = NULL; 865 struct_ptr->start = true; 866 nodeString = CFStringCreateWithCString (kCFAllocatorDefault, 867 (const char *)localname, 868 kCFStringEncodingUTF8 869 ); 870 /* See if this is the resource type. If it is, malloc a webdav_parse_opendir_element_t element and 871 add it to the list. */ 872 if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 873 { 874 element_ptr = struct_ptr->tail; 875 876 if ( (element_ptr != NULL) && (element_ptr->seen_href == FALSE)) 877 { 878 // <rdar://problem/4173444> 879 // This is a placeholder (<D:propstat> & children came before the <D:href>) 880 element_ptr->seen_href = TRUE; 881 struct_ptr->id = WEBDAV_MULTISTATUS_ELEMENT; 882 struct_ptr->data_ptr = (void *)element_ptr; 883 } 884 else 885 { 886 // Create the new href element 887 element_ptr = create_multistatus_element(); 888 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 889 890 element_ptr->seen_href = TRUE; 891 892 if (struct_ptr->head == NULL) 893 { 894 struct_ptr->head = element_ptr; 895 } 896 else 897 { 898 list_ptr = struct_ptr->tail; 899 list_ptr->next = element_ptr; 900 } 901 struct_ptr->tail = element_ptr; 902 903 struct_ptr->id = WEBDAV_MULTISTATUS_ELEMENT; 904 struct_ptr->data_ptr = (void *)element_ptr; 905 } 906 } /* end if href */ 907 else if (((CFStringCompare(nodeString, CFSTR("status"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 908 { 909 /* If we have status then mark up the element pointer so that the 910 * add callback will know to parse the upcoming text size and store it. 911 */ 912 element_ptr = struct_ptr->tail; 913 914 // ignore the last <D:href> if we've already seen <D:/response> 915 if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE)) 916 element_ptr = NULL; 917 918 if (element_ptr == NULL) 919 { 920 // <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload 921 // 922 // The <D:href> element might appear after the <D:propstat>. To handle this 923 // case we simply create a placeholder opendir element. 924 element_ptr = create_multistatus_element(); 925 require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM); 926 927 if (struct_ptr->head == NULL) 928 { 929 struct_ptr->head = element_ptr; 930 } 931 else 932 { 933 list_ptr = struct_ptr->tail; 934 list_ptr->next = element_ptr; 935 } 936 struct_ptr->tail = element_ptr; 937 } 938 939 struct_ptr->id = WEBDAV_MULTISTATUS_STATUS; 940 struct_ptr->data_ptr = (void *)element_ptr; 941 } /* end if length */ 942 else if (((CFStringCompare(nodeString, CFSTR("response"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo)) 943 { 944 struct_ptr->id = WEBDAV_MULTISTATUS_RESPONSE; 945 struct_ptr->data_ptr = (void *)NULL; 946 } 947 else { 948 struct_ptr->id = WEBDAV_MULTISTATUS_IGNORE; 949 struct_ptr->data_ptr = (void *)NULL; 950 951 } 952 953malloc_element_ptr: 954 syslog(LOG_INFO,"malloc failed\n"); 955 956 if(nodeString) 957 CFRelease(nodeString); 958} 959/*****************************************************************************/ 960void parser_opendir_add(void *ctx, const xmlChar *localname, int length) 961{ 962 963 webdav_parse_opendir_element_t * element_ptr; 964 webdav_parse_opendir_text_t * text_ptr = NULL; 965 webdav_parse_opendir_struct_t * parent_ptr = (webdav_parse_opendir_struct_t *)ctx; 966 char * ampPointer = NULL; 967 char* str_ptr = NULL; 968 char *ep; 969 970 text_ptr = malloc(sizeof(webdav_parse_opendir_text_t)); 971 bzero(text_ptr,sizeof(webdav_parse_opendir_text_t)); 972 text_ptr->size = (CFIndex)length; 973 memcpy(text_ptr->name,localname,length); 974 /* If the parent is one of our returned directory elements, and if this is a 975 * text element, than copy the text into the name buffer provided we have room */ 976 if(parent_ptr->start == true) 977 { 978 switch (parent_ptr->id) 979 { 980 case WEBDAV_OPENDIR_ELEMENT: 981 element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr; 982 /* TO Handle a special case for ampersand */ 983 ampPointer = strstr((const char*) localname,"amp;"); 984 if(ampPointer) { 985 char * literalPtr = strchr((const char*) localname,'<'); 986 int totalLength = (int)(literalPtr - (char*)localname); 987 if(totalLength >= (length+5)) { 988 str_ptr = (char*)malloc(totalLength+1); 989 memset(str_ptr,0,totalLength+1); 990 memcpy(str_ptr,localname,totalLength); 991 ampPointer = strstr((const char*) str_ptr,"amp;"); 992 /* To handle multiple ampersands*/ 993 while(ampPointer) { 994 ampPointer+=4; 995 memcpy(&text_ptr->name[length],"&",1); 996 memcpy(&text_ptr->name[length+1],ampPointer,totalLength-length-4-1); 997 text_ptr->size = totalLength-4; 998 ampPointer = strstr((const char*)text_ptr->name,"amp;"); 999 length++; 1000 } 1001 free(str_ptr); 1002 } 1003 } 1004 /* make sure the complete name will fit in the structure */ 1005 if ((element_ptr->dir_data.d_name_URI_length + text_ptr->size) <= 1006 ((unsigned int)sizeof(element_ptr->dir_data.d_name) - 1)) 1007 { 1008 bcopy(text_ptr->name, 1009 &element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length], 1010 text_ptr->size); 1011 element_ptr->dir_data.d_name_URI_length += (uint32_t)text_ptr->size; 1012 } 1013 else 1014 { 1015 debug_string("URI too long"); 1016 parent_ptr->error = ENAMETOOLONG; 1017 } 1018 break; 1019 1020 case WEBDAV_OPENDIR_ELEMENT_LENGTH: 1021 element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr; 1022 element_ptr->statsize = strtoq((const char *)text_ptr->name, &ep, 10); 1023 break; 1024 1025 case WEBDAV_OPENDIR_ELEMENT_MODDATE: 1026 element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr; 1027 element_ptr->stattime.tv_sec = DateBytesToTime(text_ptr->name, strlen((const char *)text_ptr->name)); 1028 if (element_ptr->stattime.tv_sec == -1) 1029 { 1030 element_ptr->stattime.tv_sec = 0; 1031 } 1032 element_ptr->stattime.tv_nsec = 0; 1033 break; 1034 1035 case WEBDAV_OPENDIR_ELEMENT_CREATEDATE: 1036 // First try ISO8601 1037 element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr; 1038 element_ptr->createtime.tv_sec = ISO8601ToTime(text_ptr->name, strlen((const char *)text_ptr->name)); 1039 1040 if (element_ptr->createtime.tv_sec == -1) { 1041 // Try RFC 850, RFC 1123 1042 element_ptr->createtime.tv_sec = DateBytesToTime(text_ptr->name, strlen((const char *)text_ptr->name)); 1043 1044 } 1045 1046 if (element_ptr->createtime.tv_sec == -1) 1047 { 1048 element_ptr->createtime.tv_sec = 0; 1049 } 1050 element_ptr->createtime.tv_nsec = 0; 1051 break; 1052 1053 case WEBDAV_OPENDIR_APPLEDOUBLEHEADER: 1054 { 1055 size_t len = APPLEDOUBLEHEADER_LENGTH; 1056 1057 element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr; 1058 from_base64((const char *)text_ptr->name, (unsigned char *)element_ptr->appledoubleheader, &len); 1059 if (len == APPLEDOUBLEHEADER_LENGTH) 1060 { 1061 element_ptr->appledoubleheadervalid = TRUE; 1062 } 1063 } 1064 break; 1065 1066 default: 1067 break; 1068 } /* end of switch statement */ 1069 parent_ptr->start = false; 1070 }/* end of if it is our text element */ 1071 free(text_ptr); 1072} 1073 1074/*****************************************************************************/ 1075 1076static void parser_lock_add(void *ctx, const xmlChar *localname, int length) 1077{ 1078 webdav_parse_lock_struct_t *lock_struct = (webdav_parse_lock_struct_t *)ctx; 1079 UInt8 *text_ptr = malloc(length); 1080 bzero(text_ptr,length); 1081 memcpy(text_ptr,localname,length); 1082 UInt8* ch = NULL; 1083 1084 if (lock_struct->context == WEBDAV_LOCK_HREF) 1085 { 1086 lock_struct->context = 0; /* clear the context so the next time through it's not in WEBDAV_LOCK_HREF */ 1087 1088 /* Since the context is set to WEBDAV_LOCK_HREF, we have 1089 * found the token and we have found the href indicating 1090 * that the locktoken is coming so squirrel it away. 1091 */ 1092 1093 /* null terminate the string */ 1094 text_ptr[length] = '\0'; 1095 1096 // Trim trailing whitespace 1097 ch = &text_ptr[length-1]; 1098 while (ch > text_ptr) { 1099 if (isspace(*ch)) 1100 *ch-- = '\0'; 1101 else 1102 break; 1103 } 1104 1105 lock_struct->locktoken = (char *)text_ptr; 1106 1107 } 1108} 1109/*****************************************************************************/ 1110 1111static void parser_stat_add(void *ctx, const xmlChar *localname, int length) 1112{ 1113 UInt8 *text_ptr = (UInt8*) malloc(length); 1114 bzero(text_ptr,length); 1115 memcpy(text_ptr,localname,length); 1116 struct webdav_stat_attr *statbuf = (struct webdav_stat_attr *)ctx; 1117 struct webdav_stat_attr*parent = (struct webdav_stat_attr*)ctx; 1118 char *ep; 1119 /* 1120 * If the context reflects one of our properties then the localname must 1121 * be the text pointer with the data we want 1122 */ 1123 if(parent->start==true) 1124 { 1125 switch ((uintptr_t)parent->data) 1126 { 1127 case WEBDAV_STAT_LENGTH: 1128 /* the text pointer is the length in bytes so put it in the stat buffer */ 1129 if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE)) 1130 { 1131 statbuf->attr_stat.st_size = strtoq((const char *)text_ptr, &ep, 10); 1132 } 1133 else 1134 { 1135 /* if we got a string set to NULL we could not fit the value in our buffer, return invalid value */ 1136 statbuf->attr_stat.st_size = -1LL; 1137 } 1138 break; 1139 1140 case WEBDAV_STAT_MODDATE: 1141 /* the text pointer is the date so translate it and put it into the stat buffer */ 1142 1143 if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE)) 1144 { 1145 statbuf->attr_stat.st_mtimespec.tv_sec = DateBytesToTime(text_ptr, length); 1146 if (statbuf->attr_stat.st_mtimespec.tv_sec == -1) 1147 { 1148 statbuf->attr_stat.st_mtimespec.tv_sec = 0; 1149 } 1150 statbuf->attr_stat.st_mtimespec.tv_nsec = 0; 1151 } 1152 else 1153 { 1154 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1155 } 1156 break; 1157 1158 case WEBDAV_STAT_CREATEDATE: 1159 /* the text pointer is the date so translate it and put it into the stat buffer */ 1160 1161 if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE)) 1162 { 1163 // First try ISO8601 1164 statbuf->attr_create_time.tv_sec = ISO8601ToTime(text_ptr, length); 1165 1166 if (statbuf->attr_create_time.tv_sec == -1) { 1167 // Try RFC 850, RFC 1123 1168 statbuf->attr_create_time.tv_sec = DateBytesToTime(text_ptr, length); 1169 } 1170 1171 if (statbuf->attr_create_time.tv_sec == -1) 1172 { 1173 statbuf->attr_create_time.tv_sec = 0; 1174 } 1175 statbuf->attr_create_time.tv_nsec = 0; 1176 } 1177 else 1178 { 1179 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1180 } 1181 break; 1182 1183 default: 1184 break; 1185 } 1186 parent->start = false; 1187 } 1188 free(text_ptr); 1189} 1190 1191/*****************************************************************************/ 1192 1193static void parser_statfs_add(void *ctx, const xmlChar *localname, int length) 1194{ 1195 char *text_ptr = (char*) malloc(length); 1196 bzero(text_ptr,length); 1197 memcpy(text_ptr,(char*)localname,length); 1198 1199 struct webdav_quotas *quotas = (struct webdav_quotas *)ctx; 1200 struct webdav_quotas* parent = (struct webdav_quotas*)ctx; 1201 char *ep; 1202 /* 1203 * If the parent reflects one of our properties than the localname must 1204 * be the text pointer with the data we want 1205 */ 1206 if(parent->start == true) 1207 { 1208 switch ((uintptr_t)parent->data) 1209 { 1210 case WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES: 1211 /* the text pointer is the data so put it in the quotas buffer */ 1212 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1213 { 1214 quotas->quota_available_bytes = strtouq(text_ptr, &ep, 10); 1215 quotas->use_bytes_values = TRUE; 1216 } 1217 else 1218 { 1219 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1220 } 1221 break; 1222 1223 case WEBDAV_STATFS_QUOTA_USED_BYTES: 1224 /* the text pointer is the data so put it in the quotas buffer */ 1225 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1226 { 1227 quotas->quota_used_bytes = strtouq(text_ptr, &ep, 10); 1228 } 1229 else 1230 { 1231 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1232 } 1233 break; 1234 1235 case WEBDAV_STATFS_QUOTA: 1236 /* the text pointer is the data so put it in the quotas buffer */ 1237 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1238 { 1239 quotas->quota = strtouq(text_ptr, &ep, 10); 1240 } 1241 else 1242 { 1243 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1244 } 1245 break; 1246 1247 case WEBDAV_STATFS_QUOTAUSED: 1248 /* the text pointer is the data so put it in the quotas buffer */ 1249 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1250 { 1251 quotas->quotaused = strtouq(text_ptr, &ep, 10); 1252 } 1253 else 1254 { 1255 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1256 } 1257 break; 1258 1259 default: 1260 break; 1261 } 1262 parent->start = false; 1263 } 1264 free(text_ptr); 1265} 1266 1267/*****************************************************************************/ 1268 1269static void parser_cachevalidators_add(void *ctx, const xmlChar *localname, int length) 1270{ 1271 struct webdav_parse_cachevalidators_struct *cachevalidators_struct = (struct webdav_parse_cachevalidators_struct *)ctx; 1272 1273 /* 1274 * If the parent reflects one of our properties than the child must 1275 * be the text pointer with the data we want 1276 */if(cachevalidators_struct->start == true) 1277 { 1278 switch ((uintptr_t)cachevalidators_struct->data) 1279 { 1280 case WEBDAV_CACHEVALIDATORS_MODDATE: 1281 /* the text pointer is the date so translate it and get it into last_modified */ 1282 if (localname && (localname != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE)) 1283 { 1284 cachevalidators_struct->last_modified = DateBytesToTime(localname, length); 1285 } 1286 else 1287 { 1288 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1289 } 1290 break; 1291 1292 case WEBDAV_CACHEVALIDATORS_ETAG: 1293 /* the text pointer is the etag so copy it into last_modified */ 1294 if (localname && (localname != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE)) 1295 { 1296 size_t len = length + 1; 1297 1298 cachevalidators_struct->entity_tag = malloc(len); 1299 if ( cachevalidators_struct->entity_tag != NULL ) 1300 { 1301 strlcpy(cachevalidators_struct->entity_tag, (const char *)localname, len); 1302 } 1303 } 1304 else 1305 { 1306 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1307 } 1308 break; 1309 1310 default: 1311 break; 1312 } 1313 cachevalidators_struct->start = false; 1314 } 1315 1316} 1317 1318/*****************************************************************************/ 1319 1320static void parser_multistatus_add(void *ctx, const xmlChar *localname, int length) 1321{ 1322 webdav_parse_multistatus_element_t * element_ptr; 1323 webdav_parse_multistatus_list_t * parent_ptr = (webdav_parse_multistatus_list_t *)ctx; 1324 webdav_parse_multistatus_text_t * text_ptr = NULL; 1325 webdav_parse_multistatus_list_t * struct_ptr = (webdav_parse_multistatus_list_t *)ctx; 1326 char *ep, *ch, *endPtr; 1327 int errnum; 1328 /* If the parent is one of our returned directory elements, and if this is a 1329 * text element, than copy the text into the name buffer provided we have room */ 1330 text_ptr = malloc(sizeof(webdav_parse_multistatus_text_t)); 1331 bzero(text_ptr,sizeof(webdav_parse_multistatus_text_t)); 1332 text_ptr->size = (CFIndex)length; 1333 memcpy(text_ptr->name,localname,length); 1334 1335 if(parent_ptr->start == true) 1336 { 1337 switch (parent_ptr->id) 1338 { 1339 case WEBDAV_MULTISTATUS_ELEMENT: 1340 element_ptr = (webdav_parse_multistatus_element_t *)parent_ptr->data_ptr; 1341 1342 /* make sure the complete name will fit in the structure */ 1343 if ( (text_ptr->size) < (CFIndex)(sizeof(element_ptr->name))) 1344 { 1345 bcopy(text_ptr->name, &element_ptr->name, text_ptr->size); 1346 element_ptr->name_len += (uint32_t)text_ptr->size; 1347 element_ptr->name[element_ptr->name_len] = 0; 1348 } 1349 else 1350 { 1351 debug_string("URI too long"); 1352 struct_ptr->error = ENAMETOOLONG; 1353 } 1354 break; 1355 1356 case WEBDAV_MULTISTATUS_STATUS: 1357 /* the text pointer is status code so put it in the element statusCode field */ 1358 element_ptr = (webdav_parse_multistatus_element_t *)parent_ptr->data_ptr; 1359 ch = (char *)text_ptr->name; 1360 endPtr = ch + text_ptr->size; 1361 1362 // Find the 'H' of "HTTP/1.1 XXX Description" 1363 while (ch < endPtr) { 1364 if (*ch == 'H') 1365 break; 1366 ch++; 1367 } 1368 1369 // Did we overshoot to the end? 1370 if (ch >= endPtr) { 1371 debug_string("Cannot parse Status line, HTTP string not found"); 1372 struct_ptr->error = EIO; 1373 break; 1374 } 1375 1376 // Now find the space inbetween "HTTP/1.1" and the numeric status code 1377 while (ch < endPtr) { 1378 if (*ch == ' ') { 1379 ch++; 1380 break; 1381 } 1382 ch++; 1383 } 1384 1385 // Did we overshoot to the end? 1386 if (ch >= endPtr) { 1387 debug_string("Cannot parse Status line, numeric status code missing"); 1388 struct_ptr->error = EIO; 1389 break; 1390 } 1391 1392 // Now convert status code string to a ulong 1393 errno = 0; 1394 element_ptr->statusCode = (UInt32)strtoul((const char *)ch, &ep, 10); 1395 errnum = errno; 1396 1397 // See if the conversion was successful 1398 if ((errnum != 0) || (ep == ch)) { 1399 // nothing was converted 1400 debug_string("Cannot parse Status line, could not convert numeric status code"); 1401 struct_ptr->error = EIO; 1402 } 1403 break; 1404 1405 default: 1406 break; 1407 } /* end of switch statement */ 1408 parent_ptr->start = false; 1409 }/* end of if it is our text element */ 1410 free(text_ptr); 1411} 1412 1413/*****************************************************************************/ 1414 1415/* 1416 * GetNormalizedPathLength returns the length of the absolute path (with percent 1417 * escapes removed) portion of a CFURLRef. 1418 */ 1419static CFIndex GetNormalizedPathLength(CFURLRef anURL) 1420{ 1421 CFURLRef absoluteURL; 1422 CFStringRef escapedPath; 1423 CFStringRef unescapedPath; 1424 CFIndex result; 1425 1426 result = 0; 1427 absoluteURL = CFURLCopyAbsoluteURL(anURL); 1428 require(absoluteURL != NULL, CFURLCopyAbsoluteURL); 1429 1430 escapedPath = CFURLCopyPath(absoluteURL); 1431 require(escapedPath != NULL, CFURLCopyPath); 1432 1433 unescapedPath = CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, escapedPath, CFSTR("")); 1434 require_string(unescapedPath != NULL, CFURLCreateStringByReplacingPercentEscapes, "name was not legal UTF8"); 1435 1436 result = CFStringGetLength(unescapedPath); 1437 1438 CFRelease(unescapedPath); 1439 1440CFURLCreateStringByReplacingPercentEscapes: 1441 1442 CFRelease(escapedPath); 1443 1444CFURLCopyPath: 1445 1446 CFRelease(absoluteURL); 1447 1448CFURLCopyAbsoluteURL: 1449 1450 return ( result ); 1451} 1452 1453/*****************************************************************************/ 1454 1455/* 1456 * GetComponentName determines if the URI combined with the parent URL is a 1457 * child of the parent or is the parent itself, and if it is a child, extracts 1458 * the child's component name. 1459 * 1460 * GetComponentName returns TRUE if the URI is for a child, or returns FALSE if 1461 * the URI is the parent itself. The componentName buffer will contain the child's 1462 * component name if the result is TRUE. 1463 * 1464 * The parentPathLength parameter allows this routine to determine child/parent 1465 * status without comparing the path strings. 1466 */ 1467static Boolean GetComponentName( /* <- TRUE if http URI was not parent and component name was returned */ 1468 CFURLRef urlRef, /* -> the parent directory's URL */ 1469 CFIndex parentPathLength, /* -> the parent directory's percent decoded path length */ 1470 char *uri, /* -> the http URI from the WebDAV server */ 1471 char *componentName) /* <-> point to buffer of MAXNAMLEN + 1 bytes where URI's LastPathComponent is returned if result it TRUE */ 1472{ 1473 Boolean result; 1474 CFStringRef uriString; /* URI as CFString */ 1475 CFURLRef uriURL; /* URI converted to full URL */ 1476 CFStringRef uriName; /* URI's LastPathComponent as CFString */ 1477 1478 result = FALSE; 1479 1480 /* create a CFString from the c-string containing URI */ 1481 uriString = CFStringCreateWithCString(kCFAllocatorDefault, uri, kCFStringEncodingUTF8); 1482 require(uriString != NULL, CFStringCreateWithCString); 1483 1484 /* create a CFURL from the URI CFString and the parent URL */ 1485 uriURL = CFURLCreateWithString(kCFAllocatorDefault, uriString, urlRef); 1486 if (uriURL != NULL) { 1487 /* CFURLCreateWithString worked fine, so release uriString and keep going */ 1488 CFRelease(uriString); 1489 } 1490 else { 1491 /* Fix for Windows servers. They tend to send over names that have a space in them instead of the correct %20. This causes 1492 CFURLCreateWithString to fail. Since it might be a partially escaped URL string, try to unescape the string, then re-escape it */ 1493 CFStringRef unEscapedString; /* URI as CFString with un-escaped chars */ 1494 CFStringRef reEscapedString; /* URI as CFString with re-escaped chars */ 1495 1496 unEscapedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding (kCFAllocatorDefault, uriString, NULL, kCFStringEncodingUTF8); 1497 CFRelease(uriString); 1498 require(unEscapedString != NULL, CFURLCreateWithString); 1499 1500 reEscapedString = CFURLCreateStringByAddingPercentEscapes (kCFAllocatorDefault, unEscapedString, NULL, NULL, kCFStringEncodingUTF8); 1501 CFRelease(unEscapedString); 1502 require(reEscapedString != NULL, CFURLCreateWithString); 1503 1504 /* try again to create a CFURL from the reEscapedString and the parent URL */ 1505 uriURL = CFURLCreateWithString(kCFAllocatorDefault, reEscapedString, urlRef); 1506 CFRelease(reEscapedString); 1507 require(uriURL != NULL, CFURLCreateWithString); 1508 } 1509 1510 /* see if this is the parent or a child */ 1511 if ( GetNormalizedPathLength(uriURL) > parentPathLength ) { 1512 /* this is a child */ 1513 1514 /* get the child's name */ 1515 uriName = CFURLCopyLastPathComponent(uriURL); 1516 require_string(uriName != NULL, CFURLCopyLastPathComponent, "name was not legal UTF8"); 1517 1518 if ( CFStringGetCString(uriName, componentName, MAXNAMLEN + 1, kCFStringEncodingUTF8) ) { 1519 /* we have the child name */ 1520 result = TRUE; 1521 } 1522 else { 1523 debug_string("could not get child name (too long?)"); 1524 } 1525 CFRelease(uriName); 1526 } 1527 else { 1528 /* this is the parent, skip it */ 1529 } 1530 1531CFURLCopyLastPathComponent: 1532 1533 CFRelease(uriURL); 1534 1535CFURLCreateWithString: 1536CFStringCreateWithCString: 1537 1538 return ( result ); 1539} 1540 1541/*****************************************************************************/ 1542 1543int parse_opendir(UInt8 *xmlp, /* -> xml data returned by PROPFIND with depth of 1 */ 1544 CFIndex xmlp_len, /* -> length of xml data */ 1545 CFURLRef urlRef, /* -> the CFURL to the parent directory */ 1546 uid_t uid, /* -> uid of the user making the request */ 1547 struct node_entry *parent_node) /* -> pointer to the parent directory's node_entry */ 1548{ 1549 int error = 0; 1550 ssize_t size = 0; 1551 struct webdav_dirent dir_data[2]; 1552 CFIndex parentPathLength; 1553 webdav_parse_opendir_struct_t opendir_struct; 1554 webdav_parse_opendir_element_t *element_ptr, *prev_element_ptr; 1555 opendir_struct.head = opendir_struct.tail = NULL; 1556 opendir_struct.error = 0; 1557 1558 xmlSAXHandler sh; 1559 memset(&sh,0,sizeof(sh)); 1560 sh.startElementNs = parser_opendir_create; 1561 sh.characters = parser_opendir_add; 1562 sh.endElementNs = parser_opendir_end; 1563 sh.initialized = XML_SAX2_MAGIC; 1564 1565 /* truncate the file, and reset the file pointer to 0 */ 1566 require(ftruncate(parent_node->file_fd, 0) == 0, ftruncate); 1567 require(lseek(parent_node->file_fd, 0, SEEK_SET) == 0, lseek); 1568 1569 int result = xmlSAXUserParseMemory( &sh,&opendir_struct,(char*)xmlp,(int)xmlp_len); 1570 require(result == 0, ParserCreate); 1571 /* parse the XML -- exit now if error during parse */ 1572 1573 /* if the directory is not deleted, write "." and ".." */ 1574 if ( !NODE_IS_DELETED(parent_node) ) 1575 { 1576 bzero(dir_data, sizeof(dir_data)); 1577 1578 dir_data[0].d_ino = parent_node->fileid; 1579 dir_data[0].d_reclen = sizeof(struct webdav_dirent); 1580 dir_data[0].d_type = DT_DIR; 1581 dir_data[0].d_namlen = 1; 1582 dir_data[0].d_name[0] = '.'; 1583 1584 dir_data[1].d_ino = 1585 (dir_data[0].d_ino == WEBDAV_ROOTFILEID) ? WEBDAV_ROOTPARENTFILEID : parent_node->parent->fileid; 1586 dir_data[1].d_reclen = sizeof(struct webdav_dirent); 1587 dir_data[1].d_type = DT_DIR; 1588 dir_data[1].d_namlen = 2; 1589 dir_data[1].d_name[0] = '.'; 1590 dir_data[1].d_name[1] = '.'; 1591 1592 size = write(parent_node->file_fd, dir_data, sizeof(struct webdav_dirent) * 2); 1593 require(size == (sizeof(struct webdav_dirent) * 2), write_dot_dotdot); 1594 } 1595 1596 /* 1597 * Important: the xml we get back from the server includes the info 1598 * on the parent directory as well as all of its children. 1599 * 1600 * The elements returned by PROPFIND contain http URI. So, give a parent URL of 1601 * http://host/parent/, the responses could be: 1602 * absolute URL: http://host/parent/child 1603 * absolute path: /parent/child 1604 * relative path: child 1605 * So, if all URLs are normalized to an absolute path with percent escapes 1606 * removed, then the children will always be longer than the parent. 1607 */ 1608 1609 /* get the parent directory's path length */ 1610 parentPathLength = GetNormalizedPathLength(urlRef); 1611 1612 /* invalidate any children nodes -- they'll be marked valid by nodecache_get_node */ 1613 (void) nodecache_invalidate_directory_node_time(parent_node); 1614 1615 /* look at the list of elements */ 1616 for (element_ptr = opendir_struct.head; element_ptr != NULL; element_ptr = element_ptr->next) 1617 { 1618 char namebuffer[MAXNAMLEN + 1]; 1619 struct webdav_stat_attr statbuf; 1620 1621 // Skip any placeholder that never saw a matching <D:href> element 1622 if (element_ptr->seen_href == FALSE) 1623 continue; 1624 1625 /* make element_ptr->dir_data.d_name a cstring */ 1626 element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length] = '\0'; 1627 //syslog(LOG_ERR,"element_ptr->dir_data.d_name is %s\n",element_ptr->dir_data.d_name); 1628 /* get the component name if this element is not the parent */ 1629 if ( GetComponentName(urlRef, parentPathLength, element_ptr->dir_data.d_name, namebuffer) ) 1630 { 1631 /* this is a child */ 1632 struct node_entry *element_node; 1633 size_t name_len; 1634 1635 name_len = strlen(namebuffer); 1636 //syslog(LOG_ERR,"namebuffer is %s\n",namebuffer); 1637 /* get (or create) a cache node for this element */ 1638 error = nodecache_get_node(parent_node, name_len, namebuffer, TRUE, FALSE, 1639 element_ptr->dir_data.d_type == DT_DIR ? WEBDAV_DIR_TYPE : WEBDAV_FILE_TYPE, &element_node); 1640 if (error) 1641 { 1642 debug_string("nodecache_get_node failed"); 1643 continue; 1644 } 1645 /* move just the element name over element_ptr->dir_data.d_name */ 1646 bcopy(element_node->name, element_ptr->dir_data.d_name, element_node->name_length); 1647 1648 element_ptr->dir_data.d_name[element_node->name_length] = '\0'; 1649 element_ptr->dir_data.d_namlen = element_node->name_length; 1650 1651 /* set the file number */ 1652 element_ptr->dir_data.d_ino = element_node->fileid; 1653 //syslog(LOG_ERR,"element_node->fileid : %d\n",element_node->fileid); 1654 /* 1655 * Prepare to cache this element's attributes, since it's 1656 * highly likely a stat will follow reading the directory. 1657 */ 1658 1659 bzero(&statbuf, sizeof(struct webdav_stat_attr)); 1660 1661 /* the first thing to do is fill in the fields we cannot get from the server. */ 1662 statbuf.attr_stat.st_dev = 0; 1663 /* Why 1 for st_nlink? 1664 * Getting the real link count for directories is expensive. 1665 * Setting it to 1 lets FTS(3) (and other utilities that assume 1666 * 1 means a file system doesn't support link counts) work. 1667 */ 1668 statbuf.attr_stat.st_nlink = 1; 1669 statbuf.attr_stat.st_uid = UNKNOWNUID; 1670 statbuf.attr_stat.st_gid = UNKNOWNUID; 1671 statbuf.attr_stat.st_rdev = 0; 1672 statbuf.attr_stat.st_blksize = WEBDAV_IOSIZE; 1673 statbuf.attr_stat.st_flags = 0; 1674 statbuf.attr_stat.st_gen = 0; 1675 1676 /* set all times to the last modified time since we cannot get the other times */ 1677 statbuf.attr_stat.st_atimespec = statbuf.attr_stat.st_mtimespec = statbuf.attr_stat.st_ctimespec = element_ptr->stattime; 1678 1679 /* set create time if we have it */ 1680 if (element_ptr->createtime.tv_sec) 1681 statbuf.attr_create_time = element_ptr->createtime; 1682 //syslog(LOG_ERR,"element_ptr->dir_data.d_type : %d\n",element_ptr->dir_data.d_type); 1683 if (element_ptr->dir_data.d_type == DT_DIR) 1684 { 1685 statbuf.attr_stat.st_mode = S_IFDIR | S_IRWXU; 1686 statbuf.attr_stat.st_size = WEBDAV_DIR_SIZE; 1687 /* appledoubleheadervalid is never valid for directories */ 1688 element_ptr->appledoubleheadervalid = FALSE; 1689 } 1690 else 1691 { 1692 statbuf.attr_stat.st_mode = S_IFREG | S_IRWXU; 1693 statbuf.attr_stat.st_size = element_ptr->statsize; 1694 /* appledoubleheadervalid is valid for files only if the server 1695 * returned the appledoubleheader property and file size is 1696 * the size of the appledoubleheader (APPLEDOUBLEHEADER_LENGTH bytes). 1697 */ 1698 element_ptr->appledoubleheadervalid = 1699 (element_ptr->appledoubleheadervalid && (element_ptr->statsize == APPLEDOUBLEHEADER_LENGTH)); 1700 //syslog(LOG_ERR, "element_ptr->appledoubleheadervalid %d",element_ptr->appledoubleheadervalid); 1701 } 1702 1703 /* calculate number of S_BLKSIZE blocks */ 1704 statbuf.attr_stat.st_blocks = ((statbuf.attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE); 1705 1706 /* set the fileid in statbuf*/ 1707 statbuf.attr_stat.st_ino = element_node->fileid; 1708 1709 /* Now cache the stat structure (ignoring errors) */ 1710 (void) nodecache_add_attributes(element_node, uid, &statbuf, 1711 element_ptr->appledoubleheadervalid ? element_ptr->appledoubleheader : NULL); 1712 1713 /* Complete the task of getting the regular name into the dirent */ 1714 1715 size = write(parent_node->file_fd, (void *)&element_ptr->dir_data, element_ptr->dir_data.d_reclen); 1716 require(size == element_ptr->dir_data.d_reclen, write_element); 1717 } 1718 else 1719 { 1720 struct node_entry *temp_node; 1721 /* it was the parent */ 1722 1723 /* we are reading this directory, so mark it "recent" */ 1724 (void) nodecache_get_node(parent_node, 0, NULL, TRUE, TRUE, WEBDAV_DIR_TYPE, &temp_node); 1725 1726 /* 1727 * Prepare to cache this element's attributes, since it's 1728 * highly likely a stat will follow reading the directory. 1729 */ 1730 1731 bzero(&statbuf, sizeof(struct webdav_stat_attr)); 1732 1733 /* the first thing to do is fill in the fields we cannot get from the server. */ 1734 statbuf.attr_stat.st_dev = 0; 1735 /* Why 1 for st_nlink? 1736 * Getting the real link count for directories is expensive. 1737 * Setting it to 1 lets FTS(3) (and other utilities that assume 1738 * 1 means a file system doesn't support link counts) work. 1739 */ 1740 statbuf.attr_stat.st_nlink = 1; 1741 statbuf.attr_stat.st_uid = UNKNOWNUID; 1742 statbuf.attr_stat.st_gid = UNKNOWNUID; 1743 statbuf.attr_stat.st_rdev = 0; 1744 statbuf.attr_stat.st_blksize = WEBDAV_IOSIZE; 1745 statbuf.attr_stat.st_flags = 0; 1746 statbuf.attr_stat.st_gen = 0; 1747 1748 /* set all times to the last modified time since we cannot get the other times */ 1749 statbuf.attr_stat.st_atimespec = statbuf.attr_stat.st_mtimespec = statbuf.attr_stat.st_ctimespec = element_ptr->stattime; 1750 1751 /* set create time if we have it */ 1752 if (element_ptr->createtime.tv_sec) 1753 statbuf.attr_create_time = element_ptr->createtime; 1754 1755 statbuf.attr_stat.st_mode = S_IFDIR | S_IRWXU; 1756 statbuf.attr_stat.st_size = WEBDAV_DIR_SIZE; 1757 1758 /* calculate number of S_BLKSIZE blocks */ 1759 statbuf.attr_stat.st_blocks = ((statbuf.attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE); 1760 1761 /* set the fileid in statbuf*/ 1762 statbuf.attr_stat.st_ino = parent_node->fileid; 1763 1764 /* Now cache the stat structure (ignoring errors) */ 1765 (void) nodecache_add_attributes(parent_node, uid, &statbuf, NULL); 1766 } 1767 } /* for element_ptr */ 1768 1769 /* delete any children nodes that are still invalid */ 1770 (void) nodecache_delete_invalid_directory_nodes(parent_node); 1771 1772 /* free any elements allocated */ 1773 element_ptr = opendir_struct.head; 1774 while (element_ptr) 1775 { 1776 prev_element_ptr = element_ptr; 1777 element_ptr = element_ptr->next; 1778 free(prev_element_ptr); 1779 } 1780 1781 return ( 0 ); 1782 1783 /**********************/ 1784 1785write_element: 1786 /* free any elements allocated */ 1787 element_ptr = opendir_struct.head; 1788 while (element_ptr) 1789 { 1790 prev_element_ptr = element_ptr; 1791 element_ptr = element_ptr->next; 1792 free(prev_element_ptr); 1793 } 1794write_dot_dotdot: 1795 /* directory is in unknown condition - erase whatever is there */ 1796 (void) ftruncate(parent_node->file_fd, 0); 1797ParserCreate: 1798lseek: 1799ftruncate: 1800 return ( EIO ); 1801} 1802 1803/*****************************************************************************/ 1804webdav_parse_multistatus_list_t * 1805parse_multi_status( 1806 UInt8 *xmlp, /* -> xml data returned by PROPFIND with depth of 1 */ 1807 CFIndex xmlp_len) /* -> length of xml data */ 1808{ 1809 webdav_parse_multistatus_list_t *multistatus_list; 1810 1811 multistatus_list = malloc(sizeof(webdav_parse_multistatus_list_t)); 1812 require(multistatus_list != NULL, malloc_list); 1813 1814 multistatus_list->error = 0; 1815 multistatus_list->head = NULL; 1816 multistatus_list->tail = NULL; 1817 1818 xmlSAXHandler sh; 1819 memset(&sh,0,sizeof(sh)); 1820 sh.startElementNs = parser_multistatus_create; 1821 sh.characters = parser_multistatus_add; 1822 sh.endElementNs = parser_multistatus_end; 1823 sh.initialized = XML_SAX2_MAGIC; 1824 1825 int result = xmlSAXUserParseMemory( &sh,multistatus_list,(char*)xmlp,(int)xmlp_len); 1826 require(result == 0, ParserCreate); 1827 1828ParserCreate: 1829malloc_list: 1830 return (multistatus_list); 1831 1832 return ( 0 ); 1833} 1834 1835 1836/*****************************************************************************/ 1837 1838int parse_file_count(const UInt8 *xmlp, CFIndex xmlp_len, int *file_count) 1839{ 1840 xmlSAXHandler sh; 1841 memset(&sh,0,sizeof(sh)); 1842 sh.startElementNs = parser_file_count_create; 1843 sh.endElementNs = parser_file_count_end; 1844 sh.initialized = XML_SAX2_MAGIC; 1845 *file_count = 0; 1846 1847 xmlSAXUserParseMemory( &sh,file_count,(char*)xmlp,(int)xmlp_len); 1848 1849 return ( 0 ); 1850} 1851 1852/*****************************************************************************/ 1853 1854int parse_stat(const UInt8 *xmlp, CFIndex xmlp_len, struct webdav_stat_attr *statbuf) 1855{ 1856 xmlSAXHandler sh; 1857 memset(&sh,0,sizeof(sh)); 1858 sh.startElementNs = parser_stat_create; 1859 sh.endElementNs = parse_stat_end; 1860 sh.characters = parser_stat_add; 1861 sh.initialized = XML_SAX2_MAGIC; 1862 bzero((void *)statbuf, sizeof(struct webdav_stat_attr)); 1863 1864 xmlSAXUserParseMemory( &sh,statbuf,(char*)xmlp,(int)xmlp_len); 1865 /* Coming back from the parser: 1866 * statbuf->attr_stat_info.attr_stat.st_mode will be 0 or will have S_IFDIR set if the object is a directory. 1867 * statbuf->attr_stat_info.attr_stat.st_mtimespec will be 0 or will have the last modified time. 1868 * statbuf->attr_stat_info.attr_create_time will be 0 or the file creation time 1869 * statbuf->attr_stat_info.attr_stat.st_size will be set to the resource size if the object is a file. 1870 * 1871 * So, the first thing to do is fill in the fields we cannot get from the server. 1872 */ 1873 1874 statbuf->attr_stat.st_dev = 0; 1875 /* Why 1 for st_nlink? 1876 * Getting the real link count for directories is expensive. 1877 * Setting it to 1 lets FTS(3) (and other utilities that assume 1878 * 1 means a file system doesn't support link counts) work. 1879 */ 1880 statbuf->attr_stat.st_nlink = 1; 1881 statbuf->attr_stat.st_uid = UNKNOWNUID; 1882 statbuf->attr_stat.st_gid = UNKNOWNUID; 1883 statbuf->attr_stat.st_rdev = 0; 1884 statbuf->attr_stat.st_blksize = WEBDAV_IOSIZE; 1885 statbuf->attr_stat.st_flags = 0; 1886 statbuf->attr_stat.st_gen = 0; 1887 1888 /* set last accessed and changed times to last modified time since we cannot get them */ 1889 statbuf->attr_stat.st_atimespec = statbuf->attr_stat.st_ctimespec = statbuf->attr_stat.st_mtimespec; 1890 1891 /* was this a directory? */ 1892 if ( S_ISDIR(statbuf->attr_stat.st_mode) ) 1893 { 1894 /* yes - add the directory access permissions */ 1895 statbuf->attr_stat.st_mode |= S_IRWXU; 1896 /* fake up the directory size */ 1897 statbuf->attr_stat.st_size = WEBDAV_DIR_SIZE; 1898 } 1899 else 1900 { 1901 /* no - mark it as a regular file and set the file access permissions 1902 * (for now, everything is either a file or a directory) 1903 */ 1904 statbuf->attr_stat.st_mode = S_IFREG | S_IRWXU; 1905 } 1906 1907 /* calculate number of S_BLKSIZE blocks */ 1908 statbuf->attr_stat.st_blocks = ((statbuf->attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE); 1909 1910 return ( 0 ); 1911} 1912 1913/*****************************************************************************/ 1914 1915int parse_statfs(const UInt8 *xmlp, CFIndex xmlp_len, struct statfs *statfsbuf) 1916{ 1917 xmlSAXHandler sh; 1918 memset(&sh,0,sizeof(sh)); 1919 sh.startElementNs = parser_statfs_create; 1920 sh.characters = parser_statfs_add; 1921 sh.endElementNs = parser_statfs_end; 1922 sh.initialized = XML_SAX2_MAGIC; 1923 1924 struct webdav_quotas quotas; 1925 bzero((void *)statfsbuf, sizeof(struct statfs)); 1926 bzero((void *)"as, sizeof(struct webdav_quotas)); 1927 xmlSAXUserParseMemory( &sh,"as,(char*)xmlp,(int)xmlp_len); 1928 /* were the IETF quota properties returned? */ 1929 if ( quotas.use_bytes_values ) 1930 { 1931 uint64_t total_bytes; 1932 uint64_t total_blocks; 1933 uint32_t bsize; 1934 1935 /* calculate the total bytes (available + used) */ 1936 total_bytes = quotas.quota_available_bytes + quotas.quota_used_bytes; 1937 if ( (total_bytes >= quotas.quota_available_bytes) && (total_bytes >= quotas.quota_used_bytes) ) 1938 { 1939 /* 1940 * calculate the smallest file system block size (bsize) that's a 1941 * multiple of S_BLKSIZE, is >= S_BLKSIZE, and is <= LONG_MAX 1942 */ 1943 bsize = S_BLKSIZE / 2; 1944 do 1945 { 1946 bsize *= 2; 1947 total_blocks = ((total_bytes + bsize - 1) / bsize); 1948 } while ( total_blocks > LONG_MAX ); 1949 1950 /* stuff the results into statfsbuf */ 1951 statfsbuf->f_bsize = bsize; 1952#ifdef __LP64__ 1953 statfsbuf->f_blocks = total_blocks; 1954 statfsbuf->f_bavail = statfsbuf->f_bfree = quotas.quota_available_bytes / bsize; 1955#else 1956 statfsbuf->f_blocks = (long)total_blocks; 1957 statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota_available_bytes / bsize); 1958#endif 1959 } 1960 /* else we were handed values we cannot represent so leave statfsbuf zeroed (no quota support for this file system) */ 1961 } 1962 else 1963 { 1964 /* use the deprecated quota and quotaused if they were returned */ 1965 if ( (quotas.quota != 0) && (quotas.quota > quotas.quotaused) ) 1966 { 1967#ifdef __LP64__ 1968 statfsbuf->f_bavail = statfsbuf->f_bfree = (quotas.quota - quotas.quotaused); 1969#else 1970 statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota - quotas.quotaused); 1971#endif 1972 } 1973 else 1974 { 1975 statfsbuf->f_bavail = statfsbuf->f_bfree = 0; 1976 } 1977#ifdef __LP64__ 1978 statfsbuf->f_blocks = quotas.quota; 1979#else 1980 statfsbuf->f_blocks = (long)quotas.quota; 1981#endif 1982 statfsbuf->f_bsize = S_BLKSIZE; 1983 } 1984 1985 statfsbuf->f_iosize = WEBDAV_IOSIZE; 1986 1987 return ( 0 ); 1988} 1989 1990/*****************************************************************************/ 1991 1992int parse_lock(const UInt8 *xmlp, CFIndex xmlp_len, char **locktoken) 1993{ 1994 xmlSAXHandler sh; 1995 memset(&sh,0,sizeof(sh)); 1996 sh.startElementNs = parser_lock_create; 1997 sh.characters = parser_lock_add; 1998 sh.endElementNs = parser_lock_end; 1999 sh.initialized = XML_SAX2_MAGIC; 2000 webdav_parse_lock_struct_t lock_struct; 2001 lock_struct.context = 0; 2002 lock_struct.locktoken = NULL; /* NULL coming into this function */ 2003 2004 xmlSAXUserParseMemory( &sh,&lock_struct,(char*)xmlp,(int)xmlp_len); 2005 2006 *locktoken = (char *)lock_struct.locktoken; 2007 if (*locktoken == NULL) 2008 { 2009 debug_string("error parsing lock token"); 2010 } 2011 2012 return ( 0 ); 2013} 2014 2015/*****************************************************************************/ 2016 2017int parse_cachevalidators(const UInt8 *xmlp, CFIndex xmlp_len, time_t *last_modified, char **entity_tag) 2018{ 2019 xmlSAXHandler sh; 2020 memset(&sh,0,sizeof(sh)); 2021 sh.startElementNs = parser_cachevalidators_create; 2022 sh.characters = parser_cachevalidators_add; 2023 sh.endElementNs = parser_cachevalidators_end; 2024 sh.initialized = XML_SAX2_MAGIC; 2025 struct webdav_parse_cachevalidators_struct cachevalidators_struct; 2026 /* results if parser fails or values are not found */ 2027 cachevalidators_struct.last_modified = 0; 2028 cachevalidators_struct.entity_tag = NULL; 2029 2030 xmlSAXUserParseMemory( &sh,&cachevalidators_struct,(char*)xmlp,(int)xmlp_len); 2031 2032 if ( cachevalidators_struct.last_modified != 0 ) 2033 { 2034 *last_modified = cachevalidators_struct.last_modified; 2035 } 2036 else 2037 { 2038 time(last_modified); 2039 } 2040 *entity_tag = cachevalidators_struct.entity_tag; 2041 2042 return ( 0 ); 2043} 2044 2045/*****************************************************************************/ 2046