1/* 2 * Copyright (c) 2000-2008 Apple 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 <SystemConfiguration/SystemConfiguration.h> 27#include <SystemConfiguration/SCDynamicStorePrivate.h> 28#include <SystemConfiguration/SCDynamicStoreKey.h> 29#include <CoreServices/CoreServices.h> 30#include <CoreServices/CoreServicesPriv.h> 31#include <sched.h> 32#include <sys/mount.h> 33#include <sys/sysctl.h> 34#include <Security/Security.h> 35#include <netdb.h> 36#include <stdio.h> 37 38#include "webdav_parse.h" 39#include "webdav_requestqueue.h" 40#include "webdav_authcache.h" 41#include "webdav_network.h" 42#include "webdav_utils.h" 43#include "webdav_cookie.h" 44#include "EncodedSourceID.h" 45#include "LogMessage.h" 46 47extern char *CopyCFStringToCString(CFStringRef theString); 48 49 50/******************************************************************************/ 51 52#define WEBDAV_WRITESEQ_RSP_TIMEOUT 60 /* in seconds */ 53 54#define X_APPLE_REALM_SUPPORT_VALUE "1.0" /* Value for the X_APPLE_REALM_SUPPORT header field */ 55 56struct HeaderFieldValue 57{ 58 CFStringRef headerField; 59 CFStringRef value; 60}; 61 62// Specifies how to handle http 3xx redirection 63enum RedirectAction { 64 REDIRECT_DISABLE = 0, // Do not allow redirection 65 REDIRECT_MANUAL = 1, // Handle redirection manually 66 REDIRECT_AUTO = 2 // Let CFNetwork handle redirecion, i.e. set kCFStreamPropertyHTTPShouldAutoredirect on stream 67}; 68 69/******************************************************************************/ 70 71// The maximum size of an upload or download to allow the 72// system to cache. 73extern uint64_t webdavCacheMaximumSize; 74 75static CFStringRef userAgentHeaderValue = NULL; /* The User-Agent request-header value */ 76static CFIndex first_read_len = 4096; /* bytes. Amount to download at open so first read at offset 0 doesn't stall */ 77static CFStringRef X_Source_Id_HeaderValue = NULL; /* the X-Source-Id header value, or NULL if not iDisk */ 78static CFStringRef X_Apple_Realm_Support_HeaderValue = NULL; /* the X-Apple-Realm-Support header value, or NULL if not iDisk */ 79 80static SCDynamicStoreRef gProxyStore; 81 82/******************************************************************************/ 83 84static pthread_mutex_t gNetworkGlobals_lock; 85/* these variables are protected by gNetworkGlobals_lock */ 86static CFDictionaryRef gProxyDict = NULL; 87static int gHttpProxyEnabled; 88static char gHttpProxyServer[MAXHOSTNAMELEN]; 89static int gHttpProxyPort; 90static int gHttpsProxyEnabled; 91static char gHttpsProxyServer[MAXHOSTNAMELEN]; 92static int gHttpsProxyPort; 93static CFMutableDictionaryRef gSSLPropertiesDict = NULL; 94static struct ReadStreamRec gReadStreams[WEBDAV_REQUEST_THREADS + 1]; /* one for every request thread plus one for the pulse thread */ 95 96/******************************************************************************/ 97 98static int network_stat( 99 uid_t uid, /* -> uid of the user making the request */ 100 struct node_entry *node, /* -> the node associated with this request (NULL means root node) */ 101 CFURLRef urlRef, /* -> url to the resource */ 102 enum RedirectAction redirectAction, /* how to handle http 3xx redirection */ 103 struct webdav_stat_attr *statbuf); /* <- stat information is returned in this buffer */ 104 105static int network_dir_is_empty( 106 uid_t uid, /* -> uid of the user making the request */ 107 CFURLRef urlRef); /* -> url to check */ 108 109static CFStringRef CFStringCreateRFC2616DateStringWithTimeT( /* <- CFString containing RFC 1123 date, NULL if error */ 110 time_t clock); /* -> time_t value */ 111 112/*****************************************************************************/ 113 114#define ISO8601_UTC "%04d-%02d-%02dT%02d:%02d:%02dZ" 115#define ISO8601_BEHIND_UTC "%04d-%02d-%02dT%02d:%02d:%02d-%02d:%02d" 116#define ISO8601_AHEAD_UTC "%04d-%02d-%02dT%02d:%02d:%02d+%02d:%02d" 117 118/* 119 * ISO8601_To_Time parses an ISO8601 formatted date/time 'C' string 120 * and returns time_t. If the parse fails, this function return (-1). 121 * 122 * Examples: 123 * 124 * 1994-11-05T08:15:30-05:00 corresponds to November 5, 1994, 8:15:30 am, US Eastern Standard Time. 125 * 126 * 1994-11-05T13:15:30Z corresponds to the same instant. 127 * 128 */ 129 130time_t ISO8601ToTime( /* <- time_t value */ 131 const UInt8 *bytes, /* -> pointer to bytes to parse */ 132 CFIndex length) /* -> number of bytes to parse */ 133{ 134 int num, tm_sec, tm_min, tm_hour, tm_year, tm_mon, tm_day; 135 int utc_offset, utc_offset_hour, utc_offset_min; 136 struct tm tm_temp; 137 time_t clock; 138 const UInt8 *ch; 139 boolean_t done, have_z; 140 141 done = FALSE; 142 utc_offset = utc_offset_hour = utc_offset_min = 0; 143 clock = -1; 144 145 if ( (bytes == NULL) || (length == 0)) 146 return (clock); 147 148 memset(&tm_temp, 0, sizeof(struct tm)); 149 150 // Try ISO8601_UTC "1994-11-05T13:15:30Z" 151 have_z = FALSE; 152 ch = bytes + length - 1; 153 154 while (ch > bytes) { 155 if (isspace(*ch)) { 156 ch--; 157 continue; 158 } 159 160 if (*ch == 'Z') 161 have_z = TRUE; 162 break; 163 } 164 165 if (ch <= bytes) 166 return (clock); // not a valid date/time string, nothing but white space 167 168 if (have_z == TRUE) { 169 num = sscanf((const char *)bytes, ISO8601_UTC, &tm_year, &tm_mon, &tm_day, 170 &tm_hour, &tm_min, &tm_sec); 171 if (num == 6) { 172 done = TRUE; 173 } 174 } 175 176 if (done == FALSE) { 177 // Try ISO8601_BEHIND_UTC "1994-11-05T08:15:30-05:00" 178 num = sscanf((const char *)bytes, ISO8601_BEHIND_UTC, &tm_year, &tm_mon, &tm_day, 179 &tm_hour, &tm_min, &tm_sec, 180 &utc_offset_hour, &utc_offset_min); 181 if (num == 8) { 182 // Need to add offset later to get UTC 183 utc_offset = (utc_offset_hour * 3600) + (utc_offset_min * 60); 184 done = TRUE; 185 } 186 } 187 188 if (done == FALSE) { 189 // Try ISO8601_AHEAD_UTC "1994-11-05T08:15:30+05:00" 190 num = sscanf((const char *)bytes, ISO8601_AHEAD_UTC, &tm_year, &tm_mon, &tm_day, 191 &tm_hour, &tm_min, &tm_sec, 192 &utc_offset_hour, &utc_offset_min); 193 if (num == 8) { 194 // Need to subtract offset later to get UTC 195 utc_offset = - (utc_offset_hour * 3600) - (utc_offset_min * 60); 196 done = TRUE; 197 } 198 } 199 200 if (done == TRUE) { 201 // fill in tm_temp and adjust before converting to time_t 202 tm_temp.tm_sec = tm_sec; 203 tm_temp.tm_min = tm_min; 204 tm_temp.tm_hour = tm_hour; 205 tm_temp.tm_mday = tm_day; 206 tm_temp.tm_mon = tm_mon - 1; 207 tm_temp.tm_year = tm_year - 1900; 208 tm_temp.tm_isdst = -1; 209 210 // Convert to time_t 211 clock = timegm(&tm_temp); 212 213 // apply offset so we end up with UTC 214 clock += utc_offset; 215 } 216 217 return (clock); 218} 219 220/*****************************************************************************/ 221 222/* 223 * CFStringCreateRFC2616DateStringWithTimeT creates a RFC 1123 date CFString from 224 * a time_t time. 225 */ 226static CFStringRef CFStringCreateRFC2616DateStringWithTimeT( /* <- CFString containing RFC 1123 date, NULL if error */ 227 time_t clock) /* -> time_t value */ 228{ 229 struct tm *tm_temp; 230 CFGregorianDate gdate; 231 CFStringRef result; 232 233 require_action_quiet(clock != -1, InvalidClock, result = NULL); 234 235 /* get struct tm (in GMT) from time_t */ 236 tm_temp = gmtime(&clock); 237 238 /* copy the struct tm into CFGregorianDate */ 239 gdate.second = tm_temp->tm_sec; 240 gdate.minute = tm_temp->tm_min; 241 gdate.hour = tm_temp->tm_hour; 242 gdate.day = tm_temp->tm_mday; 243 gdate.month = tm_temp->tm_mon + 1; 244 gdate.year = tm_temp->tm_year + 1900; 245 246 /* get a CFStringRef with the RFC 1123 date string */ 247 result = _CFStringCreateRFC2616DateStringWithGregorianDate(kCFAllocatorDefault, &gdate, NULL); 248 249InvalidClock: 250 251 return ( result ); 252} 253 254/*****************************************************************************/ 255 256/* 257 * SkipCodedURL finds the end of a Coded-URL using the rules 258 * (rfc 2518, section 9.4 and rfc 2396): 259 * 260 * Coded-URL = "<" absoluteURI ">" 261 * 262 * On input, the bytes parameter points to the character AFTER the initial 263 * '<' character. The function result is a pointer to the '>' character that 264 * terminates the Coded-URL or the end of the C string. 265 */ 266static const char * SkipCodedURL(const char *bytes) 267{ 268 /* the end of the string or Coded-URL? */ 269 while ( (*bytes != '\0') && (*bytes != '>') ) 270 { 271 /* skip character */ 272 ++bytes; 273 } 274 275 return ( bytes ); 276} 277 278/*****************************************************************************/ 279 280/* 281 * SkipToken finds the end of a token using the rules (rfc 2616, section 2.2): 282 * 283 * token = 1*<any CHAR except CTLs or separators> 284 * CTL = <any US-ASCII control character (octets 0 - 31) and 285 * DEL (127)> 286 * separators = "(" | ")" | "<" | ">" | "@" 287 * | "," | ";" | ":" | "\" | <"> 288 * | "/" | "[" | "]" | "?" | "=" 289 * | "{" | "}" | SP | HT 290 * 291 * The function result is a pointer to the first non token character or the 292 * end of the C string. 293 */ 294static const char * SkipToken(const char *bytes) 295{ 296 while ( *bytes != '\0' ) 297 { 298 /* CTL - US-ASCII control character (octets 0 - 31) */ 299 if ( (unsigned char)*bytes <= 31 ) 300 { 301 /* not a token character - done */ 302 goto Done; 303 } 304 else 305 { 306 switch ( *bytes ) 307 { 308 /* CTL - DEL (127) */ 309 case '\x7f': 310 /* separators */ 311 case '(': 312 case ')': 313 case '<': 314 case '>': 315 case '@': 316 case ',': 317 case ';': 318 case ':': 319 case '\\': 320 case '\"': 321 case '/': 322 case '[': 323 case ']': 324 case '\?': 325 case '=': 326 case '{': 327 case '}': 328 case ' ': 329 case '\t': 330 /* not a token character - done */ 331 goto Done; 332 break; 333 334 default: 335 /* skip token characters */ 336 ++bytes; 337 break; 338 } 339 } 340 } 341 342Done: 343 return (bytes); 344} 345 346/*****************************************************************************/ 347 348/* 349 * SkipLWS finds the end of a run of LWS using the rule 350 * (rfc 2616, section 2.2): 351 * 352 * LWS = [CRLF] 1*( SP | HT ) 353 * 354 * The function result is a pointer to the first non LWS character or the end 355 * of the C string. 356 */ 357static const char * SkipLWS(const char *bytes) 358{ 359 while ( *bytes != '\0' ) 360 { 361 if ( (*bytes == ' ') || (*bytes == '\t') ) 362 { 363 /* skip SP and HT characters */ 364 ++bytes; 365 continue; 366 } 367 /* 368 * skip CRLF only if followed by SP or HT (in which case the SP 369 * or HT can be skipped, too) 370 */ 371 else if ( *bytes == '\x0d' ) /* CR? */ 372 { 373 /* LF? */ 374 if ( bytes[1] == '\x0a' ) 375 { 376 /* SP or HT? */ 377 if ( (bytes[2] == ' ') || (bytes[2] == '\t') ) 378 { 379 /* skip CRLF followed by SP or HT */ 380 bytes += 3; 381 continue; 382 } 383 } 384 } 385 386 /* found the end of the LWS run */ 387 break; 388 } 389 390 return ( bytes ); 391} 392 393/*****************************************************************************/ 394 395static int set_global_stream_properties(CFReadStreamRef readStreamRef) 396{ 397 int error, mutexerror; 398 399 mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock); 400 require_noerr_action(mutexerror, pthread_mutex_lock, error = mutexerror; webdav_kill(-1)); 401 402 error = (CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyHTTPProxy, gProxyDict) == TRUE) ? 0 : 1; 403 404 mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock); 405 require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1)); 406 407pthread_mutex_unlock: 408pthread_mutex_lock: 409 410 return ( error ); 411} 412 413/******************************************************************************/ 414 415int network_get_proxy_settings( 416 int *httpProxyEnabled, /* <- true if HTTP proxy is enabled */ 417 char **httpProxyServer, /* <- HTTP proxy server name */ 418 int *httpProxyPort, /* <- HTTP proxy port number */ 419 int *httpsProxyEnabled, /* <- true if HTTPS proxy is enabled */ 420 char **httpsProxyServer, /* <- HTTPS proxy server name */ 421 int* httpsProxyPort) /* <- HTTPS proxy port number */ 422{ 423 int error, mutexerror; 424 425 error = 0; 426 427 mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock); 428 require_noerr_action(mutexerror, pthread_mutex_lock, error = mutexerror; webdav_kill(-1)); 429 430 *httpProxyServer = malloc(MAXHOSTNAMELEN); 431 require_action(*httpProxyServer != NULL, malloc_httpProxyServer, error = ENOMEM); 432 433 *httpsProxyServer = malloc(MAXHOSTNAMELEN); 434 require_action(*httpsProxyServer != NULL, malloc_httpsProxyServer, free(*httpProxyServer); error = ENOMEM); 435 436 *httpProxyEnabled = gHttpProxyEnabled; 437 memcpy(*httpProxyServer, gHttpProxyServer, MAXHOSTNAMELEN); 438 *httpProxyPort = gHttpProxyPort; 439 440 *httpsProxyEnabled = gHttpsProxyEnabled; 441 memcpy(*httpsProxyServer, gHttpsProxyServer, MAXHOSTNAMELEN); 442 *httpsProxyPort = gHttpsProxyPort; 443 444malloc_httpsProxyServer: 445malloc_httpProxyServer: 446 447 mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock); 448 require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1)); 449 450pthread_mutex_unlock: 451pthread_mutex_lock: 452 453 return ( error ); 454} 455 456/******************************************************************************/ 457 458int network_update_proxy(void *arg) 459{ 460#pragma unused(arg) 461 int error, mutexerror; 462 463 error = 0; 464 465 mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock); 466 require_noerr_action(mutexerror, pthread_mutex_lock, error = mutexerror; webdav_kill(-1)); 467 468 /* release the old proxies dictionary */ 469 if ( gProxyDict ) 470 { 471 CFRelease(gProxyDict); 472 } 473 474 /* slam everything to default disabled state in case something fails */ 475 gHttpProxyEnabled = 0; 476 gHttpProxyServer[0] = '\0'; 477 gHttpProxyPort = 0; 478 479 gHttpsProxyEnabled = 0; 480 gHttpsProxyServer[0] = '\0'; 481 gHttpsProxyPort = 0; 482 483 /* get the current internet proxy dictionary */ 484 gProxyDict = SCDynamicStoreCopyProxies(gProxyStore); 485 if ( gProxyDict != NULL ) 486 { 487 CFNumberRef cf_enabled; 488 int enabled; 489 CFStringRef cf_host; 490 CFNumberRef cf_port; 491 492 /* 493 * take care of HTTP proxies 494 */ 495 /* are HTTP proxies enabled? */ 496 cf_enabled = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPEnable); 497 if ( (cf_enabled != NULL) && CFNumberGetValue(cf_enabled, kCFNumberIntType, &enabled) && enabled ) 498 { 499 /* get the HTTP proxy */ 500 cf_host = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPProxy); 501 if ( (cf_host != NULL) && CFStringGetCString(cf_host, gHttpProxyServer, sizeof(gHttpProxyServer), kCFStringEncodingUTF8) ) 502 { 503 /* get the HTTP proxy port */ 504 cf_port = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPPort); 505 if ( (cf_port != NULL) && CFNumberGetValue(cf_port, kCFNumberIntType, &gHttpProxyPort) ) 506 { 507 if ( gHttpProxyPort == 0 ) 508 { 509 /* no port specified so use the default HTTP port */ 510 gHttpProxyPort = kHttpDefaultPort; 511 } 512 gHttpProxyEnabled = 1; 513 } 514 } 515 } 516 517 /* 518 * take care of HTTPS proxies 519 */ 520 /* are HTTP proxies enabled? */ 521 cf_enabled = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPSEnable); 522 if ( (cf_enabled != NULL) && CFNumberGetValue(cf_enabled, kCFNumberIntType, &enabled) && enabled ) 523 { 524 /* get the HTTP proxy */ 525 cf_host = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPSProxy); 526 if ( (cf_host != NULL) && CFStringGetCString(cf_host, gHttpsProxyServer, sizeof(gHttpsProxyServer), kCFStringEncodingUTF8) ) 527 { 528 /* get the HTTP proxy port */ 529 cf_port = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPSPort); 530 if ( (cf_port != NULL) && CFNumberGetValue(cf_port, kCFNumberIntType, &gHttpsProxyPort) ) 531 { 532 if ( gHttpsProxyPort == 0 ) 533 { 534 /* no port specified so use the default HTTPS port */ 535 gHttpsProxyPort = kHttpsDefaultPort; 536 } 537 gHttpsProxyEnabled = 1; 538 } 539 } 540 } 541 } 542 543 mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock); 544 require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1)); 545 546 /* remove proxy authentications */ 547 error = authcache_proxy_invalidate(); 548 549pthread_mutex_unlock: 550pthread_mutex_lock: 551 552 return ( error ); 553} 554 555/*****************************************************************************/ 556 557/* 558 The InitUserAgentHeaderValue initializes the string userAgentHeaderValue which 559 is sent with every request to the server. The User-Agent request-header field 560 is defined in RFC 2616, section 14.43 as: 561 User-Agent = "User-Agent" ":" 1*( product | comment ) 562 section 3.8 defines product as: 563 product = token ["/" product-version] 564 product-version = token 565 section 2.2 defines comment as: 566 comment = "(" *( ctext | quoted-pair | comment ) ")" 567 ctext = <any TEXT excluding "(" and ")"> 568 quoted-pair = "\" CHAR 569 570 We want our User-Agent request-header field to look something like: 571 "User-Agent: WebDAVFS/1.1 (0110800000) Darwin/5.3 (Power Macintosh)" 572 where: 573 1.1 = the CFBundleShortVersionString from webdav.fs bundle 574 0110800000 = webdav.fs bundle's numeric version 575 Darwin = CTL_KERN/KERN_OSTYPE 576 5.3 = CTL_KERN/KERN_OSRELEASE 577 Power Macintosh = CTL_HW/HW_MACHINE 578 579 webdav.fs bundle is located at: 580 /System/Library/Filesystems/webdav.fs 581 582 If the data from webdav.fs bundle could not be obtained, then we'll 583 fall back to the generic User-Agent request-header string WebDAV FS 584 used to use. 585 586 IMPORTANT: The user-agent header MUST start with the 587 product token "WebDAVFS" because there are WebDAV servers 588 that special case this client. 589 */ 590 591static int InitUserAgentHeaderValue(int add_mirror_comment) 592{ 593 char buf[128]; 594 int mib[2]; 595 char ostype[128]; 596 char osrelease[128]; 597 char machine[128]; 598 size_t len; 599 CFURLRef url; 600 CFBundleRef bundle; 601 CFDictionaryRef dict; 602 CFStringRef shortVersion; 603 CFIndex shortVersionLen; 604 char *webdavfsVersionStr; 605 UInt32 webdavfsVersion; 606 int result; 607 608 /* Get the ostype, osrelease, and machine strings using sysctl*/ 609 mib[0] = CTL_KERN; 610 mib[1] = KERN_OSTYPE; 611 len = sizeof ostype; 612 if (sysctl(mib, 2, ostype, &len, 0, 0) < 0) 613 { 614 ostype[0] = '\0'; 615 } 616 mib[1] = KERN_OSRELEASE; 617 len = sizeof osrelease; 618 if (sysctl(mib, 2, osrelease, &len, 0, 0) < 0) 619 { 620 osrelease[0] = '\0'; 621 } 622 mib[0] = CTL_HW; 623 mib[1] = HW_MACHINE; 624 len = sizeof machine; 625 if (sysctl(mib, 2, machine, &len, 0, 0) < 0) 626 { 627 machine[0] = '\0'; 628 } 629 630 /* We don't have it yet */ 631 webdavfsVersionStr = NULL; 632 webdavfsVersion = 0x010080000; /* 1.0 final */ 633 634 /* Create the CFURLRef to the webdav.fs bundle's version.plist */ 635 url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 636 CFSTR("/System/Library/Filesystems/webdav.fs"), 637 kCFURLPOSIXPathStyle, true); 638 if ( url != NULL ) 639 { 640 /* Create the bundle */ 641 bundle = CFBundleCreate(kCFAllocatorDefault, url); 642 if ( bundle != NULL ) 643 { 644 /* Get the bundle's numeric version */ 645 webdavfsVersion = CFBundleGetVersionNumber(bundle); 646 647 /* Get the Info dictionary */ 648 dict = CFBundleGetInfoDictionary(bundle); 649 if ( dict != NULL ) 650 { 651 /* Get the CFBundleShortVersionString (display string) */ 652 shortVersion = CFDictionaryGetValue(dict, CFSTR("CFBundleShortVersionString")); 653 if ( shortVersion != NULL ) 654 { 655 /* Get the bundleVersionStr */ 656 shortVersionLen = CFStringGetLength(shortVersion) + 1; 657 webdavfsVersionStr = malloc((size_t)shortVersionLen); 658 if ( webdavfsVersionStr != NULL ) 659 { 660 /* Convert it to a C string */ 661 if ( !CFStringGetCString(shortVersion, webdavfsVersionStr, shortVersionLen, kCFStringEncodingUTF8) ) 662 { 663 /* If we can't get it, free the memory */ 664 free(webdavfsVersionStr); 665 webdavfsVersionStr = NULL; 666 } 667 } 668 } 669 } 670 /* release created bundle */ 671 CFRelease(bundle); 672 } 673 /* release created url */ 674 CFRelease(url); 675 } 676 677 if ( webdavfsVersionStr != NULL ) 678 { 679 /* if everything worked, use the new format User-Agent request-header string */ 680 snprintf(buf, sizeof(buf), "WebDAVFS/%s (%.8lx) %s%s/%s (%s)", 681 webdavfsVersionStr, (unsigned long)webdavfsVersion, (add_mirror_comment ? "(mirrored) " : ""), ostype, osrelease, machine); 682 free(webdavfsVersionStr); 683 } 684 else 685 { 686 /* create the generic User-Agent request-header string WebDAV FS used to use */ 687 snprintf(buf, sizeof(buf), "WebDAVFS/1.4 %s/%s (%s)", 688 ostype, osrelease, machine); 689 } 690 691 userAgentHeaderValue = CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingUTF8); 692 require_action(userAgentHeaderValue != NULL, CFStringCreateWithCString, result = ENOMEM); 693 694 result = 0; 695 696CFStringCreateWithCString: 697 698 return ( result ); 699} 700 701/*****************************************************************************/ 702 703/* 704 * The get_first_read_len function sets the global 705 * first_read_len. It is set to the system's page size so that if the 706 * first read after an open starts at offset 0, that page will already be 707 * downloaded into the cache file. 708 */ 709static void get_first_read_len(void) 710{ 711 int mib[2]; 712 size_t len; 713 int result; 714 int pagesize; 715 716 /* get the hardware page size */ 717 mib[0] = CTL_HW; 718 mib[1] = HW_PAGESIZE; 719 len = sizeof(int); 720 result = sysctl(mib, 2, &pagesize, &len, 0, 0); 721 if ( result < 0 ) 722 { 723 /* set first_read_len to PowerPC page size */ 724 first_read_len = 4096; 725 } 726 else 727 { 728 first_read_len = pagesize; 729 } 730} 731 732/******************************************************************************/ 733 734int network_init(const UInt8 *uri, CFIndex uriLength, int *store_notify_fd, int add_mirror_comment) 735{ 736 int error; 737 pthread_mutexattr_t mutexattr; 738 CFStringRef notification_string; 739 CFArrayRef keys; 740 int index; 741 742 gProxyDict = NULL; 743 error = 0; 744 gBasePathStr[0] = 0; 745 746 /* set up the lock on the queues */ 747 error = pthread_mutexattr_init(&mutexattr); 748 require_noerr(error, pthread_mutexattr_init); 749 750 error = pthread_mutex_init(&gNetworkGlobals_lock, &mutexattr); 751 require_noerr(error, pthread_mutex_init); 752 753 /* create a dynnamic store */ 754 gProxyStore = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("WebDAVFS"), NULL, NULL); 755 require_action(gProxyStore != NULL, SCDynamicStoreCreate, error = ENOMEM); 756 757 /* open a file descriptor to be notified on */ 758 require_action(SCDynamicStoreNotifyFileDescriptor(gProxyStore, 0, store_notify_fd), 759 SCDynamicStoreNotifyFileDescriptor, error = ENOMEM); 760 761 /* create a keys for network proxy changes */ 762 notification_string = SCDynamicStoreKeyCreateProxies(kCFAllocatorDefault); 763 require_action(notification_string != NULL, SCDynamicStoreKeyCreateProxies, error = ENOMEM); 764 765 keys = CFArrayCreate(kCFAllocatorDefault, (const void **)¬ification_string, 1, &kCFTypeArrayCallBacks); 766 require_action(keys != NULL, CFArrayCreate, error = ENOMEM); 767 CFRelease(notification_string); 768 769 require_action(SCDynamicStoreSetNotificationKeys(gProxyStore, keys, NULL), 770 SCDynamicStoreSetNotificationKeys, error = ENOMEM); 771 CFRelease(keys); 772 773 /* get the initial internet proxy settings */ 774 error = network_update_proxy(NULL); 775 require_noerr_quiet(error, network_update_proxy); 776 777 /* create gBaseURL */ 778 gBaseURL = CFURLCreateAbsoluteURLWithBytes(kCFAllocatorDefault, uri, uriLength, kCFStringEncodingUTF8, NULL, FALSE); 779 require_action_string(gBaseURL != NULL, CFURLCreateAbsoluteURLWithBytes, error = ENOMEM, "name was not legal UTF8"); 780 781 /* 782 * Make sure the gBaseURL doesn't have any components that it shouldn't have. 783 * All it should have are the scheme, the host, the port, and the path. 784 * UserInfo (name and password) and ResourceSpecifier (params, queries and fragments) 785 * components are not allowed. 786 */ 787 require_action(((CFURLGetByteRangeForComponent(gBaseURL, kCFURLComponentUserInfo, NULL).location == kCFNotFound) && 788 (CFURLGetByteRangeForComponent(gBaseURL, kCFURLComponentResourceSpecifier, NULL).location == kCFNotFound)), 789 IllegalURLComponent, error = EINVAL); 790 791 gBasePath = CFURLCopyPath(gBaseURL); 792 if (gBasePath != NULL) { 793 CFStringGetCString(gBasePath, gBasePathStr, MAXPATHLEN, kCFStringEncodingUTF8); 794 } 795 796 /* initialize first_read_len variable */ 797 get_first_read_len(); 798 799 /* initialize userAgentHeaderValue */ 800 error = InitUserAgentHeaderValue((X_Source_Id_HeaderValue != NULL) && add_mirror_comment); 801 if ( error ) 802 { 803 /* not likely to fail, but just in case */ 804 exit(error); 805 } 806 807 /* initialize the gReadStreams array */ 808 for ( index = 0; index < (WEBDAV_REQUEST_THREADS + 1); ++index ) 809 { 810 gReadStreams[index].inUse = 0; /* not in use */ 811 gReadStreams[index].readStreamRef = NULL; /* no stream */ 812 gReadStreams[index].uniqueValue = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), index); /* unique string */ 813 } 814 815IllegalURLComponent: 816CFURLCreateAbsoluteURLWithBytes: 817network_update_proxy: 818SCDynamicStoreSetNotificationKeys: 819CFArrayCreate: 820SCDynamicStoreKeyCreateProxies: 821SCDynamicStoreNotifyFileDescriptor: 822SCDynamicStoreCreate: 823pthread_mutex_init: 824pthread_mutexattr_init: 825 826 return ( error ); 827} 828 829/******************************************************************************/ 830 831/* 832 * create_cfurl_from_node 833 * 834 * Creates a CFURL to the node if no name is provided, or to the node's named 835 * child if a name is provided. 836 * 837 * The caller is responsible for releasing the CFURL returned. 838 */ 839static CFURLRef create_cfurl_from_node( 840 struct node_entry *node, 841 char *name, 842 size_t name_length) 843{ 844 CFURLRef tempUrlRef, baseURL; 845 CFURLRef urlRef; 846 char *node_path; 847 bool pathHasRedirection; 848 int error; 849 850 urlRef = NULL; 851 pathHasRedirection = false; 852 853 /* 854 * Get the UT8 path (not percent escaped) from the root to the node (if any). 855 * If the path is returned and it is to a directory, it will end with a slash. 856 */ 857 error = nodecache_get_path_from_node(node, &pathHasRedirection, &node_path); 858 require_noerr_quiet(error, nodecache_get_path_from_node); 859 860 /* append the name (if any) */ 861 if ( name != NULL && name_length != 0 ) 862 { 863 strncat(node_path, name, name_length); 864 } 865 866 /* is there any relative path? */ 867 if ( *node_path != '\0' ) 868 { 869 CFStringRef stringRef; 870 CFStringRef escapedPathRef; 871 872 escapedPathRef = NULL; 873 874 /* convert the relative path to a CFString */ 875 stringRef = CFStringCreateWithCString(kCFAllocatorDefault, node_path, kCFStringEncodingUTF8); 876 require_string(stringRef != NULL, CFStringCreateWithCString, "name was not legal UTF8"); 877 878 /* create the URL */ 879 if (pathHasRedirection == true) { 880 escapedPathRef = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, stringRef, NULL, CFSTR(";?"), kCFStringEncodingUTF8); 881 require(escapedPathRef != NULL, CFURLCreateStringByAddingPercentEscapes); 882 883 // this path already has a base URL since it contains a redirected node 884 urlRef = CFURLCreateWithString(kCFAllocatorDefault, escapedPathRef, NULL); 885 886 if (urlRef == NULL) 887 logDebugCFString("create_cfurl_from_node: CFURLCreateWithString error:", escapedPathRef); 888 889 require(urlRef != NULL, CFURLCreateWithString); 890 } 891 else { 892 /* 893 * Then percent escape everything that CFURLCreateStringByAddingPercentEscapes() 894 * considers illegal URL characters plus the characters ";" and "?" which are 895 * not legal pchar (see rfc2396) characters, and ":" so that names in the root 896 * directory do not look like absolute URLs with some weird scheme. 897 */ 898 escapedPathRef = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, stringRef, NULL, CFSTR(":;?"), kCFStringEncodingUTF8); 899 require(escapedPathRef != NULL, CFURLCreateStringByAddingPercentEscapes); 900 901 baseURL = nodecache_get_baseURL(); 902 urlRef = CFURLCreateWithString(kCFAllocatorDefault, escapedPathRef, baseURL); 903 CFRelease(baseURL); 904 require(urlRef != NULL, CFURLCreateWithString); 905 } 906 907 /* and then make an absolute copy of it */ 908 tempUrlRef = urlRef; 909 urlRef = CFURLCopyAbsoluteURL(tempUrlRef); 910 911 CFRelease(tempUrlRef); 912CFURLCreateWithString: 913 if (escapedPathRef != NULL) 914 CFRelease(escapedPathRef); 915CFURLCreateStringByAddingPercentEscapes: 916 CFRelease(stringRef); 917CFStringCreateWithCString: 918 ; 919 } 920 else 921 { 922 /* no relative path -- use the base URL */ 923 urlRef = nodecache_get_baseURL(); 924 } 925 926 free(node_path); 927 928nodecache_get_path_from_node: 929 930 return ( urlRef ); 931} 932 933/******************************************************************************/ 934 935static int translate_status_to_error(UInt32 statusCode) 936{ 937 int result; 938 uint64_t code = statusCode; 939 940 /* switch by Nxx range */ 941 switch ( statusCode / 100 ) 942 { 943 case 1: /* Informational 1xx */ 944 syslog(LOG_ERR,"unexpected statusCode %llu", code); 945 result = ENOENT; /* CFNetwork eats 1xx responses so this should never happen */ 946 break; 947 case 2: /* Successful 2xx */ 948 result = 0; 949 break; 950 case 3: /* Redirection 3xx */ 951 syslog(LOG_ERR,"unexpected statusCode %llu", code); 952 result = ENOENT; 953 break; 954 case 4: /* Client Error 4xx */ 955 switch ( statusCode ) 956 { 957 case 401: /* 401 Unauthorized */ 958 case 402: /* Payment required */ 959 case 403: /* Forbidden */ 960 case 405: /* 405 Method Not Allowed. A few DAV servers return when folder isn't writable 961 * <rdar://problem/4294372> MSG: Error message (filename too long) displayed when that's not the issue 962 */ 963 /* 964 * We return EPERM here so that Finder exhibits the correct behavior. 965 * Returning EAUTH can result in strange behavior such as files 966 * dissappearing, as seen in: 967 * <rdar://problem/6140701> Invalid credentials leads to scary user experience 968 * 969 */ 970 syslog(LOG_DEBUG,"unexpected statusCode %llu", code); 971 result = EPERM; 972 break; 973 case 407: /* 407 Proxy Authentication Required */ 974 syslog(LOG_ERR,"unexpected statusCode %llu", code); 975 result = EAUTH; 976 break; 977 case 404: /* Not Found */ 978 case 409: /* Conflict (path prefix does not exist) */ 979 case 410: /* Gone */ 980 result = ENOENT; 981 break; 982 case 413: /* Request Entity Too Large (this server doesn't allow large PUTs) */ 983 result = EFBIG; 984 syslog(LOG_ERR,"unexpected statusCode %llu", code); 985 break; 986 case 414: /* Request URI Too Long */ 987 syslog(LOG_ERR,"unexpected statusCode %llu", code); 988 result = ENAMETOOLONG; 989 break; 990 case 416: /* Requested Range Not Available */ 991 syslog(LOG_DEBUG,"unexpected statusCode %llu", code); 992 result = EINVAL; 993 break; 994 case 423: /* Locked (WebDAV) */ 995 case 424: /* Failed Dependency (WebDAV) (EBUSY when a directory cannot be MOVE'd) */ 996 syslog(LOG_ERR,"unexpected statusCode %llu", code); 997 result = EBUSY; 998 break; 999 default: 1000 syslog(LOG_ERR,"unexpected statusCode %llu", code); 1001 result = EINVAL; /* unexpected */ 1002 break; 1003 } 1004 break; 1005 case 5: /* Server Error 5xx */ 1006 if ( statusCode == 507 ) /* Insufficient Storage (WebDAV) */ 1007 { 1008 syslog(LOG_ERR,"unexpected statusCode %llu", code); 1009 result = ENOSPC; 1010 } 1011 else 1012 { 1013 syslog(LOG_ERR,"unexpected statusCode %llu", code); 1014 result = ENOENT; /* unexpected */ 1015 } 1016 break; 1017 default: /* only the 1xx through 5xx ranges are defined */ 1018 syslog(LOG_ERR,"unexpected statusCode %llu", code); 1019 result = EIO; 1020 break; 1021 } 1022 1023 return ( result ); 1024} 1025 1026/******************************************************************************/ 1027 1028/* returns TRUE if SSL properties were correctly applied (or were not needed) */ 1029static int ApplySSLProperties(CFReadStreamRef readStreamRef) 1030{ 1031 int result = TRUE; 1032 1033 if ( gSSLPropertiesDict != NULL ) 1034 { 1035 result = (CFReadStreamSetProperty(readStreamRef, kCFStreamPropertySSLSettings, gSSLPropertiesDict) == TRUE); 1036 } 1037 1038 return ( result ); 1039} 1040 1041/******************************************************************************/ 1042 1043/* 1044 * get_ReadStreamRec 1045 * 1046 * Tries to return a ReadStreamRec that's not in use and open. If that's not 1047 * possible, returns the first ReadStreamRec that's not in use and closed. 1048 */ 1049static struct ReadStreamRec *get_ReadStreamRec(void) 1050{ 1051 int index; 1052 struct ReadStreamRec *result; 1053 int mutexerror; 1054 1055 result = NULL; 1056 1057 /* grab gNetworkGlobals_lock */ 1058 mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock); 1059 require_noerr_action(mutexerror, pthread_mutex_lock, webdav_kill(-1)); 1060 1061 for ( index = 0; index < (WEBDAV_REQUEST_THREADS + 1); ++index ) 1062 { 1063 if ( !gReadStreams[index].inUse ) 1064 { 1065 /* found one not in use */ 1066 1067 /* if it still looks open, grab it */ 1068 if ( gReadStreams[index].readStreamRef != NULL ) 1069 { 1070 result = &gReadStreams[index]; /* get pointer to it */ 1071 result->inUse = TRUE; /* mark it in use */ 1072 break; 1073 } 1074 else if ( result == NULL ) 1075 { 1076 /* else... keep track of the first closed one in case we don't find an open one */ 1077 result = &gReadStreams[index]; 1078 } 1079 } 1080 } 1081 1082 if ( result != NULL ) 1083 { 1084 /* should always happen */ 1085 result->inUse = TRUE; /* mark it in use */ 1086 } 1087 1088 /* release gNetworkGlobals_lock */ 1089 mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock); 1090 require_noerr_action(mutexerror, pthread_mutex_unlock, webdav_kill(-1)); 1091 1092pthread_mutex_unlock: 1093pthread_mutex_lock: 1094 1095 return ( result ); 1096} 1097 1098/******************************************************************************/ 1099 1100/* 1101 * release_ReadStreamRec 1102 * 1103 * Release a ReadStreamRec. 1104 */ 1105static void release_ReadStreamRec(struct ReadStreamRec *theReadStreamRec) 1106{ 1107 int mutexerror; 1108 1109 /* grab gNetworkGlobals_lock */ 1110 mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock); 1111 require_noerr_action(mutexerror, pthread_mutex_lock, webdav_kill(-1)); 1112 1113 /* release theReadStreamRec */ 1114 theReadStreamRec->inUse = FALSE; 1115 1116 /* release gNetworkGlobals_lock */ 1117 mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock); 1118 require_noerr_action(mutexerror, pthread_mutex_unlock, webdav_kill(-1)); 1119 1120pthread_mutex_unlock: 1121pthread_mutex_lock: 1122 1123 return; 1124} 1125 1126/******************************************************************************/ 1127 1128/* 1129 * returns EAGAIN if entire transaction should be retried 1130 * returns ECANCELED if the user clicked cancel in the certificate UI 1131 * returns EIO if this was not an SSL error 1132 */ 1133static int HandleSSLErrors(CFReadStreamRef readStreamRef) 1134{ 1135 CFStreamError streamError; 1136 SInt32 error; 1137 int result; 1138 1139 result = EIO; 1140 1141 streamError = CFReadStreamGetError(readStreamRef); 1142 if ( streamError.domain == kCFStreamErrorDomainSSL ) 1143 { 1144 error = streamError.error; 1145 1146 if ( gSSLPropertiesDict == NULL ) 1147 { 1148 gSSLPropertiesDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1149 require(gSSLPropertiesDict != NULL, CFDictionaryCreateMutable); 1150 } 1151 1152 /* if we haven't tried falling back from TLS to SSL and the errror indicates that might work... */ 1153 if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLLevel) == NULL) && 1154 (((error <= errSSLProtocol) && (error > errSSLXCertChainInvalid)) || 1155 ((error <= errSSLCrypto) && (error > errSSLUnknownRootCert)) || 1156 ((error <= errSSLClosedNoNotify) && (error > errSSLPeerBadCert)) || 1157 (error == errSSLIllegalParam) || 1158 ((error <= errSSLPeerAccessDenied) && (error > errSSLLast))) ) 1159 { 1160 /* retry with fall back from TLS to SSL */ 1161 CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelSSLv3); 1162 result = EAGAIN; 1163 } 1164 else 1165 { 1166 switch ( error ) 1167 { 1168 case errSSLCertExpired: 1169 case errSSLCertNotYetValid: 1170 if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLAllowsExpiredCertificates) == NULL) ) 1171 { 1172 // We are just trying to check for a proxy server, so it's okay to continue 1173 CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue); 1174 CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue); 1175 } 1176 result=EAGAIN; 1177 break; 1178 1179 case errSSLBadCert: 1180 case errSSLXCertChainInvalid: 1181 case errSSLHostNameMismatch: 1182 /* The certificate for this server is invalid */ 1183 if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLValidatesCertificateChain) == NULL) ) 1184 { 1185 CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse); 1186 } 1187 result=EAGAIN; 1188 break; 1189 1190 case errSSLUnknownRootCert: 1191 case errSSLNoRootCert: 1192 /* The certificate for this server was signed by an unknown certifying authority */ 1193 if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLAllowsAnyRoot) == NULL) ) 1194 { 1195 CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue); 1196 } 1197 result=EAGAIN; 1198 break; 1199 1200 default: 1201 result = EIO; 1202 break; 1203 } 1204 } 1205 } 1206 1207CFDictionaryCreateMutable: 1208 1209 return ( result ); 1210} 1211 1212/******************************************************************************/ 1213 1214/* 1215 * returns ETIMEDOUT for server reachability type errors 1216 * returns EXIO otherwise 1217 * returns EIO if this was not an SSL error 1218 */ 1219 1220static int stream_error_to_errno(CFStreamError *streamError) 1221{ 1222 int result = ENXIO; 1223 1224 if (streamError->domain == kCFStreamErrorDomainPOSIX) 1225 { 1226 switch (streamError->error) { 1227 case EADDRNOTAVAIL: 1228 case ENETDOWN: 1229 case ETIMEDOUT: 1230 case ECONNRESET: 1231 case ENETUNREACH: 1232 case ECONNREFUSED: 1233 /* These errors affect mobility, so return ETIMEDOUT */ 1234 syslog(LOG_ERR, "stream_error: Posix error %d", (int)streamError->error); 1235 result = ETIMEDOUT; 1236 break; 1237 default: 1238 syslog(LOG_ERR, "stream_error: Posix error %d", (int)streamError->error); 1239 result = ENXIO; 1240 break; 1241 } 1242 } 1243 else if (streamError->domain == kCFStreamErrorDomainNetDB) 1244 { 1245 switch (streamError->error) { 1246 case EAI_NODATA: 1247 /* no address associated with host name */ 1248 syslog(LOG_ERR, "stream_error: NetDB error EAI_NODATA"); 1249 result = ETIMEDOUT; 1250 break; 1251 default: 1252 syslog(LOG_ERR, "stream_error: NetDB error %d", (int)streamError->error); 1253 result = ENXIO; 1254 break; 1255 } 1256 } 1257 else { 1258 syslog(LOG_ERR, "stream_error: Domain %d Error %d", (int) streamError->domain, (int)streamError->error); 1259 result = ENXIO; 1260 } 1261 return result; 1262} 1263 1264/******************************************************************************/ 1265 1266 /* create the HTTP read stream with CFReadStreamCreateForHTTPRequest */ 1267 /* turn on automatic redirection */ 1268 /* add proxies (if any) */ 1269 /* apply any SSL properties we've already negotiated with the server */ 1270 /* open the read stream */ 1271 1272 1273 /* set the file position to 0 */ 1274 /* create a stream from the file */ 1275 /* create the HTTP read stream with CFReadStreamCreateForStreamedHTTPRequest */ 1276 /* Note: PUTs cannot be automatically redirected -- see rfc2616 section 10.3.x */ 1277 /* add proxies (if any) */ 1278 /* apply any SSL properties we've already negotiated with the server */ 1279 /* open the read stream */ 1280 1281 1282 /* create the HTTP read stream with CFReadStreamCreateForHTTPRequest */ 1283 /* (conditional) turn on automatic redirection */ 1284 /* add proxies (if any) */ 1285 /* apply any SSL properties we've already negotiated with the server */ 1286 /* open the read stream and handle SSL errors */ 1287 1288static int open_stream_for_transaction( 1289 CFHTTPMessageRef request, /* -> the request to send */ 1290 CFReadStreamRef fdStream, /* -> if not NULL, the file stream */ 1291 int auto_redirect, /* -> if TRUE, set kCFStreamPropertyHTTPShouldAutoredirect on stream */ 1292 int *retryTransaction, /* -> if TRUE, return EAGAIN on errors when streamError is kCFStreamErrorDomainPOSIX/EPIPE and set retryTransaction to FALSE */ 1293 struct ReadStreamRec **readStreamRecPtr) /* <- pointer to the ReadStreamRec in use */ 1294{ 1295 int result, error; 1296 struct ReadStreamRec *theReadStreamRec; 1297 CFReadStreamRef newReadStreamRef; 1298 CFSocketNativeHandle sock; 1299 CFDataRef sockWrapper = NULL; 1300 1301 result = error = 0; 1302 *readStreamRecPtr = NULL; 1303 1304 /* create the HTTP read stream */ 1305 if ( fdStream != NULL ) 1306 { 1307 newReadStreamRef = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request, fdStream); 1308 require(newReadStreamRef != NULL, CFReadStreamCreateForStreamedHTTPRequest); 1309 } 1310 else 1311 { 1312 newReadStreamRef = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request); 1313 require(newReadStreamRef != NULL, CFReadStreamCreateForHTTPRequest); 1314 } 1315 1316 /* add persistent property */ 1317 CFReadStreamSetProperty(newReadStreamRef, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); 1318 1319 /* turn on automatic redirection */ 1320 if ( auto_redirect ) 1321 { 1322 require(CFReadStreamSetProperty(newReadStreamRef, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue) != FALSE, SetAutoredirectProperty); 1323 } 1324 1325 /* add proxies (if any) */ 1326 require_quiet(set_global_stream_properties(newReadStreamRef) == 0, set_global_stream_properties); 1327 1328 /* apply any SSL properties we've already negotiated with the server */ 1329 ApplySSLProperties(newReadStreamRef); 1330 1331 /* get a ReadStreamRec that was not in use */ 1332 theReadStreamRec = get_ReadStreamRec(); 1333 1334 /* (after unlocking) make sure we got a ReadStreamRec */ 1335 require(theReadStreamRec != NULL, get_ReadStreamRec); 1336 1337 /* add the unique property from the ReadStreamRec to newReadStreamRef */ 1338 require(CFReadStreamSetProperty(newReadStreamRef, CFSTR("WebdavConnectionNumber"), theReadStreamRec->uniqueValue) != FALSE, SetWebdavConnectionNumberProperty); 1339 1340 /* open the read stream and handle SSL errors */ 1341 if ( CFReadStreamOpen(newReadStreamRef) == FALSE ) 1342 { 1343 result = HandleSSLErrors(newReadStreamRef); 1344 if ( result != EAGAIN ) 1345 { 1346 CFStreamError streamError; 1347 1348 streamError = CFReadStreamGetError(newReadStreamRef); 1349 if ( *retryTransaction && 1350 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 1351 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) ) 1352 { 1353 /* if we get a POSIX EPIPE or HTTP Connection Lost error back from the stream, retry the transaction once */ 1354 syslog(LOG_INFO,"open_stream_for_transaction: CFStreamError: domain %ld, error %lld -- retrying", streamError.domain, (SInt64)streamError.error); 1355 *retryTransaction = FALSE; 1356 result = EAGAIN; 1357 } 1358 else 1359 { 1360 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 1361 { 1362 syslog(LOG_ERR,"open_stream_for_transaction: CFStreamError: domain %ld, error %lld", streamError.domain, (int64_t)streamError.error); 1363 } 1364 set_connectionstate(WEBDAV_CONNECTION_DOWN); 1365 result = stream_error_to_errno(&streamError); 1366 } 1367 } 1368 goto CFReadStreamOpen; 1369 } 1370 1371 /* close and release old read stream */ 1372 if ( theReadStreamRec->readStreamRef != NULL ) 1373 { 1374 CFReadStreamClose(theReadStreamRec->readStreamRef); 1375 CFRelease(theReadStreamRec->readStreamRef); 1376 } 1377 1378 /* Set SO_NOADDRERR on the socket so we will know about EADDRNOTAVAIL errors ASAP */ 1379 sockWrapper = (CFDataRef)CFReadStreamCopyProperty(newReadStreamRef, kCFStreamPropertySocketNativeHandle); 1380 1381 if (sockWrapper) 1382 { 1383 CFRange r = {0, sizeof(CFSocketNativeHandle)}; 1384 CFDataGetBytes(sockWrapper, r, (UInt8 *)&sock); 1385 CFRelease(sockWrapper); 1386 int flag = 1; 1387 setsockopt(sock, SOL_SOCKET, SO_NOADDRERR, &flag, (socklen_t)sizeof(flag)); 1388 } 1389 1390 /* save new read stream */ 1391 theReadStreamRec->readStreamRef = newReadStreamRef; 1392 1393 /* return the ReadStreamRec to the caller */ 1394 *readStreamRecPtr = theReadStreamRec; 1395 1396 return ( 0 ); 1397 1398 /**********************/ 1399 1400CFReadStreamOpen: 1401SetWebdavConnectionNumberProperty: 1402 1403 release_ReadStreamRec(theReadStreamRec); 1404 1405get_ReadStreamRec: 1406set_global_stream_properties: 1407SetAutoredirectProperty: 1408 1409 CFRelease(newReadStreamRef); 1410 1411CFReadStreamCreateForHTTPRequest: 1412CFReadStreamCreateForStreamedHTTPRequest: 1413 1414 *readStreamRecPtr = NULL; 1415 if ( result == 0 ) 1416 { 1417 result = EIO; 1418 } 1419 1420 return ( result ); 1421} 1422 1423/******************************************************************************/ 1424 1425/* 1426 * stream_get_transaction 1427 * 1428 * Creates an HTTP stream, sends the request and returns the response and response body. 1429 */ 1430static int stream_get_transaction( 1431 CFHTTPMessageRef request, /* -> the request to send */ 1432 int *retryTransaction, /* -> if TRUE, return EAGAIN on errors when streamError is kCFStreamErrorDomainPOSIX/EPIPE and set retryTransaction to FALSE */ 1433 struct node_entry *node, /* -> node to get into */ 1434 CFHTTPMessageRef *response) 1435{ 1436 struct ReadStreamRec *readStreamRecPtr; 1437 UInt8 *buffer; 1438 CFIndex totalRead; 1439 CFIndex bytesRead; 1440 CFTypeRef theResponsePropertyRef; 1441 int background_load; 1442 CFStringRef connectionHeaderRef; 1443 CFStringRef setCookieHeaderRef; 1444 CFHTTPMessageRef responseMessage; 1445 int result; 1446 1447 result = 0; 1448 1449 /* 1450 * If we're down and the mount is supposed to fail on disconnects 1451 * instead of retrying, just return an error. 1452 */ 1453 require_quiet(!gSuppressAllUI || (get_connectionstate() == WEBDAV_CONNECTION_UP), connection_down); 1454 1455 result = open_stream_for_transaction(request, NULL, TRUE, retryTransaction, &readStreamRecPtr); 1456 require_noerr_quiet(result, open_stream_for_transaction); 1457 1458 /* malloc a buffer big enough for first read */ 1459 buffer = malloc(first_read_len); 1460 require(buffer != NULL, malloc_buffer); 1461 1462 /* Send the message and get up to first_read_len bytes of response */ 1463 totalRead = 0; 1464 background_load = FALSE; 1465 while ( 1 ) 1466 { 1467 bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer + totalRead, first_read_len - totalRead); 1468 if ( bytesRead > 0 ) 1469 { 1470 totalRead += bytesRead; 1471 if ( totalRead >= first_read_len ) 1472 { 1473 /* is there more data to read? */ 1474 if ( CFReadStreamGetStatus(readStreamRecPtr->readStreamRef) == kCFStreamStatusAtEnd ) 1475 { 1476 /* there are no more bytes to read */ 1477 background_load = FALSE; 1478 } 1479 else 1480 { 1481 /* we'll need to hand off to get the rest of the file data */ 1482 background_load = TRUE; 1483 } 1484 break; 1485 } 1486 } 1487 else if ( bytesRead == 0 ) 1488 { 1489 /* there are no more bytes to read */ 1490 background_load = FALSE; 1491 break; 1492 } 1493 else 1494 { 1495 CFStreamError streamError; 1496 1497 streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef); 1498 if ( *retryTransaction && 1499 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 1500 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) ) 1501 { 1502 /* if we get a POSIX EPIPE or HTTP Connection Lost error back from the stream, retry the transaction once */ 1503 syslog(LOG_INFO,"stream_get_transaction: CFStreamError: domain %ld, error %lld -- retrying", streamError.domain, (SInt64)streamError.error); 1504 *retryTransaction = FALSE; 1505 result = EAGAIN; 1506 } 1507 else 1508 { 1509 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 1510 { 1511 syslog(LOG_ERR,"stream_get_transaction: CFStreamError: domain %ld, error %lld", streamError.domain, (SInt64)streamError.error); 1512 } 1513 set_connectionstate(WEBDAV_CONNECTION_DOWN); 1514 result = stream_error_to_errno(&streamError); 1515 } 1516 goto CFReadStreamRead; 1517 } 1518 }; 1519 1520 /* get the response header */ 1521 theResponsePropertyRef = CFReadStreamCopyProperty(readStreamRecPtr->readStreamRef, kCFStreamPropertyHTTPResponseHeader); 1522 require(theResponsePropertyRef != NULL, GetResponseHeader); 1523 1524 /* fun with casting a "const void *" CFTypeRef away */ 1525 responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef)); 1526 1527 /* 1528 * if WEBDAV_DOWNLOAD_NEVER 1529 * download entire file 1530 * else if WEBDAV_DOWNLOAD_FINISHED 1531 * then use If-Modified-Since: node->file_last_modified date 1532 * //and If-None-Match node->file_entity_tag to the cache file 1533 * 200 = getting whole file 1534 * 304 = not modified; current copy is OK 1535 * //else if has node->file_entity_tag and NOT weak 1536 * // download partial file with If-Range: node->file_entity_tag 1537 * // 206 = getting remainder 1538 * // 200 = getting whole file 1539 * else //if has node->file_last_modified 1540 * download partial file with If-Range: node->file_last_modified date 1541 * 206 = getting remainder 1542 * 200 = getting whole file 1543 */ 1544 /* get the status code */ 1545 switch ( CFHTTPMessageGetResponseStatusCode(responseMessage) ) 1546 { 1547 case 200: /* OK - download whole file from beginning */ 1548 /* clear the flags */ 1549 require(fchflags(node->file_fd, 0) == 0, fchflags); 1550 /* truncate the file */ 1551 require_action(ftruncate(node->file_fd, 0LL) != -1, ftruncate, syslog(LOG_ERR,"errno %d", errno)); 1552 /* reset the position to 0 */ 1553 require(lseek(node->file_fd, 0LL, SEEK_SET) >= 0, lseek); 1554 /* write the bytes in buffer to the cache file*/ 1555 require(write(node->file_fd, buffer, (size_t)totalRead) == (ssize_t)totalRead, write); 1556 1557 // If file is large, turn off data caching 1558 if (node->attr_stat_info.attr_stat.st_size > (off_t)webdavCacheMaximumSize) { 1559 fcntl(node->file_fd, F_NOCACHE, 1); 1560 } 1561 break; 1562 1563 case 206: /* Partial Content - download from EOF */ 1564 /* clear the flags */ 1565 require(fchflags(node->file_fd, 0) == 0, fchflags); 1566 /* seek to EOF */ 1567 require(lseek(node->file_fd, 0LL, SEEK_END) >= 0, lseek); 1568 /* write the bytes in buffer to the cache file*/ 1569 require(write(node->file_fd, buffer, (size_t)totalRead) >= 0, write); 1570 break; 1571 1572 case 304: /* Not Modified - the cache file is still good */ 1573 background_load = FALSE; 1574 break; 1575 1576 default: 1577 background_load = FALSE; 1578 break; 1579 } 1580 1581 free(buffer); 1582 buffer = NULL; 1583 1584 set_connectionstate(WEBDAV_CONNECTION_UP); 1585 1586 /* Get the Connection header (if any) */ 1587 readStreamRecPtr->connectionClose = FALSE; 1588 connectionHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Connection")); 1589 if ( connectionHeaderRef != NULL ) 1590 { 1591 /* is the connection-token is "close"? */ 1592 if ( CFStringCompare(connectionHeaderRef, CFSTR("close"), kCFCompareCaseInsensitive) == kCFCompareEqualTo ) 1593 { 1594 /* yes -- then the server closed this connection, so close and release the read stream now */ 1595 readStreamRecPtr->connectionClose = TRUE; 1596 } 1597 CFRelease(connectionHeaderRef); 1598 } 1599 1600 // Handle cookies 1601 setCookieHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Set-Cookie")); 1602 if (setCookieHeaderRef != NULL) { 1603 handle_cookies(setCookieHeaderRef, request); 1604 CFRelease(setCookieHeaderRef); 1605 } 1606 1607 1608 if ( background_load ) 1609 { 1610 int error; 1611 /* 1612 * As a hack, set the NODUMP bit so that the kernel 1613 * knows that we are in the process of filling up the file 1614 */ 1615 require(fchflags(node->file_fd, UF_NODUMP) == 0, fchflags); 1616 1617 node->file_status = WEBDAV_DOWNLOAD_IN_PROGRESS; 1618 1619 /* pass the node and readStreamRef off to another thread to finish */ 1620 error = requestqueue_enqueue_download(node, readStreamRecPtr); 1621 require_noerr_quiet(error, webdav_requestqueue_enqueue_new_download); 1622 } 1623 else 1624 { 1625 node->file_status = WEBDAV_DOWNLOAD_FINISHED; 1626 1627 if ( readStreamRecPtr->connectionClose ) 1628 { 1629 /* close and release the stream */ 1630 CFReadStreamClose(readStreamRecPtr->readStreamRef); 1631 CFRelease(readStreamRecPtr->readStreamRef); 1632 readStreamRecPtr->readStreamRef = NULL; 1633 } 1634 1635 /* make this ReadStreamRec is available again */ 1636 release_ReadStreamRec(readStreamRecPtr); 1637 } 1638 1639 *response = responseMessage; 1640 1641 return ( 0 ); 1642 1643 /**********************/ 1644 1645webdav_requestqueue_enqueue_new_download: 1646fchflags: 1647ftruncate: 1648lseek: 1649write: 1650GetResponseHeader: 1651CFReadStreamRead: 1652 1653 if ( buffer != NULL ) 1654 { 1655 free(buffer); 1656 } 1657 1658malloc_buffer: 1659 1660 /* close and release the read stream on errors */ 1661 CFReadStreamClose(readStreamRecPtr->readStreamRef); 1662 CFRelease(readStreamRecPtr->readStreamRef); 1663 readStreamRecPtr->readStreamRef = NULL; 1664 1665 /* make this ReadStreamRec is available again */ 1666 release_ReadStreamRec(readStreamRecPtr); 1667 1668open_stream_for_transaction: 1669connection_down: 1670 1671 *response = NULL; 1672 1673 if ( result == 0 ) 1674 { 1675 result = EIO; 1676 } 1677 1678 return ( result ); 1679} 1680 1681/******************************************************************************/ 1682 1683/* 1684 * stream_transaction_from_file 1685 * 1686 * Creates an HTTP stream with the read stream coming from file_fd, 1687 * sends the request and returns the response. The response body (if any) is 1688 * read and disposed. 1689 */ 1690static int stream_transaction_from_file( 1691 CFHTTPMessageRef request, 1692 int file_fd, 1693 int *retryTransaction, /* -> if TRUE, return EAGAIN on errors when streamError is kCFStreamErrorDomainPOSIX/EPIPE and set retryTransaction to FALSE */ 1694 CFHTTPMessageRef *response) 1695{ 1696 CFReadStreamRef fdStream; 1697 struct ReadStreamRec *readStreamRecPtr; 1698 void *buffer; 1699 CFIndex bytesRead; 1700 CFTypeRef theResponsePropertyRef; 1701 off_t contentLength; 1702 CFStringRef contentLengthString; 1703 CFStringRef connectionHeaderRef; 1704 CFStringRef setCookieHeaderRef; 1705 CFHTTPMessageRef responseMessage; 1706 int result; 1707 1708 result = 0; 1709 1710 /* 1711 * If we're down and the mount is supposed to fail on disconnects 1712 * instead of retrying, just return an error. 1713 */ 1714 require_quiet(!gSuppressAllUI || (get_connectionstate() == WEBDAV_CONNECTION_UP), connection_down); 1715 1716 /* get the file length */ 1717 contentLength = lseek(file_fd, 0LL, SEEK_END); 1718 require(contentLength != -1, lseek); 1719 1720 /* create a string with the file length for the Content-Length header */ 1721 contentLengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%qd"), contentLength); 1722 /* CFReadStreamCreateForStreamedHTTPRequest will use chunked transfer-encoding if the Content-Length header cannot be provided */ 1723 if ( contentLengthString != NULL ) 1724 { 1725 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Length"), contentLengthString); 1726 CFRelease(contentLengthString); 1727 } 1728 1729 /* set the file position to 0 */ 1730 verify(lseek(file_fd, 0LL, SEEK_SET) != -1); 1731 1732 /* create a stream from the file */ 1733 CFStreamCreatePairWithSocket(kCFAllocatorDefault, file_fd, &fdStream, NULL); 1734 require(fdStream != NULL, CFReadStreamCreateWithFile); 1735 1736 result = open_stream_for_transaction(request, fdStream, FALSE, retryTransaction, &readStreamRecPtr); 1737 require_noerr_quiet(result, open_stream_for_transaction); 1738 1739 /* malloc a buffer big enough for most responses */ 1740 buffer = malloc(BODY_BUFFER_SIZE); 1741 require(buffer != NULL, malloc_currentbuffer); 1742 1743 /* Send the message and eat the response */ 1744 while ( 1 ) 1745 { 1746 bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer, BODY_BUFFER_SIZE); 1747 if ( bytesRead > 0 ) 1748 { 1749 continue; 1750 } 1751 else if ( bytesRead == 0 ) 1752 { 1753 /* there are no more bytes to read */ 1754 break; 1755 } 1756 else 1757 { 1758 CFStreamError streamError; 1759 1760 streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef); 1761 if ( *retryTransaction && ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 1762 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) ) 1763 { 1764 /* if we get a POSIX errror back from the stream, retry the transaction once */ 1765 syslog(LOG_INFO,"stream_transaction_from_file: CFStreamError: domain %ld, error %lld -- retrying", streamError.domain, (SInt64)streamError.error); 1766 *retryTransaction = FALSE; 1767 result = EAGAIN; 1768 } 1769 else 1770 { 1771 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 1772 { 1773 syslog(LOG_ERR,"stream_transaction_from_file: CFStreamError: domain %ld, error %lld", streamError.domain, (SInt64)streamError.error); 1774 } 1775 set_connectionstate(WEBDAV_CONNECTION_DOWN); 1776 result = stream_error_to_errno(&streamError); 1777 } 1778 goto CFReadStreamRead; 1779 } 1780 }; 1781 1782 free(buffer); 1783 1784 /* get the response header */ 1785 theResponsePropertyRef = CFReadStreamCopyProperty(readStreamRecPtr->readStreamRef, kCFStreamPropertyHTTPResponseHeader); 1786 require(theResponsePropertyRef != NULL, GetResponseHeader); 1787 1788 /* fun with casting a "const void *" CFTypeRef away */ 1789 responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef)); 1790 1791 set_connectionstate(WEBDAV_CONNECTION_UP); 1792 1793 /* Get the Connection header (if any) */ 1794 connectionHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Connection")); 1795 /* is the connection-token is "close"? */ 1796 if ( connectionHeaderRef != NULL ) 1797 { 1798 if ( CFStringCompare(connectionHeaderRef, CFSTR("close"), kCFCompareCaseInsensitive) == kCFCompareEqualTo ) 1799 { 1800 /* yes -- then the server closed this connection, so close and release the read stream now */ 1801 CFReadStreamClose(readStreamRecPtr->readStreamRef); 1802 CFRelease(readStreamRecPtr->readStreamRef); 1803 readStreamRecPtr->readStreamRef = NULL; 1804 } 1805 CFRelease(connectionHeaderRef); 1806 } 1807 1808 // Handle cookies 1809 setCookieHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Set-Cookie")); 1810 if (setCookieHeaderRef != NULL) { 1811 handle_cookies(setCookieHeaderRef, request); 1812 CFRelease(setCookieHeaderRef); 1813 } 1814 1815 CFRelease(fdStream); 1816 1817 /* make this ReadStreamRec is available again */ 1818 release_ReadStreamRec(readStreamRecPtr); 1819 1820 /* fun with casting a "const void *" CFTypeRef away */ 1821 *response = responseMessage; 1822 1823 return ( 0 ); 1824 1825 /**********************/ 1826 1827CFReadStreamRead: 1828 1829 free(buffer); 1830 1831GetResponseHeader: 1832malloc_currentbuffer: 1833 1834 CFReadStreamClose(readStreamRecPtr->readStreamRef); 1835 CFRelease(readStreamRecPtr->readStreamRef); 1836 readStreamRecPtr->readStreamRef = NULL; 1837 1838 /* make this ReadStreamRec is available again */ 1839 release_ReadStreamRec(readStreamRecPtr); 1840 1841 CFRelease(fdStream); 1842 1843CFReadStreamCreateWithFile: 1844 1845lseek: 1846open_stream_for_transaction: 1847connection_down: 1848 1849 *response = NULL; 1850 1851 if ( result == 0 ) 1852 { 1853 result = EIO; 1854 } 1855 1856 return ( result ); 1857} 1858 1859/******************************************************************************/ 1860 1861/* 1862 * stream_transaction 1863 * 1864 * Creates an HTTP stream, sends the request and returns the response and response body. 1865 */ 1866static int stream_transaction( 1867 CFHTTPMessageRef request, /* -> the request to send */ 1868 int auto_redirect, /* -> if TRUE, set kCFStreamPropertyHTTPShouldAutoredirect on stream */ 1869 int *retryTransaction, /* -> if TRUE, return EAGAIN on errors when streamError is kCFStreamErrorDomainPOSIX/EPIPE and set retryTransaction to FALSE */ 1870 UInt8 **buffer, /* <- response data buffer (caller responsible for freeing) */ 1871 CFIndex *count, /* <- response data buffer length */ 1872 CFHTTPMessageRef *response) /* <- the response message */ 1873{ 1874 struct ReadStreamRec *readStreamRecPtr; 1875 CFIndex totalRead; 1876 UInt8 *currentbuffer; 1877 UInt8 *newBuffer; 1878 CFIndex bytesRead; 1879 CFIndex bytesToRead; 1880 CFIndex bufferSize; 1881 CFTypeRef theResponsePropertyRef; 1882 CFStringRef connectionHeaderRef; 1883 CFStringRef setCookieHeaderRef; 1884 CFHTTPMessageRef responseMessage; 1885 int result; 1886 1887 result = 0; 1888 1889 /* 1890 * If we're down and the mount is supposed to fail on disconnects 1891 * instead of retrying, just return an error. 1892 */ 1893 require_quiet(!gSuppressAllUI || (get_connectionstate() == WEBDAV_CONNECTION_UP), connection_down); 1894 1895 /* get an open ReadStreamRec */ 1896 result = open_stream_for_transaction(request, NULL, auto_redirect, retryTransaction, &readStreamRecPtr); 1897 require_noerr_quiet(result, open_stream_for_transaction); 1898 1899 /* malloc a buffer big enough for most responses */ 1900 bufferSize = BODY_BUFFER_SIZE; 1901 currentbuffer = malloc(bufferSize); 1902 require(currentbuffer != NULL, malloc_currentbuffer); 1903 1904 /* Send the message and get the response */ 1905 totalRead = 0; 1906 while ( 1 ) 1907 { 1908 bytesToRead = bufferSize - totalRead; 1909 bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, currentbuffer + totalRead, bytesToRead); 1910 if ( bytesRead > 0 ) 1911 { 1912 totalRead += bytesRead; 1913 1914 /* is currentbuffer getting close to full? */ 1915 if ( (bytesToRead - bytesRead) < (BODY_BUFFER_SIZE / 2) ) 1916 { 1917 /* yes, so get larger currentbuffer for next read */ 1918 bufferSize += BODY_BUFFER_SIZE; 1919 newBuffer = realloc(currentbuffer, bufferSize); 1920 require(newBuffer != NULL, realloc); 1921 1922 currentbuffer = newBuffer; 1923 } 1924 } 1925 else if ( bytesRead == 0 ) 1926 { 1927 /* there are no more bytes to read */ 1928 break; 1929 } 1930 else 1931 { 1932 result = HandleSSLErrors(readStreamRecPtr->readStreamRef); 1933 if ( result != EAGAIN ) 1934 { 1935 if ( result != ECANCELED ) 1936 { 1937 CFStreamError streamError; 1938 1939 streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef); 1940 if ( *retryTransaction && 1941 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 1942 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) ) 1943 { 1944 /* if we get a POSIX EPIPE or HTTP Connection Lost error back from the stream, retry the transaction once */ 1945 syslog(LOG_INFO,"stream_transaction: CFStreamError: domain %ld, error %lld -- retrying", streamError.domain, (SInt64)streamError.error); 1946 *retryTransaction = FALSE; 1947 result = EAGAIN; 1948 } 1949 else 1950 { 1951 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 1952 { 1953 syslog(LOG_ERR,"stream_transaction: CFStreamError: domain %ld, error %lld", streamError.domain, (SInt64)streamError.error); 1954 } 1955 set_connectionstate(WEBDAV_CONNECTION_DOWN); 1956 result = stream_error_to_errno(&streamError); 1957 } 1958 } 1959 } 1960 goto CFReadStreamRead; 1961 } 1962 }; 1963 1964 /* get the response header */ 1965 theResponsePropertyRef = CFReadStreamCopyProperty(readStreamRecPtr->readStreamRef, kCFStreamPropertyHTTPResponseHeader); 1966 require(theResponsePropertyRef != NULL, GetResponseHeader); 1967 1968 /* fun with casting a "const void *" CFTypeRef away */ 1969 responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef)); 1970 1971 set_connectionstate(WEBDAV_CONNECTION_UP); 1972 1973 /* Get the Connection header (if any) */ 1974 connectionHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Connection")); 1975 /* is the connection-token is "close"? */ 1976 if ( connectionHeaderRef != NULL ) 1977 { 1978 if ( CFStringCompare(connectionHeaderRef, CFSTR("close"), kCFCompareCaseInsensitive) == kCFCompareEqualTo ) 1979 { 1980 /* yes -- then the server closed this connection, so close and release the read stream now */ 1981 CFReadStreamClose(readStreamRecPtr->readStreamRef); 1982 CFRelease(readStreamRecPtr->readStreamRef); 1983 readStreamRecPtr->readStreamRef = NULL; 1984 } 1985 CFRelease(connectionHeaderRef); 1986 } 1987 1988 // Handle cookies 1989 setCookieHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Set-Cookie")); 1990 if (setCookieHeaderRef != NULL) { 1991 handle_cookies(setCookieHeaderRef, request); 1992 CFRelease(setCookieHeaderRef); 1993 } 1994 1995 /* make this ReadStreamRec is available again */ 1996 release_ReadStreamRec(readStreamRecPtr); 1997 1998 *response = responseMessage; 1999 *count = totalRead; 2000 *buffer = currentbuffer; 2001 2002 return ( 0 ); 2003 2004 /**********************/ 2005 2006GetResponseHeader: 2007CFReadStreamRead: 2008realloc: 2009 2010 free(currentbuffer); 2011 2012malloc_currentbuffer: 2013 2014 /* close and release the read stream on errors */ 2015 CFReadStreamClose(readStreamRecPtr->readStreamRef); 2016 CFRelease(readStreamRecPtr->readStreamRef); 2017 readStreamRecPtr->readStreamRef = NULL; 2018 2019 /* make this ReadStreamRec is available again */ 2020 release_ReadStreamRec(readStreamRecPtr); 2021 2022open_stream_for_transaction: 2023connection_down: 2024 2025 *response = NULL; 2026 *count = 0; 2027 *buffer = NULL; 2028 if ( result == 0 ) 2029 { 2030 result = EIO; 2031 } 2032 2033 return ( result ); 2034} 2035 2036/******************************************************************************/ 2037 2038/* 2039 * send_transaction 2040 * 2041 * Creates a request, adds the message body, headers and authentication if needed, 2042 * and then calls stream_transaction() to send the request to the server and get 2043 * the server's response. If the caller requests the response body and/or the 2044 * response message, they are returned. Otherwise, they are freed/released. 2045 * 2046 * The 'node' parameter is needed for handling http redirects: 2047 * auto_redirect true - node involved in the transaction, NULL if root node. 2048 * auto_redirect false - node is not used. 2049 */ 2050static int send_transaction( 2051 uid_t uid, /* -> uid of the user making the request */ 2052 CFURLRef url, /* -> url to the resource */ 2053 struct node_entry *node, /* <- the node involved in the transaction (needed to handle http redirects if auto_redirect if false) */ 2054 CFStringRef requestMethod, /* -> the request method */ 2055 CFDataRef bodyData, /* -> message body data, or NULL if no body */ 2056 CFIndex headerCount, /* -> number of headers */ 2057 struct HeaderFieldValue *headers, /* -> pointer to array of struct HeaderFieldValue, or NULL if none */ 2058 enum RedirectAction redirectAction, /* -> specifies how to handle http 3xx redirection */ 2059 UInt8 **buffer, /* <- if not NULL, response data buffer is returned here (caller responsible for freeing) */ 2060 CFIndex *count, /* <- if not NULL, response data buffer length is returned here*/ 2061 CFHTTPMessageRef *response) /* <- if not NULL, response is returned here */ 2062{ 2063 int error; 2064 CFIndex i; 2065 struct HeaderFieldValue *headerPtr; 2066 CFHTTPMessageRef message; 2067 CFHTTPMessageRef responseRef; 2068 CFIndex statusCode; 2069 UInt32 auth_generation; 2070 UInt8 *responseBuffer; 2071 CFIndex responseBufferLength; 2072 int retryTransaction; 2073 int auto_redirect; 2074 2075 error = 0; 2076 responseBuffer = NULL; 2077 responseBufferLength = 0; 2078 message = NULL; 2079 responseRef = NULL; 2080 statusCode = 0; 2081 auth_generation = 0; 2082 retryTransaction = TRUE; 2083 2084 if (redirectAction == REDIRECT_AUTO) 2085 auto_redirect = TRUE; 2086 else 2087 auto_redirect = FALSE; 2088 2089 /* the transaction/authentication loop */ 2090 do 2091 { 2092 /* release message if left from previous loop */ 2093 if ( message != NULL ) 2094 { 2095 CFRelease(message); 2096 message = NULL; 2097 } 2098 /* create a CFHTTP message object */ 2099 message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, url, kCFHTTPVersion1_1); 2100 require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO); 2101 2102 /* set the message body (if any) */ 2103 if ( bodyData != NULL ) 2104 { 2105 CFHTTPMessageSetBody(message, bodyData); 2106 } 2107 2108 /* change the User-Agent header */ 2109 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("User-Agent"), userAgentHeaderValue); 2110 2111 /* add the X-Source-Id header if needed */ 2112 if ( (X_Source_Id_HeaderValue != NULL) && (auto_redirect == false)) 2113 { 2114 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("X-Source-Id"), X_Source_Id_HeaderValue); 2115 } 2116 2117 /* add the X-Apple-Realm-Support header if needed */ 2118 if ( X_Apple_Realm_Support_HeaderValue != NULL ) 2119 { 2120 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("X-Apple-Realm-Support"), X_Apple_Realm_Support_HeaderValue); 2121 } 2122 2123 /* add cookies (if any) */ 2124 add_cookie_headers(message, url); 2125 2126 /* add other HTTP headers (if any) */ 2127 for ( i = 0, headerPtr = headers; i < headerCount; ++i, ++headerPtr ) 2128 { 2129 CFHTTPMessageSetHeaderFieldValue(message, headerPtr->headerField, headerPtr->value); 2130 } 2131 2132 /* apply credentials (if any) */ 2133 /* 2134 * statusCode will be 401 or 407 and responseRef will not be NULL if we've already been through the loop; 2135 * statusCode will be 0 and responseRef will be NULL if this is the first time through. 2136 */ 2137 error = authcache_apply(uid, message, (UInt32)statusCode, responseRef, &auth_generation); 2138 if ( error != 0 ) 2139 { 2140 break; 2141 } 2142 2143 /* stream_transaction returns responseRef and responseBuffer so release them if left from previous loop */ 2144 if ( responseBuffer != NULL ) 2145 { 2146 free(responseBuffer); 2147 responseBuffer = NULL; 2148 responseBufferLength = 0; 2149 } 2150 if ( responseRef != NULL ) 2151 { 2152 CFRelease(responseRef); 2153 responseRef = NULL; 2154 } 2155 /* now that everything's ready to send, send it */ 2156 error = stream_transaction(message, auto_redirect, &retryTransaction, &responseBuffer, &responseBufferLength, &responseRef); 2157 if ( error == EAGAIN ) 2158 { 2159 statusCode = 0; 2160 /* responseRef will be left NULL on retries */ 2161 } 2162 else 2163 { 2164 if ( error != 0 ) 2165 { 2166 break; 2167 } 2168 2169 /* get the status code */ 2170 statusCode = CFHTTPMessageGetResponseStatusCode(responseRef); 2171 2172 if ( (redirectAction == REDIRECT_MANUAL) && ((statusCode / 100) == 3)) { 2173 // Handle a 3XX Redirection 2174 error = nodecache_redirect_node(url, node, responseRef, statusCode); 2175 if (!error) { 2176 // Let the caller know the node was redirected 2177 error = EDESTADDRREQ; 2178 break; 2179 } 2180 } 2181 } 2182 } while ( error == EAGAIN || statusCode == 401 || statusCode == 407 ); 2183 2184CFHTTPMessageCreateRequest: 2185 2186 if ( error == 0 ) 2187 { 2188 error = (int)translate_status_to_error((UInt32)statusCode); 2189 if ( error == 0 ) 2190 { 2191 /* 2192 * when we get here with no errors, then we need to tell the authcache the 2193 * transaction worked so it can mark the credentials valid and, if needed 2194 * add the credentials to the keychain. If the auth_generation changed, then 2195 * another transaction updated the authcache element after we got it. 2196 */ 2197 (void) authcache_valid(uid, message, auth_generation); 2198 } 2199 else 2200 { 2201 if ( responseBuffer != NULL ) 2202 { 2203 free(responseBuffer); 2204 responseBuffer = NULL; 2205 } 2206 } 2207 } 2208 2209 if ( message != NULL ) 2210 { 2211 CFRelease(message); 2212 } 2213 2214 /* return requested output parameters */ 2215 if ( buffer != NULL ) 2216 { 2217 *buffer = responseBuffer; 2218 } 2219 else 2220 { 2221 if ( responseBuffer != NULL ) 2222 { 2223 free(responseBuffer); 2224 } 2225 } 2226 2227 if ( count != NULL ) 2228 { 2229 *count = responseBufferLength; 2230 } 2231 2232 if ( response != NULL ) 2233 { 2234 /* the caller wants the response and will release it */ 2235 *response = responseRef; 2236 } 2237 else 2238 { 2239 if ( responseRef != NULL ) 2240 { 2241 CFRelease(responseRef); 2242 } 2243 } 2244 2245 return ( error ); 2246} 2247 2248/*****************************************************************************/ 2249 2250/* 2251 * ParseDAVLevel parses a DAV header's field-value (if any) to get the DAV level. 2252 * Input: 2253 * responsePropertyRef the response message 2254 * Outputs: 2255 * dav_level the dav_level (0 = DAV not supported) 2256 * 2257 * The rules for message headers are (rfc 2518, section 9.1): 2258 * 2259 * DAV = "DAV" ":" "1" ["," "2"] ["," 1#extend] 2260 * extend = Coded-URL | token 2261 * 2262 * (Note: The rules for extend are still not in the rfc - they were taken from 2263 * messages in the WebDAV discussion list and are needed to interoperability 2264 * with Apache 2.0 servers which put Coded-URLs in DAV headers.) 2265 */ 2266static void ParseDAVLevel(CFHTTPMessageRef responsePropertyRef, int *dav_level) 2267{ 2268 CFStringRef davHeaderRef; 2269 const char *field_value; 2270 char buffer[4096]; 2271 const char *token; 2272 2273 *dav_level = 0; 2274 2275 davHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responsePropertyRef, CFSTR("DAV")); 2276 if ( davHeaderRef ) 2277 { 2278 field_value = CFStringGetCStringPtr(davHeaderRef, kCFStringEncodingUTF8); 2279 if ( field_value == NULL ) 2280 { 2281 if ( CFStringGetCString(davHeaderRef, buffer, 4096, kCFStringEncodingUTF8) ) 2282 { 2283 field_value = buffer; 2284 } 2285 } 2286 CFRelease(davHeaderRef); 2287 2288 if ( field_value != NULL ) 2289 { 2290 while ( *field_value != '\0' ) 2291 { 2292 /* find first non-LWS character */ 2293 field_value = SkipLWS(field_value); 2294 if ( *field_value == '\0' ) 2295 { 2296 /* if we're at end of string, break out of main while loop */ 2297 break; 2298 } 2299 2300 /* is value a token or a Coded-URL? */ 2301 if ( *field_value == '<' ) 2302 { 2303 /* it's a Coded-URL, so eat it */ 2304 2305 /* skip over '<' */ 2306 ++field_value; 2307 2308 /* find '>"' marking the end of the quoted-string */ 2309 field_value = SkipCodedURL(field_value); 2310 2311 if ( *field_value != '\0' ) 2312 { 2313 /* skip over '>' */ 2314 ++field_value; 2315 } 2316 } 2317 else 2318 { 2319 /* it's a token */ 2320 2321 /* mark start of the value token */ 2322 token = field_value; 2323 2324 /* find the end of the value token */ 2325 field_value = SkipToken(field_value); 2326 2327 /* could this token be '1' or '2'? */ 2328 if ( (field_value - token) == 1 ) 2329 { 2330 if ( (*token == '1') && (*dav_level < 1) ) 2331 { 2332 *dav_level = 1; 2333 } 2334 else if ( *token == '2' && (*dav_level < 2) ) 2335 { 2336 *dav_level = 2; 2337 } 2338 } 2339 } 2340 2341 /* skip over LWS (if any) */ 2342 field_value = SkipLWS(field_value); 2343 2344 /* if there's any string left after the LWS... */ 2345 if ( *field_value != '\0' ) 2346 { 2347 /* we should have found a comma */ 2348 if ( *field_value != ',' ) 2349 { 2350 /* if not, break out of main while loop */ 2351 break; 2352 } 2353 2354 /* skip over one or more commas */ 2355 while ( *field_value == ',' ) 2356 { 2357 ++field_value; 2358 } 2359 } 2360 2361 /* 2362 * field_value is now pointing at first character after comma 2363 * delimiter, or at end of string 2364 */ 2365 } 2366 } 2367 } 2368} 2369 2370/*****************************************************************************/ 2371static void identifyServerType(CFHTTPMessageRef responsePropertyRef) 2372{ 2373 CFStringRef serverHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responsePropertyRef, CFSTR("Server")); 2374 if ( serverHeaderRef != NULL ) { 2375 if (CFStringHasPrefix(serverHeaderRef, CFSTR("Microsoft-IIS/")) == TRUE) 2376 gServerIdent = WEBDAV_MICROSOFT_IIS_SERVER; 2377 2378 CFRelease(serverHeaderRef); 2379 } 2380} 2381 2382/******************************************************************************/ 2383static int network_getDAVLevel( 2384 uid_t uid, /* -> uid of the user making the request */ 2385 CFURLRef urlRef, /* -> url */ 2386 int *dav_level) /* <- DAV level */ 2387{ 2388 int error; 2389 CFHTTPMessageRef response; 2390 /* the 3 headers */ 2391 CFIndex headerCount = 1; 2392 struct HeaderFieldValue headers[] = { 2393 { CFSTR("Accept"), CFSTR("*/*") }, 2394 }; 2395 2396 *dav_level = 0; 2397 2398 /* send request to the server and get the response */ 2399 error = send_transaction(uid, urlRef, NULL, CFSTR("OPTIONS"), NULL, 2400 headerCount, headers, REDIRECT_MANUAL, NULL, NULL, &response); 2401 if ( !error ) { 2402 /* get the DAV level */ 2403 ParseDAVLevel(response, dav_level); 2404 2405 /* identify the type of server */ 2406 identifyServerType(response); 2407 2408 /* release the response buffer */ 2409 CFRelease(response); 2410 } 2411 2412 return ( error ); 2413} 2414 2415/*****************************************************************************/ 2416 2417static 2418int get_from_attributes_cache(struct node_entry *node, uid_t uid) 2419{ 2420 ssize_t size; 2421 int result; 2422 2423 result = FALSE; /* default to FALSE */ 2424 2425 if ( node_appledoubleheader_valid(node, uid) ) 2426 { 2427 require(fchflags(node->file_fd, 0) == 0, fchflags); 2428 require(lseek(node->file_fd, (off_t)0, SEEK_SET) != -1, lseek); 2429 require(ftruncate(node->file_fd, 0LL) != -1, ftruncate); 2430 /* we found the AppleDouble header in memcache */ 2431 size = write(node->file_fd, (void *)node->attr_appledoubleheader, APPLEDOUBLEHEADER_LENGTH); 2432 if ( size != APPLEDOUBLEHEADER_LENGTH ) 2433 { 2434 debug_string("write failed"); 2435 /* attempt to seek back to start of file, make sure it's empty, and then reset its status */ 2436 (void) lseek(node->file_fd, (off_t)0, SEEK_SET); 2437 (void) ftruncate(node->file_fd, 0LL); 2438 node->file_status = WEBDAV_DOWNLOAD_NEVER; 2439 node->file_validated_time = 0; 2440 node->file_last_modified = -1; 2441 if ( node->file_entity_tag != NULL ) 2442 { 2443 free(node->file_entity_tag); 2444 node->file_entity_tag = NULL; 2445 } 2446 } 2447 else 2448 { 2449 node->file_status = WEBDAV_DOWNLOAD_FINISHED; 2450 node->file_validated_time = node->attr_appledoubleheader_time; 2451 node->file_last_modified = (node->attr_stat_info.attr_stat.st_mtimespec.tv_sec != 0) ? node->attr_stat_info.attr_stat.st_mtimespec.tv_sec : -1; 2452 /* ••••• should I get the etag when I get attr_appledoubleheader? probably */ 2453 if ( node->file_entity_tag != NULL ) 2454 { 2455 free(node->file_entity_tag); 2456 node->file_entity_tag = NULL; 2457 } 2458 result = TRUE; 2459 } 2460 } 2461 2462ftruncate: 2463lseek: 2464fchflags: 2465 2466 return ( result ); 2467} 2468 2469/******************************************************************************/ 2470 2471/* 2472 * network_stat handles requests from network_lookup, network_getattr 2473 * and network_mount. 2474 */ 2475static int network_stat( 2476 uid_t uid, /* -> uid of the user making the request */ 2477 struct node_entry *node, /* <- the node involved in the request */ 2478 CFURLRef urlRef, /* -> url to the resource */ 2479 enum RedirectAction redirectAction, /* how to handle http 3xx redirection */ 2480 struct webdav_stat_attr *statbuf) /* <- stat information is returned in this buffer */ 2481{ 2482 int error; 2483 UInt8 *responseBuffer; 2484 CFIndex count; 2485 CFDataRef bodyData; 2486 /* the xml for the message body */ 2487 const UInt8 xmlString[] = 2488 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 2489 "<D:propfind xmlns:D=\"DAV:\">\n" 2490 "<D:prop>\n" 2491 "<D:getlastmodified/>\n" 2492 "<D:getcontentlength/>\n" 2493 "<D:creationdate/>\n" 2494 "<D:resourcetype/>\n" 2495 "</D:prop>\n" 2496 "</D:propfind>\n"; 2497 /* the 3 headers */ 2498 CFIndex headerCount = 3; 2499 struct HeaderFieldValue headers[] = { 2500 { CFSTR("Accept"), CFSTR("*/*") }, 2501 { CFSTR("Content-Type"), CFSTR("text/xml") }, 2502 { CFSTR("Depth"), CFSTR("0") }, 2503 { CFSTR("translate"), CFSTR("f") } 2504 }; 2505 2506 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 2507 /* translate flag only for Microsoft IIS Server */ 2508 headerCount += 1; 2509 } 2510 2511 /* create the message body with the xml */ 2512 bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull); 2513 require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO); 2514 2515 /* send request to the server and get the response */ 2516 error = send_transaction(uid, urlRef, node, CFSTR("PROPFIND"), bodyData, 2517 headerCount, headers, redirectAction, &responseBuffer, &count, NULL); 2518 if ( !error ) 2519 { 2520 /* parse the statbuf from the response buffer */ 2521 error = parse_stat(responseBuffer, count, statbuf); 2522 2523 /* free the response buffer */ 2524 free(responseBuffer); 2525 } 2526 2527 /* release the message body */ 2528 CFRelease(bodyData); 2529 2530CFDataCreateWithBytesNoCopy: 2531 2532 return ( error ); 2533} 2534 2535/******************************************************************************/ 2536 2537static int network_dir_is_empty( 2538 uid_t uid, /* -> uid of the user making the request */ 2539 CFURLRef urlRef) /* -> url to check */ 2540{ 2541 int error; 2542 UInt8 *responseBuffer; 2543 CFIndex count; 2544 CFDataRef bodyData; 2545 /* the xml for the message body */ 2546 const UInt8 xmlString[] = 2547 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 2548 "<D:propfind xmlns:D=\"DAV:\">\n" 2549 "<D:prop>\n" 2550 "<D:resourcetype/>\n" 2551 "</D:prop>\n" 2552 "</D:propfind>\n"; 2553 /* the 3 headers */ 2554 CFIndex headerCount = 3; 2555 struct HeaderFieldValue headers[] = { 2556 { CFSTR("Accept"), CFSTR("*/*") }, 2557 { CFSTR("Content-Type"), CFSTR("text/xml") }, 2558 { CFSTR("Depth"), CFSTR("1") }, 2559 { CFSTR("translate"), CFSTR("f") } 2560 }; 2561 2562 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 2563 /* translate flag only for Microsoft IIS Server */ 2564 headerCount += 1; 2565 } 2566 2567 error = 0; 2568 responseBuffer = NULL; 2569 2570 /* create the message body with the xml */ 2571 bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull); 2572 require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO); 2573 2574 /* send request to the server and get the response */ 2575 error = send_transaction(uid, urlRef, NULL, CFSTR("PROPFIND"), bodyData, 2576 headerCount, headers, REDIRECT_AUTO, &responseBuffer, &count, NULL); 2577 if ( !error ) 2578 { 2579 int num_entries; 2580 2581 /* parse responseBuffer to get the number of entries */ 2582 error = parse_file_count(responseBuffer, count, &num_entries); 2583 if ( !error ) 2584 { 2585 if (num_entries > 1) 2586 { 2587 /* 2588 * An empty directory will have just one entry for itself as far 2589 * as the server is concerned. If there is more than that we need 2590 * to return ENOTEMPTY since we don't allow deleting directories 2591 * which have anything in them. 2592 */ 2593 error = ENOTEMPTY; 2594 } 2595 } 2596 /* free the response buffer */ 2597 free(responseBuffer); 2598 } 2599 2600 /* release the message body */ 2601 CFRelease(bodyData); 2602 2603CFDataCreateWithBytesNoCopy: 2604 2605 return ( error ); 2606} 2607 2608/******************************************************************************/ 2609 2610int network_lookup( 2611 uid_t uid, /* -> uid of the user making the request */ 2612 struct node_entry *node, /* -> parent node */ 2613 char *name, /* -> filename to find */ 2614 size_t name_length, /* -> length of name */ 2615 struct webdav_stat_attr *statbuf) /* <- stat information is returned in this buffer except for st_ino */ 2616{ 2617 int error, cnt; 2618 CFURLRef urlRef; 2619 2620 error = 0; 2621 2622 cnt = 0; 2623 while (cnt < WEBDAV_MAX_REDIRECTS) { 2624 /* create a CFURL to the node plus name */ 2625 urlRef = create_cfurl_from_node(node, name, name_length); 2626 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 2627 2628 /* let network_stat do the rest of the work */ 2629 error = network_stat(uid, node, urlRef, REDIRECT_AUTO, statbuf); 2630 2631 CFRelease(urlRef); 2632 2633 if (error != EDESTADDRREQ) 2634 break; 2635 cnt++; 2636 } 2637 2638 if (error == EDESTADDRREQ) { 2639 syslog(LOG_ERR, "%s: Too many http redirects", __FUNCTION__); 2640 error = EIO; 2641 } 2642 2643create_cfurl_from_node: 2644 2645 return ( error ); 2646} 2647 2648/******************************************************************************/ 2649 2650int network_getattr( 2651 uid_t uid, /* -> uid of the user making the request */ 2652 struct node_entry *node, /* -> node */ 2653 struct webdav_stat_attr *statbuf) /* <- stat information is returned in this buffer */ 2654{ 2655 int error, cnt; 2656 CFURLRef urlRef; 2657 2658 error = 0; 2659 2660 cnt = 0; 2661 while ( cnt < WEBDAV_MAX_REDIRECTS) { 2662 /* create a CFURL to the node */ 2663 urlRef = create_cfurl_from_node(node, NULL, 0); 2664 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 2665 2666 /* let network_stat do the rest of the work */ 2667 error = network_stat(uid, node, urlRef, REDIRECT_MANUAL, statbuf); 2668 2669 if ( error == 0 ) 2670 { 2671 /* network_stat gets all of the struct stat fields except for st_ino so fill it in here with the fileid of the node */ 2672 statbuf->attr_stat.st_ino = node->fileid; 2673 } 2674 2675 CFRelease(urlRef); 2676 2677 if (error != EDESTADDRREQ) 2678 break; 2679 cnt++; 2680 } 2681 2682 if (error == EDESTADDRREQ) 2683 error = EIO; 2684 2685create_cfurl_from_node: 2686 2687 return ( error ); 2688} 2689 2690/******************************************************************************/ 2691 2692/* NOTE: this will do both the OPTIONS and the PROPFIND. */ 2693/* NOTE: if webdavfs is changed to support advlocks, then 2694 * server_mount_flags parameter is not needed. 2695 */ 2696/* 2697 * The only errors expected from network_mount are: 2698 * ECANCELED - the user could not authenticate and cancelled the mount 2699 * ENODEV - we could not connect to the server (bad URL, server down, etc.) 2700 */ 2701int network_mount( 2702 uid_t uid, /* -> uid of the user making the request */ 2703 int *server_mount_flags) /* <- flags to OR in with mount flags (i.e., MNT_RDONLY) */ 2704{ 2705 int error; 2706 CFURLRef urlRef; 2707 int dav_level, cnt; 2708 struct webdav_stat_attr statbuf; 2709 2710 cnt = 0; 2711 while (cnt < WEBDAV_MAX_REDIRECTS) { 2712 urlRef = nodecache_get_baseURL(); 2713 error = network_getDAVLevel(uid, urlRef, &dav_level); 2714 CFRelease(urlRef); 2715 if (error != EDESTADDRREQ) 2716 break; 2717 cnt++; 2718 } 2719 2720 if ( error == 0 ) 2721 { 2722 if ( dav_level > 2 ) 2723 { 2724 /* pin it to 2 -- the highest we care about */ 2725 dav_level = 2; 2726 } 2727 switch (dav_level) 2728 { 2729 case 1: 2730 *server_mount_flags |= MNT_RDONLY; 2731 break; 2732 2733 case 2: 2734 /* DAV supports LOCKs */ 2735 break; 2736 2737 default: 2738 debug_string("network_mount: WebDAV protocol not supported"); 2739 error = ENODEV; 2740 break; 2741 } 2742 2743 if ( error == 0 ) 2744 { 2745 cnt = 0; 2746 while (cnt < WEBDAV_MAX_REDIRECTS) { 2747 urlRef = nodecache_get_baseURL(); 2748 error = network_stat(uid, NULL, urlRef, REDIRECT_MANUAL, &statbuf); 2749 CFRelease(urlRef); 2750 if (error != EDESTADDRREQ) 2751 break; 2752 cnt++; 2753 } 2754 2755 if ( error ) 2756 { 2757 if (error == EDESTADDRREQ) { 2758 syslog(LOG_ERR, "%s: Too many redirects for network_stat: mount cancelled", __FUNCTION__); 2759 error = EIO; 2760 } 2761 else if (error != EACCES) 2762 { 2763 syslog(LOG_ERR, "%s: network_stat returned error %d", __FUNCTION__, error); 2764 error = ENODEV; 2765 } 2766 else 2767 { 2768 syslog(LOG_DEBUG, "%s: network_stat returned EACCES", __FUNCTION__); 2769 error = EAUTH; 2770 } 2771 } 2772 else if ( !S_ISDIR(statbuf.attr_stat.st_mode) ) 2773 { 2774 /* the PROFIND was successful, but the URL was to a file, not a collection */ 2775 debug_string("network_mount: URL is not a collection resource (directory)"); 2776 error = ENODEV; 2777 } 2778 } 2779 } 2780 else 2781 { 2782 if (error == EDESTADDRREQ) { 2783 syslog(LOG_ERR, "%s: Too many redirects for OPTIONS: mount cancelled", __FUNCTION__); 2784 error = EIO; 2785 } 2786 else if ( error != EACCES ) 2787 { 2788 syslog(LOG_ERR, "%s: network_getDAVLevel returned error %d", __FUNCTION__, error); 2789 error = ENODEV; 2790 } 2791 else 2792 { 2793 syslog(LOG_DEBUG, "%s: network_getDAVLevel returned EACCES", __FUNCTION__); 2794 error = EAUTH; 2795 } 2796 } 2797 2798 return ( error ); 2799} 2800 2801/******************************************************************************/ 2802 2803int network_finish_download( 2804 struct node_entry *node, 2805 struct ReadStreamRec *readStreamRecPtr) 2806{ 2807 UInt8 *buffer; 2808 CFIndex bytesRead; 2809 2810 /* malloc a buffer */ 2811 buffer = malloc(BODY_BUFFER_SIZE); 2812 require(buffer != NULL, malloc_buffer); 2813 2814 while ( 1 ) 2815 { 2816 /* were we asked to terminate the download? */ 2817 if ( (node->file_status & WEBDAV_DOWNLOAD_TERMINATED) != 0 ) 2818 { 2819 /* 2820 * Call CFReadStreamRead one more time. This may block but this is 2821 * the only way to know at termination if the download was 2822 * finished, or if the download was incomplete. 2823 */ 2824 bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer, 1); /* make it a small read */ 2825 if ( bytesRead == 0 ) 2826 { 2827 /* 2828 * The download was complete the last time through the loop. 2829 * Break and let the caller mark this download finished. 2830 */ 2831 break; 2832 } 2833 else 2834 { 2835 /* 2836 * The download wasn't complete the last time through the loop. 2837 * Throw out these bytes (we'll get them if the file is reopened) 2838 * and let the caller mark this download aborted. 2839 */ 2840 goto terminated; 2841 } 2842 } 2843 2844 bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer, BODY_BUFFER_SIZE); 2845 if ( bytesRead > 0 ) 2846 { 2847 require(write(node->file_fd, buffer, (size_t)bytesRead) == (ssize_t)bytesRead, write); 2848 } 2849 else if ( bytesRead == 0 ) 2850 { 2851 /* there are no more bytes to read */ 2852 break; 2853 } 2854 else 2855 { 2856 CFStreamError streamError; 2857 2858 streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef); 2859 syslog(LOG_ERR,"network_finish_download: CFStreamError: domain %ld, error %lld", streamError.domain, (SInt64)streamError.error); 2860 goto CFReadStreamRead; 2861 break; 2862 } 2863 }; 2864 2865 free(buffer); 2866 2867 if ( readStreamRecPtr->connectionClose ) 2868 { 2869 /* close and release the stream */ 2870 CFReadStreamClose(readStreamRecPtr->readStreamRef); 2871 CFRelease(readStreamRecPtr->readStreamRef); 2872 readStreamRecPtr->readStreamRef = NULL; 2873 } 2874 2875 /* make this ReadStreamRec is available again */ 2876 release_ReadStreamRec(readStreamRecPtr); 2877 2878 return ( 0 ); 2879 2880terminated: 2881write: 2882CFReadStreamRead: 2883 2884 free(buffer); 2885 2886malloc_buffer: 2887 2888 /* close and release the read stream on errors */ 2889 CFReadStreamClose(readStreamRecPtr->readStreamRef); 2890 CFRelease(readStreamRecPtr->readStreamRef); 2891 readStreamRecPtr->readStreamRef = NULL; 2892 2893 /* make this ReadStreamRec is available again */ 2894 release_ReadStreamRec(readStreamRecPtr); 2895 2896 return ( EIO ); 2897} 2898 2899/******************************************************************************/ 2900 2901int network_server_ping(u_int32_t delay) 2902{ 2903 int error; 2904 CFHTTPMessageRef response; 2905 CFURLRef urlRef = nodecache_get_baseURL(); 2906 2907 /* the 3 headers */ 2908 CFIndex headerCount = 1; 2909 struct HeaderFieldValue headers[] = { 2910 { CFSTR("Accept"), CFSTR("*/*") }, 2911 }; 2912 2913 /* first delay a bit */ 2914 if (delay) 2915 sleep(delay); 2916 2917 /* send an OPTIONS request to the server and get the response */ 2918 error = send_transaction(gProcessUID, urlRef, NULL, CFSTR("OPTIONS"), NULL, 2919 headerCount, headers, REDIRECT_AUTO, NULL, NULL, &response); 2920 CFRelease(urlRef); 2921 2922 if ( !error ) { 2923 set_connectionstate(WEBDAV_CONNECTION_UP); 2924 CFRelease(response); /* release the response buffer */ 2925 } 2926 else { 2927 /* Still no host connectivity */ 2928 syslog(LOG_ERR, "WebDAV server still not responding..."); 2929 2930 /* Determine next sleep delay */ 2931 switch (delay) { 2932 2933 case 0: 2934 delay = 1; 2935 break; 2936 case 1: 2937 delay = 2; 2938 break; 2939 case 2: 2940 delay = 4; 2941 break; 2942 case 4: 2943 delay = 8; 2944 break; 2945 default: 2946 delay = 12; 2947 break; 2948 } 2949 2950 /* queue another ping request */ 2951 requestqueue_enqueue_server_ping(delay); 2952 } 2953 2954 return ( error ); 2955} 2956 2957/******************************************************************************/ 2958 2959void writeseqReadResponseCallback(CFReadStreamRef str, CFStreamEventType event, void* arg) 2960{ 2961 struct stream_put_ctx *ctx; 2962 CFStreamError streamError; 2963 CFIndex bytesRead; 2964 CFTypeRef theResponsePropertyRef; 2965 CFHTTPMessageRef responseMessage; 2966 CFStringRef setCookieHeaderRef; 2967 CFIndex statusCode; 2968 int error; 2969 2970 ctx = (struct stream_put_ctx *)arg; 2971 2972 switch(event) 2973 { 2974 case kCFStreamEventHasBytesAvailable: 2975 bytesRead = CFReadStreamRead(str, ctx->rspBuf + ctx->totalRead, WEBDAV_WRITESEQ_RSPBUF_LEN - ctx->totalRead); 2976 2977 if (bytesRead < 0 ) { 2978 streamError = CFReadStreamGetError(str); 2979 if (!(ctx->is_retry) && 2980 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 2981 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost))) 2982 { 2983 /* 2984 * We got an EPIPE or HTTP Connection Lost error from the stream. We retry the PUT request 2985 * for these errors conditions 2986 */ 2987 syslog(LOG_DEBUG,"%s: EventHasBytesAvailable CFStreamError: domain %ld, error %lld (retrying)", 2988 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 2989 pthread_mutex_lock(&ctx->ctx_lock); 2990 ctx->finalStatus = EAGAIN; 2991 ctx->finalStatusValid = true; 2992 pthread_mutex_unlock(&ctx->ctx_lock); 2993 } 2994 else 2995 { 2996 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 2997 { 2998 syslog(LOG_ERR,"%s: EventHasBytesAvailable CFStreamError: domain %ld, error %lld", 2999 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 3000 } 3001 set_connectionstate(WEBDAV_CONNECTION_DOWN); 3002 pthread_mutex_lock(&ctx->ctx_lock); 3003 ctx->finalStatus = stream_error_to_errno(&streamError); 3004 ctx->finalStatusValid = true; 3005 pthread_mutex_unlock(&ctx->ctx_lock); 3006 } 3007 goto out1; 3008 } 3009 3010 ctx->totalRead += bytesRead; 3011 break; 3012 3013 case kCFStreamEventOpenCompleted: 3014 // syslog(LOG_DEBUG,"writeseqReadResponseCallback: kCFStreamEventOpenCompleted\n"); 3015 break; 3016 3017 case kCFStreamEventErrorOccurred: 3018 error = HandleSSLErrors(str); 3019 3020 if ( error == EAGAIN ) { 3021 syslog(LOG_DEBUG,"%s: EventHasErrorOccurred: HandleSSLErrors: EAGAIN", __FUNCTION__); 3022 pthread_mutex_lock(&ctx->ctx_lock); 3023 ctx->finalStatus = EAGAIN; 3024 ctx->finalStatusValid = true; 3025 pthread_mutex_unlock(&ctx->ctx_lock); 3026 } 3027 else { 3028 streamError = CFReadStreamGetError(str); 3029 3030 if (!(ctx->is_retry) && 3031 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 3032 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost))) 3033 { 3034 /* 3035 * We got an EPIPE or HTTP Connection Lost error from the stream. We retry the PUT request 3036 * for these error conditions 3037 */ 3038 syslog(LOG_DEBUG,"%s: EventHasErrorOccurred CFStreamError: domain %ld, error %lld (retrying)", 3039 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 3040 pthread_mutex_lock(&ctx->ctx_lock); 3041 ctx->finalStatus = EAGAIN; 3042 ctx->finalStatusValid = true; 3043 pthread_mutex_unlock(&ctx->ctx_lock); 3044 } 3045 else 3046 { 3047 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 3048 { 3049 syslog(LOG_ERR,"%s: EventErrorOccurred CFStreamError: domain %ld, error %lld", 3050 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 3051 } 3052 set_connectionstate(WEBDAV_CONNECTION_DOWN); 3053 pthread_mutex_lock(&ctx->ctx_lock); 3054 ctx->finalStatus = stream_error_to_errno(&streamError); 3055 ctx->finalStatusValid = true; 3056 pthread_mutex_unlock(&ctx->ctx_lock); 3057 } 3058 } 3059 break; 3060 3061 case kCFStreamEventEndEncountered: 3062 // syslog(LOG_DEBUG,"writeseqReadResponseCallback: kCFStreamEventEndEncountered\n"); 3063 3064 /* get the response header */ 3065 theResponsePropertyRef = CFReadStreamCopyProperty(str, kCFStreamPropertyHTTPResponseHeader); 3066 if (theResponsePropertyRef == NULL) 3067 { 3068 syslog(LOG_DEBUG,"%s: EventEndEncountered failed to obtain response header", __FUNCTION__); 3069 pthread_mutex_lock(&ctx->ctx_lock); 3070 ctx->finalStatus = EIO; 3071 ctx->finalStatusValid = true; 3072 pthread_mutex_unlock(&ctx->ctx_lock); 3073 goto out1; 3074 } 3075 3076 /* fun with casting a "const void *" CFTypeRef away */ 3077 responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef)); 3078 statusCode = CFHTTPMessageGetResponseStatusCode(responseMessage); 3079 error = translate_status_to_error((UInt32)statusCode); 3080 3081 // Handle cookies 3082 setCookieHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Set-Cookie")); 3083 if (setCookieHeaderRef != NULL) { 3084 handle_cookies(setCookieHeaderRef, ctx->request); 3085 CFRelease(setCookieHeaderRef); 3086 } 3087 3088 pthread_mutex_lock(&ctx->ctx_lock); 3089 ctx->finalStatus = error; 3090 ctx->finalStatusValid = true; 3091 pthread_mutex_unlock(&ctx->ctx_lock); 3092 3093 CFRelease (theResponsePropertyRef); 3094 3095 // syslog(LOG_ERR,"WRITESEQ: writeSeqRspReadCB: finalStatus: %d\n", error); 3096 break; 3097 3098 default: 3099 break; 3100 } 3101 3102out1:; 3103 3104} 3105 3106/******************************************************************************/ 3107 3108void writeseqWriteCallback(CFWriteStreamRef str, 3109 CFStreamEventType event, 3110 void* arg) 3111{ 3112 struct stream_put_ctx *ctx; 3113 CFStreamError streamError; 3114 3115 3116 ctx = (struct stream_put_ctx *)arg; 3117 3118 // Let's see what's going on here 3119 switch(event) 3120 { 3121 case kCFStreamEventCanAcceptBytes: 3122 // syslog(LOG_DEBUG,"%s: writeSeqWriteCB: kCFStreamEventCanAcceptBytes\n", __FUNCTION__); 3123 pthread_mutex_lock(&ctx->ctx_lock); 3124 ctx->canAcceptBytesEvents++; 3125 pthread_mutex_unlock(&ctx->ctx_lock); 3126 break; 3127 3128 case kCFStreamEventOpenCompleted: 3129 // syslog(LOG_DEBUG,"%s: writeSeqWriteCB: kCFStreamEventOpenCompleted\n", __FUNCTION__); 3130 pthread_mutex_lock(&ctx->ctx_lock); 3131 ctx->writeStreamOpenEventReceived = true; 3132 pthread_mutex_unlock(&ctx->ctx_lock); 3133 break; 3134 3135 case kCFStreamEventErrorOccurred: 3136 streamError = CFWriteStreamGetError(str); 3137 3138 pthread_mutex_lock(&ctx->ctx_lock); 3139 if (!(ctx->is_retry) && 3140 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 3141 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost))) 3142 { 3143 /* 3144 * We got an EPIPE or HTTP Connection Lost error from the stream. We retry the PUT request 3145 * for these errors conditions 3146 */ 3147 syslog(LOG_DEBUG,"%s: EventErrorOccurred CFStreamError: domain %ld, error %lld (retrying)", 3148 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 3149 ctx->finalStatus = EAGAIN; 3150 ctx->finalStatusValid = true; 3151 pthread_mutex_unlock(&ctx->ctx_lock); 3152 } 3153 else 3154 { 3155 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 3156 { 3157 syslog(LOG_ERR,"%s: EventErrorOccurred CFStreamError: domain %ld, error %lld", 3158 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 3159 } 3160 set_connectionstate(WEBDAV_CONNECTION_DOWN); 3161 ctx->finalStatus = EIO; 3162 ctx->finalStatusValid = 1; 3163 pthread_mutex_unlock(&ctx->ctx_lock); 3164 } 3165 break; 3166 3167 case kCFStreamEventEndEncountered: 3168 // syslog(LOG_ERR,"writeSeqWriteCB:EventEndEncountered"); 3169 break; 3170 3171 default: 3172 break; 3173 } 3174} 3175 3176/******************************************************************************/ 3177 3178CFDataRef managerMessagePortCallback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) 3179{ 3180 #pragma unused(local,msgid,data,info) 3181 3182 // Callback does nothing. Just used to wakeup writemgr thread 3183 3184 return NULL; 3185} 3186 3187/******************************************************************************/ 3188 3189int cleanup_seq_write(struct stream_put_ctx *ctx) 3190{ 3191 struct timespec timeout; 3192 struct seqwrite_mgr_req *mgr_req; 3193 int error; 3194 3195 if ( ctx == NULL ) { 3196 syslog(LOG_ERR, "%s: context passed in was NULL", __FUNCTION__); 3197 return (-1); 3198 } 3199 3200 mgr_req = (struct seqwrite_mgr_req *) malloc(sizeof(struct seqwrite_mgr_req)); 3201 3202 if (mgr_req == NULL) { 3203 syslog(LOG_ERR, "%s: no mem for mgr request", __FUNCTION__); 3204 return (-1); 3205 } 3206 bzero(mgr_req, sizeof(struct seqwrite_mgr_req)); 3207 3208 mgr_req->refCount = 1; // hold on to a reference until we're done 3209 mgr_req->type = SEQWRITE_CLOSE; 3210 3211 timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_RSP_TIMEOUT; /* time out in seconds */ 3212 timeout.tv_nsec = 0; 3213 3214 // If mgr is running, tell it to close down 3215 pthread_mutex_lock(&ctx->ctx_lock); 3216 if (ctx->mgr_status == WR_MGR_RUNNING) { 3217 // queue request 3218 queue_writemgr_request_locked(ctx, mgr_req); 3219 } 3220 3221 while (ctx->mgr_status != WR_MGR_DONE) { 3222 error = pthread_cond_timedwait(&ctx->ctx_condvar, &ctx->ctx_lock, &timeout); 3223 if ((error != 0) && (error != ETIMEDOUT)) { 3224 syslog(LOG_ERR, "%s: pthread_cond_timewait error %d\n", __FUNCTION__, error); 3225 ctx->finalStatus = EIO; 3226 ctx->finalStatusValid = true; 3227 break; 3228 } else { 3229 // recalc timeout 3230 timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_RSP_TIMEOUT; /* time out in seconds */ 3231 timeout.tv_nsec = 0; 3232 } 3233 } 3234 3235 error = ctx->finalStatus; 3236 release_writemgr_request_locked(mgr_req); 3237 pthread_mutex_unlock(&ctx->ctx_lock); 3238 3239 /* clean up the streams */ 3240 if (ctx->request != NULL) { 3241 CFRelease(ctx->request); 3242 ctx->request = NULL; 3243 } 3244 3245 if (ctx->wrStreamRef != NULL) { 3246 CFRelease(ctx->wrStreamRef); 3247 ctx->wrStreamRef = NULL; 3248 } 3249 3250 if (ctx->rdStreamRef != NULL) { 3251 CFReadStreamClose(ctx->rdStreamRef); 3252 CFRelease(ctx->rdStreamRef); 3253 ctx->rdStreamRef = NULL; 3254 } 3255 3256 if (ctx->rspStreamRef != NULL) { 3257 CFReadStreamClose(ctx->rspStreamRef); 3258 CFRelease(ctx->rspStreamRef); 3259 ctx->rspStreamRef = NULL; 3260 } 3261 3262 if (ctx->mgrPort != NULL) { 3263 CFMessagePortInvalidate(ctx->mgrPort); 3264 CFRelease(ctx->mgrPort); 3265 ctx->mgrPort = NULL; 3266 } 3267 3268 if (ctx->mgr_rl != NULL) { 3269 CFRelease(ctx->mgr_rl); 3270 ctx->mgr_rl = NULL; 3271 } 3272 3273 return (error); 3274} 3275 3276/******************************************************************************/ 3277 3278int network_open( 3279 uid_t uid, /* -> uid of the user making the request */ 3280 struct node_entry *node, /* -> node to open */ 3281 int write_access) /* -> open requires write access */ 3282{ 3283 int error; 3284 int ask_server; 3285 3286 if ( !write_access ) 3287 { 3288 if ( ((node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED) && !NODE_FILE_INVALID(node) ) 3289 { 3290 /* OK by our simple heuristics */ 3291 /* the file was completely downloaded very recently, skip the server check */ 3292 ask_server = FALSE; 3293 } 3294 else 3295 { 3296 /* attempt to retrieve file contents from the attributes_cache */ 3297 if ( get_from_attributes_cache(node, uid) ) 3298 { 3299 /* the file contents were retrieved from the attributes_cache */ 3300 ask_server = FALSE; 3301 } 3302 else 3303 { 3304 /* we need to ask the server */ 3305 ask_server = TRUE; 3306 } 3307 } 3308 } 3309 else 3310 { 3311 /* if we just created the file and it's completely downloaded, we won't check */ 3312 if ( NODE_FILE_RECENTLY_CREATED(node) && 3313 ((node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED) ) 3314 { 3315 ask_server = FALSE; 3316 } 3317 else 3318 { 3319 /* we must check with server when opening with write access */ 3320 ask_server = TRUE; 3321 } 3322 } 3323 3324 if ( ask_server ) 3325 { 3326 CFURLRef urlRef; 3327 CFHTTPMessageRef message; 3328 CFHTTPMessageRef responseRef; 3329 CFIndex statusCode; 3330 UInt32 auth_generation; 3331 int retryTransaction; 3332 3333 error = 0; 3334 message = NULL; 3335 responseRef = NULL; 3336 statusCode = 0; 3337 auth_generation = 0; 3338 retryTransaction = TRUE; 3339 3340 /* create a CFURL to the node */ 3341 urlRef = create_cfurl_from_node(node, NULL, 0); 3342 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 3343 3344 /* the transaction/authentication loop */ 3345 do 3346 { 3347 /* release message if left from previous loop */ 3348 if ( message != NULL ) 3349 { 3350 CFRelease(message); 3351 message = NULL; 3352 } 3353 /* create a CFHTTP message object */ 3354 message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), urlRef, kCFHTTPVersion1_1); 3355 require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO); 3356 3357 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 3358 /* translate flag and no-cache only for Microsoft IIS Server */ 3359 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("translate"), CFSTR("f")); 3360 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Pragma"), CFSTR("no-cache")); 3361 } 3362 3363 /* Change the User-Agent header */ 3364 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("User-Agent"), userAgentHeaderValue); 3365 3366 /* add the X-Source-Id header if needed */ 3367 if ( X_Source_Id_HeaderValue != NULL ) 3368 { 3369 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("X-Source-Id"), X_Source_Id_HeaderValue); 3370 } 3371 3372 /* add cookies (if any) */ 3373 add_cookie_headers(message, urlRef); 3374 3375 /* add other HTTP headers */ 3376 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*")); 3377 3378 /* 3379 * If the status isn't WEBDAV_DOWNLOAD_NEVER, we need to add some conditional headers. 3380 * If adding the headers fails, then we can continue without them -- it'll just 3381 * force the file to be downloaded. 3382 */ 3383 if ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) != WEBDAV_DOWNLOAD_NEVER ) 3384 { 3385 CFStringRef httpDateString; 3386 3387 httpDateString = CFStringCreateRFC2616DateStringWithTimeT(node->file_last_modified); 3388 if ( httpDateString != NULL ) 3389 { 3390 if ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED ) 3391 { 3392 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("If-Modified-Since"), httpDateString); 3393 } 3394 else 3395 { 3396 off_t currentLength; 3397 CFStringRef currentLengthString; 3398 3399 currentLength = lseek(node->file_fd, 0LL, SEEK_END); 3400 if ( currentLength != -1 ) 3401 { 3402 /* create a string with the file length for the Range header */ 3403 currentLengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%qd-"), currentLength); 3404 /* CFReadStreamCreateForStreamedHTTPRequest will use chunked transfer-encoding if the Content-Length header cannot be provided */ 3405 if ( currentLengthString != NULL ) 3406 { 3407 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("If-Range"), httpDateString); 3408 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), currentLengthString); 3409 CFRelease(currentLengthString); 3410 } 3411 } 3412 } 3413 CFRelease(httpDateString); 3414 } 3415 } 3416 3417 /* apply credentials (if any) */ 3418 /* 3419 * statusCode will be 401 or 407 and responseRef will not be NULL if we've already been through the loop; 3420 * statusCode will be 0 and responseRef will be NULL if this is the first time through. 3421 */ 3422 error = authcache_apply(uid, message, (UInt32)statusCode, responseRef, &auth_generation); 3423 if ( error != 0 ) 3424 { 3425 break; 3426 } 3427 3428 /* stream_transaction returns responseRef so release it if left from previous loop */ 3429 if ( responseRef != NULL ) 3430 { 3431 CFRelease(responseRef); 3432 responseRef = NULL; 3433 } 3434 /* now that everything's ready to send, send it */ 3435 error = stream_get_transaction(message, &retryTransaction, node, &responseRef); 3436 if ( error == EAGAIN ) 3437 { 3438 statusCode = 0; 3439 /* responseRef will be left NULL on retries */ 3440 } 3441 else 3442 { 3443 if ( error != 0 ) 3444 { 3445 break; 3446 } 3447 3448 /* get the status code */ 3449 statusCode = CFHTTPMessageGetResponseStatusCode(responseRef); 3450 } 3451 } while ( error == EAGAIN || statusCode == 401 || statusCode == 407 ); 3452 3453CFHTTPMessageCreateRequest: 3454 3455 if ( error == 0 ) 3456 { 3457 /* 304 Not Modified means the cache file is still good, so make it 200 before translating */ 3458 if ( statusCode == 304 ) 3459 { 3460 statusCode = 200; 3461 } 3462 3463 error = translate_status_to_error((UInt32)statusCode); 3464 if ( error == 0 ) 3465 { 3466 /* 3467 * when we get here with no errors, then we need to tell the authcache the 3468 * transaction worked so it can mark the credentials valid and, if needed 3469 * add the credentials to the keychain. If the auth_generation changed, then 3470 * another transaction updated the authcache element after we got it. 3471 */ 3472 (void) authcache_valid(uid, message, auth_generation); 3473 time(&node->file_validated_time); 3474 { 3475 CFStringRef headerRef; 3476 const char *field_value; 3477 char buffer[4096]; 3478 char *file_entity_tag; 3479 3480 headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("Last-Modified")); 3481 if ( headerRef ) 3482 { 3483 node->file_last_modified = DateStringToTime(headerRef); 3484 CFRelease(headerRef); 3485 } 3486 headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("ETag")); 3487 if ( headerRef ) 3488 { 3489 field_value = CFStringGetCStringPtr(headerRef, kCFStringEncodingUTF8); 3490 if ( field_value == NULL ) 3491 { 3492 if ( CFStringGetCString(headerRef, buffer, 4096, kCFStringEncodingUTF8) ) 3493 { 3494 field_value = buffer; 3495 } 3496 } 3497 if ( field_value != NULL ) 3498 { 3499 size_t len = strlen(field_value) + 1; 3500 3501 file_entity_tag = malloc(len); 3502 if ( file_entity_tag != NULL ) 3503 { 3504 strlcpy(file_entity_tag, field_value, len); 3505 if ( node->file_entity_tag != NULL ) 3506 { 3507 free(node->file_entity_tag); 3508 } 3509 node->file_entity_tag = file_entity_tag; 3510 } 3511 } 3512 CFRelease(headerRef); 3513 } 3514 } 3515 } 3516 } 3517 3518 if ( message != NULL ) 3519 { 3520 CFRelease(message); 3521 } 3522 3523 if ( responseRef != NULL ) 3524 { 3525 CFRelease(responseRef); 3526 } 3527 3528 CFRelease(urlRef); 3529 3530create_cfurl_from_node: 3531 ; 3532 } 3533 else 3534 { 3535 /* what we have cached is OK */ 3536 error = 0; 3537 } 3538 3539 return ( error ); 3540} 3541 3542/******************************************************************************/ 3543 3544int network_statfs( 3545 uid_t uid, /* -> uid of the user making the request */ 3546 struct node_entry *node, /* -> root node */ 3547 struct statfs *fs_attr) /* <- file system information */ 3548{ 3549 int error; 3550 CFURLRef urlRef; 3551 UInt8 *responseBuffer; 3552 CFIndex count, redir_cnt; 3553 CFDataRef bodyData; 3554 const UInt8 xmlString[] = 3555 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 3556 "<D:propfind xmlns:D=\"DAV:\">\n" 3557 "<D:prop>\n" 3558 "<D:quota-available-bytes/>\n" 3559 "<D:quota-used-bytes/>\n" 3560 "<D:quota/>\n" 3561 "<D:quotaused/>\n" 3562 "</D:prop>\n" 3563 "</D:propfind>\n"; 3564 /* the 3 headers */ 3565 CFIndex headerCount = 3; 3566 struct HeaderFieldValue headers[] = { 3567 { CFSTR("Accept"), CFSTR("*/*") }, 3568 { CFSTR("Content-Type"), CFSTR("text/xml") }, 3569 { CFSTR("Depth"), CFSTR("0") }, 3570 { CFSTR("translate"), CFSTR("f") } 3571 }; 3572 3573 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 3574 /* translate flag only for Microsoft IIS Server */ 3575 headerCount += 1; 3576 } 3577 3578 /* create a CFDataRef with the xml that is our message body */ 3579 bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull); 3580 require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO); 3581 3582 redir_cnt = 0; 3583 while (redir_cnt < WEBDAV_MAX_REDIRECTS) { 3584 /* create a CFURL to the node */ 3585 urlRef = create_cfurl_from_node(node, NULL, 0); 3586 if( urlRef == NULL) { 3587 error = EIO; 3588 break; 3589 } 3590 3591 /* send request to the server and get the response */ 3592 error = send_transaction(uid, urlRef, node, CFSTR("PROPFIND"), bodyData, 3593 headerCount, headers, REDIRECT_MANUAL, &responseBuffer, &count, NULL); 3594 CFRelease(urlRef); 3595 3596 if ( !error ) 3597 { 3598 /* parse responseBuffer to get the statfsbuf */ 3599 error = parse_statfs(responseBuffer, count, fs_attr); 3600 3601 /* free the response buffer */ 3602 free(responseBuffer); 3603 break; 3604 } 3605 3606 if (error != EDESTADDRREQ) 3607 break; 3608 3609 redir_cnt++; 3610 } 3611 3612 /* release the message body */ 3613 CFRelease(bodyData); 3614 3615 if (error == EDESTADDRREQ) 3616 error = EIO; 3617 3618CFDataCreateWithBytesNoCopy: 3619 3620 return ( error ); 3621} 3622 3623/******************************************************************************/ 3624 3625int network_create( 3626 uid_t uid, /* -> uid of the user making the request */ 3627 struct node_entry *node, /* -> parent node */ 3628 char *name, /* -> file name to create */ 3629 size_t name_length, /* -> length of name */ 3630 time_t *creation_date) /* <- date of the creation */ 3631{ 3632 int error; 3633 CFURLRef urlRef; 3634 CFHTTPMessageRef response; 3635 /* the 1 header */ 3636 CFIndex headerCount = 1; 3637 struct HeaderFieldValue headers[] = { 3638 { CFSTR("Accept"), CFSTR("*/*") }, 3639 { CFSTR("translate"), CFSTR("f") }, 3640 { CFSTR("Pragma"), CFSTR("no-cache") } 3641 }; 3642 3643 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 3644 /* translate flag and no-cache only for Microsoft IIS Server */ 3645 headerCount += 2; 3646 } 3647 3648 *creation_date = -1; 3649 3650 /* create a CFURL to the node plus name */ 3651 urlRef = create_cfurl_from_node(node, name, name_length); 3652 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 3653 3654 /* send request to the server and get the response */ 3655 error = send_transaction(uid, urlRef, NULL, CFSTR("PUT"), NULL, 3656 headerCount, headers, REDIRECT_DISABLE, NULL, NULL, &response); 3657 if ( !error ) 3658 { 3659 CFStringRef dateHeaderRef; 3660 3661 dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Date")); 3662 if ( dateHeaderRef != NULL ) 3663 { 3664 *creation_date = DateStringToTime(dateHeaderRef); 3665 3666 CFRelease(dateHeaderRef); 3667 } 3668 /* release the response buffer */ 3669 CFRelease(response); 3670 } 3671 3672 CFRelease(urlRef); 3673 3674create_cfurl_from_node: 3675 3676 return ( error ); 3677} 3678 3679/******************************************************************************/ 3680 3681static void create_http_request_message(CFHTTPMessageRef *message_p, CFURLRef urlRef, off_t file_len) { 3682 CFStringRef expectedLengthString = NULL; 3683 3684 /* release message if left from previous loop */ 3685 if ( *message_p != NULL ) 3686 { 3687 CFRelease(*message_p); 3688 *message_p = NULL; 3689 } 3690 /* create a CFHTTP message object */ 3691 *message_p = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("PUT"), urlRef, kCFHTTPVersion1_1); 3692 /* require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO); */ 3693 if (*message_p != NULL) { 3694 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 3695 /* translate flag and no-cache only for Microsoft IIS Server */ 3696 CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("translate"), CFSTR("f")); 3697 CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("Pragma"), CFSTR("no-cache")); 3698 } 3699 3700 /* Change the User-Agent header */ 3701 CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("User-Agent"), userAgentHeaderValue); 3702 3703 /* add the X-Source-Id header if needed */ 3704 if ( X_Source_Id_HeaderValue != NULL ) 3705 { 3706 CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("X-Source-Id"), X_Source_Id_HeaderValue); 3707 } 3708 3709 /* add cookies (if any) */ 3710 add_cookie_headers(*message_p, urlRef); 3711 3712 /* add other HTTP headers */ 3713 CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("Accept"), CFSTR("*/*")); 3714 3715 if ( file_len != 0 ) { 3716 expectedLengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%qi"), file_len); 3717 if ( expectedLengthString != NULL ) 3718 { 3719 CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("X-Expected-Entity-Length"), expectedLengthString); 3720 CFRelease(expectedLengthString); 3721 } 3722 } 3723 } 3724} 3725 3726/******************************************************************************/ 3727 3728static void add_last_mod_etag(CFHTTPMessageRef responseRef, time_t *file_last_modified, char **file_entity_tag) { 3729 CFStringRef headerRef; 3730 const char *field_value; 3731 char buffer[4096]; 3732 3733 headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("Last-Modified")); 3734 if ( headerRef ) 3735 { 3736 *file_last_modified = DateStringToTime(headerRef); 3737 CFRelease(headerRef); 3738 } 3739 headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("ETag")); 3740 if ( headerRef ) 3741 { 3742 field_value = CFStringGetCStringPtr(headerRef, kCFStringEncodingUTF8); 3743 if ( field_value == NULL ) 3744 { 3745 if ( CFStringGetCString(headerRef, buffer, 4096, kCFStringEncodingUTF8) ) 3746 { 3747 field_value = buffer; 3748 } 3749 } 3750 if ( field_value != NULL ) 3751 { 3752 size_t len = strlen(field_value) + 1; 3753 3754 *file_entity_tag = malloc(len); 3755 if ( *file_entity_tag != NULL ) 3756 { 3757 strlcpy(*file_entity_tag, field_value, len); 3758 } 3759 } 3760 CFRelease(headerRef); 3761 } 3762} 3763 3764/******************************************************************************/ 3765 3766static void close_socket_pair(int sockfd[2]) 3767{ 3768 close(sockfd[0]); 3769 close(sockfd[1]); 3770} 3771 3772/******************************************************************************/ 3773 3774static bool create_bound_streams(struct stream_put_ctx *ctx) { 3775 /* **************************** */ 3776 if ( socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->sockfd) < 0) { 3777 syslog(LOG_ERR,"%s: socketpair creation failed.", __FUNCTION__); 3778 return false; 3779 } 3780 3781 /* Create CFStreams for the raw sockets */ 3782 CFStreamCreatePairWithSocket(kCFAllocatorDefault, ctx->sockfd[0], &ctx->rdStreamRef, NULL); 3783 CFStreamCreatePairWithSocket(kCFAllocatorDefault, ctx->sockfd[1], NULL, &ctx->wrStreamRef); 3784 3785 if ( (ctx->rdStreamRef == NULL) || (ctx->wrStreamRef == NULL) ) 3786 { 3787 syslog(LOG_ERR, "%s: Null Stream Pair: rdStreamRef %p wrStreamRef %p.", __FUNCTION__, ctx->rdStreamRef, ctx->wrStreamRef ); 3788 close_socket_pair(ctx->sockfd); 3789 3790 return false; 3791 } 3792 3793 /* Ensure that the underlying sockets get closed when the streams are closed. */ 3794 if ( CFReadStreamSetProperty(ctx->rdStreamRef, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue) == false || 3795 CFWriteStreamSetProperty(ctx->wrStreamRef, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue) == false ) 3796 { 3797 syslog(LOG_ERR, "%s: failed to set kCFStreamPropertyShouldCloseNativeSocket.", __FUNCTION__); 3798 close_socket_pair(ctx->sockfd); 3799 /* Caller is responsible for closing rd & wr streams */ 3800 3801 return false; 3802 } 3803 3804 return true; 3805} 3806 3807/******************************************************************************/ 3808 3809int setup_seq_write( 3810 uid_t uid, /* -> uid of the user making the request */ 3811 struct node_entry *node, /* -> node we're writing */ 3812 off_t file_length) /* -> file length hint sent from the kernel */ 3813{ 3814 int error; 3815 CFURLRef urlRef = NULL; 3816 UInt32 statusCode; 3817 CFHTTPMessageRef responseRef; 3818 UInt32 auth_generation; 3819 CFStringRef lockTokenRef; 3820 char *file_entity_tag; 3821 pthread_mutexattr_t mutexattr; 3822 (void) uid; 3823 struct timespec timeout; 3824 3825 error = 0; 3826 file_entity_tag = NULL; 3827 responseRef = NULL; 3828 statusCode = 0; 3829 auth_generation = 0; 3830 3831 // ********************** 3832 // *** setup put_ctx **** 3833 // ********************** 3834 node->put_ctx = (struct stream_put_ctx *) malloc (sizeof(struct stream_put_ctx)); 3835 if (node->put_ctx == NULL) { 3836 syslog(LOG_ERR, "%s: failed to alloc ctx", __FUNCTION__); 3837 error = ENOMEM; 3838 return (error); 3839 } 3840 3841 /* initialize structs */ 3842 memset(node->put_ctx,0,sizeof(struct stream_put_ctx)); 3843 3844 error = pthread_mutexattr_init(&mutexattr); 3845 if (error) { 3846 syslog(LOG_ERR, "%s: init ctx mutexattr failed, error %d", __FUNCTION__, error); 3847 error = EIO; 3848 return (error); 3849 } 3850 3851 error = pthread_mutex_init(&node->put_ctx->ctx_lock, &mutexattr); 3852 if (error) { 3853 syslog(LOG_ERR, "%s: init ctx_lock failed, error %d", __FUNCTION__, error); 3854 error = EIO; 3855 return (error); 3856 } 3857 3858 error = pthread_cond_init(&node->put_ctx->ctx_condvar, NULL); 3859 if (error) { 3860 syslog(LOG_ERR, "%s: init ctx_condvar failed, error %d", __FUNCTION__, error); 3861 error = EIO; 3862 return (error); 3863 } 3864 3865 /* create a CFURL to the node */ 3866 urlRef = create_cfurl_from_node(node, NULL, 0); 3867 if (urlRef == NULL) 3868 { 3869 syslog(LOG_ERR, "%s: create_cfurl_from_node failed", __FUNCTION__); 3870 error = EIO; 3871 return (error); 3872 } 3873 3874 create_http_request_message(&node->put_ctx->request, urlRef, file_length); /* can this be moved outside the loop? */ 3875 3876 if (node->put_ctx->request == NULL) 3877 { 3878 syslog(LOG_ERR, "%s: create_http_request_message failed", __FUNCTION__); 3879 error = EIO; 3880 return (error); 3881 } 3882 3883 /* is there a lock token? */ 3884 if ( node->file_locktoken != NULL ) 3885 { 3886 /* in the unlikely event that this fails, the PUT may fail */ 3887 lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken); 3888 if ( lockTokenRef != NULL ) 3889 { 3890 CFHTTPMessageSetHeaderFieldValue(node->put_ctx->request, CFSTR("If"), lockTokenRef ); 3891 CFRelease(lockTokenRef); 3892 lockTokenRef = NULL; 3893 } 3894 } 3895 else 3896 { 3897 lockTokenRef = NULL; 3898 } 3899 3900 /* apply credentials (if any) */ 3901 /* 3902 * statusCode will be 401 or 407 will not be NULL if we've already been through the loop; 3903 * statusCode will be 0 and responseRef will be NULL if this is the first time through. 3904 */ 3905 error = authcache_apply(uid, node->put_ctx->request, statusCode, responseRef, &auth_generation); 3906 if ( error != 0 ) 3907 { 3908 syslog(LOG_ERR, "%s: authcache_apply, error %d", __FUNCTION__, error); 3909 goto out1; 3910 } 3911 3912 3913 if(create_bound_streams(node->put_ctx) == false) { 3914 syslog(LOG_ERR, "%s: failed to create bound streams", __FUNCTION__); 3915 error = EIO; 3916 goto out1; 3917 } 3918 3919 // *************************************** 3920 // *** CREATE THE RESPONSE READ STREAM *** 3921 // *************************************** 3922 // Create the response read stream, passing the Read stream of the pair 3923 node->put_ctx->rspStreamRef = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, 3924 node->put_ctx->request, 3925 node->put_ctx->rdStreamRef); 3926 if (node->put_ctx->rspStreamRef == NULL) { 3927 syslog(LOG_ERR,"%s: CFReadStreamCreateForStreamedHTTPRequest failed\n", __FUNCTION__); 3928 goto out1; 3929 } 3930 3931 /* add proxies (if any) */ 3932 if (set_global_stream_properties(node->put_ctx->rspStreamRef) != 0) { 3933 syslog(LOG_ERR,"%s: set_global_stream_properties failed\n", __FUNCTION__); 3934 goto out1; 3935 } 3936 3937 /* apply any SSL properties we've already negotiated with the server */ 3938 ApplySSLProperties(node->put_ctx->rspStreamRef); 3939 3940 // Fire up the manager thread now 3941 requestqueue_enqueue_seqwrite_manager(node->put_ctx); 3942 3943 // Wait for manager to start running 3944 timeout.tv_sec = time(NULL) + WEBDAV_MANAGER_STARTUP_TIMEOUT; 3945 timeout.tv_nsec = 0; 3946 pthread_mutex_lock(&node->put_ctx->ctx_lock); 3947 while (node->put_ctx->mgr_status == WR_MGR_VIRGIN) { 3948 error = pthread_cond_timedwait(&node->put_ctx->ctx_condvar, &node->put_ctx->ctx_lock, &timeout); 3949 3950 if (error != 0) { 3951 syslog(LOG_ERR, "%s: pthread_cond_timedwait returned error %d", __FUNCTION__, error); 3952 node->put_ctx->finalStatus = EIO; 3953 node->put_ctx->finalStatusValid = true; 3954 error = EIO; 3955 pthread_mutex_unlock(&node->put_ctx->ctx_lock); 3956 goto out1; 3957 } else { 3958 // recalc timeout value 3959 timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_REQUEST_TIMEOUT; 3960 timeout.tv_nsec = 0; 3961 } 3962 } 3963 3964 pthread_mutex_unlock(&node->put_ctx->ctx_lock); 3965 3966out1: 3967 CFRelease(urlRef); 3968 return ( error ); 3969} 3970 3971void network_seqwrite_manager(struct stream_put_ctx *ctx) 3972{ 3973 CFMessagePortRef localPort; 3974 CFRunLoopSourceRef runLoopSource; 3975 CFStreamError streamError; 3976 CFIndex bytesWritten, len; 3977 struct seqwrite_mgr_req *curr_req = NULL; 3978 CFStringRef msgPortNameString = NULL; 3979 int result; 3980 bool didReceiveClose; 3981 3982 localPort = NULL; 3983 runLoopSource = NULL; 3984 didReceiveClose = false; 3985 3986 pthread_mutex_lock(&ctx->ctx_lock); 3987 ctx->mgr_rl = CFRunLoopGetCurrent(); 3988 CFRetain(ctx->mgr_rl); 3989 pthread_mutex_unlock(&ctx->ctx_lock); 3990 3991 // ************************************* 3992 // *** Schedule CFMessagePort Source *** 3993 // ************************************* 3994 3995 // generate a unique msg port name 3996 char msgPortName[WRITE_MGR_MSG_PORT_NAME_BUFSIZE]; 3997 snprintf(msgPortName, WRITE_MGR_MSG_PORT_NAME_BUFSIZE, WRITE_MGR_MSG_PORT_NAME_TEMPLATE, WRITE_MGR_MSG_PORT_NAME_BASE_STRING, getpid(), (void*)ctx); 3998 msgPortNameString = CFStringCreateWithBytes(kCFAllocatorDefault, 3999 (uint8_t*)msgPortName, 4000 strlen(msgPortName), 4001 kCFStringEncodingASCII, false); 4002 4003 if (msgPortNameString == NULL) { 4004 syslog(LOG_ERR, "%s: No mem for msgPortNameString\n", __FUNCTION__); 4005 pthread_mutex_lock(&ctx->ctx_lock); 4006 ctx->finalStatusValid = true; 4007 ctx->finalStatus = EIO; 4008 ctx->mgr_status = WR_MGR_DONE; 4009 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4010 pthread_mutex_unlock(&ctx->ctx_lock); 4011 goto out1; 4012 } 4013 4014 localPort = CFMessagePortCreateLocal(kCFAllocatorDefault, msgPortNameString, 4015 managerMessagePortCallback, NULL, NULL); 4016 4017 if (localPort == NULL) { 4018 syslog(LOG_ERR, "%s: CFMessagePortCreateLocal failed\n", __FUNCTION__); 4019 pthread_mutex_lock(&ctx->ctx_lock); 4020 ctx->finalStatusValid = true; 4021 ctx->finalStatus = EIO; 4022 ctx->mgr_status = WR_MGR_DONE; 4023 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4024 pthread_mutex_unlock(&ctx->ctx_lock); 4025 goto out1; 4026 } 4027 4028 runLoopSource = CFMessagePortCreateRunLoopSource( kCFAllocatorDefault, localPort, 0); 4029 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); 4030 4031 // Init a remote port so other threads can send to our Message Port 4032 ctx->mgrPort = CFMessagePortCreateRemote(kCFAllocatorDefault, msgPortNameString); 4033 4034 if (ctx->mgrPort == NULL) { 4035 syslog(LOG_ERR, "%s: CFMessagePortCreateRemote failed\n", __FUNCTION__); 4036 pthread_mutex_lock(&ctx->ctx_lock); 4037 ctx->finalStatusValid = true; 4038 ctx->finalStatus = EIO; 4039 ctx->mgr_status = WR_MGR_DONE; 4040 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4041 pthread_mutex_unlock(&ctx->ctx_lock); 4042 goto out1; 4043 } 4044 4045 // Done with msgPortNameString 4046 CFRelease(msgPortNameString); 4047 msgPortNameString = NULL; 4048 4049 // Setup our client context 4050 CFStreamClientContext mgrContext = {0, ctx, NULL, NULL, NULL}; 4051 4052 // ***************************** 4053 // *** Schedule Write stream *** 4054 // ***************************** 4055 CFWriteStreamSetClient (ctx->wrStreamRef, 4056 kCFStreamEventCanAcceptBytes | 4057 kCFStreamEventErrorOccurred | 4058 kCFStreamEventOpenCompleted | 4059 kCFStreamEventEndEncountered, 4060 writeseqWriteCallback, 4061 &mgrContext); 4062 4063 CFWriteStreamScheduleWithRunLoop(ctx->wrStreamRef, 4064 ctx->mgr_rl, 4065 kCFRunLoopDefaultMode); 4066 4067 if (CFWriteStreamOpen(ctx->wrStreamRef) != true) { 4068 syslog(LOG_ERR, "%s: failed to open write stream\n", __FUNCTION__); 4069 pthread_mutex_lock(&ctx->ctx_lock); 4070 ctx->finalStatusValid = true; 4071 ctx->finalStatus = EIO; 4072 ctx->mgr_status = WR_MGR_DONE; 4073 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4074 pthread_mutex_unlock(&ctx->ctx_lock); 4075 goto out1; 4076 } 4077 4078 // ******************************** 4079 // *** Schedule Response stream *** 4080 // ******************************** 4081 if ( !CFReadStreamSetClient(ctx->rspStreamRef, 4082 kCFStreamEventHasBytesAvailable | 4083 kCFStreamEventErrorOccurred | 4084 kCFStreamEventOpenCompleted | 4085 kCFStreamEventEndEncountered, 4086 writeseqReadResponseCallback, &mgrContext) ) 4087 { 4088 syslog(LOG_ERR, "%s: failed to set response stream client", __FUNCTION__); 4089 pthread_mutex_lock(&ctx->ctx_lock); 4090 ctx->finalStatus = EIO; 4091 ctx->finalStatusValid = true; 4092 ctx->mgr_status = WR_MGR_DONE; 4093 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4094 pthread_mutex_unlock(&ctx->ctx_lock); 4095 goto out1; 4096 } 4097 4098 CFReadStreamScheduleWithRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode); 4099 4100 /* open the response stream */ 4101 if ( CFReadStreamOpen(ctx->rspStreamRef) == FALSE ) 4102 { 4103 result = HandleSSLErrors(ctx->rspStreamRef); 4104 4105 if ( result == EAGAIN ) { 4106 syslog(LOG_DEBUG, "%s: CFReadStreamOpen: HandleSSLErrors: EAGAIN", __FUNCTION__); 4107 4108 CFReadStreamUnscheduleFromRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode); 4109 pthread_mutex_lock(&ctx->ctx_lock); 4110 ctx->finalStatus = EAGAIN; 4111 ctx->finalStatusValid = true; 4112 ctx->mgr_status = WR_MGR_DONE; 4113 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4114 pthread_mutex_unlock(&ctx->ctx_lock); 4115 } 4116 else { 4117 streamError = CFReadStreamGetError(ctx->rspStreamRef); 4118 if (!(ctx->is_retry) && 4119 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 4120 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost))) 4121 { 4122 /* 4123 * We got an EPIPE or HTTP Connection Lost error from the stream. We retry the PUT request 4124 * for these errors conditions 4125 */ 4126 syslog(LOG_DEBUG,"%s: CFReadStreamOpen: CFStreamError: domain %ld, error %lld (retrying)", 4127 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 4128 4129 CFReadStreamUnscheduleFromRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode); 4130 pthread_mutex_lock(&ctx->ctx_lock); 4131 ctx->finalStatus = EAGAIN; 4132 ctx->finalStatusValid = true; 4133 ctx->mgr_status = WR_MGR_DONE; 4134 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4135 pthread_mutex_unlock(&ctx->ctx_lock); 4136 } 4137 else { 4138 syslog(LOG_ERR,"%s: CFReadStreamOpen failed: CFStreamError: domain %ld, error %lld", 4139 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 4140 4141 4142 set_connectionstate(WEBDAV_CONNECTION_DOWN); 4143 4144 CFReadStreamUnscheduleFromRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode); 4145 pthread_mutex_lock(&ctx->ctx_lock); 4146 ctx->finalStatus = stream_error_to_errno(&streamError); 4147 ctx->finalStatusValid = true; 4148 ctx->mgr_status = WR_MGR_DONE; 4149 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4150 pthread_mutex_unlock(&ctx->ctx_lock); 4151 } 4152 } 4153 goto out1; 4154 } // <--- if( CFReadStreamOpen() == FALSE ) 4155 4156 // Everything is initalized, so we 4157 // can say we're running now 4158 pthread_mutex_lock(&ctx->ctx_lock); 4159 ctx->mgr_status = WR_MGR_RUNNING; 4160 pthread_cond_signal(&ctx->ctx_condvar); // signal setup thread 4161 pthread_mutex_unlock(&ctx->ctx_lock); 4162 4163 // Run the Runloop and handle callbacks 4164 while(1) 4165 { 4166 pthread_mutex_lock(&ctx->ctx_lock); 4167 4168 if (curr_req == NULL) { 4169 // dequeue the next request 4170 curr_req = dequeue_writemgr_request_locked(ctx); 4171 } 4172 4173 // Are we all done? 4174 if ((curr_req != NULL) && (curr_req->type == SEQWRITE_CLOSE)) { 4175 // syslog(LOG_DEBUG, "%s: SEQWRITE_CLOSE, closing write stream", __FUNCTION__); 4176 release_writemgr_request_locked(curr_req); 4177 curr_req = NULL; 4178 didReceiveClose = true; 4179 CFWriteStreamClose(ctx->wrStreamRef); 4180 } 4181 4182 if (ctx->finalStatusValid == true) { 4183 // syslog(LOG_DEBUG, "%s: finalStatusValid is true, exiting now", __FUNCTION__); 4184 4185 if (curr_req == NULL) { 4186 // dequeue the next request 4187 curr_req = dequeue_writemgr_request_locked(ctx); 4188 } 4189 4190 // cleanup 4191 while (curr_req) { 4192 if ( curr_req->type == SEQWRITE_CHUNK ) { 4193 // wake thread sleeping on this request 4194 pthread_mutex_lock(&curr_req->req_lock); 4195 curr_req->error = ctx->finalStatus; 4196 curr_req->request_done = true; 4197 pthread_cond_signal(&curr_req->req_condvar); 4198 pthread_mutex_unlock(&curr_req->req_lock); 4199 } 4200 release_writemgr_request_locked(curr_req); 4201 4202 curr_req = dequeue_writemgr_request_locked(ctx); 4203 } 4204 4205 // signal cleanup thread and exit 4206 ctx->mgr_status = WR_MGR_DONE; 4207 pthread_cond_signal(&ctx->ctx_condvar); 4208 pthread_mutex_unlock(&ctx->ctx_lock); 4209 4210 break; 4211 } 4212 4213 // Can we Write? 4214 if ( (ctx->canAcceptBytesEvents !=0) && (curr_req != NULL) && (didReceiveClose == false) ) { 4215 pthread_mutex_unlock(&ctx->ctx_lock); 4216 4217 // Now write the data 4218 len = curr_req->chunkLen - curr_req->chunkWritten; 4219 if (len <= 0) { 4220 // syslog(LOG_DEBUG,"%s: chunk written succesfully",__FUNCTION__); 4221 4222 if (len < 0) 4223 syslog(LOG_DEBUG,"%s: negative len value %ld for chunkLen %ld, chunkWritten %ld", 4224 __FUNCTION__, len, curr_req->chunkLen, curr_req->chunkWritten); 4225 4226 // wake thread sleeping on this request 4227 pthread_mutex_lock(&curr_req->req_lock); 4228 curr_req->error = 0; 4229 curr_req->request_done = true; 4230 pthread_cond_signal(&curr_req->req_condvar); 4231 pthread_mutex_unlock(&curr_req->req_lock); 4232 release_writemgr_request(ctx, curr_req); 4233 curr_req = NULL; 4234 continue; 4235 } else { 4236 // syslog(LOG_DEBUG,"%s: chunkWritten: %u len: %ld\n", 4237 // __FUNCTION__, curr_req->chunkWritten, len); 4238 4239 pthread_mutex_lock(&ctx->ctx_lock); 4240 ctx->canAcceptBytesEvents--; 4241 pthread_mutex_unlock(&ctx->ctx_lock); 4242 bytesWritten = CFWriteStreamWrite(ctx->wrStreamRef, (UInt8*)(curr_req->data + curr_req->chunkWritten), len); 4243 4244 if (bytesWritten < 0 ) { 4245 // bad 4246 streamError = CFWriteStreamGetError(ctx->wrStreamRef); 4247 if (!(ctx->is_retry) && 4248 ((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) || 4249 (streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost))) 4250 { 4251 /* 4252 * We got an EPIPE or HTTP Connection Lost error from the stream. We retry the PUT request 4253 * for these errors conditions 4254 */ 4255 syslog(LOG_DEBUG,"%s: bytesWritten < 0, CFStreamError: domain %ld, error %lld (retrying)", 4256 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 4257 4258 // wake thread sleeping on this request 4259 pthread_mutex_lock(&curr_req->req_lock); 4260 curr_req->error = EAGAIN; 4261 curr_req->request_done = true; 4262 pthread_cond_signal(&curr_req->req_condvar); 4263 pthread_mutex_unlock(&curr_req->req_lock); 4264 release_writemgr_request(ctx, curr_req); 4265 curr_req = NULL; 4266 } 4267 else 4268 { 4269 if ( get_connectionstate() == WEBDAV_CONNECTION_UP ) 4270 { 4271 syslog(LOG_DEBUG,"%s: CFStreamError: domain %ld, error %lld", 4272 __FUNCTION__, streamError.domain, (SInt64)streamError.error); 4273 } 4274 set_connectionstate(WEBDAV_CONNECTION_DOWN); 4275 4276 // wake thread sleeping on this request 4277 pthread_mutex_lock(&curr_req->req_lock); 4278 curr_req->error = EIO; 4279 curr_req->request_done = true; 4280 pthread_cond_signal(&curr_req->req_condvar); 4281 pthread_mutex_unlock(&curr_req->req_lock); 4282 release_writemgr_request(ctx, curr_req); 4283 curr_req = NULL; 4284 } 4285 } 4286 else 4287 curr_req->chunkWritten += bytesWritten; 4288 } 4289 } 4290 else 4291 pthread_mutex_unlock(&ctx->ctx_lock); 4292 4293 pthread_mutex_lock(&ctx->ctx_lock); 4294 if ( (ctx->canAcceptBytesEvents == 0) || (curr_req == NULL && ctx->req_head == NULL)) { 4295 pthread_mutex_unlock(&ctx->ctx_lock); 4296 CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, TRUE); 4297 } else { 4298 pthread_mutex_unlock(&ctx->ctx_lock); 4299 } 4300 } 4301 4302out1: 4303 if (localPort != NULL) { 4304 CFMessagePortInvalidate(localPort); 4305 CFRelease(localPort); 4306 } 4307 4308 if (runLoopSource != NULL) 4309 CFRelease(runLoopSource); 4310 if (msgPortNameString != NULL) 4311 CFRelease(msgPortNameString); 4312 return; 4313} 4314 4315/******************************************************************************/ 4316 4317// Note: ctx->lock must be held before calling this routine 4318int queue_writemgr_request_locked(struct stream_put_ctx *ctx, struct seqwrite_mgr_req *req) 4319{ 4320 SInt32 status; 4321 4322 if (ctx == NULL) { 4323 syslog(LOG_ERR, "%s: NULL ctx arg", __FUNCTION__); 4324 return (-1); 4325 } 4326 4327 if (req == NULL) { 4328 syslog(LOG_ERR, "%s: NULL request arg", __FUNCTION__); 4329 return (-1); 4330 } 4331 4332 // queue request 4333 if (ctx->req_head == NULL) { 4334 ctx->req_head = req; 4335 ctx->req_tail = req; 4336 req->prev = NULL; 4337 req->next = NULL; 4338 } else { 4339 ctx->req_tail->next = req; 4340 req->prev = ctx->req_tail; 4341 req->next = NULL; 4342 ctx->req_tail = req; 4343 } 4344 4345 // Add a reference 4346 req->refCount++; 4347 4348 // fire manager's runloop source 4349 status = CFMessagePortSendRequest( 4350 ctx->mgrPort, 4351 (SInt32) WRITE_MGR_NEW_REQUEST_ID, 4352 NULL, 4353 WRITE_MGR_MSG_PORTSEND_TIMEOUT, 4354 WRITE_MGR_MSG_PORTSEND_TIMEOUT, 4355 NULL, NULL); 4356 4357 if (status != kCFMessagePortSuccess) { 4358 syslog(LOG_ERR, "%s: CFMessagePort error %lld\n", __FUNCTION__, (SInt64)status); 4359 return (-1); 4360 } 4361 else 4362 return (0); 4363} 4364 4365/******************************************************************************/ 4366// Note: ctx->lock must be held before calling this routine 4367struct seqwrite_mgr_req *dequeue_writemgr_request_locked(struct stream_put_ctx *ctx) 4368{ 4369 struct seqwrite_mgr_req *req; 4370 4371 req = ctx->req_head; 4372 if (req != NULL) { 4373 // dequeue the request 4374 if (ctx->req_head == ctx->req_tail) { 4375 // only one in queue 4376 ctx->req_head = NULL; 4377 ctx->req_tail = NULL; 4378 } else { 4379 ctx->req_head = ctx->req_head->next; 4380 ctx->req_head->prev = NULL; 4381 } 4382 } 4383 4384 return req; 4385} 4386 4387/******************************************************************************/ 4388// Note: ctx->lock must be held before calling this routine 4389void release_writemgr_request_locked(struct seqwrite_mgr_req *req) 4390{ 4391 if (req->refCount) 4392 req->refCount--; 4393 4394 if (req->refCount == 0) { 4395 // no references remain, can free now 4396 if (req->data != NULL) 4397 free(req->data); 4398 free(req); 4399 } 4400} 4401 4402/******************************************************************************/ 4403void release_writemgr_request(struct stream_put_ctx *ctx, struct seqwrite_mgr_req *req) 4404{ 4405 pthread_mutex_lock(&ctx->ctx_lock); 4406 release_writemgr_request_locked(req); 4407 pthread_mutex_unlock(&ctx->ctx_lock); 4408} 4409 4410/******************************************************************************/ 4411 4412int network_fsync( 4413 uid_t uid, /* -> uid of the user making the request */ 4414 struct node_entry *node, /* -> node to sync with server */ 4415 off_t *file_length, /* <- length of file */ 4416 time_t *file_last_modified) /* <- date of last modification */ 4417{ 4418 int error; 4419 CFURLRef urlRef; 4420 CFHTTPMessageRef message; 4421 CFHTTPMessageRef responseRef; 4422 CFIndex statusCode; 4423 UInt32 auth_generation; 4424 CFStringRef lockTokenRef; 4425 char *file_entity_tag; 4426 int retryTransaction; 4427 4428 error = 0; 4429 *file_last_modified = -1; 4430 *file_length = -1; 4431 file_entity_tag = NULL; 4432 message = NULL; 4433 responseRef = NULL; 4434 statusCode = 0; 4435 auth_generation = 0; 4436 retryTransaction = TRUE; 4437 off_t contentLength; 4438 4439 /* create a CFURL to the node */ 4440 urlRef = create_cfurl_from_node(node, NULL, 0); 4441 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 4442 4443 /* get the file length */ 4444 contentLength = lseek(node->file_fd, 0LL, SEEK_END); 4445 4446 /* set the file position back to 0 */ 4447 lseek(node->file_fd, 0LL, SEEK_SET); 4448 4449 4450 // If this file is large, turn off data caching during the upload 4451 if (contentLength > (off_t)webdavCacheMaximumSize) 4452 fcntl(node->file_fd, F_NOCACHE, 1); 4453 4454 /* the transaction/authentication loop */ 4455 do 4456 { 4457 create_http_request_message(&message, urlRef, 0); 4458 require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO); 4459 4460 /* is there a lock token? */ 4461 if ( node->file_locktoken != NULL ) 4462 { 4463 /* in the unlikely event that this fails, the PUT may fail */ 4464 lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken); 4465 if ( lockTokenRef != NULL ) 4466 { 4467 CFHTTPMessageSetHeaderFieldValue(message, CFSTR("If"), lockTokenRef ); 4468 CFRelease(lockTokenRef); 4469 lockTokenRef = NULL; 4470 } 4471 } 4472 else 4473 { 4474 lockTokenRef = NULL; 4475 } 4476 4477 /* apply credentials (if any) */ 4478 /* 4479 * statusCode will be 401 or 407 and responseRef will not be NULL if we've already been through the loop; 4480 * statusCode will be 0 and responseRef will be NULL if this is the first time through. 4481 */ 4482 error = authcache_apply(uid, message, (UInt32)statusCode, responseRef, &auth_generation); 4483 if ( error != 0 ) 4484 { 4485 break; 4486 } 4487 4488 /* stream_transaction returns responseRef so release it if left from previous loop */ 4489 if ( responseRef != NULL ) 4490 { 4491 CFRelease(responseRef); 4492 responseRef = NULL; 4493 } 4494 /* now that everything's ready to send, send it */ 4495 4496 error = stream_transaction_from_file(message, node->file_fd, &retryTransaction, &responseRef); 4497 if ( error == EAGAIN ) 4498 { 4499 statusCode = 0; 4500 /* responseRef will be left NULL on retries */ 4501 } 4502 else if ( error != 0 ) 4503 { 4504 break; 4505 } 4506 else 4507 { 4508 /* get the status code */ 4509 statusCode = CFHTTPMessageGetResponseStatusCode(responseRef); 4510 } 4511 4512 } while ( error == EAGAIN || statusCode == 401 || statusCode == 407 ); 4513 4514CFHTTPMessageCreateRequest: 4515 4516 if ( error == 0 ) 4517 { 4518 error = translate_status_to_error((UInt32)statusCode); 4519 if ( error == 0 ) 4520 { 4521 /* 4522 * when we get here with no errors, then we need to tell the authcache the 4523 * transaction worked so it can mark the credentials valid and, if needed 4524 * add the credentials to the keychain. If the auth_generation changed, then 4525 * another transaction updated the authcache element after we got it. 4526 */ 4527 (void) authcache_valid(uid, message, auth_generation); 4528 add_last_mod_etag(responseRef, file_last_modified, &file_entity_tag); 4529 } 4530 } 4531 4532 if ( message != NULL ) 4533 { 4534 CFRelease(message); 4535 } 4536 4537 if ( responseRef != NULL ) 4538 { 4539 CFRelease(responseRef); 4540 } 4541 4542 if ( (error == 0) && (*file_last_modified == -1) && (file_entity_tag == NULL) ) 4543 { 4544 int propError; 4545 UInt8 *responseBuffer; 4546 CFIndex count; 4547 CFDataRef bodyData; 4548 /* the xml for the message body */ 4549 const UInt8 xmlString[] = 4550 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 4551 "<D:propfind xmlns:D=\"DAV:\">\n" 4552 "<D:prop>\n" 4553 "<D:getlastmodified/>\n" 4554 "<D:getetag/>\n" 4555 "</D:prop>\n" 4556 "</D:propfind>\n"; 4557 /* the 3 headers */ 4558 CFIndex headerCount = 3; 4559 struct HeaderFieldValue headers[] = { 4560 { CFSTR("Accept"), CFSTR("*/*") }, 4561 { CFSTR("Content-Type"), CFSTR("text/xml") }, 4562 { CFSTR("Depth"), CFSTR("0") }, 4563 { CFSTR("translate"), CFSTR("f") } 4564 }; 4565 4566 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 4567 /* translate flag only for Microsoft IIS Server */ 4568 headerCount += 1; 4569 } 4570 4571 propError = 0; 4572 responseBuffer = NULL; 4573 4574 /* create the message body with the xml */ 4575 bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull); 4576 require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, propError = EIO); 4577 4578 /* send request to the server and get the response */ 4579 propError = send_transaction(uid, urlRef, NULL, CFSTR("PROPFIND"), bodyData, 4580 headerCount, headers, REDIRECT_AUTO, &responseBuffer, &count, NULL); 4581 if ( propError == 0 ) 4582 { 4583 /* parse responseBuffer to get file_last_modified and/or file_entity_tag */ 4584 propError = parse_cachevalidators(responseBuffer, count, file_last_modified, &file_entity_tag); 4585 /* free the response buffer */ 4586 free(responseBuffer); 4587 } 4588 4589 /* release the message body */ 4590 CFRelease(bodyData); 4591 4592CFDataCreateWithBytesNoCopy: 4593 ; 4594 } 4595 4596 CFRelease(urlRef); 4597 4598create_cfurl_from_node: 4599 4600 if ( !error ) 4601 { 4602 node->file_last_modified = *file_last_modified; 4603 if ( node->file_entity_tag != NULL ) 4604 { 4605 free(node->file_entity_tag); 4606 } 4607 node->file_entity_tag = file_entity_tag; 4608 4609 /* get the file length */ 4610 *file_length = lseek(node->file_fd, 0LL, SEEK_END); 4611 } 4612 4613 return ( error ); 4614} 4615 4616// 4617// network_handle_multistatus_reply 4618// 4619// This routine will parse an 207 multistatus reply in 'responseBuffer', returning the http status code 4620// in 'statusCode' arg for the resource given by 'urlRef' arg. 4621// 4622// Return Values: 4623// 4624// SUCCESS: Returns zero, status code returned in 'statusCode' arg. 4625// FAILURE: Returns non-zero, nothing returned (contents of'statusCode' arg undefined). 4626// 4627static int network_handle_multistatus_reply(CFURLRef urlRef, UInt8 *responseBuffer, CFIndex responseBufferLen, CFIndex *statusCode) 4628{ 4629 webdav_parse_multistatus_list_t *statusList; 4630 webdav_parse_multistatus_element_t *elementPtr, *nextElementPtr; 4631 CFStringRef urlStrRef; 4632 char *urlStr, *urlPtr, *st; 4633 size_t urlLen, matchLen; 4634 int error; 4635 4636 error = EIO; 4637 statusList = NULL; 4638 urlStrRef = NULL; 4639 urlStr = NULL; 4640 4641 // parse multistatus reply to get the error 4642 statusList = parse_multi_status(responseBuffer, responseBufferLen); 4643 if (statusList == NULL) 4644 goto parsed_nothing; 4645 4646 urlStrRef = CFURLGetString(urlRef); 4647 4648 if (urlStrRef == NULL) 4649 goto parsed_nothing; 4650 4651 CFRetain(urlStrRef); 4652 4653 urlStr = CopyCFStringToCString(urlStrRef); 4654 4655 if (urlStr == NULL) 4656 goto parsed_nothing; 4657 4658 // Scan for the "//" in urlStr scheme://host:port/... 4659 urlPtr = strstr(urlStr, "//"); 4660 4661 // urlPtr pointing at "//host:port/..." 4662 // Advance past the "//" 4663 if (urlPtr != NULL) 4664 urlPtr++; 4665 if (urlPtr != NULL) 4666 urlPtr++; 4667 4668 // urlPtr pointing at "host:port/..." 4669 // Advance to first "/" trailing "hostname:port" 4670 if (urlPtr != NULL) 4671 urlPtr = strstr(urlPtr, "/"); // Advance to first "/" following hostname in urlStr 4672 4673 if (urlPtr == NULL) 4674 urlPtr = urlStr; // Didn't find "scheme://host:port" in urlStr 4675 4676 urlLen = strlen(urlPtr); 4677 4678 elementPtr = statusList->head; 4679 while (elementPtr != NULL) { 4680 if (elementPtr->name == NULL) { 4681 continue; // skipit 4682 } 4683 4684 if (elementPtr->seen_href == FALSE) 4685 continue; // skipit 4686 4687 matchLen = strlen((char *)elementPtr->name); 4688 4689 if (matchLen <= 0) { 4690 continue; // skipit 4691 } 4692 4693 if (matchLen > urlLen) { 4694 continue; // skipit 4695 } 4696 4697 st = strnstr((char *)elementPtr->name, urlPtr, urlLen); 4698 4699 if ( st != NULL) { 4700 error = 0; 4701 *statusCode = elementPtr->statusCode; 4702 break; 4703 } 4704 4705 elementPtr = elementPtr->next; 4706 } 4707 4708parsed_nothing: 4709 // cleanup 4710 if (urlStr) 4711 free(urlStr); 4712 if (urlStrRef) 4713 CFRelease(urlStrRef); 4714 if (statusList) { 4715 elementPtr = statusList->head; 4716 while (elementPtr != NULL) { 4717 nextElementPtr = elementPtr->next; 4718 free(elementPtr); 4719 elementPtr = nextElementPtr; 4720 } 4721 free(statusList); 4722 } 4723 4724 return (error); 4725} 4726 4727/******************************************************************************/ 4728 4729static int network_delete( 4730 uid_t uid, /* -> uid of the user making the request */ 4731 CFURLRef urlRef, /* -> url to delete */ 4732 struct node_entry *node, /* -> node to remove on the server */ 4733 time_t *remove_date) /* <- date of the removal */ 4734{ 4735 int error; 4736 CFStringRef lockTokenRef; 4737 CFHTTPMessageRef responseRef; 4738 UInt8 *responseBuffer; 4739 CFIndex count, statusCode; 4740 CFStringRef urlStrRef; 4741 char *urlStr; 4742 4743 /* possibly 2 headers */ 4744 CFIndex headerCount; 4745 struct HeaderFieldValue headers2[] = { 4746 { CFSTR("Accept"), CFSTR("*/*") }, 4747 { CFSTR("If"), NULL }, 4748 { CFSTR("translate"), CFSTR("f") } 4749 }; 4750 struct HeaderFieldValue headers1[] = { 4751 { CFSTR("Accept"), CFSTR("*/*") }, 4752 { CFSTR("translate"), CFSTR("f") } 4753 }; 4754 4755 *remove_date = -1; 4756 4757 responseRef = NULL; 4758 urlStrRef = NULL; 4759 urlStr = NULL; 4760 4761 if ( node->file_locktoken != NULL ) 4762 { 4763 /* in the unlikely event that this fails, the DELETE will fail */ 4764 lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken); 4765 if ( lockTokenRef != NULL ) 4766 { 4767 headerCount = 2; 4768 headers2[1].value = lockTokenRef; 4769 } 4770 else 4771 { 4772 headerCount = 1; 4773 } 4774 } 4775 else 4776 { 4777 lockTokenRef = NULL; 4778 headerCount = 1; 4779 } 4780 4781 /* send request to the server and get the response */ 4782 if (headerCount == 1) { 4783 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 4784 /* translate flag only for Microsoft IIS Server */ 4785 headerCount += 1; 4786 } 4787 error = send_transaction(uid, urlRef, NULL, CFSTR("DELETE"), NULL, headerCount, headers1, REDIRECT_DISABLE, &responseBuffer, &count, &responseRef); 4788 } 4789 else { 4790 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 4791 /* translate flag only for Microsoft IIS Server */ 4792 headerCount += 1; 4793 } 4794 error = send_transaction(uid, urlRef, NULL, CFSTR("DELETE"), NULL, headerCount, headers2, REDIRECT_DISABLE, &responseBuffer, &count, &responseRef); 4795 } 4796 4797 if ( !error ) 4798 { 4799 // Grab the status code from the response 4800 statusCode = CFHTTPMessageGetResponseStatusCode(responseRef); 4801 4802 if (statusCode == 207) 4803 { 4804 // A 207 on a DELETE request is almost always a failed dependency error. 4805 // The multistatus reply allows the server to specify the the actual dependency. 4806 error = network_handle_multistatus_reply(urlRef, responseBuffer, count, &statusCode); 4807 4808 urlStrRef = CFURLGetString(urlRef); 4809 if (urlStrRef) { 4810 CFRetain(urlStrRef); 4811 urlStr = CopyCFStringToCString(urlStrRef); 4812 } 4813 4814 // Log a message 4815 if (!error) { 4816 if (urlStr) { 4817 syslog(LOG_ERR, "Error deleting %s, http status code %ld\n", urlStr, statusCode); 4818 } 4819 else 4820 syslog(LOG_ERR, "A Delete request failed, http status code %ld\n", statusCode); 4821 } 4822 else { 4823 if (urlStr) { 4824 syslog(LOG_ERR, "A Delete request failed for %s\n", urlStr); 4825 } 4826 else 4827 syslog(LOG_ERR, "A Delete request failed, unable to parse reply\n"); 4828 } 4829 4830 // clean up a bit 4831 if (urlStr) 4832 free(urlStr); 4833 if (urlStrRef) 4834 CFRelease(urlStrRef); 4835 4836 // Regardless of the failed dependency, the bottom line is this file or folder in question is busy 4837 error = EBUSY; 4838 } 4839 else { 4840 CFStringRef dateHeaderRef; 4841 4842 dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("Date")); 4843 if ( dateHeaderRef != NULL ) 4844 { 4845 *remove_date = DateStringToTime(dateHeaderRef); 4846 4847 CFRelease(dateHeaderRef); 4848 } 4849 } 4850 4851 // Release the response buffer 4852 if (responseBuffer) 4853 free(responseBuffer); 4854 4855 CFRelease(responseRef); 4856 } 4857 4858 if ( lockTokenRef != NULL ) 4859 { 4860 CFRelease(lockTokenRef); 4861 } 4862 4863 return ( error ); 4864} 4865 4866/******************************************************************************/ 4867 4868int network_remove( 4869 uid_t uid, /* -> uid of the user making the request */ 4870 struct node_entry *node, /* -> file node to remove on the server */ 4871 time_t *remove_date) /* <- date of the removal */ 4872{ 4873 int error; 4874 CFURLRef urlRef; 4875 4876 error = 0; 4877 4878 /* create a CFURL to the node */ 4879 urlRef = create_cfurl_from_node(node, NULL, 0); 4880 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 4881 4882 /* let network_delete do the rest of the work */ 4883 error = network_delete(uid, urlRef, node, remove_date); 4884 4885 CFRelease(urlRef); 4886 4887create_cfurl_from_node: 4888 4889 return ( error ); 4890} 4891 4892/******************************************************************************/ 4893 4894int network_rmdir( 4895 uid_t uid, /* -> uid of the user making the request */ 4896 struct node_entry *node, /* -> directory node to remove on the server */ 4897 time_t *remove_date) /* <- date of the removal */ 4898{ 4899 int error; 4900 CFURLRef urlRef; 4901 4902 error = 0; 4903 4904 /* create a CFURL to the node */ 4905 urlRef = create_cfurl_from_node(node, NULL, 0); 4906 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 4907 4908 /* make sure the directory is empty */ 4909 error = network_dir_is_empty(uid, urlRef); 4910 if ( !error ) 4911 { 4912 /* let network_delete do the rest of the work */ 4913 error = network_delete(uid, urlRef, node, remove_date); 4914 } 4915 4916 CFRelease(urlRef); 4917 4918create_cfurl_from_node: 4919 4920 return ( error ); 4921} 4922 4923/******************************************************************************/ 4924 4925/* NOTE: this will call network_dir_is_empty() if to_node is a valid and a directory. 4926 */ 4927int network_rename( 4928 uid_t uid, /* -> uid of the user making the request */ 4929 struct node_entry *from_node, /* node to move */ 4930 struct node_entry *to_node, /* node to move over (ignored if NULL) */ 4931 struct node_entry *to_dir_node, /* directory node move into (ignored if to_node != NULL) */ 4932 char *to_name, /* new name for the object (ignored if to_node != NULL) */ 4933 size_t to_name_length, /* length of to_name (ignored if to_node != NULL) */ 4934 time_t *rename_date) /* <- date of the rename */ 4935{ 4936 int error; 4937 CFURLRef urlRef; 4938 CFURLRef destinationUrlRef; 4939 CFStringRef destinationRef; 4940 CFHTTPMessageRef response; 4941 CFArrayRef lockTokenArr; 4942 CFStringRef lockTokenRef; 4943 CFIndex i, lockTokenCount, headerIndex, headerCount; 4944 bool needTranslateFlag; 4945 struct HeaderFieldValue *headers; 4946 4947 headerCount = 2; // the 2 headers "Accept" and "Destination" 4948 needTranslateFlag = false; 4949 lockTokenCount = 0; 4950 headerIndex = 0; 4951 lockTokenArr = NULL; 4952 headers = NULL; 4953 *rename_date = -1; 4954 urlRef = NULL; 4955 destinationUrlRef = NULL; 4956 destinationRef = NULL; 4957 4958 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 4959 /* translate flag only for Microsoft IIS Server */ 4960 headerCount += 1; 4961 needTranslateFlag = true; 4962 } 4963 4964 // First retrieve from_node's lock tocken(s) 4965 lockTokenArr = nodecache_get_locktokens(from_node); 4966 4967 if (lockTokenArr != NULL) 4968 lockTokenCount = CFArrayGetCount(lockTokenArr); 4969 4970 headerCount += lockTokenCount; 4971 4972 // Now allocate space for headers 4973 headers = (struct HeaderFieldValue *)malloc(sizeof(struct HeaderFieldValue) * headerCount); 4974 require_action_quiet(headers != NULL, exit, error = ENOMEM); 4975 4976 // Setup initial headers 4977 headers[headerIndex].headerField = CFSTR("Accept"); 4978 headers[headerIndex].value = CFSTR("*/*"); 4979 headerIndex++; 4980 4981 headers[headerIndex].headerField = CFSTR("Destination"); 4982 headers[headerIndex].value = NULL; 4983 headerIndex++; 4984 4985 if (needTranslateFlag == true) { 4986 /* translate flag only for Microsoft IIS Server */ 4987 headers[headerIndex].headerField = CFSTR("translate"); 4988 headers[headerIndex].value = CFSTR("f"); 4989 headerIndex++; 4990 } 4991 4992 // Add locktokens if any 4993 if (lockTokenCount) { 4994 for (i = 0; i < lockTokenCount; i++) { 4995 lockTokenRef = (CFStringRef)CFArrayGetValueAtIndex(lockTokenArr, i); 4996 if ( lockTokenRef != NULL ) 4997 { 4998 headers[headerIndex].headerField = CFSTR("If"); 4999 headers[headerIndex].value = lockTokenRef; 5000 headerIndex++; 5001 } 5002 } 5003 } 5004 5005 /* create a CFURL to the from_node */ 5006 urlRef = create_cfurl_from_node(from_node, NULL, 0); 5007 require_action_quiet(urlRef != NULL, exit, error = EIO); 5008 5009 /* create the URL for the destination */ 5010 if ( to_node != NULL ) 5011 { 5012 /* use to_node */ 5013 5014 /* create a CFURL to the to_node */ 5015 destinationUrlRef = create_cfurl_from_node(to_node, NULL, 0); 5016 require_action_quiet(destinationUrlRef != NULL, exit, error = EIO); 5017 5018 /* if source and destination are equal, there's nothing to do so leave with no error */ 5019 require_action_quiet( !CFEqual(urlRef, destinationUrlRef), exit, error = 0); 5020 5021 /* is the destination a directory? */ 5022 if ( to_node->node_type == WEBDAV_DIR_TYPE ) 5023 { 5024 /* make sure the directory is empty before attempting to move over it */ 5025 error = network_dir_is_empty(uid, destinationUrlRef); 5026 require_noerr_quiet(error, exit); 5027 } 5028 } 5029 else 5030 { 5031 /* use to_dir_node and to_name */ 5032 5033 /* create a CFURL to the to_dir_node plus name */ 5034 destinationUrlRef = create_cfurl_from_node(to_dir_node, to_name, to_name_length); 5035 require_action_quiet(destinationUrlRef != NULL, exit, error = EIO); 5036 5037 /* if source and destination are equal, there's nothing to do so leave with no error */ 5038 require_action_quiet( !CFEqual(urlRef, destinationUrlRef), exit, error = 0); 5039 } 5040 5041 destinationRef = CFURLGetString(destinationUrlRef); 5042 require_action(destinationRef != NULL, exit, error = EIO); 5043 5044 headers[1].value = destinationRef; /* done with that mess... */ 5045 5046 /* send request to the server and get the response */ 5047 error = send_transaction(uid, urlRef, NULL, CFSTR("MOVE"), NULL, 5048 headerCount, headers, REDIRECT_DISABLE, NULL, NULL, &response); 5049 if ( !error ) 5050 { 5051 CFStringRef dateHeaderRef; 5052 5053 dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Date")); 5054 if ( dateHeaderRef != NULL ) 5055 { 5056 *rename_date = DateStringToTime(dateHeaderRef); 5057 5058 CFRelease(dateHeaderRef); 5059 } 5060 /* release the response buffer */ 5061 CFRelease(response); 5062 } 5063 5064exit: 5065 5066 if ( destinationUrlRef != NULL ) 5067 { 5068 CFRelease(destinationUrlRef); 5069 } 5070 if ( urlRef != NULL ) 5071 { 5072 CFRelease(urlRef); 5073 } 5074 if ( lockTokenArr != NULL) 5075 { 5076 CFRelease(lockTokenArr); 5077 } 5078 if (headers != NULL) 5079 { 5080 free(headers); 5081 } 5082 5083 return ( error ); 5084} 5085 5086/******************************************************************************/ 5087 5088int network_lock( 5089 uid_t uid, /* -> uid of the user making the request (ignored if refreshing) */ 5090 int refresh, /* -> if FALSE, we're getting the lock (for uid); if TRUE, we're refreshing the lock */ 5091 struct node_entry *node) /* -> node to get/renew server lock on */ 5092{ 5093 int error; 5094 CFURLRef urlRef; 5095 CFHTTPMessageRef responseRef; 5096 UInt8 *responseBuffer; 5097 CFIndex count, statusCode; 5098 CFDataRef bodyData; 5099 CFStringRef urlStrRef; 5100 char *urlStr; 5101 char* locktokentofree = NULL; 5102 uid_t file_locktoken_uid = 0; 5103 const UInt8 xmlString[] = 5104 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 5105 "<D:lockinfo xmlns:D=\"DAV:\">\n" 5106 "<D:lockscope><D:exclusive/></D:lockscope>\n" 5107 "<D:locktype><D:write/></D:locktype>\n" 5108 "<D:owner>\n" 5109 "<D:href>http://www.apple.com/webdav_fs/</D:href>\n" /* this used to be "default-owner" instead of the url */ 5110 "</D:owner>\n" 5111 "</D:lockinfo>\n"; 5112 /* the 3 headers */ 5113 CFIndex headerCount = 4; 5114 struct HeaderFieldValue headers5[] = { 5115 { CFSTR("Accept"), CFSTR("*/*") }, 5116 { CFSTR("Depth"), CFSTR("0") }, 5117 { CFSTR("Timeout"), NULL }, 5118 { CFSTR("Content-Type"), NULL }, 5119 { CFSTR("If"), NULL }, 5120 { CFSTR("translate"), CFSTR("f") } 5121 }; 5122 struct HeaderFieldValue headers4[] = { 5123 { CFSTR("Accept"), CFSTR("*/*") }, 5124 { CFSTR("Depth"), CFSTR("0") }, 5125 { CFSTR("Timeout"), NULL }, 5126 { CFSTR("Content-Type"), NULL }, 5127 { CFSTR("translate"), CFSTR("f") } 5128 }; 5129 CFStringRef timeoutSpecifierRef; 5130 CFStringRef lockTokenRef; 5131 5132 responseRef = NULL; 5133 lockTokenRef = NULL; 5134 urlStrRef = NULL; 5135 urlStr = NULL; 5136 5137 lock_node_cache(); 5138 locktokentofree = node->file_locktoken; 5139 node->file_locktoken = NULL; 5140 file_locktoken_uid = node->file_locktoken_uid; 5141 node->file_locktoken_uid = 0; 5142 unlock_node_cache(); 5143 5144 /* create a CFURL to the node */ 5145 urlRef = create_cfurl_from_node(node, NULL, 0); 5146 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 5147 5148 timeoutSpecifierRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("Second-%s"), gtimeout_string); 5149 require_action(timeoutSpecifierRef != NULL, CFStringCreateWithFormat_timeoutSpecifierRef, error = EIO); 5150 5151 headers4[2].value = timeoutSpecifierRef; 5152 headers5[2].value = timeoutSpecifierRef; 5153 5154 if ( refresh ) 5155 { 5156 /* if refreshing, use the uid associated with the file_locktoken */ 5157 uid = file_locktoken_uid; 5158 5159 /* if refreshing the lock, there's no message body */ 5160 bodyData = NULL; 5161 5162 headerCount = 5; 5163 headers5[3].value = CFSTR("text/xml"); 5164 lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), locktokentofree); 5165 require_action(lockTokenRef != NULL, CFStringCreateWithFormat_lockTokenRef, error = EIO); 5166 5167 headers5[4].value = lockTokenRef; 5168 } 5169 else 5170 { 5171 lockTokenRef = NULL; 5172 /* create a CFDataRef with the xml that is our message body */ 5173 bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull); 5174 require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO); 5175 5176 headerCount = 4; 5177 headers4[3].value = CFSTR("text/xml; charset=\"utf-8\""); 5178 } 5179 5180 /* send request to the server and get the response */ 5181 if (headerCount == 4) { 5182 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 5183 /* translate flag only for Microsoft IIS Server */ 5184 headerCount += 1; 5185 } 5186 error = send_transaction(uid, urlRef, NULL, CFSTR("LOCK"), bodyData, headerCount, headers4, REDIRECT_DISABLE, &responseBuffer, &count, &responseRef); 5187 } 5188 else { 5189 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 5190 /* translate flag only for Microsoft IIS Server */ 5191 headerCount += 1; 5192 } 5193 error = send_transaction(uid, urlRef, NULL, CFSTR("LOCK"), bodyData, headerCount, headers5, REDIRECT_DISABLE, &responseBuffer, &count, &responseRef); 5194 } 5195 5196 if ( !error ) 5197 { 5198 // Grab the status code from the response 5199 statusCode = CFHTTPMessageGetResponseStatusCode(responseRef); 5200 CFRelease(responseRef); 5201 5202 if (statusCode == 207) 5203 { 5204 // A 207 on a LOCK request is almost always a failed dependency error (i.e. http status 424). 5205 // The multistatus reply allows the server to specify the actual dependency. 5206 error = network_handle_multistatus_reply(urlRef, responseBuffer, count, &statusCode); 5207 5208 urlStrRef = CFURLGetString(urlRef); 5209 if (urlStrRef) { 5210 CFRetain(urlStrRef); 5211 urlStr = CopyCFStringToCString(urlStrRef); 5212 } 5213 5214 // Log a message 5215 if (!error) { 5216 if (urlStr) { 5217 syslog(LOG_ERR, "Error locking %s, http status code %ld\n", urlStr, statusCode); 5218 } 5219 else 5220 syslog(LOG_ERR, "Lock request failed, http status code %ld\n", statusCode); 5221 } 5222 else { 5223 if (urlStr) { 5224 syslog(LOG_ERR, "Lock request failed for %s\n", urlStr); 5225 } 5226 else 5227 syslog(LOG_ERR, "A Lock request failed, unable to parse reply\n"); 5228 } 5229 5230 // Regardless of the dependency, the bottom line is this file or folder in question is busy 5231 error = EBUSY; 5232 5233 // clean up a bit 5234 if (urlStr) 5235 free(urlStr); 5236 if (urlStrRef) 5237 CFRelease(urlStrRef); 5238 } 5239 else { 5240 char *locktoken = NULL; 5241 5242 /* parse responseBuffer to get the lock token */ 5243 error = parse_lock(responseBuffer, count, &locktoken); 5244 5245 lock_node_cache(); 5246 if (!error) 5247 { 5248 node->file_locktoken = locktoken; 5249 if ( locktokentofree != NULL ) 5250 { 5251 5252 free(locktokentofree); 5253 locktokentofree = NULL; 5254 } 5255 /* file_locktoken_uid is already set if refreshing */ 5256 if ( !refresh ) 5257 { 5258 node->file_locktoken_uid = uid; 5259 } 5260 } else { 5261 node->file_locktoken = locktokentofree; 5262 locktokentofree = NULL; 5263 } 5264 unlock_node_cache(); 5265 } 5266 5267 // Release the response buffer 5268 if (responseBuffer) 5269 free(responseBuffer); 5270 } 5271 5272 if ( bodyData != NULL ) 5273 { 5274 CFRelease(bodyData); 5275 } 5276 5277 if ( lockTokenRef != NULL ) 5278 { 5279 CFRelease(lockTokenRef); 5280 } 5281 5282CFDataCreateWithBytesNoCopy: 5283CFStringCreateWithFormat_lockTokenRef: 5284 5285 CFRelease(timeoutSpecifierRef); 5286 5287CFStringCreateWithFormat_timeoutSpecifierRef: 5288 5289 CFRelease(urlRef); 5290 5291create_cfurl_from_node: 5292 5293 return ( error ); 5294} 5295 5296/******************************************************************************/ 5297 5298int network_unlock(struct node_entry *node) 5299{ 5300 int error = 0; 5301 lock_node_cache(); 5302 if ( node->file_locktoken != NULL ) { 5303 /* nothing we can do network_unlock fails -- the lock will time out eventually */ 5304 error = network_unlock_with_nodecache_locked(node); 5305 } 5306 unlock_node_cache(); 5307 return error; 5308} 5309 5310/******************************************************************************/ 5311 5312int network_unlock_with_nodecache_locked( 5313 struct node_entry *node) /* -> node to unlock on server */ 5314{ 5315 int error; 5316 CFURLRef urlRef; 5317 CFStringRef lockTokenRef; 5318 char* locktokentofree = NULL; 5319 uid_t file_locktoken_uid = 0; 5320 /* the 2 headers */ 5321 CFIndex headerCount = 2; 5322 struct HeaderFieldValue headers[] = { 5323 { CFSTR("Accept"), CFSTR("*/*") }, 5324 { CFSTR("Lock-Token"), NULL }, 5325 { CFSTR("translate"), CFSTR("f") } 5326 }; 5327 5328 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 5329 /* translate flag only for Microsoft IIS Server */ 5330 headerCount += 1; 5331 } 5332 5333 locktokentofree = node->file_locktoken; 5334 node->file_locktoken = NULL; 5335 file_locktoken_uid = node->file_locktoken_uid; 5336 node->file_locktoken_uid = 0; 5337 5338 /*unlocking the node cache before calling send_transaction()*/ 5339 unlock_node_cache(); 5340 5341 /* create a CFURL to the node */ 5342 urlRef = create_cfurl_from_node(node, NULL, 0); 5343 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 5344 5345 /* in the unlikely event that this fails, the DELETE will fail */ 5346 lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%s>"), locktokentofree); 5347 require_action_quiet(lockTokenRef != NULL, CFStringCreateWithFormat, error = EIO); 5348 5349 headers[1].value = lockTokenRef; 5350 5351 /* send request to the server and get the response */ 5352 /* Note: we use the credentials of the user than obtained the LOCK */ 5353 error = send_transaction(file_locktoken_uid, urlRef, NULL, CFSTR("UNLOCK"), NULL, 5354 headerCount, headers, REDIRECT_DISABLE, NULL, NULL, NULL); 5355 5356 CFRelease(lockTokenRef); 5357 5358CFStringCreateWithFormat: 5359 5360 CFRelease(urlRef); 5361 5362create_cfurl_from_node: 5363 5364 free(locktokentofree); 5365 locktokentofree = NULL; 5366 5367 /*Locking the node cache as the function returns a locked node*/ 5368 lock_node_cache(); 5369 5370 return ( error ); 5371} 5372 5373/******************************************************************************/ 5374 5375int network_readdir( 5376 uid_t uid, /* -> uid of the user making the request */ 5377 int cache, /* -> if TRUE, perform additional caching */ 5378 struct node_entry *node) /* -> directory node to read */ 5379{ 5380 int error, redir_cnt; 5381 CFURLRef urlRef; 5382 UInt8 *responseBuffer; 5383 CFIndex count; 5384 CFDataRef bodyData; 5385 const UInt8 xmlString[] = 5386 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 5387 "<D:propfind xmlns:D=\"DAV:\">\n" 5388 "<D:prop>\n" 5389 "<D:getlastmodified/>\n" 5390 "<D:getcontentlength/>\n" 5391 "<D:creationdate/>\n" 5392 "<D:resourcetype/>\n" 5393 "</D:prop>\n" 5394 "</D:propfind>\n"; 5395 const UInt8 xmlStringCache[] = 5396 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 5397 "<D:propfind xmlns:D=\"DAV:\">\n" 5398 "<D:prop xmlns:A=\"http://www.apple.com/webdav_fs/props/\">\n" 5399 "<D:getlastmodified/>\n" 5400 "<D:getcontentlength/>\n" 5401 "<D:creationdate/>\n" 5402 "<D:resourcetype/>\n" 5403 "<A:appledoubleheader/>\n" 5404 "</D:prop>\n" 5405 "</D:propfind>\n"; 5406 /* the 3 headers */ 5407 CFIndex headerCount = 3; 5408 struct HeaderFieldValue headers[] = { 5409 { CFSTR("Accept"), CFSTR("*/*") }, 5410 { CFSTR("Content-Type"), CFSTR("text/xml") }, 5411 { CFSTR("Depth"), CFSTR("1") }, 5412 { CFSTR("translate"), CFSTR("f") } 5413 }; 5414 5415 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 5416 /* translate flag only for Microsoft IIS Server */ 5417 headerCount += 1; 5418 } 5419 5420 /* create a CFDataRef with the xml that is our message body */ 5421 bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 5422 (cache ? xmlStringCache : xmlString), strlen((const char *)(cache ? xmlStringCache : xmlString)), kCFAllocatorNull); 5423 require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO); 5424 5425 /* send request to the server and get the response */ 5426 redir_cnt = 0; 5427 while (redir_cnt < WEBDAV_MAX_REDIRECTS) { 5428 /* create a CFURL to the node */ 5429 urlRef = create_cfurl_from_node(node, NULL, 0); 5430 5431 if (urlRef == NULL) { 5432 error = EIO; 5433 break; 5434 } 5435 5436 error = send_transaction(uid, urlRef, node, CFSTR("PROPFIND"), bodyData, 5437 headerCount, headers, REDIRECT_MANUAL, &responseBuffer, &count, NULL); 5438 if ( !error ) 5439 { 5440 /* parse responseBuffer to create the directory file */ 5441 error = parse_opendir(responseBuffer, count, urlRef, uid, node); 5442 /* free the response buffer */ 5443 free(responseBuffer); 5444 CFRelease(urlRef); 5445 break; 5446 } 5447 5448 CFRelease(urlRef); 5449 5450 if (error != EDESTADDRREQ) 5451 break; 5452 5453 redir_cnt++; 5454 } 5455 5456 /* release the message body */ 5457 CFRelease(bodyData); 5458 5459CFDataCreateWithBytesNoCopy: 5460 5461 return ( error ); 5462} 5463 5464/******************************************************************************/ 5465 5466int network_mkdir( 5467 uid_t uid, /* -> uid of the user making the request */ 5468 struct node_entry *node, /* -> parent node */ 5469 char *name, /* -> directory name to create */ 5470 size_t name_length, /* -> length of name */ 5471 time_t *creation_date) /* <- date of the creation */ 5472{ 5473 int error; 5474 CFURLRef urlRef; 5475 CFHTTPMessageRef response; 5476 /* the 3 headers */ 5477 CFIndex headerCount = 1; 5478 struct HeaderFieldValue headers[] = { 5479 { CFSTR("Accept"), CFSTR("*/*") }, 5480 { CFSTR("translate"), CFSTR("f") } 5481 }; 5482 5483 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 5484 /* translate flag only for Microsoft IIS Server */ 5485 headerCount += 1; 5486 } 5487 5488 *creation_date = -1; 5489 5490 /* create a CFURL to the node plus name */ 5491 urlRef = create_cfurl_from_node(node, name, name_length); 5492 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 5493 5494 /* send request to the server and get the response */ 5495 error = send_transaction(uid, urlRef, NULL, CFSTR("MKCOL"), NULL, 5496 headerCount, headers, REDIRECT_DISABLE, NULL, NULL, &response); 5497 if ( !error ) 5498 { 5499 CFStringRef dateHeaderRef; 5500 5501 dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Date")); 5502 if ( dateHeaderRef != NULL ) 5503 { 5504 *creation_date = DateStringToTime(dateHeaderRef); 5505 5506 CFRelease(dateHeaderRef); 5507 } 5508 /* release the response buffer */ 5509 CFRelease(response); 5510 } 5511 5512 CFRelease(urlRef); 5513 5514create_cfurl_from_node: 5515 5516 return ( error ); 5517} 5518 5519/******************************************************************************/ 5520 5521int network_read( 5522 uid_t uid, /* -> uid of the user making the request */ 5523 struct node_entry *node, /* -> node to read */ 5524 off_t offset, /* -> position within the file at which the read is to begin */ 5525 size_t count, /* -> number of bytes of data to be read */ 5526 char **buffer, /* <- buffer data was read into (allocated by network_read) */ 5527 size_t *actual_count) /* <- number of bytes actually read */ 5528{ 5529 int error; 5530 CFURLRef urlRef; 5531 UInt8 *responseBuffer; 5532 CFIndex responseCount; 5533 CFStringRef byteRangesSpecifierRef; 5534 /* the 2 headers -- the range value will be computed below */ 5535 CFIndex headerCount = 2; 5536 struct HeaderFieldValue headers[] = { 5537 { CFSTR("Accept"), CFSTR("*/*") }, 5538 { CFSTR("Range"), NULL }, 5539 { CFSTR("translate"), CFSTR("f") }, 5540 { CFSTR("Pragma"), CFSTR("no-cache") } 5541 }; 5542 5543 if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) { 5544 /* translate flag and no-cache only for Microsoft IIS Server */ 5545 headerCount += 2; 5546 } 5547 5548 *buffer = NULL; 5549 *actual_count = 0; 5550 5551 /* create a CFURL to the node */ 5552 urlRef = create_cfurl_from_node(node, NULL, 0); 5553 require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO); 5554 5555 byteRangesSpecifierRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%qd-%qd"), offset, offset + count - 1); 5556 require_action(byteRangesSpecifierRef != NULL, CFStringCreateWithFormat, error = EIO); 5557 5558 headers[1].value = byteRangesSpecifierRef; 5559 5560 /* send request to the server and get the response */ 5561 error = send_transaction(uid, urlRef, NULL, CFSTR("GET"), NULL, 5562 headerCount, headers, REDIRECT_AUTO, &responseBuffer, &responseCount, NULL); 5563 if ( !error ) 5564 { 5565 if ( (size_t)responseCount > count ) 5566 { 5567 /* don't return more than we asked for */ 5568 responseCount = count; 5569 } 5570 *buffer = (char *)responseBuffer; 5571 *actual_count = responseCount; 5572 } 5573 5574 CFRelease(byteRangesSpecifierRef); 5575 5576CFStringCreateWithFormat: 5577 5578 CFRelease(urlRef); 5579 5580create_cfurl_from_node: 5581 5582 return ( error ); 5583} 5584 5585/******************************************************************************/ 5586