#include "webdavd.h"
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCDynamicStorePrivate.h>
#include <SystemConfiguration/SCDynamicStoreKey.h>
#include <CoreServices/CoreServices.h>
#include <CoreServices/CoreServicesPriv.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <Security/Security.h>
#include <netdb.h>
#include <stdio.h>
#include "webdav_parse.h"
#include "webdav_requestqueue.h"
#include "webdav_authcache.h"
#include "webdav_network.h"
#include "EncodedSourceID.h"
#define WEBDAV_WRITESEQ_RSP_TIMEOUT 60
#define kSSLClientPropTLSServerCertificateChain CFSTR("TLSServerCertificateChain")
#define kSSLClientPropTLSTrustClientStatus CFSTR("TLSTrustClientStatus")
#define kSSLClientPropTLSServerHostName CFSTR("TLSServerHostName")
#define CFENVFORMATSTRING "__CF_USER_TEXT_ENCODING=0x%X:0:0"
struct HeaderFieldValue
{
CFStringRef headerField;
CFStringRef value;
};
extern uint64_t webdavCacheMaximumSize;
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 = (int)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 = (int)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;
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, (unsigned long)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");
require_action(((CFURLGetByteRangeForComponent(gBaseURL, kCFURLComponentUserInfo, NULL).location == kCFNotFound) &&
(CFURLGetByteRangeForComponent(gBaseURL, kCFURLComponentResourceSpecifier, NULL).location == kCFNotFound)),
IllegalURLComponent, error = EINVAL);
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);
}
IllegalURLComponent:
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 402:
case 403:
case 405:
result = EPERM;
break;
case 407:
result = EAUTH;
break;
case 404:
case 409:
case 410:
result = ENOENT;
break;
case 413:
result = EFBIG;
break;
case 414:
result = ENAMETOOLONG;
break;
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;
CFIndex 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 CFUserTextEncodingEnvSetting[sizeof(CFENVFORMATSTRING) + 20];
char *env[] = {CFUserTextEncodingEnvSetting, "", (char *) 0 };
snprintf(CFUserTextEncodingEnvSetting, sizeof(CFUserTextEncodingEnvSetting), CFENVFORMATSTRING, getuid());
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 stream_error_to_errno(CFStreamError *streamError)
{
int result = ENXIO;
if (streamError->domain == kCFStreamErrorDomainPOSIX)
{
switch (streamError->error) {
case EADDRNOTAVAIL:
case ENETDOWN:
case ETIMEDOUT:
case ECONNRESET:
case ENETUNREACH:
case ECONNREFUSED:
syslog(LOG_ERR, "stream_error: Posix error %d", (int)streamError->error);
result = ETIMEDOUT;
break;
default:
syslog(LOG_ERR, "stream_error: Posix error %d", (int)streamError->error);
result = ENXIO;
break;
}
}
else if (streamError->domain == kCFStreamErrorDomainNetDB)
{
switch (streamError->error) {
case EAI_NODATA:
syslog(LOG_ERR, "stream_error: NetDB error EAI_NODATA");
result = ETIMEDOUT;
break;
default:
syslog(LOG_ERR, "stream_error: NetDB error %d", (int)streamError->error);
result = ENXIO;
break;
}
}
else {
syslog(LOG_ERR, "stream_error: Domain %d Error %d", (int) streamError->domain, (int)streamError->error);
result = ENXIO;
}
return result;
}
static int open_stream_for_transaction(
CFHTTPMessageRef request,
CFReadStreamRef fdStream,
int auto_redirect,
int *retryTransaction,
struct ReadStreamRec **readStreamRecPtr)
{
int result, error;
struct ReadStreamRec *theReadStreamRec;
CFReadStreamRef newReadStreamRef;
CFSocketNativeHandle sock;
CFDataRef sockWrapper = NULL;
result = error = 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) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) )
{
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 = stream_error_to_errno(&streamError);
}
}
goto CFReadStreamOpen;
}
if ( theReadStreamRec->readStreamRef != NULL )
{
CFReadStreamClose(theReadStreamRec->readStreamRef);
CFRelease(theReadStreamRec->readStreamRef);
}
sockWrapper = (CFDataRef)CFReadStreamCopyProperty(newReadStreamRef, kCFStreamPropertySocketNativeHandle);
if (sockWrapper)
{
CFRange r = {0, sizeof(CFSocketNativeHandle)};
CFDataGetBytes(sockWrapper, r, (UInt8 *)&sock);
CFRelease(sockWrapper);
int flag = 1;
setsockopt(sock, SOL_SOCKET, SO_NOADDRERR, &flag, (socklen_t)sizeof(flag));
}
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) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) )
{
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 = stream_error_to_errno(&streamError);
}
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);
if (node->attr_stat.st_size > (off_t)webdavCacheMaximumSize) {
fcntl(node->file_fd, F_NOCACHE, 1);
}
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) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) )
{
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 = stream_error_to_errno(&streamError);
}
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) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)) )
{
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 = stream_error_to_errno(&streamError);
}
}
}
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;
CFIndex 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, (UInt32)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 = (int)translate_status_to_error((UInt32)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 void identifyServerType(CFHTTPMessageRef responsePropertyRef)
{
CFStringRef serverHeaderRef = CFHTTPMessageCopyHeaderFieldValue(responsePropertyRef, CFSTR("Server"));
if ( serverHeaderRef != NULL ) {
if (CFStringHasPrefix(serverHeaderRef, CFSTR("AppleIDiskServer")) == TRUE)
gServerIdent = WEBDAV_IDISK_SERVER;
else if (CFStringHasPrefix(serverHeaderRef, CFSTR("Microsoft-IIS/")) == TRUE)
gServerIdent = WEBDAV_MICROSOFT_IIS_SERVER;
CFRelease(serverHeaderRef);
}
}
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);
identifyServerType(response);
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") },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
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") },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 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 = EAUTH;
}
}
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 = EAUTH;
}
}
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_server_ping(u_int32_t delay)
{
int error;
CFHTTPMessageRef response;
CFURLRef urlRef = gBaseURL;
CFIndex headerCount = 1;
struct HeaderFieldValue headers[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
};
if (delay)
sleep(delay);
error = send_transaction(gProcessUID, urlRef, CFSTR("OPTIONS"), NULL,
headerCount, headers, TRUE, NULL, NULL, &response);
if ( !error ) {
set_connectionstate(WEBDAV_CONNECTION_UP);
CFRelease(response);
}
else {
syslog(LOG_ERR, "WebDAV server still not responding...");
switch (delay) {
case 0:
delay = 1;
break;
case 1:
delay = 2;
break;
case 2:
delay = 4;
break;
case 4:
delay = 8;
break;
default:
delay = 12;
break;
}
requestqueue_enqueue_server_ping(delay);
}
return ( error );
}
void writeseqReadResponseCallback(CFReadStreamRef str, CFStreamEventType event, void* arg)
{
struct stream_put_ctx *ctx;
CFStreamError streamError;
CFIndex bytesRead;
CFTypeRef theResponsePropertyRef;
CFHTTPMessageRef responseMessage;
CFIndex statusCode;
int error;
ctx = (struct stream_put_ctx *)arg;
switch(event)
{
case kCFStreamEventHasBytesAvailable:
bytesRead = CFReadStreamRead(str, ctx->rspBuf + ctx->totalRead, WEBDAV_WRITESEQ_RSPBUF_LEN - ctx->totalRead);
if (bytesRead < 0 ) {
streamError = CFReadStreamGetError(str);
if (!(ctx->is_retry) &&
((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)))
{
syslog(LOG_DEBUG,"%s: EventHasBytesAvailable CFStreamError: domain %ld, error %d (retrying)",
__FUNCTION__, streamError.domain, streamError.error);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = EAGAIN;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_ERR,"%s: EventHasBytesAvailable CFStreamError: domain %ld, error %d",
__FUNCTION__, streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = stream_error_to_errno(&streamError);
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
}
goto out1;
}
ctx->totalRead += bytesRead;
break;
case kCFStreamEventOpenCompleted:
break;
case kCFStreamEventErrorOccurred:
error = HandleSSLErrors(str);
if ( error == EAGAIN ) {
syslog(LOG_DEBUG,"%s: EventHasErrorOccurred: HandleSSLErrors: EAGAIN", __FUNCTION__);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = EAGAIN;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
}
else {
streamError = CFReadStreamGetError(str);
if (!(ctx->is_retry) &&
((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)))
{
syslog(LOG_DEBUG,"%s: EventHasErrorOccurred CFStreamError: domain %ld, error %d (retrying)",
__FUNCTION__, streamError.domain, streamError.error);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = EAGAIN;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_ERR,"%s: EventErrorOccurred CFStreamError: domain %ld, error %d",
__FUNCTION__, streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = stream_error_to_errno(&streamError);
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
}
}
break;
case kCFStreamEventEndEncountered:
theResponsePropertyRef = CFReadStreamCopyProperty(str, kCFStreamPropertyHTTPResponseHeader);
if (theResponsePropertyRef == NULL)
{
syslog(LOG_DEBUG,"%s: EventEndEncountered failed to obtain response header", __FUNCTION__);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
responseMessage = *((CFHTTPMessageRef*)((void*)&theResponsePropertyRef));
statusCode = CFHTTPMessageGetResponseStatusCode(responseMessage);
error = translate_status_to_error((UInt32)statusCode);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = error;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
CFRelease (theResponsePropertyRef);
break;
default:
break;
}
out1:;
}
void writeseqWriteCallback(CFWriteStreamRef str,
CFStreamEventType event,
void* arg)
{
struct stream_put_ctx *ctx;
CFStreamError streamError;
ctx = (struct stream_put_ctx *)arg;
switch(event)
{
case kCFStreamEventCanAcceptBytes:
pthread_mutex_lock(&ctx->ctx_lock);
ctx->canAcceptBytesEvents++;
pthread_mutex_unlock(&ctx->ctx_lock);
break;
case kCFStreamEventOpenCompleted:
pthread_mutex_lock(&ctx->ctx_lock);
ctx->writeStreamOpenEventReceived = true;
pthread_mutex_unlock(&ctx->ctx_lock);
break;
case kCFStreamEventErrorOccurred:
streamError = CFWriteStreamGetError(str);
pthread_mutex_lock(&ctx->ctx_lock);
if (!(ctx->is_retry) &&
((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)))
{
syslog(LOG_DEBUG,"%s: EventErrorOccurred CFStreamError: domain %ld, error %d (retrying)",
__FUNCTION__, streamError.domain, streamError.error);
ctx->finalStatus = EAGAIN;
ctx->finalStatusValid = true;
pthread_mutex_unlock(&ctx->ctx_lock);
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_ERR,"%s: EventErrorOccurred CFStreamError: domain %ld, error %d",
__FUNCTION__, streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
ctx->finalStatus = EIO;
ctx->finalStatusValid = 1;
pthread_mutex_unlock(&ctx->ctx_lock);
}
break;
case kCFStreamEventEndEncountered:
break;
default:
break;
}
}
CFDataRef managerMessagePortCallback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
{
#pragma unused(local,msgid,data,info)
return NULL;
}
int cleanup_seq_write(struct stream_put_ctx *ctx)
{
struct timespec timeout;
struct seqwrite_mgr_req *mgr_req;
int error;
if ( ctx == NULL ) {
syslog(LOG_ERR, "%s: context passed in was NULL", __FUNCTION__);
return (-1);
}
mgr_req = (struct seqwrite_mgr_req *) malloc(sizeof(struct seqwrite_mgr_req));
if (mgr_req == NULL) {
syslog(LOG_ERR, "%s: no mem for mgr request", __FUNCTION__);
return (-1);
}
bzero(mgr_req, sizeof(struct seqwrite_mgr_req));
mgr_req->refCount = 1; mgr_req->type = SEQWRITE_CLOSE;
timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_RSP_TIMEOUT;
timeout.tv_nsec = 0;
pthread_mutex_lock(&ctx->ctx_lock);
if (ctx->mgr_status == WR_MGR_RUNNING) {
queue_writemgr_request_locked(ctx, mgr_req);
}
while (ctx->mgr_status != WR_MGR_DONE) {
error = pthread_cond_timedwait(&ctx->ctx_condvar, &ctx->ctx_lock, &timeout);
if ((error != 0) && (error != ETIMEDOUT)) {
syslog(LOG_ERR, "%s: pthread_cond_timewait error %d\n", __FUNCTION__, error);
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
break;
} else {
timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_RSP_TIMEOUT;
timeout.tv_nsec = 0;
}
}
error = ctx->finalStatus;
release_writemgr_request_locked(mgr_req);
pthread_mutex_unlock(&ctx->ctx_lock);
if (ctx->wrStreamRef != NULL) {
CFRelease(ctx->wrStreamRef);
ctx->wrStreamRef = NULL;
}
if (ctx->rdStreamRef != NULL) {
CFReadStreamClose(ctx->rdStreamRef);
CFRelease(ctx->rdStreamRef);
ctx->rdStreamRef = NULL;
}
if (ctx->rspStreamRef != NULL) {
CFReadStreamClose(ctx->rspStreamRef);
CFRelease(ctx->rspStreamRef);
ctx->rspStreamRef = NULL;
}
if (ctx->mgrPort != NULL) {
CFMessagePortInvalidate(ctx->mgrPort);
CFRelease(ctx->mgrPort);
ctx->mgrPort = NULL;
}
if (ctx->mgr_rl != NULL) {
CFRelease(ctx->mgr_rl);
ctx->mgr_rl = NULL;
}
return (error);
}
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;
CFIndex 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);
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("translate"), CFSTR("f"));
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Pragma"), CFSTR("no-cache"));
}
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, (UInt32)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((UInt32)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-available-bytes/>\n"
"<D:quota-used-bytes/>\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") },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
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("*/*") },
{ CFSTR("translate"), CFSTR("f") },
{ CFSTR("Pragma"), CFSTR("no-cache") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 2;
}
*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 );
}
static void create_http_request_message(CFHTTPMessageRef *message_p, CFURLRef urlRef, off_t file_len) {
CFStringRef expectedLengthString = NULL;
if ( *message_p != NULL )
{
CFRelease(*message_p);
*message_p = NULL;
}
*message_p = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("PUT"), urlRef, kCFHTTPVersion1_1);
if (*message_p != NULL) {
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("translate"), CFSTR("f"));
CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("Pragma"), CFSTR("no-cache"));
}
CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("User-Agent"), userAgentHeaderValue);
if ( X_Source_Id_HeaderValue != NULL )
{
CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("X-Source-Id"), X_Source_Id_HeaderValue);
}
CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("Accept"), CFSTR("*/*"));
if ( file_len != 0 ) {
expectedLengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%qi"), file_len);
if ( expectedLengthString != NULL )
{
CFHTTPMessageSetHeaderFieldValue(*message_p, CFSTR("X-Expected-Entity-Length"), expectedLengthString);
CFRelease(expectedLengthString);
}
}
}
}
static void add_last_mod_etag(CFHTTPMessageRef responseRef, time_t *file_last_modified, char **file_entity_tag) {
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);
}
}
static void close_socket_pair(int sockfd[2])
{
close(sockfd[0]);
close(sockfd[1]);
}
static bool create_bound_streams(struct stream_put_ctx *ctx) {
if ( socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->sockfd) < 0) {
syslog(LOG_ERR,"%s: socketpair creation failed.", __FUNCTION__);
return false;
}
CFStreamCreatePairWithSocket(kCFAllocatorDefault, ctx->sockfd[0], &ctx->rdStreamRef, NULL);
CFStreamCreatePairWithSocket(kCFAllocatorDefault, ctx->sockfd[1], NULL, &ctx->wrStreamRef);
if ( (ctx->rdStreamRef == NULL) || (ctx->wrStreamRef == NULL) )
{
syslog(LOG_ERR, "%s: Null Stream Pair: rdStreamRef %p wrStreamRef %p.", __FUNCTION__, ctx->rdStreamRef, ctx->wrStreamRef );
close_socket_pair(ctx->sockfd);
return false;
}
if ( CFReadStreamSetProperty(ctx->rdStreamRef, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue) == false ||
CFWriteStreamSetProperty(ctx->wrStreamRef, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue) == false )
{
syslog(LOG_ERR, "%s: failed to set kCFStreamPropertyShouldCloseNativeSocket.", __FUNCTION__);
close_socket_pair(ctx->sockfd);
return false;
}
if (CFReadStreamOpen (ctx->rdStreamRef) == false) {
syslog(LOG_ERR, "%s: couldn't open read stream.", __FUNCTION__);
return false;
}
return true;
}
int setup_seq_write(
uid_t uid,
struct node_entry *node,
off_t file_length)
{
int error;
CFURLRef urlRef = NULL;
CFHTTPMessageRef message;
UInt32 statusCode;
CFHTTPMessageRef responseRef;
UInt32 auth_generation;
CFStringRef lockTokenRef;
char *file_entity_tag;
pthread_mutexattr_t mutexattr;
(void) uid;
struct timespec timeout;
error = 0;
file_entity_tag = NULL;
message = NULL;
responseRef = NULL;
statusCode = 0;
auth_generation = 0;
node->put_ctx = (struct stream_put_ctx *) malloc (sizeof(struct stream_put_ctx));
if (node->put_ctx == NULL) {
syslog(LOG_ERR, "%s: failed to alloc ctx", __FUNCTION__);
error = ENOMEM;
return (error);
}
memset(node->put_ctx,0,sizeof(struct stream_put_ctx));
error = pthread_mutexattr_init(&mutexattr);
if (error) {
syslog(LOG_ERR, "%s: init ctx mutexattr failed, error %d", __FUNCTION__, error);
error = EIO;
return (error);
}
error = pthread_mutex_init(&node->put_ctx->ctx_lock, &mutexattr);
if (error) {
syslog(LOG_ERR, "%s: init ctx_lock failed, error %d", __FUNCTION__, error);
error = EIO;
return (error);
}
error = pthread_cond_init(&node->put_ctx->ctx_condvar, NULL);
if (error) {
syslog(LOG_ERR, "%s: init ctx_condvar failed, error %d", __FUNCTION__, error);
error = EIO;
return (error);
}
urlRef = create_cfurl_from_node(node, NULL, 0);
if (urlRef == NULL)
{
syslog(LOG_ERR, "%s: create_cfurl_from_node failed", __FUNCTION__);
error = EIO;
return (error);
}
create_http_request_message(&message, urlRef, file_length);
if (message == NULL)
{
syslog(LOG_ERR, "%s: create_http_request_message failed", __FUNCTION__);
error = EIO;
return (error);
}
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 )
{
syslog(LOG_ERR, "%s: authcache_apply, error %d", __FUNCTION__, error);
goto out1;
}
if(create_bound_streams(node->put_ctx) == false) {
syslog(LOG_ERR, "%s: failed to create bound streams", __FUNCTION__);
error = EIO;
goto out1;
}
node->put_ctx->rspStreamRef = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault,
message,
node->put_ctx->rdStreamRef);
if (node->put_ctx->rspStreamRef == NULL) {
syslog(LOG_ERR,"%s: CFReadStreamCreateForStreamedHTTPRequest failed\n", __FUNCTION__);
goto out1;
}
if (set_global_stream_properties(node->put_ctx->rspStreamRef) != 0) {
syslog(LOG_ERR,"%s: set_global_stream_properties failed\n", __FUNCTION__);
goto out1;
}
ApplySSLProperties(node->put_ctx->rspStreamRef);
requestqueue_enqueue_seqwrite_manager(node->put_ctx);
timeout.tv_sec = time(NULL) + WEBDAV_MANAGER_STARTUP_TIMEOUT;
timeout.tv_nsec = 0;
pthread_mutex_lock(&node->put_ctx->ctx_lock);
while (node->put_ctx->mgr_status == WR_MGR_VIRGIN) {
error = pthread_cond_timedwait(&node->put_ctx->ctx_condvar, &node->put_ctx->ctx_lock, &timeout);
if (error != 0) {
syslog(LOG_ERR, "%s: pthread_cond_timedwait returned error %d", __FUNCTION__, error);
node->put_ctx->finalStatus = EIO;
node->put_ctx->finalStatusValid = true;
error = EIO;
pthread_mutex_unlock(&node->put_ctx->ctx_lock);
goto out1;
} else {
timeout.tv_sec = time(NULL) + WEBDAV_WRITESEQ_REQUEST_TIMEOUT;
timeout.tv_nsec = 0;
}
}
pthread_mutex_unlock(&node->put_ctx->ctx_lock);
out1:
CFRelease(message);
CFRelease(urlRef);
return ( error );
}
void network_seqwrite_manager(struct stream_put_ctx *ctx)
{
CFMessagePortRef localPort;
CFRunLoopSourceRef runLoopSource;
CFStreamError streamError;
CFIndex bytesWritten, len;
struct seqwrite_mgr_req *curr_req = NULL;
CFStringRef msgPortNameString = NULL;
int result;
bool didReceiveClose;
localPort = NULL;
runLoopSource = NULL;
didReceiveClose = false;
pthread_mutex_lock(&ctx->ctx_lock);
ctx->mgr_rl = CFRunLoopGetCurrent();
CFRetain(ctx->mgr_rl);
pthread_mutex_unlock(&ctx->ctx_lock);
char msgPortName[WRITE_MGR_MSG_PORT_NAME_BUFSIZE];
sprintf(msgPortName, WRITE_MGR_MSG_PORT_NAME_TEMPLATE, WRITE_MGR_MSG_PORT_NAME_BASE_STRING, getpid(), (void*)ctx);
msgPortNameString = CFStringCreateWithBytes(kCFAllocatorDefault,
(uint8_t*)msgPortName,
strlen(msgPortName),
kCFStringEncodingASCII, false);
if (msgPortNameString == NULL) {
syslog(LOG_ERR, "%s: No mem for msgPortNameString\n", __FUNCTION__);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatusValid = true;
ctx->finalStatus = EIO;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
localPort = CFMessagePortCreateLocal(kCFAllocatorDefault, msgPortNameString,
managerMessagePortCallback, NULL, NULL);
if (localPort == NULL) {
syslog(LOG_ERR, "%s: CFMessagePortCreateLocal failed\n", __FUNCTION__);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatusValid = true;
ctx->finalStatus = EIO;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
runLoopSource = CFMessagePortCreateRunLoopSource( kCFAllocatorDefault, localPort, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
ctx->mgrPort = CFMessagePortCreateRemote(kCFAllocatorDefault, msgPortNameString);
if (ctx->mgrPort == NULL) {
syslog(LOG_ERR, "%s: CFMessagePortCreateRemote failed\n", __FUNCTION__);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatusValid = true;
ctx->finalStatus = EIO;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
CFRelease(msgPortNameString);
msgPortNameString = NULL;
CFStreamClientContext mgrContext = {0, ctx, NULL, NULL, NULL};
CFWriteStreamSetClient (ctx->wrStreamRef,
kCFStreamEventCanAcceptBytes |
kCFStreamEventErrorOccurred |
kCFStreamEventOpenCompleted |
kCFStreamEventEndEncountered,
writeseqWriteCallback,
&mgrContext);
CFWriteStreamScheduleWithRunLoop(ctx->wrStreamRef,
ctx->mgr_rl,
kCFRunLoopDefaultMode);
if (CFWriteStreamOpen(ctx->wrStreamRef) != true) {
syslog(LOG_ERR, "%s: failed to open write stream\n", __FUNCTION__);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatusValid = true;
ctx->finalStatus = EIO;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
if ( !CFReadStreamSetClient(ctx->rspStreamRef,
kCFStreamEventHasBytesAvailable |
kCFStreamEventErrorOccurred |
kCFStreamEventOpenCompleted |
kCFStreamEventEndEncountered,
writeseqReadResponseCallback, &mgrContext) )
{
syslog(LOG_ERR, "%s: failed to set response stream client", __FUNCTION__);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = EIO;
ctx->finalStatusValid = true;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
goto out1;
}
CFReadStreamScheduleWithRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode);
if ( CFReadStreamOpen(ctx->rspStreamRef) == FALSE )
{
result = HandleSSLErrors(ctx->rspStreamRef);
if ( result == EAGAIN ) {
syslog(LOG_DEBUG, "%s: CFReadStreamOpen: HandleSSLErrors: EAGAIN", __FUNCTION__);
CFReadStreamUnscheduleFromRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = EAGAIN;
ctx->finalStatusValid = true;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
}
else {
streamError = CFReadStreamGetError(ctx->rspStreamRef);
if (!(ctx->is_retry) &&
((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)))
{
syslog(LOG_DEBUG,"%s: CFReadStreamOpen: CFStreamError: domain %ld, error %d (retrying)",
__FUNCTION__, streamError.domain, streamError.error);
CFReadStreamUnscheduleFromRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = EAGAIN;
ctx->finalStatusValid = true;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
}
else {
syslog(LOG_ERR,"%s: CFReadStreamOpen failed: CFStreamError: domain %ld, error %d",
__FUNCTION__, streamError.domain, streamError.error);
set_connectionstate(WEBDAV_CONNECTION_DOWN);
CFReadStreamUnscheduleFromRunLoop(ctx->rspStreamRef, ctx->mgr_rl, kCFRunLoopDefaultMode);
pthread_mutex_lock(&ctx->ctx_lock);
ctx->finalStatus = stream_error_to_errno(&streamError);
ctx->finalStatusValid = true;
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
}
}
goto out1;
}
pthread_mutex_lock(&ctx->ctx_lock);
ctx->mgr_status = WR_MGR_RUNNING;
pthread_cond_signal(&ctx->ctx_condvar); pthread_mutex_unlock(&ctx->ctx_lock);
while(1)
{
pthread_mutex_lock(&ctx->ctx_lock);
if (curr_req == NULL) {
curr_req = dequeue_writemgr_request_locked(ctx);
}
if ((curr_req != NULL) && (curr_req->type == SEQWRITE_CLOSE)) {
release_writemgr_request_locked(curr_req);
curr_req = NULL;
didReceiveClose = true;
CFWriteStreamClose(ctx->wrStreamRef);
}
if (ctx->finalStatusValid == true) {
if (curr_req == NULL) {
curr_req = dequeue_writemgr_request_locked(ctx);
}
while (curr_req) {
if ( curr_req->type == SEQWRITE_CHUNK ) {
pthread_mutex_lock(&curr_req->req_lock);
curr_req->error = ctx->finalStatus;
curr_req->request_done = true;
pthread_cond_signal(&curr_req->req_condvar);
pthread_mutex_unlock(&curr_req->req_lock);
}
release_writemgr_request_locked(curr_req);
curr_req = dequeue_writemgr_request_locked(ctx);
}
ctx->mgr_status = WR_MGR_DONE;
pthread_cond_signal(&ctx->ctx_condvar);
pthread_mutex_unlock(&ctx->ctx_lock);
break;
}
if ( (ctx->canAcceptBytesEvents !=0) && (curr_req != NULL) && (didReceiveClose == false) ) {
pthread_mutex_unlock(&ctx->ctx_lock);
len = curr_req->chunkLen - curr_req->chunkWritten;
if (len <= 0) {
if (len < 0)
syslog(LOG_DEBUG,"%s: negative len value %ld for chunkLen %ld, chunkWritten %ld",
__FUNCTION__, curr_req->chunkLen, curr_req->chunkWritten);
pthread_mutex_lock(&curr_req->req_lock);
curr_req->error = 0;
curr_req->request_done = true;
pthread_cond_signal(&curr_req->req_condvar);
pthread_mutex_unlock(&curr_req->req_lock);
release_writemgr_request(ctx, curr_req);
curr_req = NULL;
continue;
} else {
pthread_mutex_lock(&ctx->ctx_lock);
ctx->canAcceptBytesEvents--;
pthread_mutex_unlock(&ctx->ctx_lock);
bytesWritten = CFWriteStreamWrite(ctx->wrStreamRef, (UInt8*)(curr_req->data + curr_req->chunkWritten), len);
if (bytesWritten < 0 ) {
streamError = CFWriteStreamGetError(ctx->wrStreamRef);
if (!(ctx->is_retry) &&
((streamError.domain == kCFStreamErrorDomainPOSIX && streamError.error == EPIPE) ||
(streamError.domain == kCFStreamErrorDomainHTTP && streamError.error == kCFStreamErrorHTTPConnectionLost)))
{
syslog(LOG_DEBUG,"%s: bytesWritten < 0, CFStreamError: domain %ld, error %d (retrying)",
__FUNCTION__, streamError.domain, streamError.error);
pthread_mutex_lock(&curr_req->req_lock);
curr_req->error = EAGAIN;
curr_req->request_done = true;
pthread_cond_signal(&curr_req->req_condvar);
pthread_mutex_unlock(&curr_req->req_lock);
release_writemgr_request(ctx, curr_req);
curr_req = NULL;
}
else
{
if ( get_connectionstate() == WEBDAV_CONNECTION_UP )
{
syslog(LOG_DEBUG,"%s: CFStreamError: domain %ld, error %d",
__FUNCTION__, streamError.domain, streamError.error);
}
set_connectionstate(WEBDAV_CONNECTION_DOWN);
pthread_mutex_lock(&curr_req->req_lock);
curr_req->error = EIO;
curr_req->request_done = true;
pthread_cond_signal(&curr_req->req_condvar);
pthread_mutex_unlock(&curr_req->req_lock);
release_writemgr_request(ctx, curr_req);
curr_req = NULL;
}
}
else
curr_req->chunkWritten += bytesWritten;
}
}
else
pthread_mutex_unlock(&ctx->ctx_lock);
pthread_mutex_lock(&ctx->ctx_lock);
if ( (ctx->canAcceptBytesEvents == 0) || (curr_req == NULL && ctx->req_head == NULL)) {
pthread_mutex_unlock(&ctx->ctx_lock);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, TRUE);
} else {
pthread_mutex_unlock(&ctx->ctx_lock);
}
}
out1:
if (localPort != NULL) {
CFMessagePortInvalidate(localPort);
CFRelease(localPort);
}
if (runLoopSource != NULL)
CFRelease(runLoopSource);
if (msgPortNameString != NULL)
CFRelease(msgPortNameString);
return;
}
int queue_writemgr_request_locked(struct stream_put_ctx *ctx, struct seqwrite_mgr_req *req)
{
SInt32 status;
if (ctx == NULL) {
syslog(LOG_ERR, "%s: NULL ctx arg", __FUNCTION__);
return (-1);
}
if (req == NULL) {
syslog(LOG_ERR, "%s: NULL request arg", __FUNCTION__);
return (-1);
}
if (ctx->req_head == NULL) {
ctx->req_head = req;
ctx->req_tail = req;
req->prev = NULL;
req->next = NULL;
} else {
ctx->req_tail->next = req;
req->prev = ctx->req_tail;
req->next = NULL;
ctx->req_tail = req;
}
req->refCount++;
status = CFMessagePortSendRequest(
ctx->mgrPort,
(SInt32) WRITE_MGR_NEW_REQUEST_ID,
NULL,
WRITE_MGR_MSG_PORTSEND_TIMEOUT,
WRITE_MGR_MSG_PORTSEND_TIMEOUT,
NULL, NULL);
if (status != kCFMessagePortSuccess) {
syslog(LOG_ERR, "%s: CFMessagePort error %d\n", __FUNCTION__, status);
return (-1);
}
else
return (0);
}
struct seqwrite_mgr_req *dequeue_writemgr_request_locked(struct stream_put_ctx *ctx)
{
struct seqwrite_mgr_req *req;
req = ctx->req_head;
if (req != NULL) {
if (ctx->req_head == ctx->req_tail) {
ctx->req_head = NULL;
ctx->req_tail = NULL;
} else {
ctx->req_head = ctx->req_head->next;
ctx->req_head->prev = NULL;
}
}
return req;
}
void release_writemgr_request_locked(struct seqwrite_mgr_req *req)
{
if (req->refCount)
req->refCount--;
if (req->refCount == 0) {
if (req->data != NULL)
free(req->data);
free(req);
}
}
void release_writemgr_request(struct stream_put_ctx *ctx, struct seqwrite_mgr_req *req)
{
pthread_mutex_lock(&ctx->ctx_lock);
release_writemgr_request_locked(req);
pthread_mutex_unlock(&ctx->ctx_lock);
}
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;
CFIndex 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;
off_t contentLength;
urlRef = create_cfurl_from_node(node, NULL, 0);
require_action_quiet(urlRef != NULL, create_cfurl_from_node, error = EIO);
contentLength = lseek(node->file_fd, 0LL, SEEK_END);
lseek(node->file_fd, 0LL, SEEK_SET);
if (contentLength > (off_t)webdavCacheMaximumSize)
fcntl(node->file_fd, F_NOCACHE, 1);
do
{
create_http_request_message(&message, urlRef, 0);
require_action(message != NULL, CFHTTPMessageCreateRequest, error = EIO);
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, (UInt32)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;
}
else
{
statusCode = CFHTTPMessageGetResponseStatusCode(responseRef);
}
} while ( error == EAGAIN || statusCode == 401 || statusCode == 407 );
CFHTTPMessageCreateRequest:
if ( error == 0 )
{
error = translate_status_to_error((UInt32)statusCode);
if ( error == 0 )
{
(void) authcache_valid(uid, message, auth_generation);
add_last_mod_etag(responseRef, file_last_modified, &file_entity_tag);
}
}
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") },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
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 headers2[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("If"), NULL },
{ CFSTR("translate"), CFSTR("f") }
};
struct HeaderFieldValue headers1[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("translate"), CFSTR("f") }
};
*remove_date = -1;
if ( node->file_locktoken != NULL )
{
lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken);
if ( lockTokenRef != NULL )
{
headerCount = 2;
headers2[1].value = lockTokenRef;
}
else
{
headerCount = 1;
}
}
else
{
lockTokenRef = NULL;
headerCount = 1;
}
if (headerCount == 1) {
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
error = send_transaction(uid, urlRef, CFSTR("DELETE"), NULL, headerCount, headers1, FALSE, NULL, NULL, &response);
}
else {
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
error = send_transaction(uid, urlRef, CFSTR("DELETE"), NULL, headerCount, headers2, 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 },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
*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 headers5[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Depth"), CFSTR("0") },
{ CFSTR("Timeout"), NULL },
{ CFSTR("Content-Type"), NULL },
{ CFSTR("If"), NULL },
{ CFSTR("translate"), CFSTR("f") }
};
struct HeaderFieldValue headers4[] = {
{ CFSTR("Accept"), CFSTR("*/*") },
{ CFSTR("Depth"), CFSTR("0") },
{ CFSTR("Timeout"), NULL },
{ CFSTR("Content-Type"), NULL },
{ CFSTR("translate"), CFSTR("f") }
};
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);
headers4[2].value = timeoutSpecifierRef;
headers5[2].value = timeoutSpecifierRef;
if ( refresh )
{
uid = node->file_locktoken_uid;
bodyData = NULL;
headerCount = 5;
headers5[3].value = CFSTR("text/xml");
lockTokenRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("(<%s>)"), node->file_locktoken);
require_action(lockTokenRef != NULL, CFStringCreateWithFormat_lockTokenRef, error = EIO);
headers5[4].value = lockTokenRef;
}
else
{
lockTokenRef = NULL;
bodyData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlString, strlen((const char *)xmlString), kCFAllocatorNull);
require_action(bodyData != NULL, CFDataCreateWithBytesNoCopy, error = EIO);
headerCount = 4;
headers4[3].value = CFSTR("text/xml; charset=\"utf-8\"");
}
if (headerCount == 4) {
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
error = send_transaction(uid, urlRef, CFSTR("LOCK"), bodyData, headerCount, headers4, FALSE, &responseBuffer, &count, NULL);
}
else {
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
error = send_transaction(uid, urlRef, CFSTR("LOCK"), bodyData, headerCount, headers5, 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 },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
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") },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 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("*/*") },
{ CFSTR("translate"), CFSTR("f") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 1;
}
*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 },
{ CFSTR("translate"), CFSTR("f") },
{ CFSTR("Pragma"), CFSTR("no-cache") }
};
if (gServerIdent & WEBDAV_MICROSOFT_IIS_SERVER) {
headerCount += 2;
}
*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 );
}