#include "webdavd.h"
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCDynamicStorePrivate.h>
#include <SystemConfiguration/SCDynamicStoreKey.h>
#include <CoreServices/CoreServices.h>
#include <CoreServices/CoreServicesPriv.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <Security/Security.h>
#include "webdav_parse.h"
#include "webdav_requestqueue.h"
#include "webdav_authcache.h"
#include "webdav_network.h"
#include "EncodedSourceID.h"
#define BODY_BUFFER_SIZE 0x10000
#define kSSLClientPropTLSServerCertificateChain CFSTR("TLSServerCertificateChain")
#define kSSLClientPropTLSTrustClientStatus CFSTR("TLSTrustClientStatus")
#define kSSLClientPropTLSServerHostName CFSTR("TLSServerHostName")
struct HeaderFieldValue
{
CFStringRef headerField;
CFStringRef value;
};
static CFStringRef userAgentHeaderValue = NULL;
static CFIndex first_read_len = 4096;
static CFStringRef X_Source_Id_HeaderValue = NULL;
static SCDynamicStoreRef gProxyStore;
static pthread_mutex_t gNetworkGlobals_lock;
static CFDictionaryRef gProxyDict = NULL;
static int gHttpProxyEnabled;
static char gHttpProxyServer[MAXHOSTNAMELEN];
static int gHttpProxyPort;
static int gHttpsProxyEnabled;
static char gHttpsProxyServer[MAXHOSTNAMELEN];
static int gHttpsProxyPort;
static CFMutableDictionaryRef gSSLPropertiesDict = NULL;
static struct ReadStreamRec gReadStreams[WEBDAV_REQUEST_THREADS + 1];
static int network_stat(
uid_t uid,
CFURLRef urlRef,
struct stat *statbuf);
static int network_dir_is_empty(
uid_t uid,
CFURLRef urlRef);
static time_t DateStringToTime(
CFStringRef str);
static CFStringRef CFStringCreateRFC2616DateStringWithTimeT(
time_t clock);
time_t DateBytesToTime(
const UInt8 *bytes,
CFIndex length)
{
const UInt8 *finish;
CFGregorianDate gdate;
struct tm tm_temp;
time_t clock;
finish = _CFGregorianDateCreateWithBytes(kCFAllocatorDefault, bytes, length, &gdate, NULL);
require_action(finish != bytes, _CFGregorianDateCreateWithBytes, clock = -1);
memset(&tm_temp, 0, sizeof(struct tm));
tm_temp.tm_sec = gdate.second;
tm_temp.tm_min = gdate.minute;
tm_temp.tm_hour = gdate.hour;
tm_temp.tm_mday = gdate.day;
tm_temp.tm_mon = gdate.month - 1;
tm_temp.tm_year = gdate.year - 1900;
clock = timegm(&tm_temp);
_CFGregorianDateCreateWithBytes:
return ( clock );
}
static time_t DateStringToTime(
CFStringRef str)
{
CFIndex count;
CFGregorianDate gdate;
struct tm tm_temp;
time_t clock;
count = _CFGregorianDateCreateWithString(kCFAllocatorDefault, str, &gdate, NULL);
require_action(count != 0, _CFGregorianDateCreateWithString, clock = -1);
memset(&tm_temp, 0, sizeof(struct tm));
tm_temp.tm_sec = gdate.second;
tm_temp.tm_min = gdate.minute;
tm_temp.tm_hour = gdate.hour;
tm_temp.tm_mday = gdate.day;
tm_temp.tm_mon = gdate.month - 1;
tm_temp.tm_year = gdate.year - 1900;
clock = timegm(&tm_temp);
_CFGregorianDateCreateWithString:
return ( clock );
}
static CFStringRef CFStringCreateRFC2616DateStringWithTimeT(
time_t clock)
{
struct tm *tm_temp;
CFGregorianDate gdate;
CFStringRef result;
require_action_quiet(clock != -1, InvalidClock, result = NULL);
tm_temp = gmtime(&clock);
gdate.second = tm_temp->tm_sec;
gdate.minute = tm_temp->tm_min;
gdate.hour = tm_temp->tm_hour;
gdate.day = tm_temp->tm_mday;
gdate.month = tm_temp->tm_mon + 1;
gdate.year = tm_temp->tm_year + 1900;
result = _CFStringCreateRFC2616DateStringWithGregorianDate(kCFAllocatorDefault, &gdate, NULL);
InvalidClock:
return ( result );
}
static const char * SkipCodedURL(const char *bytes)
{
while ( (*bytes != '\0') && (*bytes != '>') )
{
++bytes;
}
return ( bytes );
}
static const char * SkipToken(const char *bytes)
{
while ( *bytes != '\0' )
{
if ( (unsigned char)*bytes <= 31 )
{
goto Done;
}
else
{
switch ( *bytes )
{
case '\x7f':
case '(':
case ')':
case '<':
case '>':
case '@':
case ',':
case ';':
case ':':
case '\\':
case '\"':
case '/':
case '[':
case ']':
case '\?':
case '=':
case '{':
case '}':
case ' ':
case '\t':
goto Done;
break;
default:
++bytes;
break;
}
}
}
Done:
return (bytes);
}
static const char * SkipLWS(const char *bytes)
{
while ( *bytes != '\0' )
{
if ( (*bytes == ' ') || (*bytes == '\t') )
{
++bytes;
continue;
}
else if ( *bytes == '\x0d' )
{
if ( bytes[1] == '\x0a' )
{
if ( (bytes[2] == ' ') || (bytes[2] == '\t') )
{
bytes += 3;
continue;
}
}
}
break;
}
return ( bytes );
}
static int set_global_stream_properties(CFReadStreamRef readStreamRef)
{
int error, mutexerror;
error = 0;
mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_lock, error = mutexerror; webdav_kill(-1));
error = (CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyHTTPProxy, gProxyDict) == TRUE) ? 0 : 1;
mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1));
pthread_mutex_unlock:
pthread_mutex_lock:
return ( error );
}
int network_get_proxy_settings(
int *httpProxyEnabled,
char **httpProxyServer,
int *httpProxyPort,
int *httpsProxyEnabled,
char **httpsProxyServer,
int* httpsProxyPort)
{
int error, mutexerror;
error = 0;
mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_lock, error = mutexerror; webdav_kill(-1));
*httpProxyServer = malloc(MAXHOSTNAMELEN);
require_action(*httpProxyServer != NULL, malloc_httpProxyServer, error = ENOMEM);
*httpsProxyServer = malloc(MAXHOSTNAMELEN);
require_action(*httpsProxyServer != NULL, malloc_httpsProxyServer, free(*httpProxyServer); error = ENOMEM);
*httpProxyEnabled = gHttpProxyEnabled;
memcpy(*httpProxyServer, gHttpProxyServer, MAXHOSTNAMELEN);
*httpProxyPort = gHttpProxyPort;
*httpsProxyEnabled = gHttpsProxyEnabled;
memcpy(*httpsProxyServer, gHttpsProxyServer, MAXHOSTNAMELEN);
*httpsProxyPort = gHttpsProxyPort;
malloc_httpsProxyServer:
malloc_httpProxyServer:
mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1));
pthread_mutex_unlock:
pthread_mutex_lock:
return ( error );
}
int network_update_proxy(void *arg)
{
#pragma unused(arg)
int error, mutexerror;
error = 0;
mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_lock, error = mutexerror; webdav_kill(-1));
if ( gProxyDict )
{
CFRelease(gProxyDict);
}
gHttpProxyEnabled = 0;
gHttpProxyServer[0] = '\0';
gHttpProxyPort = 0;
gHttpsProxyEnabled = 0;
gHttpsProxyServer[0] = '\0';
gHttpsProxyPort = 0;
gProxyDict = SCDynamicStoreCopyProxies(gProxyStore);
if ( gProxyDict != NULL )
{
CFNumberRef cf_enabled;
int enabled;
CFStringRef cf_host;
CFNumberRef cf_port;
cf_enabled = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPEnable);
if ( (cf_enabled != NULL) && CFNumberGetValue(cf_enabled, kCFNumberIntType, &enabled) && enabled )
{
cf_host = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPProxy);
if ( (cf_host != NULL) && CFStringGetCString(cf_host, gHttpProxyServer, sizeof(gHttpProxyServer), kCFStringEncodingUTF8) )
{
cf_port = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPPort);
if ( (cf_port != NULL) && CFNumberGetValue(cf_port, kCFNumberIntType, &gHttpProxyPort) )
{
if ( gHttpProxyPort == 0 )
{
gHttpProxyPort = kHttpDefaultPort;
}
gHttpProxyEnabled = 1;
}
}
}
cf_enabled = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPSEnable);
if ( (cf_enabled != NULL) && CFNumberGetValue(cf_enabled, kCFNumberIntType, &enabled) && enabled )
{
cf_host = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPSProxy);
if ( (cf_host != NULL) && CFStringGetCString(cf_host, gHttpsProxyServer, sizeof(gHttpsProxyServer), kCFStringEncodingUTF8) )
{
cf_port = CFDictionaryGetValue(gProxyDict, kSCPropNetProxiesHTTPSPort);
if ( (cf_port != NULL) && CFNumberGetValue(cf_port, kCFNumberIntType, &gHttpsProxyPort) )
{
if ( gHttpsProxyPort == 0 )
{
gHttpsProxyPort = kHttpsDefaultPort;
}
gHttpsProxyEnabled = 1;
}
}
}
}
mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_unlock, error = mutexerror; webdav_kill(-1));
error = authcache_proxy_invalidate();
pthread_mutex_unlock:
pthread_mutex_lock:
return ( error );
}
static int InitUserAgentHeaderValue(int add_mirror_comment)
{
char buf[128];
int mib[2];
char ostype[128];
char osrelease[128];
char machine[128];
size_t len;
CFURLRef url;
CFBundleRef bundle;
CFDictionaryRef dict;
CFStringRef shortVersion;
CFIndex shortVersionLen;
char *webdavfsVersionStr;
UInt32 webdavfsVersion;
int result;
mib[0] = CTL_KERN;
mib[1] = KERN_OSTYPE;
len = sizeof ostype;
if (sysctl(mib, 2, ostype, &len, 0, 0) < 0)
{
ostype[0] = '\0';
}
mib[1] = KERN_OSRELEASE;
len = sizeof osrelease;
if (sysctl(mib, 2, osrelease, &len, 0, 0) < 0)
{
osrelease[0] = '\0';
}
mib[0] = CTL_HW;
mib[1] = HW_MACHINE;
len = sizeof machine;
if (sysctl(mib, 2, machine, &len, 0, 0) < 0)
{
machine[0] = '\0';
}
webdavfsVersionStr = NULL;
webdavfsVersion = 0x010080000;
url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
CFSTR("/System/Library/Filesystems/webdav.fs"),
kCFURLPOSIXPathStyle, true);
if ( url != NULL )
{
bundle = CFBundleCreate(kCFAllocatorDefault, url);
if ( bundle != NULL )
{
webdavfsVersion = CFBundleGetVersionNumber(bundle);
dict = CFBundleGetInfoDictionary(bundle);
if ( dict != NULL )
{
shortVersion = CFDictionaryGetValue(dict, CFSTR("CFBundleShortVersionString"));
if ( shortVersion != NULL )
{
shortVersionLen = CFStringGetLength(shortVersion) + 1;
webdavfsVersionStr = malloc((size_t)shortVersionLen);
if ( webdavfsVersionStr != NULL )
{
if ( !CFStringGetCString(shortVersion, webdavfsVersionStr, shortVersionLen, kCFStringEncodingUTF8) )
{
free(webdavfsVersionStr);
webdavfsVersionStr = NULL;
}
}
}
}
CFRelease(bundle);
}
CFRelease(url);
}
if ( webdavfsVersionStr != NULL )
{
snprintf(buf, sizeof(buf), "WebDAVFS/%s (%.8lx) %s%s/%s (%s)",
webdavfsVersionStr, webdavfsVersion, (add_mirror_comment ? "(mirrored) " : ""), ostype, osrelease, machine);
free(webdavfsVersionStr);
}
else
{
snprintf(buf, sizeof(buf), "WebDAVFS/1.4 %s/%s (%s)",
ostype, osrelease, machine);
}
userAgentHeaderValue = CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingUTF8);
require_action(userAgentHeaderValue != NULL, CFStringCreateWithCString, result = ENOMEM);
result = 0;
CFStringCreateWithCString:
return ( result );
}
static void get_first_read_len(void)
{
int mib[2];
size_t len;
int result;
int pagesize;
mib[0] = CTL_HW;
mib[1] = HW_PAGESIZE;
len = sizeof(int);
result = sysctl(mib, 2, &pagesize, &len, 0, 0);
if ( result < 0 )
{
first_read_len = 4096;
}
else
{
first_read_len = pagesize;
}
}
static void InitXSourceIdHeaderValue(void)
{
CFStringRef hostName;
char encodedIdBuffer[32];
X_Source_Id_HeaderValue = NULL;
hostName = CFURLCopyHostName(gBaseURL);
require_quiet(hostName != NULL, CFURLCopyHostName);
require_quiet(CFStringCompare(hostName, CFSTR("idisk.mac.com"), kCFCompareCaseInsensitive) == kCFCompareEqualTo, NotIdisk);
require_quiet(GetEncodedSourceID(encodedIdBuffer), GetEncodedSourceID);
X_Source_Id_HeaderValue = CFStringCreateWithCString(kCFAllocatorDefault, encodedIdBuffer, kCFStringEncodingUTF8);
GetEncodedSourceID:
NotIdisk:
CFRelease(hostName);
CFURLCopyHostName:
return;
}
int network_init(const UInt8 *uri, CFIndex uriLength, int *store_notify_fd, int add_mirror_comment)
{
int error;
pthread_mutexattr_t mutexattr;
CFStringRef notification_string;
CFArrayRef keys;
int index;
gProxyDict = NULL;
error = 0;
error = pthread_mutexattr_init(&mutexattr);
require_noerr(error, pthread_mutexattr_init);
error = pthread_mutex_init(&gNetworkGlobals_lock, &mutexattr);
require_noerr(error, pthread_mutex_init);
gProxyStore = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("WebDAVFS"), NULL, NULL);
require_action(gProxyStore != NULL, SCDynamicStoreCreate, error = ENOMEM);
require_action(SCDynamicStoreNotifyFileDescriptor(gProxyStore, 0, store_notify_fd),
SCDynamicStoreNotifyFileDescriptor, error = ENOMEM);
notification_string = SCDynamicStoreKeyCreateProxies(kCFAllocatorDefault);
require_action(notification_string != NULL, SCDynamicStoreKeyCreateProxies, error = ENOMEM);
keys = CFArrayCreate(kCFAllocatorDefault, (const void **)¬ification_string, 1, &kCFTypeArrayCallBacks);
require_action(keys != NULL, CFArrayCreate, error = ENOMEM);
CFRelease(notification_string);
require_action(SCDynamicStoreSetNotificationKeys(gProxyStore, keys, NULL),
SCDynamicStoreSetNotificationKeys, error = ENOMEM);
CFRelease(keys);
error = network_update_proxy(NULL);
require_noerr_quiet(error, network_update_proxy);
gBaseURL = CFURLCreateAbsoluteURLWithBytes(kCFAllocatorDefault, uri, uriLength, kCFStringEncodingUTF8, NULL, FALSE);
require_action_string(gBaseURL != NULL, CFURLCreateAbsoluteURLWithBytes, error = ENOMEM, "name was not legal UTF8");
get_first_read_len();
InitXSourceIdHeaderValue();
error = InitUserAgentHeaderValue((X_Source_Id_HeaderValue != NULL) && add_mirror_comment);
if ( error )
{
exit(error);
}
for ( index = 0; index < (WEBDAV_REQUEST_THREADS + 1); ++index )
{
gReadStreams[index].inUse = 0;
gReadStreams[index].readStreamRef = NULL;
gReadStreams[index].uniqueValue = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), index);
}
CFURLCreateAbsoluteURLWithBytes:
network_update_proxy:
SCDynamicStoreSetNotificationKeys:
CFArrayCreate:
SCDynamicStoreKeyCreateProxies:
SCDynamicStoreNotifyFileDescriptor:
SCDynamicStoreCreate:
pthread_mutex_init:
pthread_mutexattr_init:
return ( error );
}
static CFURLRef create_cfurl_from_node(
struct node_entry *node,
char *name,
size_t name_length)
{
CFURLRef tempUrlRef;
CFURLRef urlRef;
char *node_path;
int error;
urlRef = NULL;
error = nodecache_get_path_from_node(node, &node_path);
require_noerr_quiet(error, nodecache_get_path_from_node);
if ( name != NULL && name_length != 0 )
{
strncat(node_path, name, name_length);
}
if ( *node_path != '\0' )
{
CFStringRef stringRef;
CFStringRef escapedPathRef;
stringRef = CFStringCreateWithCString(kCFAllocatorDefault, node_path, kCFStringEncodingUTF8);
require_string(stringRef != NULL, CFStringCreateWithCString, "name was not legal UTF8");
escapedPathRef = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, stringRef, NULL, CFSTR(":;?"), kCFStringEncodingUTF8);
require(escapedPathRef != NULL, CFURLCreateStringByAddingPercentEscapes);
urlRef = CFURLCreateWithString(kCFAllocatorDefault, escapedPathRef, gBaseURL);
require(urlRef != NULL, CFURLCreateWithString);
tempUrlRef = urlRef;
urlRef = CFURLCopyAbsoluteURL(tempUrlRef);
CFRelease(tempUrlRef);
CFURLCreateWithString:
CFRelease(escapedPathRef);
CFURLCreateStringByAddingPercentEscapes:
CFRelease(stringRef);
CFStringCreateWithCString:
;
}
else
{
CFRetain(gBaseURL);
urlRef = gBaseURL;
}
free(node_path);
nodecache_get_path_from_node:
return ( urlRef );
}
static int translate_status_to_error(UInt32 statusCode)
{
int result;
switch ( statusCode / 100 )
{
case 1:
syslog(LOG_ERR,"unexpected statusCode %ld", statusCode);
result = ENOENT;
break;
case 2:
result = 0;
break;
case 3:
syslog(LOG_ERR,"unexpected statusCode %ld", statusCode);
result = ENOENT;
break;
case 4:
switch ( statusCode )
{
case 401:
case 407:
result = EAUTH;
break;
case 402:
case 403:
result = EPERM;
break;
case 404:
case 409:
case 410:
result = ENOENT;
break;
case 414:
result = ENAMETOOLONG;
case 423:
case 424:
result = EBUSY;
break;
default:
syslog(LOG_ERR,"unexpected statusCode %ld", statusCode);
result = EINVAL;
break;
}
break;
case 5:
if ( statusCode == 507 )
{
result = ENOSPC;
}
else
{
syslog(LOG_ERR,"unexpected statusCode %ld", statusCode);
result = ENOENT;
}
break;
default:
syslog(LOG_ERR,"unexpected statusCode %ld", statusCode);
result = EIO;
break;
}
return ( result );
}
static int ApplySSLProperties(CFReadStreamRef readStreamRef)
{
int result = TRUE;
if ( gSSLPropertiesDict != NULL )
{
result = (CFReadStreamSetProperty(readStreamRef, kCFStreamPropertySSLSettings, gSSLPropertiesDict) == TRUE);
}
return ( result );
}
static struct ReadStreamRec *get_ReadStreamRec(void)
{
int index;
struct ReadStreamRec *result;
int mutexerror;
result = NULL;
mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_lock, webdav_kill(-1));
for ( index = 0; index < (WEBDAV_REQUEST_THREADS + 1); ++index )
{
if ( !gReadStreams[index].inUse )
{
if ( gReadStreams[index].readStreamRef != NULL )
{
result = &gReadStreams[index];
result->inUse = TRUE;
break;
}
else if ( result == NULL )
{
result = &gReadStreams[index];
}
}
}
if ( result != NULL )
{
result->inUse = TRUE;
}
mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_unlock, webdav_kill(-1));
pthread_mutex_unlock:
pthread_mutex_lock:
return ( result );
}
static void release_ReadStreamRec(struct ReadStreamRec *theReadStreamRec)
{
int mutexerror;
mutexerror = pthread_mutex_lock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_lock, webdav_kill(-1));
theReadStreamRec->inUse = FALSE;
mutexerror = pthread_mutex_unlock(&gNetworkGlobals_lock);
require_noerr_action(mutexerror, pthread_mutex_unlock, webdav_kill(-1));
pthread_mutex_unlock:
pthread_mutex_lock:
return;
}
static CFDataRef SecCertificateCreateCFData(SecCertificateRef cert)
{
CSSM_DATA cert_data;
CFDataRef data;
OSStatus status;
status = SecCertificateGetData(cert, &cert_data);
require_noerr_action(status, SecCertificateGetData, data = NULL);
data = CFDataCreate(NULL, cert_data.Data, cert_data.Length);
SecCertificateGetData:
return (data);
}
static CFArrayRef SecCertificateArrayCreateCFDataArray(CFArrayRef certs)
{
CFMutableArrayRef array;
int count;
int i;
const void *certRef;
count = CFArrayGetCount(certs);
array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
require(array != NULL, CFArrayCreateMutable);
for (i = 0; i < count; ++i)
{
SecCertificateRef cert;
CFDataRef data;
certRef = CFArrayGetValueAtIndex(certs, i);
cert = *((SecCertificateRef*)((void*)&certRef));
require(cert != NULL, CFArrayGetValueAtIndex);
data = SecCertificateCreateCFData(cert);
require(data != NULL, SecCertificateCreateCFData);
CFArrayAppendValue(array, data);
CFRelease(data);
}
return (array);
SecCertificateCreateCFData:
CFArrayGetValueAtIndex:
CFRelease(array);
CFArrayCreateMutable:
return (NULL);
}
static int ConfirmCertificate(CFReadStreamRef readStreamRef, SInt32 error)
{
int result;
CFMutableDictionaryRef dict;
CFArrayRef certs;
CFArrayRef certs_data;
CFNumberRef error_number;
CFStringRef host_name;
CFDataRef theData;
int fd[2];
int pid, terminated_pid;
union wait status;
char *env[] = {"__CF_USER_TEXT_ENCODING=0x1D29:0:0", "", (char *) 0 };
result = FALSE;
fd[0] = fd[1] = -1;
dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
require(dict != NULL, CFDictionaryCreateMutable);
certs = (CFArrayRef)CFReadStreamCopyProperty(readStreamRef, kCFStreamPropertySSLPeerCertificates);
require(certs != NULL, CFReadStreamCopyProperty);
certs_data = SecCertificateArrayCreateCFDataArray(certs);
CFRelease(certs);
require(certs_data != NULL, CFReadStreamCopyProperty);
CFDictionaryAddValue(dict, kSSLClientPropTLSServerCertificateChain, certs_data);
CFRelease(certs_data);
error_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &error);
require(error_number != NULL, CFNumberCreate);
CFDictionaryAddValue(dict, kSSLClientPropTLSTrustClientStatus, error_number);
CFRelease(error_number);
host_name = CFURLCopyHostName(gBaseURL);
require(host_name != NULL, CFURLCopyHostName);
CFDictionaryAddValue(dict, kSSLClientPropTLSServerHostName, host_name);
CFRelease(host_name);
theData = CFPropertyListCreateXMLData(kCFAllocatorDefault, dict);
require(theData != NULL, CFPropertyListCreateXMLData);
CFRelease(dict);
dict = NULL;
require(pipe(fd) >= 0, pipe);
pid = fork();
require (pid >= 0, fork);
if ( pid > 0 )
{
size_t length;
ssize_t bytes_written;
close(fd[0]);
fd[0] = -1;
length = CFDataGetLength(theData);
bytes_written = write(fd[1], CFDataGetBytePtr(theData), length);
require(bytes_written == (ssize_t)length, write);
close(fd[1]);
fd[1] = -1;
while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 )
{
if ( errno != EINTR )
{
break;
}
}
if ( (terminated_pid == pid) && (WIFEXITED(status)) )
{
result = WEXITSTATUS(status) == 0;
}
else
{
result = FALSE;
}
}
else
{
close(fd[1]);
fd[1] = -1;
if ( fd[0] != STDIN_FILENO )
{
require(dup2(fd[0], STDIN_FILENO) == STDIN_FILENO, dup2);
close(fd[0]);
fd[0] = -1;
}
require(execle(PRIVATE_CERT_UI_COMMAND, PRIVATE_CERT_UI_COMMAND, (char *) 0, env) >= 0, execl);
}
return ( result );
execl:
dup2:
write:
fork:
if (fd[0] != -1)
{
close(fd[0]);
}
if (fd[1] != -1)
{
close(fd[1]);
}
pipe:
CFPropertyListCreateXMLData:
CFURLCopyHostName:
CFNumberCreate:
CFReadStreamCopyProperty:
if ( dict != NULL )
{
CFRelease(dict);
}
CFDictionaryCreateMutable:
return ( FALSE );
}
static int HandleSSLErrors(CFReadStreamRef readStreamRef)
{
CFStreamError streamError;
SInt32 error;
int result;
result = EIO;
streamError = CFReadStreamGetError(readStreamRef);
if ( streamError.domain == kCFStreamErrorDomainSSL )
{
error = streamError.error;
if ( gSSLPropertiesDict == NULL )
{
gSSLPropertiesDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
require(gSSLPropertiesDict != NULL, CFDictionaryCreateMutable);
}
if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLLevel) == NULL) &&
(((error <= errSSLProtocol) && (error > errSSLXCertChainInvalid)) ||
((error <= errSSLCrypto) && (error > errSSLUnknownRootCert)) ||
((error <= errSSLClosedNoNotify) && (error > errSSLPeerBadCert)) ||
(error == errSSLIllegalParam) ||
((error <= errSSLPeerAccessDenied) && (error > errSSLLast))) )
{
CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLLevel, kCFStreamSocketSecurityLevelSSLv3);
result = EAGAIN;
}
else
{
switch ( error )
{
case errSSLCertExpired:
case errSSLCertNotYetValid:
if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLAllowsExpiredCertificates) == NULL) )
{
if ( ConfirmCertificate(readStreamRef, error) )
{
result = EAGAIN;
CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
}
else
{
result = ECANCELED;
}
}
break;
case errSSLBadCert:
case errSSLXCertChainInvalid:
case errSSLHostNameMismatch:
if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLValidatesCertificateChain) == NULL) )
{
if ( ConfirmCertificate(readStreamRef, error) )
{
result = EAGAIN;
CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
}
else
{
result = ECANCELED;
}
}
break;
case errSSLUnknownRootCert:
case errSSLNoRootCert:
if ( (CFDictionaryGetValue(gSSLPropertiesDict, kCFStreamSSLAllowsAnyRoot) == NULL) )
{
if ( ConfirmCertificate(readStreamRef, error) )
{
result = EAGAIN;
CFDictionarySetValue(gSSLPropertiesDict, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
}
else
{
result = ECANCELED;
}
}
break;
default:
result = EIO;
break;
}
}
}
CFDictionaryCreateMutable:
return ( result );
}
static int open_stream_for_transaction(
CFHTTPMessageRef request,
CFReadStreamRef fdStream,
int auto_redirect,
int *retryTransaction,
struct ReadStreamRec **readStreamRecPtr)
{
int result;
struct ReadStreamRec *theReadStreamRec;
CFReadStreamRef newReadStreamRef;
result = 0;
*readStreamRecPtr = NULL;
if ( fdStream != NULL )
{
newReadStreamRef = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request, fdStream);
require(newReadStreamRef != NULL, CFReadStreamCreateForStreamedHTTPRequest);
}
else
{
newReadStreamRef = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
require(newReadStreamRef != NULL, CFReadStreamCreateForHTTPRequest);
}
CFReadStreamSetProperty(newReadStreamRef, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
if ( auto_redirect )
{
require(CFReadStreamSetProperty(newReadStreamRef, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue) != FALSE, SetAutoredirectProperty);
}
require_quiet(set_global_stream_properties(newReadStreamRef) == 0, set_global_stream_properties);
ApplySSLProperties(newReadStreamRef);
theReadStreamRec = get_ReadStreamRec();
require(theReadStreamRec != NULL, get_ReadStreamRec);
require(CFReadStreamSetProperty(newReadStreamRef, CFSTR("WebdavConnectionNumber"), theReadStreamRec->uniqueValue) != FALSE, SetWebdavConnectionNumberProperty);
if ( CFReadStreamOpen(newReadStreamRef) == FALSE )
{
result = HandleSSLErrors(newReadStreamRef);
if ( result != EAGAIN )
{
CFStreamError streamError;
streamError = CFReadStreamGetError(newReadStreamRef);
if ( *retryTransaction && streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE)
{
syslog(LOG_INFO,"open_stream_for_transaction: CFStreamError: domain %ld, error %ld -- retrying", streamError.domain, streamError.error);
*retryTransaction = FALSE;
result = EAGAIN;
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_ERR,"open_stream_for_transaction: CFStreamError: domain %ld, error %ld", streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
result = ENXIO;
}
}
goto CFReadStreamOpen;
}
if ( theReadStreamRec->readStreamRef != NULL )
{
CFReadStreamClose(theReadStreamRec->readStreamRef);
CFRelease(theReadStreamRec->readStreamRef);
}
theReadStreamRec->readStreamRef = newReadStreamRef;
*readStreamRecPtr = theReadStreamRec;
return ( 0 );
CFReadStreamOpen:
SetWebdavConnectionNumberProperty:
release_ReadStreamRec(theReadStreamRec);
get_ReadStreamRec:
set_global_stream_properties:
SetAutoredirectProperty:
CFRelease(newReadStreamRef);
CFReadStreamCreateForHTTPRequest:
CFReadStreamCreateForStreamedHTTPRequest:
*readStreamRecPtr = NULL;
if ( result == 0 )
{
result = EIO;
}
return ( result );
}
static int stream_get_transaction(
CFHTTPMessageRef request,
int *retryTransaction,
struct node_entry *node,
CFHTTPMessageRef *response)
{
struct ReadStreamRec *readStreamRecPtr;
UInt8 *buffer;
CFIndex totalRead;
CFIndex bytesRead;
CFTypeRef theResponsePropertyRef;
int background_load;
CFStringRef connectionHeaderRef;
CFHTTPMessageRef responseMessage;
int result;
result = 0;
require_quiet(!gSuppressAllUI || (get_connectionstate() == WEBDAV_CONNECTION_UP), connection_down);
result = open_stream_for_transaction(request, NULL, TRUE, retryTransaction, &readStreamRecPtr);
require_noerr_quiet(result, open_stream_for_transaction);
buffer = malloc(first_read_len);
require(buffer != NULL, malloc_buffer);
totalRead = 0;
background_load = FALSE;
while ( 1 )
{
bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer + totalRead, first_read_len - totalRead);
if ( bytesRead > 0 )
{
totalRead += bytesRead;
if ( totalRead >= first_read_len )
{
if ( CFReadStreamGetStatus(readStreamRecPtr->readStreamRef) == kCFStreamStatusAtEnd )
{
background_load = FALSE;
}
else
{
background_load = TRUE;
}
break;
}
}
else if ( bytesRead == 0 )
{
background_load = FALSE;
break;
}
else
{
CFStreamError streamError;
streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef);
if ( *retryTransaction && streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE )
{
syslog(LOG_INFO,"stream_get_transaction: CFStreamError: domain %ld, error %ld -- retrying", streamError.domain, streamError.error);
*retryTransaction = FALSE;
result = EAGAIN;
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_ERR,"stream_get_transaction: CFStreamError: domain %ld, error %ld", streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
result = ENXIO;
}
goto CFReadStreamRead;
}
};
theResponsePropertyRef = CFReadStreamCopyProperty(readStreamRecPtr->readStreamRef, kCFStreamPropertyHTTPResponseHeader);
require(theResponsePropertyRef != NULL, GetResponseHeader);
responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef));
switch ( CFHTTPMessageGetResponseStatusCode(responseMessage) )
{
case 200:
require(fchflags(node->file_fd, 0) == 0, fchflags);
require_action(ftruncate(node->file_fd, 0LL) != -1, ftruncate, syslog(LOG_ERR,"errno %d", errno));
require(lseek(node->file_fd, 0LL, SEEK_SET) >= 0, lseek);
require(write(node->file_fd, buffer, (size_t)totalRead) == (ssize_t)totalRead, write);
break;
case 206:
require(fchflags(node->file_fd, 0) == 0, fchflags);
require(lseek(node->file_fd, 0LL, SEEK_END) >= 0, lseek);
require(write(node->file_fd, buffer, (size_t)totalRead) >= 0, write);
break;
case 304:
background_load = FALSE;
break;
default:
background_load = FALSE;
break;
}
free(buffer);
buffer = NULL;
set_connectionstate(WEBDAV_CONNECTION_UP);
readStreamRecPtr->connectionClose = FALSE;
connectionHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Connection"));
if ( connectionHeaderRef != NULL )
{
if ( CFStringCompare(connectionHeaderRef, CFSTR("close"), kCFCompareCaseInsensitive) == kCFCompareEqualTo )
{
readStreamRecPtr->connectionClose = TRUE;
}
CFRelease(connectionHeaderRef);
}
if ( background_load )
{
int error;
require(fchflags(node->file_fd, UF_NODUMP) == 0, fchflags);
node->file_status = WEBDAV_DOWNLOAD_IN_PROGRESS;
error = requestqueue_enqueue_download(node, readStreamRecPtr);
require_noerr_quiet(error, webdav_requestqueue_enqueue_new_download);
}
else
{
node->file_status = WEBDAV_DOWNLOAD_FINISHED;
if ( readStreamRecPtr->connectionClose )
{
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
}
release_ReadStreamRec(readStreamRecPtr);
}
*response = responseMessage;
return ( 0 );
webdav_requestqueue_enqueue_new_download:
fchflags:
ftruncate:
lseek:
write:
GetResponseHeader:
CFReadStreamRead:
if ( buffer != NULL )
{
free(buffer);
}
malloc_buffer:
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
release_ReadStreamRec(readStreamRecPtr);
open_stream_for_transaction:
connection_down:
*response = NULL;
if ( result == 0 )
{
result = EIO;
}
return ( result );
}
static int stream_transaction_from_file(
CFHTTPMessageRef request,
int file_fd,
int *retryTransaction,
CFHTTPMessageRef *response)
{
CFReadStreamRef fdStream;
struct ReadStreamRec *readStreamRecPtr;
void *buffer;
CFIndex bytesRead;
CFTypeRef theResponsePropertyRef;
off_t contentLength;
CFStringRef contentLengthString;
CFStringRef connectionHeaderRef;
CFHTTPMessageRef responseMessage;
int result;
result = 0;
require_quiet(!gSuppressAllUI || (get_connectionstate() == WEBDAV_CONNECTION_UP), connection_down);
contentLength = lseek(file_fd, 0LL, SEEK_END);
require(contentLength != -1, lseek);
contentLengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%qd"), contentLength);
if ( contentLengthString != NULL )
{
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Length"), contentLengthString);
CFRelease(contentLengthString);
}
verify(lseek(file_fd, 0LL, SEEK_SET) != -1);
CFStreamCreatePairWithSocket(kCFAllocatorDefault, file_fd, &fdStream, NULL);
require(fdStream != NULL, CFReadStreamCreateWithFile);
result = open_stream_for_transaction(request, fdStream, FALSE, retryTransaction, &readStreamRecPtr);
require_noerr_quiet(result, open_stream_for_transaction);
buffer = malloc(BODY_BUFFER_SIZE);
require(buffer != NULL, malloc_currentbuffer);
while ( 1 )
{
bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer, BODY_BUFFER_SIZE);
if ( bytesRead > 0 )
{
continue;
}
else if ( bytesRead == 0 )
{
break;
}
else
{
CFStreamError streamError;
streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef);
if ( *retryTransaction && streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE )
{
syslog(LOG_INFO,"stream_transaction_from_file: CFStreamError: domain %ld, error %ld -- retrying", streamError.domain, streamError.error);
*retryTransaction = FALSE;
result = EAGAIN;
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_ERR,"stream_transaction_from_file: CFStreamError: domain %ld, error %ld", streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
result = ENXIO;
}
goto CFReadStreamRead;
}
};
free(buffer);
theResponsePropertyRef = CFReadStreamCopyProperty(readStreamRecPtr->readStreamRef, kCFStreamPropertyHTTPResponseHeader);
require(theResponsePropertyRef != NULL, GetResponseHeader);
responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef));
set_connectionstate(WEBDAV_CONNECTION_UP);
connectionHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Connection"));
if ( connectionHeaderRef != NULL )
{
if ( CFStringCompare(connectionHeaderRef, CFSTR("close"), kCFCompareCaseInsensitive) == kCFCompareEqualTo )
{
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
}
CFRelease(connectionHeaderRef);
}
CFRelease(fdStream);
release_ReadStreamRec(readStreamRecPtr);
*response = responseMessage;
return ( 0 );
GetResponseHeader:
CFReadStreamRead:
free(buffer);
malloc_currentbuffer:
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
release_ReadStreamRec(readStreamRecPtr);
CFRelease(fdStream);
CFReadStreamCreateWithFile:
lseek:
open_stream_for_transaction:
connection_down:
*response = NULL;
if ( result == 0 )
{
result = EIO;
}
return ( result );
}
static int stream_transaction(
CFHTTPMessageRef request,
int auto_redirect,
int *retryTransaction,
UInt8 **buffer,
CFIndex *count,
CFHTTPMessageRef *response)
{
struct ReadStreamRec *readStreamRecPtr;
CFIndex totalRead;
UInt8 *currentbuffer;
UInt8 *newBuffer;
CFIndex bytesRead;
CFIndex bytesToRead;
CFIndex bufferSize;
CFTypeRef theResponsePropertyRef;
CFStringRef connectionHeaderRef;
CFHTTPMessageRef responseMessage;
int result;
result = 0;
require_quiet(!gSuppressAllUI || (get_connectionstate() == WEBDAV_CONNECTION_UP), connection_down);
result = open_stream_for_transaction(request, NULL, auto_redirect, retryTransaction, &readStreamRecPtr);
require_noerr_quiet(result, open_stream_for_transaction);
bufferSize = BODY_BUFFER_SIZE;
currentbuffer = malloc(bufferSize);
require(currentbuffer != NULL, malloc_currentbuffer);
totalRead = 0;
while ( 1 )
{
bytesToRead = bufferSize - totalRead;
bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, currentbuffer + totalRead, bytesToRead);
if ( bytesRead > 0 )
{
totalRead += bytesRead;
if ( (bytesToRead - bytesRead) < (BODY_BUFFER_SIZE / 2) )
{
bufferSize += BODY_BUFFER_SIZE;
newBuffer = realloc(currentbuffer, bufferSize);
require(newBuffer != NULL, realloc);
currentbuffer = newBuffer;
}
}
else if ( bytesRead == 0 )
{
break;
}
else
{
result = HandleSSLErrors(readStreamRecPtr->readStreamRef);
if ( result != EAGAIN )
{
if ( result != ECANCELED )
{
CFStreamError streamError;
streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef);
if ( *retryTransaction && streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE )
{
syslog(LOG_INFO,"stream_transaction: CFStreamError: domain %ld, error %ld -- retrying", streamError.domain, streamError.error);
*retryTransaction = FALSE;
result = EAGAIN;
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_ERR,"stream_transaction: CFStreamError: domain %ld, error %ld", streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
result = ENXIO;
}
}
}
goto CFReadStreamRead;
}
};
theResponsePropertyRef = CFReadStreamCopyProperty(readStreamRecPtr->readStreamRef, kCFStreamPropertyHTTPResponseHeader);
require(theResponsePropertyRef != NULL, GetResponseHeader);
responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef));
set_connectionstate(WEBDAV_CONNECTION_UP);
connectionHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Connection"));
if ( connectionHeaderRef != NULL )
{
if ( CFStringCompare(connectionHeaderRef, CFSTR("close"), kCFCompareCaseInsensitive) == kCFCompareEqualTo )
{
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
}
CFRelease(connectionHeaderRef);
}
release_ReadStreamRec(readStreamRecPtr);
*response = responseMessage;
*count = totalRead;
*buffer = currentbuffer;
return ( 0 );
GetResponseHeader:
CFReadStreamRead:
realloc:
free(currentbuffer);
malloc_currentbuffer:
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
release_ReadStreamRec(readStreamRecPtr);
open_stream_for_transaction:
connection_down:
*response = NULL;
*count = 0;
*buffer = NULL;
if ( result == 0 )
{
result = EIO;
}
return ( result );
}
static int send_transaction(
uid_t uid,
CFURLRef url,
CFStringRef requestMethod,
CFDataRef bodyData,
CFIndex headerCount,
struct HeaderFieldValue *headers,
int auto_redirect,
UInt8 **buffer,
CFIndex *count,
CFHTTPMessageRef *response)
{
int error;
CFIndex i;
struct HeaderFieldValue *headerPtr;
CFHTTPMessageRef message;
CFHTTPMessageRef responseRef;
UInt32 statusCode;
UInt32 auth_generation;
UInt8 *responseBuffer;
CFIndex responseBufferLength;
int retryTransaction;
error = 0;
responseBuffer = NULL;
responseBufferLength = 0;
message = NULL;
responseRef = NULL;
statusCode = 0;
auth_generation = 0;
retryTransaction = TRUE;
do
{
if ( message != NULL )
{
CFRelease(message);
message = NULL;
}
message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, url, kCFHTTPVersion1_1);
require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO);
if ( bodyData != NULL )
{
CFHTTPMessageSetBody(message, bodyData);
}
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("User-Agent"), userAgentHeaderValue);
if ( X_Source_Id_HeaderValue != NULL )
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("X-Source-Id"), X_Source_Id_HeaderValue);
}
for ( i = 0, headerPtr = headers, headers; i < headerCount; ++i, ++headerPtr )
{
CFHTTPMessageSetHeaderFieldValue(message, headerPtr->headerField, headerPtr->value);
}
error = authcache_apply(uid, message, statusCode, responseRef, &auth_generation);
if ( error != 0 )
{
break;
}
if ( responseBuffer != NULL )
{
free(responseBuffer);
responseBuffer = NULL;
responseBufferLength = 0;
}
if ( responseRef != NULL )
{
CFRelease(responseRef);
responseRef = NULL;
}
error = stream_transaction(message, auto_redirect, &retryTransaction, &responseBuffer, &responseBufferLength, &responseRef);
if ( error == EAGAIN )
{
statusCode = 0;
}
else
{
if ( error != 0 )
{
break;
}
statusCode = CFHTTPMessageGetResponseStatusCode(responseRef);
}
} while ( error == EAGAIN || statusCode == 401 || statusCode == 407 );
CFHTTPMessageCreateRequest:
if ( error == 0 )
{
error = translate_status_to_error(statusCode);
if ( error == 0 )
{
(void) authcache_valid(uid, message, auth_generation);
}
else
{
if ( responseBuffer != NULL )
{
free(responseBuffer);
responseBuffer = NULL;
}
}
}
if ( message != NULL )
{
CFRelease(message);
}
if ( buffer != NULL )
{
*buffer = responseBuffer;
}
else
{
if ( responseBuffer != NULL )
{
free(responseBuffer);
}
}
if ( count != NULL )
{
*count = responseBufferLength;
}
if ( response != NULL )
{
*response = responseRef;
}
else
{
if ( responseRef != NULL )
{
CFRelease(responseRef);
}
}
return ( error );
}
static void ParseDAVLevel(CFHTTPMessageRef responsePropertyRef, int *dav_level)
{
CFStringRef davHeaderRef;
const char *field_value;
char buffer[4096];
const char *token;
*dav_level = 0;
davHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responsePropertyRef, CFSTR("DAV"));
if ( davHeaderRef )
{
field_value = CFStringGetCStringPtr(davHeaderRef, kCFStringEncodingUTF8);
if ( field_value == NULL )
{
if ( CFStringGetCString(davHeaderRef, buffer, 4096, kCFStringEncodingUTF8) )
{
field_value = buffer;
}
}
CFRelease(davHeaderRef);
if ( field_value != NULL )
{
while ( *field_value != '\0' )
{
field_value = SkipLWS(field_value);
if ( *field_value == '\0' )
{
break;
}
if ( *field_value == '<' )
{
++field_value;
field_value = SkipCodedURL(field_value);
if ( *field_value != '\0' )
{
++field_value;
}
}
else
{
token = field_value;
field_value = SkipToken(field_value);
if ( (field_value - token) == 1 )
{
if ( (*token == '1') && (*dav_level < 1) )
{
*dav_level = 1;
}
else if ( *token == '2' && (*dav_level < 2) )
{
*dav_level = 2;
}
}
}
field_value = SkipLWS(field_value);
if ( *field_value != '\0' )
{
if ( *field_value != ',' )
{
break;
}
while ( *field_value == ',' )
{
++field_value;
}
}
}
}
}
}
static int network_getDAVLevel(
uid_t uid,
CFURLRef urlRef,
int *dav_level)
{
int error;
CFHTTPMessageRef response;
CFIndex headerCount = 1;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
};
*dav_level = 0;
error = send_transaction(uid, urlRef, CFSTR("OPTIONS"), NULL,
headerCount, headers, TRUE, NULL, NULL, &response);
if ( !error )
{
ParseDAVLevel(response, dav_level);
CFRelease(response);
}
return ( error );
}
static
int get_from_attributes_cache(struct node_entry *node, uid_t uid)
{
ssize_t size;
int result;
result = FALSE;
if ( node_appledoubleheader_valid(node, uid) )
{
require(fchflags(node->file_fd, 0) == 0, fchflags);
require(lseek(node->file_fd, (off_t)0, SEEK_SET) != -1, lseek);
require(ftruncate(node->file_fd, 0LL) != -1, ftruncate);
size = write(node->file_fd, (void *)node->attr_appledoubleheader, APPLEDOUBLEHEADER_LENGTH);
if ( size != APPLEDOUBLEHEADER_LENGTH )
{
debug_string("write failed");
(void) lseek(node->file_fd, (off_t)0, SEEK_SET);
(void) ftruncate(node->file_fd, 0LL);
node->file_status = WEBDAV_DOWNLOAD_NEVER;
node->file_validated_time = 0;
node->file_last_modified = -1;
if ( node->file_entity_tag != NULL )
{
free(node->file_entity_tag);
node->file_entity_tag = NULL;
}
}
else
{
node->file_status = WEBDAV_DOWNLOAD_FINISHED;
node->file_validated_time = node->attr_appledoubleheader_time;
node->file_last_modified = (node->attr_stat.st_mtimespec.tv_sec != 0) ? node->attr_stat.st_mtimespec.tv_sec : -1;
if ( node->file_entity_tag != NULL )
{
free(node->file_entity_tag);
node->file_entity_tag = NULL;
}
result = TRUE;
}
}
ftruncate:
lseek:
fchflags:
return ( result );
}
static int network_stat(
uid_t uid,
CFURLRef urlRef,
struct stat *statbuf)
{
int error;
UInt8 *responseBuffer;
CFIndex count;
CFDataRef bodyData;
const UInt8 xmlString[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:propfind xmlns:D=\"DAV:\">\n"
"<D:prop>\n"
"<D:getlastmodified/>\n"
"<D:getcontentlength/>\n"
"<D:resourcetype/>\n"
"</D:prop>\n"
"</D:propfind>\n";
CFIndex headerCount = 3;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Content-Type"), CFSTR("text/xml") },
{ CFSTR("Depth"), CFSTR("0") }
};
bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull);
require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO);
error = send_transaction(uid, urlRef, CFSTR("PROPFIND"), bodyData,
headerCount, headers, TRUE, &responseBuffer, &count, NULL);
if ( !error )
{
error = parse_stat(responseBuffer, count, statbuf);
free(responseBuffer);
}
CFRelease(bodyData);
CFDataCreateWithBytesNoCopy:
return ( error );
}
static int network_dir_is_empty(
uid_t uid,
CFURLRef urlRef)
{
int error;
UInt8 *responseBuffer;
CFIndex count;
CFDataRef bodyData;
const UInt8 xmlString[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:propfind xmlns:D=\"DAV:\">\n"
"<D:prop>\n"
"<D:resourcetype/>\n"
"</D:prop>\n"
"</D:propfind>\n";
CFIndex headerCount = 3;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Content-Type"), CFSTR("text/xml") },
{ CFSTR("Depth"), CFSTR("1") }
};
error = 0;
responseBuffer = NULL;
bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull);
require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO);
error = send_transaction(uid, urlRef, CFSTR("PROPFIND"), bodyData,
headerCount, headers, TRUE, &responseBuffer, &count, NULL);
if ( !error )
{
int num_entries;
error = parse_file_count(responseBuffer, count, &num_entries);
if ( !error )
{
if (num_entries > 1)
{
error = ENOTEMPTY;
}
}
free(responseBuffer);
}
CFRelease(bodyData);
CFDataCreateWithBytesNoCopy:
return ( error );
}
int network_lookup(
uid_t uid,
struct node_entry *node,
char *name,
size_t name_length,
struct stat *statbuf)
{
int error;
CFURLRef urlRef;
error = 0;
urlRef = create_cfurl_from_node(node, name, name_length);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
error = network_stat(uid, urlRef, statbuf);
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_getattr(
uid_t uid,
struct node_entry *node,
struct stat *statbuf)
{
int error;
CFURLRef urlRef;
error = 0;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
error = network_stat(uid, urlRef, statbuf);
if ( error == 0 )
{
statbuf->st_ino = node->fileid;
}
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_mount(
uid_t uid,
int *server_mount_flags)
{
int error;
CFURLRef urlRef;
int dav_level;
struct stat statbuf;
urlRef = gBaseURL;
error = network_getDAVLevel(uid, urlRef, &dav_level);
if ( error == 0 )
{
if ( dav_level > 2 )
{
dav_level = 2;
}
switch (dav_level)
{
case 1:
*server_mount_flags |= MNT_RDONLY;
break;
case 2:
break;
default:
debug_string("network_mount: WebDAV protocol not supported");
error = ENODEV;
break;
}
if ( error == 0 )
{
error = network_stat(uid, urlRef, &statbuf);
if ( error )
{
if (error != EACCES)
{
debug_string("network_mount: PROPFIND failed");
error = ENODEV;
}
else
{
debug_string("network_mount: mount cancelled by user");
error = ECANCELED;
}
}
else if ( !S_ISDIR(statbuf.st_mode) )
{
debug_string("network_mount: URL is not a collection resource (directory)");
error = ENODEV;
}
}
}
else
{
if ( error != EACCES )
{
debug_string("network_mount: OPTIONS failed");
error = ENODEV;
}
else
{
debug_string("network_mount: mount cancelled by user");
error = ECANCELED;
}
}
return ( error );
}
int network_finish_download(
struct node_entry *node,
struct ReadStreamRec *readStreamRecPtr)
{
UInt8 *buffer;
CFIndex bytesRead;
buffer = malloc(BODY_BUFFER_SIZE);
require(buffer != NULL, malloc_buffer);
while ( 1 )
{
if ( (node->file_status & WEBDAV_DOWNLOAD_TERMINATED) != 0 )
{
bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer, 1);
if ( bytesRead == 0 )
{
break;
}
else
{
goto terminated;
}
}
bytesRead = CFReadStreamRead(readStreamRecPtr->readStreamRef, buffer, BODY_BUFFER_SIZE);
if ( bytesRead > 0 )
{
require(write(node->file_fd, buffer, (size_t)bytesRead) == (ssize_t)bytesRead, write);
}
else if ( bytesRead == 0 )
{
break;
}
else
{
CFStreamError streamError;
streamError = CFReadStreamGetError(readStreamRecPtr->readStreamRef);
syslog(LOG_ERR,"network_finish_download: CFStreamError: domain %ld, error %ld", streamError.domain, streamError.error);
goto CFReadStreamRead;
break;
}
};
free(buffer);
if ( readStreamRecPtr->connectionClose )
{
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
}
release_ReadStreamRec(readStreamRecPtr);
return ( 0 );
terminated:
write:
CFReadStreamRead:
free(buffer);
malloc_buffer:
CFReadStreamClose(readStreamRecPtr->readStreamRef);
CFRelease(readStreamRecPtr->readStreamRef);
readStreamRecPtr->readStreamRef = NULL;
release_ReadStreamRec(readStreamRecPtr);
return ( EIO );
}
int network_open(
uid_t uid,
struct node_entry *node,
int write_access)
{
int error;
int ask_server;
if ( !write_access )
{
if ( ((node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED) && !NODE_FILE_INVALID(node) )
{
ask_server = FALSE;
}
else
{
if ( get_from_attributes_cache(node, uid) )
{
ask_server = FALSE;
}
else
{
ask_server = TRUE;
}
}
}
else
{
if ( NODE_FILE_RECENTLY_CREATED(node) &&
((node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED) )
{
ask_server = FALSE;
}
else
{
ask_server = TRUE;
}
}
if ( ask_server )
{
CFURLRef urlRef;
CFHTTPMessageRef message;
CFHTTPMessageRef responseRef;
UInt32 statusCode;
UInt32 auth_generation;
int retryTransaction;
error = 0;
message = NULL;
responseRef = NULL;
statusCode = 0;
auth_generation = 0;
retryTransaction = TRUE;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
do
{
if ( message != NULL )
{
CFRelease(message);
message = NULL;
}
message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), urlRef, kCFHTTPVersion1_1);
require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO);
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("User-Agent"), userAgentHeaderValue);
if ( X_Source_Id_HeaderValue != NULL )
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("X-Source-Id"), X_Source_Id_HeaderValue);
}
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
if ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) != WEBDAV_DOWNLOAD_NEVER )
{
CFStringRef httpDateString;
httpDateString = CFStringCreateRFC2616DateStringWithTimeT(node->file_last_modified);
if ( httpDateString != NULL )
{
if ( (node->file_status & WEBDAV_DOWNLOAD_STATUS_MASK) == WEBDAV_DOWNLOAD_FINISHED )
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("If-Modified-Since"), httpDateString);
}
else
{
off_t currentLength;
CFStringRef currentLengthString;
currentLength = lseek(node->file_fd, 0LL, SEEK_END);
if ( currentLength != -1 )
{
currentLengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%qd-"), currentLength);
if ( currentLengthString != NULL )
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("If-Range"), httpDateString);
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), currentLengthString);
CFRelease(currentLengthString);
}
}
}
CFRelease(httpDateString);
}
}
error = authcache_apply(uid, message, statusCode, responseRef, &auth_generation);
if ( error != 0 )
{
break;
}
if ( responseRef != NULL )
{
CFRelease(responseRef);
responseRef = NULL;
}
error = stream_get_transaction(message, &retryTransaction, node, &responseRef);
if ( error == EAGAIN )
{
statusCode = 0;
}
else
{
if ( error != 0 )
{
break;
}
statusCode = CFHTTPMessageGetResponseStatusCode(responseRef);
}
} while ( error == EAGAIN || statusCode == 401 || statusCode == 407 );
CFHTTPMessageCreateRequest:
if ( error == 0 )
{
if ( statusCode == 304 )
{
statusCode = 200;
}
error = translate_status_to_error(statusCode);
if ( error == 0 )
{
(void) authcache_valid(uid, message, auth_generation);
time(&node->file_validated_time);
{
CFStringRef headerRef;
const char *field_value;
char buffer[4096];
char *file_entity_tag;
headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("Last-Modified"));
if ( headerRef )
{
node->file_last_modified = DateStringToTime(headerRef);
CFRelease(headerRef);
}
headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("ETag"));
if ( headerRef )
{
field_value = CFStringGetCStringPtr(headerRef, kCFStringEncodingUTF8);
if ( field_value == NULL )
{
if ( CFStringGetCString(headerRef, buffer, 4096, kCFStringEncodingUTF8) )
{
field_value = buffer;
}
}
if ( field_value != NULL )
{
file_entity_tag = malloc(strlen(field_value) + 1);
if ( file_entity_tag != NULL )
{
strcpy(file_entity_tag, field_value);
if ( node->file_entity_tag != NULL )
{
free(node->file_entity_tag);
}
node->file_entity_tag = file_entity_tag;
}
}
CFRelease(headerRef);
}
}
}
}
if ( message != NULL )
{
CFRelease(message);
}
if ( responseRef != NULL )
{
CFRelease(responseRef);
}
CFRelease(urlRef);
create_cfurl_from_node:
;
}
else
{
error = 0;
}
return ( error );
}
int network_statfs(
uid_t uid,
struct node_entry *node,
struct statfs *fs_attr)
{
int error;
CFURLRef urlRef;
UInt8 *responseBuffer;
CFIndex count;
CFDataRef bodyData;
const UInt8 xmlString[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:propfind xmlns:D=\"DAV:\">\n"
"<D:prop>\n"
"<D:quota/>\n"
"<D:quotaused/>\n"
"</D:prop>\n"
"</D:propfind>\n";
CFIndex headerCount = 3;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Content-Type"), CFSTR("text/xml") },
{ CFSTR("Depth"), CFSTR("0") }
};
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull);
require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO);
error = send_transaction(uid, urlRef, CFSTR("PROPFIND"), bodyData,
headerCount, headers, TRUE, &responseBuffer, &count, NULL);
if ( !error )
{
error = parse_statfs(responseBuffer, count, fs_attr);
free(responseBuffer);
}
CFRelease(bodyData);
CFDataCreateWithBytesNoCopy:
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_create(
uid_t uid,
struct node_entry *node,
char *name,
size_t name_length,
time_t *creation_date)
{
int error;
CFURLRef urlRef;
CFHTTPMessageRef response;
CFIndex headerCount = 1;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") }
};
*creation_date = -1;
urlRef = create_cfurl_from_node(node, name, name_length);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
error = send_transaction(uid, urlRef, CFSTR("PUT"), NULL,
headerCount, headers, FALSE, NULL, NULL, &response);
if ( !error )
{
CFStringRef dateHeaderRef;
dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Date"));
if ( dateHeaderRef != NULL )
{
*creation_date = DateStringToTime(dateHeaderRef);
CFRelease(dateHeaderRef);
}
CFRelease(response);
}
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_fsync(
uid_t uid,
struct node_entry *node,
off_t *file_length,
time_t *file_last_modified)
{
int error;
CFURLRef urlRef;
CFHTTPMessageRef message;
CFHTTPMessageRef responseRef;
UInt32 statusCode;
UInt32 auth_generation;
CFStringRef lockTokenRef;
char *file_entity_tag;
int retryTransaction;
error = 0;
*file_last_modified = -1;
*file_length = -1;
file_entity_tag = NULL;
message = NULL;
responseRef = NULL;
statusCode = 0;
auth_generation = 0;
retryTransaction = TRUE;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
do
{
if ( message != NULL )
{
CFRelease(message);
message = NULL;
}
message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("PUT"), urlRef, kCFHTTPVersion1_1);
require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO);
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("User-Agent"), userAgentHeaderValue);
if ( X_Source_Id_HeaderValue != NULL )
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("X-Source-Id"), X_Source_Id_HeaderValue);
}
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
if ( node->file_locktoken != NULL )
{
lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken);
if ( lockTokenRef != NULL )
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("If"), lockTokenRef );
CFRelease(lockTokenRef);
lockTokenRef = NULL;
}
}
else
{
lockTokenRef = NULL;
}
error = authcache_apply(uid, message, statusCode, responseRef, &auth_generation);
if ( error != 0 )
{
break;
}
if ( responseRef != NULL )
{
CFRelease(responseRef);
responseRef = NULL;
}
error = stream_transaction_from_file(message, node->file_fd, &retryTransaction, &responseRef);
if ( error == EAGAIN )
{
statusCode = 0;
}
else
{
if ( error != 0 )
{
break;
}
statusCode = CFHTTPMessageGetResponseStatusCode(responseRef);
}
} while ( error == EAGAIN || statusCode == 401 || statusCode == 407 );
CFHTTPMessageCreateRequest:
if ( error == 0 )
{
error = translate_status_to_error(statusCode);
if ( error == 0 )
{
(void) authcache_valid(uid, message, auth_generation);
{
CFStringRef headerRef;
const char *field_value;
char buffer[4096];
headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("Last-Modified"));
if ( headerRef )
{
*file_last_modified = DateStringToTime(headerRef);
CFRelease(headerRef);
}
headerRef = CFHTTPMessageCopyHeaderFieldValue(responseRef, CFSTR("ETag"));
if ( headerRef )
{
field_value = CFStringGetCStringPtr(headerRef, kCFStringEncodingUTF8);
if ( field_value == NULL )
{
if ( CFStringGetCString(headerRef, buffer, 4096, kCFStringEncodingUTF8) )
{
field_value = buffer;
}
}
if ( field_value != NULL )
{
file_entity_tag = malloc(strlen(field_value) + 1);
if ( file_entity_tag != NULL )
{
strcpy(file_entity_tag, field_value);
}
}
CFRelease(headerRef);
}
}
}
}
if ( message != NULL )
{
CFRelease(message);
}
if ( responseRef != NULL )
{
CFRelease(responseRef);
}
if ( (error == 0) && (*file_last_modified == -1) && (file_entity_tag == NULL) )
{
int propError;
UInt8 *responseBuffer;
CFIndex count;
CFDataRef bodyData;
const UInt8 xmlString[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:propfind xmlns:D=\"DAV:\">\n"
"<D:prop>\n"
"<D:getlastmodified/>\n"
"<D:getetag/>\n"
"</D:prop>\n"
"</D:propfind>\n";
CFIndex headerCount = 3;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Content-Type"), CFSTR("text/xml") },
{ CFSTR("Depth"), CFSTR("0") }
};
propError = 0;
responseBuffer = NULL;
bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull);
require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, propError = EIO);
propError = send_transaction(uid, urlRef, CFSTR("PROPFIND"), bodyData,
headerCount, headers, TRUE, &responseBuffer, &count, NULL);
if ( propError == 0 )
{
propError = parse_cachevalidators(responseBuffer, count, file_last_modified, &file_entity_tag);
free(responseBuffer);
}
CFRelease(bodyData);
CFDataCreateWithBytesNoCopy:
;
}
CFRelease(urlRef);
create_cfurl_from_node:
if ( !error )
{
node->file_last_modified = *file_last_modified;
if ( node->file_entity_tag != NULL )
{
free(node->file_entity_tag);
}
node->file_entity_tag = file_entity_tag;
*file_length = lseek(node->file_fd, 0LL, SEEK_END);
}
return ( error );
}
static int network_delete(
uid_t uid,
CFURLRef urlRef,
struct node_entry *node,
time_t *remove_date)
{
int error;
CFStringRef lockTokenRef;
CFHTTPMessageRef response;
CFIndex headerCount;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("If"), NULL }
};
*remove_date = -1;
if ( node->file_locktoken != NULL )
{
lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken);
if ( lockTokenRef != NULL )
{
headerCount = 2;
headers[1].value = lockTokenRef;
}
else
{
headerCount = 1;
}
}
else
{
lockTokenRef = NULL;
headerCount = 1;
}
error = send_transaction(uid, urlRef, CFSTR("DELETE"), NULL,
headerCount, headers, FALSE, NULL, NULL, &response);
if ( !error )
{
CFStringRef dateHeaderRef;
dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Date"));
if ( dateHeaderRef != NULL )
{
*remove_date = DateStringToTime(dateHeaderRef);
CFRelease(dateHeaderRef);
}
CFRelease(response);
}
if ( lockTokenRef != NULL )
{
CFRelease(lockTokenRef);
}
return ( error );
}
int network_remove(
uid_t uid,
struct node_entry *node,
time_t *remove_date)
{
int error;
CFURLRef urlRef;
error = 0;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
error = network_delete(uid, urlRef, node, remove_date);
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_rmdir(
uid_t uid,
struct node_entry *node,
time_t *remove_date)
{
int error;
CFURLRef urlRef;
error = 0;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
error = network_dir_is_empty(uid, urlRef);
if ( !error )
{
error = network_delete(uid, urlRef, node, remove_date);
}
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_rename(
uid_t uid,
struct node_entry *from_node,
struct node_entry *to_node,
struct node_entry *to_dir_node,
char *to_name,
size_t to_name_length,
time_t *rename_date)
{
int error;
CFURLRef urlRef;
CFURLRef destinationUrlRef;
CFStringRef destinationRef;
CFHTTPMessageRef response;
CFIndex headerCount = 2;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Destination"), NULL }
};
*rename_date = -1;
urlRef = NULL;
destinationUrlRef = NULL;
destinationRef = NULL;
urlRef = create_cfurl_from_node(from_node, NULL, 0);
require_action_quiet(urlRef != NULL, exit, error = EIO);
if ( to_node != NULL )
{
destinationUrlRef = create_cfurl_from_node(to_node, NULL, 0);
require_action_quiet(destinationUrlRef != NULL, exit, error = EIO);
require_action_quiet( !CFEqual(urlRef, destinationUrlRef), exit, error = 0);
if ( to_node->node_type == WEBDAV_DIR_TYPE )
{
error = network_dir_is_empty(uid, destinationUrlRef);
require_noerr_quiet(error, exit);
}
}
else
{
destinationUrlRef = create_cfurl_from_node(to_dir_node, to_name, to_name_length);
require_action_quiet(destinationUrlRef != NULL, exit, error = EIO);
require_action_quiet( !CFEqual(urlRef, destinationUrlRef), exit, error = 0);
}
destinationRef = CFURLGetString(destinationUrlRef);
require_action(destinationRef != NULL, exit, error = EIO);
headers[1].value = destinationRef;
error = send_transaction(uid, urlRef, CFSTR("MOVE"), NULL,
headerCount, headers, FALSE, NULL, NULL, &response);
if ( !error )
{
CFStringRef dateHeaderRef;
dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Date"));
if ( dateHeaderRef != NULL )
{
*rename_date = DateStringToTime(dateHeaderRef);
CFRelease(dateHeaderRef);
}
CFRelease(response);
}
exit:
if ( destinationUrlRef != NULL )
{
CFRelease(destinationUrlRef);
}
if ( urlRef != NULL )
{
CFRelease(urlRef);
}
return ( error );
}
int network_lock(
uid_t uid,
int refresh,
struct node_entry *node)
{
int error;
CFURLRef urlRef;
UInt8 *responseBuffer;
CFIndex count;
CFDataRef bodyData;
const UInt8 xmlString[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:lockinfo xmlns:D=\"DAV:\">\n"
"<D:lockscope><D:exclusive/></D:lockscope>\n"
"<D:locktype><D:write/></D:locktype>\n"
"<D:owner>\n"
"<D:href>http://www.apple.com/webdav_fs/</D:href>\n"
"</D:owner>\n"
"</D:lockinfo>\n";
CFIndex headerCount = 4;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Depth"), CFSTR("0") },
{ CFSTR("Timeout"), NULL },
{ CFSTR("Content-Type"), NULL },
{ CFSTR("If"), NULL }
};
CFStringRef timeoutSpecifierRef;
CFStringRef lockTokenRef;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
timeoutSpecifierRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("Second-%s"), gtimeout_string);
require_action(timeoutSpecifierRef != NULL, CFStringCreateWithFormat_timeoutSpecifierRef, error = EIO);
headers[2].value = timeoutSpecifierRef;
if ( refresh )
{
uid = node->file_locktoken_uid;
bodyData = NULL;
headerCount = 5;
headers[3].value = CFSTR("text/xml");
lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken);
require_action(lockTokenRef != NULL, CFStringCreateWithFormat_lockTokenRef, error = EIO);
headers[4].value = lockTokenRef;
}
else
{
lockTokenRef = NULL;
bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull);
require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO);
headerCount = 4;
headers[3].value = CFSTR("text/xml; charset=\"utf-8\"");
}
error = send_transaction(uid, urlRef, CFSTR("LOCK"), bodyData,
headerCount, headers, FALSE, &responseBuffer, &count, NULL);
if ( !error )
{
char *locktoken;
error = parse_lock(responseBuffer, count, &locktoken);
if ( !error )
{
char *old_locktoken;
old_locktoken = node->file_locktoken;
node->file_locktoken = locktoken;
if ( old_locktoken != NULL )
{
free(old_locktoken);
}
if ( !refresh )
{
node->file_locktoken_uid = uid;
}
}
free(responseBuffer);
}
if ( bodyData != NULL )
{
CFRelease(bodyData);
}
if ( lockTokenRef != NULL )
{
CFRelease(lockTokenRef);
}
CFDataCreateWithBytesNoCopy:
CFStringCreateWithFormat_lockTokenRef:
CFRelease(timeoutSpecifierRef);
CFStringCreateWithFormat_timeoutSpecifierRef:
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_unlock(
struct node_entry *node)
{
int error;
CFURLRef urlRef;
CFStringRef lockTokenRef;
CFIndex headerCount = 2;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Lock-Token"), NULL }
};
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%s>"), node->file_locktoken);
require_action_quiet(lockTokenRef != NULL, CFStringCreateWithFormat, error = EIO);
headers[1].value = lockTokenRef;
error = send_transaction(node->file_locktoken_uid, urlRef, CFSTR("UNLOCK"), NULL,
headerCount, headers, FALSE, NULL, NULL, NULL);
CFRelease(lockTokenRef);
CFStringCreateWithFormat:
CFRelease(urlRef);
create_cfurl_from_node:
free(node->file_locktoken);
node->file_locktoken_uid = 0;
node->file_locktoken = NULL;
return ( error );
}
int network_readdir(
uid_t uid,
int cache,
struct node_entry *node)
{
int error;
CFURLRef urlRef;
UInt8 *responseBuffer;
CFIndex count;
CFDataRef bodyData;
const UInt8 xmlString[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:propfind xmlns:D=\"DAV:\">\n"
"<D:prop>\n"
"<D:getlastmodified/>\n"
"<D:getcontentlength/>\n"
"<D:resourcetype/>\n"
"</D:prop>\n"
"</D:propfind>\n";
const UInt8 xmlStringCache[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<D:propfind xmlns:D=\"DAV:\">\n"
"<D:prop xmlns:A=\"http://www.apple.com/webdav_fs/props/\">\n"
"<D:getlastmodified/>\n"
"<D:getcontentlength/>\n"
"<D:resourcetype/>\n"
"<A:appledoubleheader/>\n"
"</D:prop>\n"
"</D:propfind>\n";
CFIndex headerCount = 3;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Content-Type"), CFSTR("text/xml") },
{ CFSTR("Depth"), CFSTR("1") }
};
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(cache ? xmlStringCache : xmlString), strlen((const char *)(cache ? xmlStringCache : xmlString)), kCFAllocatorNull);
require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO);
error = send_transaction(uid, urlRef, CFSTR("PROPFIND"), bodyData,
headerCount, headers, TRUE, &responseBuffer, &count, NULL);
if ( !error )
{
error = parse_opendir(responseBuffer, count, urlRef, uid, node);
free(responseBuffer);
}
CFRelease(bodyData);
CFDataCreateWithBytesNoCopy:
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_mkdir(
uid_t uid,
struct node_entry *node,
char *name,
size_t name_length,
time_t *creation_date)
{
int error;
CFURLRef urlRef;
CFHTTPMessageRef response;
CFIndex headerCount = 1;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
};
*creation_date = -1;
urlRef = create_cfurl_from_node(node, name, name_length);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
error = send_transaction(uid, urlRef, CFSTR("MKCOL"), NULL,
headerCount, headers, FALSE, NULL, NULL, &response);
if ( !error )
{
CFStringRef dateHeaderRef;
dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Date"));
if ( dateHeaderRef != NULL )
{
*creation_date = DateStringToTime(dateHeaderRef);
CFRelease(dateHeaderRef);
}
CFRelease(response);
}
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}
int network_read(
uid_t uid,
struct node_entry *node,
off_t offset,
size_t count,
char **buffer,
size_t *actual_count)
{
int error;
CFURLRef urlRef;
UInt8 *responseBuffer;
CFIndex responseCount;
CFStringRef byteRangesSpecifierRef;
CFIndex headerCount = 2;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Range"), NULL }
};
*buffer = NULL;
*actual_count = 0;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
byteRangesSpecifierRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%qd-%qd"), offset, offset + count - 1);
require_action(byteRangesSpecifierRef != NULL, CFStringCreateWithFormat, error = EIO);
headers[1].value = byteRangesSpecifierRef;
error = send_transaction(uid, urlRef, CFSTR("GET"), NULL,
headerCount, headers, TRUE, &responseBuffer, &responseCount, NULL);
if ( !error )
{
if ( (size_t)responseCount > count )
{
responseCount = count;
}
*buffer = (char *)responseBuffer;
*actual_count = responseCount;
}
CFRelease(byteRangesSpecifierRef);
CFStringCreateWithFormat:
CFRelease(urlRef);
create_cfurl_from_node:
return ( error );
}