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 **)&notification_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