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+1); 1080 bzero(text_ptr,length+1); 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 // Trim trailing whitespace 1094 ch = &text_ptr[length-1]; 1095 while (ch > text_ptr) { 1096 if (isspace(*ch)) 1097 *ch-- = '\0'; 1098 else 1099 break; 1100 } 1101 1102 lock_struct->locktoken = (char *)text_ptr; 1103 1104 } 1105} 1106/*****************************************************************************/ 1107 1108static void parser_stat_add(void *ctx, const xmlChar *localname, int length) 1109{ 1110 UInt8 *text_ptr = (UInt8*) malloc(length); 1111 bzero(text_ptr,length); 1112 memcpy(text_ptr,localname,length); 1113 struct webdav_stat_attr *statbuf = (struct webdav_stat_attr *)ctx; 1114 struct webdav_stat_attr*parent = (struct webdav_stat_attr*)ctx; 1115 char *ep; 1116 /* 1117 * If the context reflects one of our properties then the localname must 1118 * be the text pointer with the data we want 1119 */ 1120 if(parent->start==true) 1121 { 1122 switch ((uintptr_t)parent->data) 1123 { 1124 case WEBDAV_STAT_LENGTH: 1125 /* the text pointer is the length in bytes so put it in the stat buffer */ 1126 if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE)) 1127 { 1128 statbuf->attr_stat.st_size = strtoq((const char *)text_ptr, &ep, 10); 1129 } 1130 else 1131 { 1132 /* if we got a string set to NULL we could not fit the value in our buffer, return invalid value */ 1133 statbuf->attr_stat.st_size = -1LL; 1134 } 1135 break; 1136 1137 case WEBDAV_STAT_MODDATE: 1138 /* the text pointer is the date so translate it and put it into the stat buffer */ 1139 1140 if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE)) 1141 { 1142 statbuf->attr_stat.st_mtimespec.tv_sec = DateBytesToTime(text_ptr, length); 1143 if (statbuf->attr_stat.st_mtimespec.tv_sec == -1) 1144 { 1145 statbuf->attr_stat.st_mtimespec.tv_sec = 0; 1146 } 1147 statbuf->attr_stat.st_mtimespec.tv_nsec = 0; 1148 } 1149 else 1150 { 1151 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1152 } 1153 break; 1154 1155 case WEBDAV_STAT_CREATEDATE: 1156 /* the text pointer is the date so translate it and put it into the stat buffer */ 1157 1158 if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE)) 1159 { 1160 // First try ISO8601 1161 statbuf->attr_create_time.tv_sec = ISO8601ToTime(text_ptr, length); 1162 1163 if (statbuf->attr_create_time.tv_sec == -1) { 1164 // Try RFC 850, RFC 1123 1165 statbuf->attr_create_time.tv_sec = DateBytesToTime(text_ptr, length); 1166 } 1167 1168 if (statbuf->attr_create_time.tv_sec == -1) 1169 { 1170 statbuf->attr_create_time.tv_sec = 0; 1171 } 1172 statbuf->attr_create_time.tv_nsec = 0; 1173 } 1174 else 1175 { 1176 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1177 } 1178 break; 1179 1180 default: 1181 break; 1182 } 1183 parent->start = false; 1184 } 1185 free(text_ptr); 1186} 1187 1188/*****************************************************************************/ 1189 1190static void parser_statfs_add(void *ctx, const xmlChar *localname, int length) 1191{ 1192 char *text_ptr = (char*) malloc(length); 1193 bzero(text_ptr,length); 1194 memcpy(text_ptr,(char*)localname,length); 1195 1196 struct webdav_quotas *quotas = (struct webdav_quotas *)ctx; 1197 struct webdav_quotas* parent = (struct webdav_quotas*)ctx; 1198 char *ep; 1199 /* 1200 * If the parent reflects one of our properties than the localname must 1201 * be the text pointer with the data we want 1202 */ 1203 if(parent->start == true) 1204 { 1205 switch ((uintptr_t)parent->data) 1206 { 1207 case WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES: 1208 /* the text pointer is the data so put it in the quotas buffer */ 1209 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1210 { 1211 quotas->quota_available_bytes = strtouq(text_ptr, &ep, 10); 1212 quotas->use_bytes_values = TRUE; 1213 } 1214 else 1215 { 1216 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1217 } 1218 break; 1219 1220 case WEBDAV_STATFS_QUOTA_USED_BYTES: 1221 /* the text pointer is the data so put it in the quotas buffer */ 1222 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1223 { 1224 quotas->quota_used_bytes = strtouq(text_ptr, &ep, 10); 1225 } 1226 else 1227 { 1228 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1229 } 1230 break; 1231 1232 case WEBDAV_STATFS_QUOTA: 1233 /* the text pointer is the data so put it in the quotas buffer */ 1234 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1235 { 1236 quotas->quota = strtouq(text_ptr, &ep, 10); 1237 } 1238 else 1239 { 1240 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1241 } 1242 break; 1243 1244 case WEBDAV_STATFS_QUOTAUSED: 1245 /* the text pointer is the data so put it in the quotas buffer */ 1246 if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE)) 1247 { 1248 quotas->quotaused = strtouq(text_ptr, &ep, 10); 1249 } 1250 else 1251 { 1252 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1253 } 1254 break; 1255 1256 default: 1257 break; 1258 } 1259 parent->start = false; 1260 } 1261 free(text_ptr); 1262} 1263 1264/*****************************************************************************/ 1265 1266static void parser_cachevalidators_add(void *ctx, const xmlChar *localname, int length) 1267{ 1268 struct webdav_parse_cachevalidators_struct *cachevalidators_struct = (struct webdav_parse_cachevalidators_struct *)ctx; 1269 1270 /* 1271 * If the parent reflects one of our properties than the child must 1272 * be the text pointer with the data we want 1273 */if(cachevalidators_struct->start == true) 1274 { 1275 switch ((uintptr_t)cachevalidators_struct->data) 1276 { 1277 case WEBDAV_CACHEVALIDATORS_MODDATE: 1278 /* the text pointer is the date so translate it and get it into last_modified */ 1279 if (localname && (localname != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE)) 1280 { 1281 cachevalidators_struct->last_modified = DateBytesToTime(localname, length); 1282 } 1283 else 1284 { 1285 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1286 } 1287 break; 1288 1289 case WEBDAV_CACHEVALIDATORS_ETAG: 1290 /* the text pointer is the etag so copy it into last_modified */ 1291 if (localname && (localname != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE)) 1292 { 1293 size_t len = length + 1; 1294 1295 cachevalidators_struct->entity_tag = calloc(1,len); 1296 if ( cachevalidators_struct->entity_tag != NULL ) 1297 { 1298 strlcpy(cachevalidators_struct->entity_tag, (const char *)localname, len); 1299 } 1300 } 1301 else 1302 { 1303 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */ 1304 } 1305 break; 1306 1307 default: 1308 break; 1309 } 1310 cachevalidators_struct->start = false; 1311 } 1312 1313} 1314 1315/*****************************************************************************/ 1316 1317static void parser_multistatus_add(void *ctx, const xmlChar *localname, int length) 1318{ 1319 webdav_parse_multistatus_element_t * element_ptr; 1320 webdav_parse_multistatus_list_t * parent_ptr = (webdav_parse_multistatus_list_t *)ctx; 1321 webdav_parse_multistatus_text_t * text_ptr = NULL; 1322 webdav_parse_multistatus_list_t * struct_ptr = (webdav_parse_multistatus_list_t *)ctx; 1323 char *ep, *ch, *endPtr; 1324 int errnum; 1325 /* If the parent is one of our returned directory elements, and if this is a 1326 * text element, than copy the text into the name buffer provided we have room */ 1327 text_ptr = malloc(sizeof(webdav_parse_multistatus_text_t)); 1328 bzero(text_ptr,sizeof(webdav_parse_multistatus_text_t)); 1329 text_ptr->size = (CFIndex)length; 1330 memcpy(text_ptr->name,localname,length); 1331 1332 if(parent_ptr->start == true) 1333 { 1334 switch (parent_ptr->id) 1335 { 1336 case WEBDAV_MULTISTATUS_ELEMENT: 1337 element_ptr = (webdav_parse_multistatus_element_t *)parent_ptr->data_ptr; 1338 1339 /* make sure the complete name will fit in the structure */ 1340 if ( (text_ptr->size) < (CFIndex)(sizeof(element_ptr->name))) 1341 { 1342 bcopy(text_ptr->name, &element_ptr->name, text_ptr->size); 1343 element_ptr->name_len += (uint32_t)text_ptr->size; 1344 element_ptr->name[element_ptr->name_len] = 0; 1345 } 1346 else 1347 { 1348 debug_string("URI too long"); 1349 struct_ptr->error = ENAMETOOLONG; 1350 } 1351 break; 1352 1353 case WEBDAV_MULTISTATUS_STATUS: 1354 /* the text pointer is status code so put it in the element statusCode field */ 1355 element_ptr = (webdav_parse_multistatus_element_t *)parent_ptr->data_ptr; 1356 ch = (char *)text_ptr->name; 1357 endPtr = ch + text_ptr->size; 1358 1359 // Find the 'H' of "HTTP/1.1 XXX Description" 1360 while (ch < endPtr) { 1361 if (*ch == 'H') 1362 break; 1363 ch++; 1364 } 1365 1366 // Did we overshoot to the end? 1367 if (ch >= endPtr) { 1368 debug_string("Cannot parse Status line, HTTP string not found"); 1369 struct_ptr->error = EIO; 1370 break; 1371 } 1372 1373 // Now find the space inbetween "HTTP/1.1" and the numeric status code 1374 while (ch < endPtr) { 1375 if (*ch == ' ') { 1376 ch++; 1377 break; 1378 } 1379 ch++; 1380 } 1381 1382 // Did we overshoot to the end? 1383 if (ch >= endPtr) { 1384 debug_string("Cannot parse Status line, numeric status code missing"); 1385 struct_ptr->error = EIO; 1386 break; 1387 } 1388 1389 // Now convert status code string to a ulong 1390 errno = 0; 1391 element_ptr->statusCode = (UInt32)strtoul((const char *)ch, &ep, 10); 1392 errnum = errno; 1393 1394 // See if the conversion was successful 1395 if ((errnum != 0) || (ep == ch)) { 1396 // nothing was converted 1397 debug_string("Cannot parse Status line, could not convert numeric status code"); 1398 struct_ptr->error = EIO; 1399 } 1400 break; 1401 1402 default: 1403 break; 1404 } /* end of switch statement */ 1405 parent_ptr->start = false; 1406 }/* end of if it is our text element */ 1407 free(text_ptr); 1408} 1409 1410/*****************************************************************************/ 1411 1412/* 1413 * GetNormalizedPathLength returns the length of the absolute path (with percent 1414 * escapes removed) portion of a CFURLRef. 1415 */ 1416static CFIndex GetNormalizedPathLength(CFURLRef anURL) 1417{ 1418 CFURLRef absoluteURL; 1419 CFStringRef escapedPath; 1420 CFStringRef unescapedPath; 1421 CFIndex result; 1422 1423 result = 0; 1424 absoluteURL = CFURLCopyAbsoluteURL(anURL); 1425 require(absoluteURL != NULL, CFURLCopyAbsoluteURL); 1426 1427 escapedPath = CFURLCopyPath(absoluteURL); 1428 require(escapedPath != NULL, CFURLCopyPath); 1429 1430 unescapedPath = CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, escapedPath, CFSTR("")); 1431 require_string(unescapedPath != NULL, CFURLCreateStringByReplacingPercentEscapes, "name was not legal UTF8"); 1432 1433 result = CFStringGetLength(unescapedPath); 1434 1435 CFRelease(unescapedPath); 1436 1437CFURLCreateStringByReplacingPercentEscapes: 1438 1439 CFRelease(escapedPath); 1440 1441CFURLCopyPath: 1442 1443 CFRelease(absoluteURL); 1444 1445CFURLCopyAbsoluteURL: 1446 1447 return ( result ); 1448} 1449 1450/*****************************************************************************/ 1451 1452/* 1453 * GetComponentName determines if the URI combined with the parent URL is a 1454 * child of the parent or is the parent itself, and if it is a child, extracts 1455 * the child's component name. 1456 * 1457 * GetComponentName returns TRUE if the URI is for a child, or returns FALSE if 1458 * the URI is the parent itself. The componentName buffer will contain the child's 1459 * component name if the result is TRUE. 1460 * 1461 * The parentPathLength parameter allows this routine to determine child/parent 1462 * status without comparing the path strings. 1463 */ 1464static Boolean GetComponentName( /* <- TRUE if http URI was not parent and component name was returned */ 1465 CFURLRef urlRef, /* -> the parent directory's URL */ 1466 CFIndex parentPathLength, /* -> the parent directory's percent decoded path length */ 1467 char *uri, /* -> the http URI from the WebDAV server */ 1468 char *componentName) /* <-> point to buffer of MAXNAMLEN + 1 bytes where URI's LastPathComponent is returned if result it TRUE */ 1469{ 1470 Boolean result; 1471 CFStringRef uriString; /* URI as CFString */ 1472 CFURLRef uriURL; /* URI converted to full URL */ 1473 CFStringRef uriName; /* URI's LastPathComponent as CFString */ 1474 1475 result = FALSE; 1476 1477 /* create a CFString from the c-string containing URI */ 1478 uriString = CFStringCreateWithCString(kCFAllocatorDefault, uri, kCFStringEncodingUTF8); 1479 require(uriString != NULL, CFStringCreateWithCString); 1480 1481 /* create a CFURL from the URI CFString and the parent URL */ 1482 uriURL = CFURLCreateWithString(kCFAllocatorDefault, uriString, urlRef); 1483 if (uriURL != NULL) { 1484 /* CFURLCreateWithString worked fine, so release uriString and keep going */ 1485 CFRelease(uriString); 1486 } 1487 else { 1488 /* Fix for Windows servers. They tend to send over names that have a space in them instead of the correct %20. This causes 1489 CFURLCreateWithString to fail. Since it might be a partially escaped URL string, try to unescape the string, then re-escape it */ 1490 CFStringRef unEscapedString; /* URI as CFString with un-escaped chars */ 1491 CFStringRef reEscapedString; /* URI as CFString with re-escaped chars */ 1492 1493 unEscapedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding (kCFAllocatorDefault, uriString, NULL, kCFStringEncodingUTF8); 1494 CFRelease(uriString); 1495 require(unEscapedString != NULL, CFURLCreateWithString); 1496 1497 reEscapedString = CFURLCreateStringByAddingPercentEscapes (kCFAllocatorDefault, unEscapedString, NULL, NULL, kCFStringEncodingUTF8); 1498 CFRelease(unEscapedString); 1499 require(reEscapedString != NULL, CFURLCreateWithString); 1500 1501 /* try again to create a CFURL from the reEscapedString and the parent URL */ 1502 uriURL = CFURLCreateWithString(kCFAllocatorDefault, reEscapedString, urlRef); 1503 CFRelease(reEscapedString); 1504 require(uriURL != NULL, CFURLCreateWithString); 1505 } 1506 1507 /* see if this is the parent or a child */ 1508 if ( GetNormalizedPathLength(uriURL) > parentPathLength ) { 1509 /* this is a child */ 1510 1511 /* get the child's name */ 1512 uriName = CFURLCopyLastPathComponent(uriURL); 1513 require_string(uriName != NULL, CFURLCopyLastPathComponent, "name was not legal UTF8"); 1514 1515 if ( CFStringGetCString(uriName, componentName, MAXNAMLEN + 1, kCFStringEncodingUTF8) ) { 1516 /* we have the child name */ 1517 result = TRUE; 1518 } 1519 else { 1520 debug_string("could not get child name (too long?)"); 1521 } 1522 CFRelease(uriName); 1523 } 1524 else { 1525 /* this is the parent, skip it */ 1526 } 1527 1528CFURLCopyLastPathComponent: 1529 1530 CFRelease(uriURL); 1531 1532CFURLCreateWithString: 1533CFStringCreateWithCString: 1534 1535 return ( result ); 1536} 1537 1538/*****************************************************************************/ 1539 1540int parse_opendir(UInt8 *xmlp, /* -> xml data returned by PROPFIND with depth of 1 */ 1541 CFIndex xmlp_len, /* -> length of xml data */ 1542 CFURLRef urlRef, /* -> the CFURL to the parent directory */ 1543 uid_t uid, /* -> uid of the user making the request */ 1544 struct node_entry *parent_node) /* -> pointer to the parent directory's node_entry */ 1545{ 1546 int error = 0; 1547 ssize_t size = 0; 1548 struct webdav_dirent dir_data[2]; 1549 CFIndex parentPathLength; 1550 webdav_parse_opendir_struct_t opendir_struct; 1551 webdav_parse_opendir_element_t *element_ptr, *prev_element_ptr; 1552 opendir_struct.head = opendir_struct.tail = NULL; 1553 opendir_struct.error = 0; 1554 1555 xmlSAXHandler sh; 1556 memset(&sh,0,sizeof(sh)); 1557 sh.startElementNs = parser_opendir_create; 1558 sh.characters = parser_opendir_add; 1559 sh.endElementNs = parser_opendir_end; 1560 sh.initialized = XML_SAX2_MAGIC; 1561 1562 /* truncate the file, and reset the file pointer to 0 */ 1563 require(ftruncate(parent_node->file_fd, 0) == 0, ftruncate); 1564 require(lseek(parent_node->file_fd, 0, SEEK_SET) == 0, lseek); 1565 1566 if(xmlp != NULL) 1567 { 1568 int result = xmlSAXUserParseMemory( &sh,&opendir_struct,(char*)xmlp,(int)xmlp_len); 1569 require(result == 0, ParserCreate); 1570 /* parse the XML -- exit now if error during parse */ 1571 } 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 if(xmlp != NULL) 1826 { 1827 int result = xmlSAXUserParseMemory( &sh,multistatus_list,(char*)xmlp,(int)xmlp_len); 1828 require(result == 0, ParserCreate); 1829 } 1830 1831ParserCreate: 1832malloc_list: 1833 return (multistatus_list); 1834 1835 return ( 0 ); 1836} 1837 1838 1839/*****************************************************************************/ 1840 1841int parse_file_count(const UInt8 *xmlp, CFIndex xmlp_len, int *file_count) 1842{ 1843 xmlSAXHandler sh; 1844 memset(&sh,0,sizeof(sh)); 1845 sh.startElementNs = parser_file_count_create; 1846 sh.endElementNs = parser_file_count_end; 1847 sh.initialized = XML_SAX2_MAGIC; 1848 *file_count = 0; 1849 1850 if(xmlp != NULL) 1851 { 1852 xmlSAXUserParseMemory( &sh,file_count,(char*)xmlp,(int)xmlp_len); 1853 } 1854 1855 return ( 0 ); 1856} 1857 1858/*****************************************************************************/ 1859 1860int parse_stat(const UInt8 *xmlp, CFIndex xmlp_len, struct webdav_stat_attr *statbuf) 1861{ 1862 xmlSAXHandler sh; 1863 memset(&sh,0,sizeof(sh)); 1864 sh.startElementNs = parser_stat_create; 1865 sh.endElementNs = parse_stat_end; 1866 sh.characters = parser_stat_add; 1867 sh.initialized = XML_SAX2_MAGIC; 1868 bzero((void *)statbuf, sizeof(struct webdav_stat_attr)); 1869 1870 if(xmlp != NULL) 1871 { 1872 xmlSAXUserParseMemory( &sh,statbuf,(char*)xmlp,(int)xmlp_len); 1873 } 1874 /* Coming back from the parser: 1875 * statbuf->attr_stat_info.attr_stat.st_mode will be 0 or will have S_IFDIR set if the object is a directory. 1876 * statbuf->attr_stat_info.attr_stat.st_mtimespec will be 0 or will have the last modified time. 1877 * statbuf->attr_stat_info.attr_create_time will be 0 or the file creation time 1878 * statbuf->attr_stat_info.attr_stat.st_size will be set to the resource size if the object is a file. 1879 * 1880 * So, the first thing to do is fill in the fields we cannot get from the server. 1881 */ 1882 1883 statbuf->attr_stat.st_dev = 0; 1884 /* Why 1 for st_nlink? 1885 * Getting the real link count for directories is expensive. 1886 * Setting it to 1 lets FTS(3) (and other utilities that assume 1887 * 1 means a file system doesn't support link counts) work. 1888 */ 1889 statbuf->attr_stat.st_nlink = 1; 1890 statbuf->attr_stat.st_uid = UNKNOWNUID; 1891 statbuf->attr_stat.st_gid = UNKNOWNUID; 1892 statbuf->attr_stat.st_rdev = 0; 1893 statbuf->attr_stat.st_blksize = WEBDAV_IOSIZE; 1894 statbuf->attr_stat.st_flags = 0; 1895 statbuf->attr_stat.st_gen = 0; 1896 1897 /* set last accessed and changed times to last modified time since we cannot get them */ 1898 statbuf->attr_stat.st_atimespec = statbuf->attr_stat.st_ctimespec = statbuf->attr_stat.st_mtimespec; 1899 1900 /* was this a directory? */ 1901 if ( S_ISDIR(statbuf->attr_stat.st_mode) ) 1902 { 1903 /* yes - add the directory access permissions */ 1904 statbuf->attr_stat.st_mode |= S_IRWXU; 1905 /* fake up the directory size */ 1906 statbuf->attr_stat.st_size = WEBDAV_DIR_SIZE; 1907 } 1908 else 1909 { 1910 /* no - mark it as a regular file and set the file access permissions 1911 * (for now, everything is either a file or a directory) 1912 */ 1913 statbuf->attr_stat.st_mode = S_IFREG | S_IRWXU; 1914 } 1915 1916 /* calculate number of S_BLKSIZE blocks */ 1917 statbuf->attr_stat.st_blocks = ((statbuf->attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE); 1918 1919 return ( 0 ); 1920} 1921 1922/*****************************************************************************/ 1923 1924int parse_statfs(const UInt8 *xmlp, CFIndex xmlp_len, struct statfs *statfsbuf) 1925{ 1926 xmlSAXHandler sh; 1927 memset(&sh,0,sizeof(sh)); 1928 sh.startElementNs = parser_statfs_create; 1929 sh.characters = parser_statfs_add; 1930 sh.endElementNs = parser_statfs_end; 1931 sh.initialized = XML_SAX2_MAGIC; 1932 1933 struct webdav_quotas quotas; 1934 bzero((void *)statfsbuf, sizeof(struct statfs)); 1935 bzero((void *)"as, sizeof(struct webdav_quotas)); 1936 1937 if(xmlp != NULL) 1938 { 1939 xmlSAXUserParseMemory( &sh,"as,(char*)xmlp,(int)xmlp_len); 1940 } 1941 /* were the IETF quota properties returned? */ 1942 if ( quotas.use_bytes_values ) 1943 { 1944 uint64_t total_bytes; 1945 uint64_t total_blocks; 1946 uint32_t bsize; 1947 1948 /* calculate the total bytes (available + used) */ 1949 total_bytes = quotas.quota_available_bytes + quotas.quota_used_bytes; 1950 if ( (total_bytes >= quotas.quota_available_bytes) && (total_bytes >= quotas.quota_used_bytes) ) 1951 { 1952 /* 1953 * calculate the smallest file system block size (bsize) that's a 1954 * multiple of S_BLKSIZE, is >= S_BLKSIZE, and is <= LONG_MAX 1955 */ 1956 bsize = S_BLKSIZE / 2; 1957 do 1958 { 1959 bsize *= 2; 1960 total_blocks = ((total_bytes + bsize - 1) / bsize); 1961 } while ( total_blocks > LONG_MAX ); 1962 1963 /* stuff the results into statfsbuf */ 1964 statfsbuf->f_bsize = bsize; 1965#ifdef __LP64__ 1966 statfsbuf->f_blocks = total_blocks; 1967 statfsbuf->f_bavail = statfsbuf->f_bfree = quotas.quota_available_bytes / bsize; 1968#else 1969 statfsbuf->f_blocks = (long)total_blocks; 1970 statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota_available_bytes / bsize); 1971#endif 1972 } 1973 /* else we were handed values we cannot represent so leave statfsbuf zeroed (no quota support for this file system) */ 1974 } 1975 else 1976 { 1977 /* use the deprecated quota and quotaused if they were returned */ 1978 if ( (quotas.quota != 0) && (quotas.quota > quotas.quotaused) ) 1979 { 1980#ifdef __LP64__ 1981 statfsbuf->f_bavail = statfsbuf->f_bfree = (quotas.quota - quotas.quotaused); 1982#else 1983 statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota - quotas.quotaused); 1984#endif 1985 } 1986 else 1987 { 1988 statfsbuf->f_bavail = statfsbuf->f_bfree = 0; 1989 } 1990#ifdef __LP64__ 1991 statfsbuf->f_blocks = quotas.quota; 1992#else 1993 statfsbuf->f_blocks = (long)quotas.quota; 1994#endif 1995 statfsbuf->f_bsize = S_BLKSIZE; 1996 } 1997 1998 statfsbuf->f_iosize = WEBDAV_IOSIZE; 1999 2000 return ( 0 ); 2001} 2002 2003/*****************************************************************************/ 2004 2005int parse_lock(const UInt8 *xmlp, CFIndex xmlp_len, char **locktoken) 2006{ 2007 xmlSAXHandler sh; 2008 memset(&sh,0,sizeof(sh)); 2009 sh.startElementNs = parser_lock_create; 2010 sh.characters = parser_lock_add; 2011 sh.endElementNs = parser_lock_end; 2012 sh.initialized = XML_SAX2_MAGIC; 2013 webdav_parse_lock_struct_t lock_struct; 2014 lock_struct.context = 0; 2015 lock_struct.locktoken = NULL; /* NULL coming into this function */ 2016 2017 if(xmlp != NULL) 2018 { 2019 xmlSAXUserParseMemory( &sh,&lock_struct,(char*)xmlp,(int)xmlp_len); 2020 } 2021 2022 *locktoken = (char *)lock_struct.locktoken; 2023 if (*locktoken == NULL) 2024 { 2025 debug_string("error parsing lock token"); 2026 } 2027 2028 return ( 0 ); 2029} 2030 2031/*****************************************************************************/ 2032 2033int parse_cachevalidators(const UInt8 *xmlp, CFIndex xmlp_len, time_t *last_modified, char **entity_tag) 2034{ 2035 xmlSAXHandler sh; 2036 memset(&sh,0,sizeof(sh)); 2037 sh.startElementNs = parser_cachevalidators_create; 2038 sh.characters = parser_cachevalidators_add; 2039 sh.endElementNs = parser_cachevalidators_end; 2040 sh.initialized = XML_SAX2_MAGIC; 2041 struct webdav_parse_cachevalidators_struct cachevalidators_struct; 2042 /* results if parser fails or values are not found */ 2043 cachevalidators_struct.last_modified = 0; 2044 cachevalidators_struct.entity_tag = NULL; 2045 2046 if(xmlp != NULL) 2047 { 2048 xmlSAXUserParseMemory( &sh,&cachevalidators_struct,(char*)xmlp,(int)xmlp_len); 2049 } 2050 2051 if ( cachevalidators_struct.last_modified != 0 ) 2052 { 2053 *last_modified = cachevalidators_struct.last_modified; 2054 } 2055 else 2056 { 2057 time(last_modified); 2058 } 2059 *entity_tag = cachevalidators_struct.entity_tag; 2060 2061 return ( 0 ); 2062} 2063 2064/*****************************************************************************/ 2065