#include "webdavd.h"
#include <CoreFoundation/CoreFoundation.h>
#include <sys/dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include "webdav_parse.h"
#include "webdav_cache.h"
#include "webdav_network.h"
#include "LogMessage.h"
extern long long
strtoq(const char *, char **, int);
static int from_base64(const char *base64str, unsigned char *outBuffer, size_t *lengthptr);
static void *parser_opendir_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context);
static void *parser_file_count_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context);
static void *parser_stat_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context);
static void *parser_statfs_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context);
static void *parser_lock_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context);
static void parser_add(CFXMLParserRef parser, void *parent, void *child, void *context);
static void parser_opendir_add(CFXMLParserRef parser, void *parent, void *child, void *context);
static void parser_stat_add(CFXMLParserRef parser, void *parent, void *child, void *context);
static void parser_statfs_add(CFXMLParserRef parser, void *parent, void *child, void *context);
static void parser_end(CFXMLParserRef parser, void *xml_type, void *context);
static void parser_opendir_end(CFXMLParserRef parser, void *my_element, void *context);
static CFDataRef parser_resolve(CFXMLParserRef parser, CFXMLExternalID *extID, void *context);
static int from_base64(const char *base64str, unsigned char *outBuffer, size_t *lengthptr)
{
char decodedChar;
unsigned long base64Length;
unsigned char *eightBitByte;
unsigned char sixBitEncoding[4];
unsigned short encodingIndex;
int endOfData;
const char *equalPtr;
const char *base64CharPtr;
const char *base64EndPtr;
base64Length = 0;
equalPtr = strchr(base64str, '=');
if ( equalPtr != NULL )
{
switch ( (equalPtr - base64str) % 4 )
{
case 0:
case 1:
goto error_exit;
break;
case 2:
if ( equalPtr[1] != '=' )
{
goto error_exit;
}
base64Length = (equalPtr - base64str) + 2;
*lengthptr += 2;
break;
case 3:
base64Length = (equalPtr - base64str) + 1;
*lengthptr += 1;
break;
}
}
else
{
base64Length = strlen(base64str);
}
if ( *lengthptr < ((base64Length / 4) * 3) )
{
goto error_exit;
}
if ( (base64Length % 4) != 0 )
{
goto error_exit;
}
eightBitByte = outBuffer;
encodingIndex = 0;
endOfData = FALSE;
base64EndPtr = (char *)((unsigned long)base64str + base64Length);
base64CharPtr = base64str;
while ( base64CharPtr < base64EndPtr )
{
decodedChar = *base64CharPtr++;
if ( (decodedChar >= 'A') && (decodedChar <= 'Z') )
{
decodedChar = decodedChar - 'A';
}
else if ( (decodedChar >= 'a') && (decodedChar <= 'z') )
{
decodedChar = decodedChar - 'a' + 26;
}
else if ( (decodedChar >= '0') && (decodedChar <= '9') )
{
decodedChar = decodedChar - '0' + 52;
}
else if ( decodedChar == '+' )
{
decodedChar = 62;
}
else if ( decodedChar == '/' )
{
decodedChar = 63;
}
else if ( decodedChar == '=' )
{
endOfData = TRUE;
}
else
{
goto error_exit;
}
if ( endOfData )
{
base64CharPtr = base64EndPtr;
}
else
{
sixBitEncoding[encodingIndex] = (unsigned char)decodedChar;
++encodingIndex;
}
if ( (encodingIndex == 4) || endOfData)
{
*eightBitByte++ =
(sixBitEncoding[0] << 2) | ((sixBitEncoding[1] & 0x30) >> 4);
if ( encodingIndex >= 3 )
{
*eightBitByte++ =
((sixBitEncoding[1] & 0x0F) << 4) | ((sixBitEncoding[2] & 0x3C) >> 2);
if ( encodingIndex == 4 )
{
*eightBitByte++ =
((sixBitEncoding[2] & 0x03) << 6) | (sixBitEncoding[3] & 0x3F);
}
}
encodingIndex = 0;
}
}
*lengthptr = eightBitByte - outBuffer;
return ( 0 );
error_exit:
*lengthptr = 0;
return ( -1 );
}
static void Substitute_Physical_Entity(CFXMLNodeRef node, UInt8 *text_ptr, CFIndex *size)
{
if (CFXMLNodeGetTypeCode(node) == kCFXMLNodeTypeEntityReference)
{
if (!strcmp((const char *)text_ptr, "amp"))
strcpy((char *)text_ptr, "&");
else if (!strcmp((const char *)text_ptr, "lt"))
strcpy((char *)text_ptr, "<");
else if (!strcmp((const char *)text_ptr, "gt"))
strcpy((char *)text_ptr, ">");
else if (!strcmp((const char *)text_ptr, "apos"))
strcpy((char *)text_ptr, "'");
else if (!strcmp((const char *)text_ptr, "quot"))
strcpy((char *)text_ptr, """");
*size = 1;
}
}
static void *parser_opendir_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
webdav_parse_opendir_element_t * element_ptr = NULL;
webdav_parse_opendir_element_t * list_ptr;
webdav_parse_opendir_struct_t * struct_ptr = (webdav_parse_opendir_struct_t *)context;
webdav_parse_opendir_return_t * return_ptr;
webdav_parse_opendir_text_t * text_ptr;
void *return_value;
CFRange comparison_range;
CFStringRef nodeString;
CFIndex nodeStringLength;
nodeString = CFXMLNodeGetString(node);
nodeStringLength = CFStringGetLength(nodeString);
return_ptr = malloc(sizeof(webdav_parse_opendir_return_t));
require_action(return_ptr != NULL, malloc_return_ptr, struct_ptr->error = ENOMEM);
return_value = (void *)return_ptr;
return_ptr->id = WEBDAV_OPENDIR_IGNORE;
return_ptr->data_ptr = (void *)NULL;
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
++comparison_range.location;
comparison_range.length = nodeStringLength - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("href"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
element_ptr = malloc(sizeof(webdav_parse_opendir_element_t));
require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
bzero(element_ptr, sizeof(webdav_parse_opendir_element_t));
return_ptr->id = WEBDAV_OPENDIR_ELEMENT;
return_ptr->data_ptr = (void *)element_ptr;
element_ptr->dir_data.d_type = DT_REG;
element_ptr->dir_data.d_namlen = 0;
element_ptr->dir_data.d_name_URI_length = 0;
element_ptr->dir_data.d_reclen = sizeof(struct webdav_dirent);
element_ptr->next = NULL;
if (struct_ptr->head == NULL)
{
struct_ptr->head = element_ptr;
}
else
{
list_ptr = struct_ptr->tail;
list_ptr->next = element_ptr;
}
struct_ptr->tail = element_ptr;
element_ptr->next = NULL;
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("collection"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
element_ptr = struct_ptr->tail;
if (element_ptr)
{
element_ptr->dir_data.d_type = DT_DIR;
return_value = NULL;
free(return_ptr);
}
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("getcontentlength"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
element_ptr = struct_ptr->tail;
if (element_ptr)
{
return_ptr->id = WEBDAV_OPENDIR_ELEMENT_LENGTH;
return_ptr->data_ptr = (void *)element_ptr;
}
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("getlastmodified"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
element_ptr = struct_ptr->tail;
if (element_ptr)
{
return_ptr->id = WEBDAV_OPENDIR_ELEMENT_MODDATE;
return_ptr->data_ptr = (void *)element_ptr;
}
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("appledoubleheader"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
element_ptr = struct_ptr->tail;
if (element_ptr)
{
return_ptr->id = WEBDAV_OPENDIR_APPLEDOUBLEHEADER;
return_ptr->data_ptr = (void *)element_ptr;
}
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
text_ptr = malloc(sizeof(webdav_parse_opendir_text_t));
require_action(text_ptr != NULL, malloc_text_ptr, struct_ptr->error = ENOMEM);
require_action(CFStringGetBytes(nodeString, CFRangeMake(0, nodeStringLength),
kCFStringEncodingUTF8, 0, 0, text_ptr->name,
sizeof(text_ptr->name) - 1, &text_ptr->size) == nodeStringLength,
name_too_long, free(text_ptr); struct_ptr->error = ENAMETOOLONG);
text_ptr->name[text_ptr->size] = '\0';
Substitute_Physical_Entity(node, text_ptr->name, &text_ptr->size);
return_ptr->id = WEBDAV_OPENDIR_TEXT;
return_ptr->data_ptr = text_ptr;
break;
case kCFXMLNodeTypeDocument:
free(return_ptr);
return_value = NULL;
break;
default:
break;
}
return (return_value);
name_too_long:
malloc_text_ptr:
malloc_element_ptr:
free(return_ptr);
malloc_return_ptr:
return (NULL);
}
static void *parser_file_count_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
CFRange comparison_range;
CFStringRef nodeString;
if ( CFXMLNodeGetTypeCode(node) == kCFXMLNodeTypeElement)
{
nodeString = CFXMLNodeGetString(node);
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
++comparison_range.location;
comparison_range.length = CFStringGetLength(nodeString) - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("href"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
++(*((int *)context));
}
}
return ( (void *)1 );
}
static void *parser_stat_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
void *return_val = (void *)WEBDAV_STAT_IGNORE;
UInt8 *text_ptr;
CFRange comparison_range;
CFStringRef nodeString;
CFIndex nodeStringLength, string_size;
nodeString = CFXMLNodeGetString(node);
nodeStringLength = CFStringGetLength(nodeString);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
++comparison_range.location;
comparison_range.length = nodeStringLength - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("getcontentlength"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_STAT_LENGTH;
}
else
{
if (((CFStringCompareWithOptions(nodeString, CFSTR("getlastmodified"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_STAT_MODDATE;
}
else
{
if (((CFStringCompareWithOptions(nodeString, CFSTR("collection"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
((struct stat *)context)->st_mode = S_IFDIR;
}
}
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
text_ptr = malloc(nodeStringLength + 1);
require_action(text_ptr != NULL, malloc_text_ptr, return_val = NULL);
require_action(CFStringGetBytes(nodeString, CFRangeMake(0, nodeStringLength),
kCFStringEncodingUTF8, 0, 0, text_ptr,
nodeStringLength, &string_size) == nodeStringLength,
CFStringGetBytes, free(text_ptr); return_val = NULL);
text_ptr[string_size] = '\0';
Substitute_Physical_Entity(node, text_ptr, &string_size);
return_val = (void *)text_ptr;
break;
default:
break;
}
CFStringGetBytes:
malloc_text_ptr:
return (return_val);
}
static void *parser_statfs_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser, context)
void *return_val = (void *)WEBDAV_STATFS_IGNORE;
UInt8 *text_ptr;
CFRange comparison_range;
CFStringRef nodeString;
CFIndex nodeStringLength, string_size;
nodeString = CFXMLNodeGetString(node);
nodeStringLength = CFStringGetLength(nodeString);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
++comparison_range.location;
comparison_range.length = nodeStringLength - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("quota-available-bytes"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES;
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("quota-used-bytes"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_STATFS_QUOTA_USED_BYTES;
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("quota"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_STATFS_QUOTA;
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("quotaused"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_STATFS_QUOTAUSED;
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
text_ptr = malloc(nodeStringLength + 1);
require_action(text_ptr != NULL, malloc_text_ptr, return_val = NULL);
require_action(CFStringGetBytes(nodeString, CFRangeMake(0, nodeStringLength),
kCFStringEncodingUTF8, 0, 0, text_ptr,
nodeStringLength, &string_size) == nodeStringLength,
CFStringGetBytes, free(text_ptr); return_val = NULL);
text_ptr[string_size] = '\0';
Substitute_Physical_Entity(node, text_ptr, &string_size);
return_val = (void *)text_ptr;
break;
default:
break;
}
CFStringGetBytes:
malloc_text_ptr:
return (return_val);
}
static void *parser_lock_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
void *return_val = (void *)WEBDAV_LOCK_CONTINUE;
UInt8 *text_ptr;
CFRange comparison_range;
webdav_parse_lock_struct_t *lock_struct = (webdav_parse_lock_struct_t *)context;
CFStringRef nodeString;
CFIndex nodeStringLength, string_size;
nodeString = CFXMLNodeGetString(node);
nodeStringLength = CFStringGetLength(nodeString);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
++comparison_range.location;
comparison_range.length = nodeStringLength - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("locktoken"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
lock_struct->context = WEBDAV_LOCK_TOKEN;
}
else
{
if (((CFStringCompareWithOptions(nodeString, CFSTR("href"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
if (lock_struct->context == WEBDAV_LOCK_TOKEN)
{
lock_struct->context = WEBDAV_LOCK_HREF;
}
else
{
lock_struct->context = 0;
}
}
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
if (lock_struct->context == WEBDAV_LOCK_HREF)
{
lock_struct->context = 0;
text_ptr = malloc(nodeStringLength + 1);
require_action(text_ptr != NULL, malloc_text_ptr, return_val = NULL);
require_action(CFStringGetBytes(nodeString, CFRangeMake(0, nodeStringLength), kCFStringEncodingUTF8,
0, 0, text_ptr, nodeStringLength, &string_size) == nodeStringLength,
CFStringGetBytes, free(text_ptr); return_val = NULL);
text_ptr[string_size] = '\0';
Substitute_Physical_Entity(node, text_ptr, &string_size);
lock_struct->locktoken = (char *)text_ptr;
return_val = NULL;
}
break;
default:
break;
}
CFStringGetBytes:
malloc_text_ptr:
return (return_val);
}
static void *parser_cachevalidators_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser, context)
void *return_val = (void *)WEBDAV_CACHEVALIDATORS_IGNORE;
UInt8 *text_ptr;
CFRange comparison_range;
CFStringRef nodeString;
CFIndex nodeStringLength, string_size;
nodeString = CFXMLNodeGetString(node);
nodeStringLength = CFStringGetLength(nodeString);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
++comparison_range.location;
comparison_range.length = nodeStringLength - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("getlastmodified"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_CACHEVALIDATORS_MODDATE;
}
else if (((CFStringCompareWithOptions(nodeString, CFSTR("getetag"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_CACHEVALIDATORS_ETAG;
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
text_ptr = malloc(nodeStringLength + 1);
require_action(text_ptr != NULL, malloc_text_ptr, return_val = NULL);
require_action(CFStringGetBytes(nodeString, CFRangeMake(0, nodeStringLength),
kCFStringEncodingUTF8, 0, 0, text_ptr,
nodeStringLength, &string_size) == nodeStringLength,
CFStringGetBytes, free(text_ptr); return_val = NULL);
text_ptr[string_size] = '\0';
Substitute_Physical_Entity(node, text_ptr, &string_size);
return_val = (void *)text_ptr;
break;
default:
break;
}
CFStringGetBytes:
malloc_text_ptr:
return (return_val);
}
static void parser_add(CFXMLParserRef parser, void *parent, void *child, void *context)
{
#pragma unused(parser, parent, child, context)
return;
}
static void parser_opendir_add(CFXMLParserRef parser, void *parent, void *child, void *context)
{
#pragma unused(parser)
webdav_parse_opendir_element_t * element_ptr;
webdav_parse_opendir_return_t * parent_ptr = (webdav_parse_opendir_return_t *)parent;
webdav_parse_opendir_return_t * child_ptr = (webdav_parse_opendir_return_t *)child;
webdav_parse_opendir_text_t * text_ptr;
webdav_parse_opendir_struct_t * struct_ptr = (webdav_parse_opendir_struct_t *)context;
char *ep;
if (child_ptr->id == WEBDAV_OPENDIR_TEXT)
{
text_ptr = (webdav_parse_opendir_text_t *)child_ptr->data_ptr;
switch (parent_ptr->id)
{
case WEBDAV_OPENDIR_ELEMENT:
element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
if ((element_ptr->dir_data.d_name_URI_length + text_ptr->size) <=
((unsigned int)sizeof(element_ptr->dir_data.d_name) - 1))
{
bcopy(text_ptr->name,
&element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length],
text_ptr->size);
element_ptr->dir_data.d_name_URI_length += (uint32_t)text_ptr->size;
}
else
{
debug_string("URI too long");
struct_ptr->error = ENAMETOOLONG;
}
break;
case WEBDAV_OPENDIR_ELEMENT_LENGTH:
element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
element_ptr->statsize = strtoq((const char *)text_ptr->name, &ep, 10);
break;
case WEBDAV_OPENDIR_ELEMENT_MODDATE:
element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
element_ptr->stattime.tv_sec = DateBytesToTime(text_ptr->name, strlen((const char *)text_ptr->name));
if (element_ptr->stattime.tv_sec == -1)
{
element_ptr->stattime.tv_sec = 0;
}
element_ptr->stattime.tv_nsec = 0;
break;
case WEBDAV_OPENDIR_APPLEDOUBLEHEADER:
{
size_t len = APPLEDOUBLEHEADER_LENGTH;
element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
from_base64((const char *)text_ptr->name, (unsigned char *)element_ptr->appledoubleheader, &len);
if (len == APPLEDOUBLEHEADER_LENGTH)
{
element_ptr->appledoubleheadervalid = TRUE;
}
}
break;
default:
break;
}
free(text_ptr);
}
}
static void parser_stat_add(CFXMLParserRef parser, void *parent, void *child, void *context)
{
#pragma unused(parser)
UInt8 *text_ptr = (UInt8 *)child;
struct stat *statbuf = (struct stat *)context;
char *ep;
switch ((uintptr_t)parent)
{
case WEBDAV_STAT_LENGTH:
if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE))
{
statbuf->st_size = strtoq((const char *)text_ptr, &ep, 10);
free(text_ptr);
}
else
{
statbuf->st_size = -1LL;
}
break;
case WEBDAV_STAT_MODDATE:
if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE))
{
statbuf->st_mtimespec.tv_sec = DateBytesToTime(text_ptr, strlen((const char *)text_ptr));
if (statbuf->st_mtimespec.tv_sec == -1)
{
statbuf->st_mtimespec.tv_sec = 0;
}
statbuf->st_mtimespec.tv_nsec = 0;
free(text_ptr);
}
else
{
}
break;
default:
if (text_ptr &&
text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE &&
text_ptr != (UInt8 *)WEBDAV_STAT_LENGTH &&
text_ptr != (UInt8 *)WEBDAV_STAT_MODDATE)
{
free(text_ptr);
}
break;
}
return;
}
static void parser_statfs_add(CFXMLParserRef parser, void *parent, void *child, void *context)
{
#pragma unused(parser)
char *text_ptr = (char *)child;
struct webdav_quotas *quotas = (struct webdav_quotas *)context;
char *ep;
switch ((uintptr_t)parent)
{
case WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES:
if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
{
quotas->quota_available_bytes = strtouq(text_ptr, &ep, 10);
quotas->use_bytes_values = TRUE;
free(text_ptr);
}
else
{
}
break;
case WEBDAV_STATFS_QUOTA_USED_BYTES:
if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
{
quotas->quota_used_bytes = strtouq(text_ptr, &ep, 10);
free(text_ptr);
}
else
{
}
break;
case WEBDAV_STATFS_QUOTA:
if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
{
quotas->quota = strtouq(text_ptr, &ep, 10);
free(text_ptr);
}
else
{
}
break;
case WEBDAV_STATFS_QUOTAUSED:
if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
{
quotas->quotaused = strtouq(text_ptr, &ep, 10);
free(text_ptr);
}
else
{
}
break;
default:
if (text_ptr &&
text_ptr != (char *)WEBDAV_STATFS_IGNORE &&
text_ptr != (char *)WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES &&
text_ptr != (char *)WEBDAV_STATFS_QUOTA_USED_BYTES &&
text_ptr != (char *)WEBDAV_STATFS_QUOTA &&
text_ptr != (char *)WEBDAV_STATFS_QUOTAUSED)
{
free(text_ptr);
}
break;
}
return;
}
static void parser_cachevalidators_add(CFXMLParserRef parser, void *parent, void *child, void *context)
{
#pragma unused(parser)
UInt8 *text_ptr = (UInt8 *)child;
struct webdav_parse_cachevalidators_struct *cachevalidators_struct = (struct webdav_parse_cachevalidators_struct *)context;
switch ((uintptr_t)parent)
{
case WEBDAV_CACHEVALIDATORS_MODDATE:
if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE))
{
cachevalidators_struct->last_modified = DateBytesToTime(text_ptr, strlen((const char *)text_ptr));
free(text_ptr);
}
else
{
}
break;
case WEBDAV_CACHEVALIDATORS_ETAG:
if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE))
{
cachevalidators_struct->entity_tag = malloc(strlen((const char *)text_ptr) + 1);
if ( cachevalidators_struct->entity_tag != NULL )
{
strcpy(cachevalidators_struct->entity_tag, (const char *)text_ptr);
}
free(text_ptr);
}
else
{
}
break;
default:
if (text_ptr &&
text_ptr != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE &&
text_ptr != (UInt8 *)WEBDAV_CACHEVALIDATORS_MODDATE &&
text_ptr != (UInt8 *)WEBDAV_CACHEVALIDATORS_ETAG)
{
free(text_ptr);
}
break;
}
return;
}
static void parser_end(CFXMLParserRef parser, void *xml_type, void *context)
{
#pragma unused(parser, xml_type, context)
return;
}
static void parser_opendir_end(CFXMLParserRef parser, void *my_element, void *context)
{
#pragma unused(parser, context)
if (my_element)
{
free(my_element);
}
return;
}
static CFDataRef parser_resolve(CFXMLParserRef parser, CFXMLExternalID *extID, void *context)
{
#pragma unused(parser, extID, context)
return (NULL);
}
static CFIndex GetNormalizedPathLength(CFURLRef anURL)
{
CFURLRef absoluteURL;
CFStringRef escapedPath;
CFStringRef unescapedPath;
CFIndex result;
result = 0;
absoluteURL = CFURLCopyAbsoluteURL(anURL);
require(absoluteURL != NULL, CFURLCopyAbsoluteURL);
escapedPath = CFURLCopyPath(absoluteURL);
require(escapedPath != NULL, CFURLCopyPath);
unescapedPath = CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, escapedPath, CFSTR(""));
require_string(unescapedPath != NULL, CFURLCreateStringByReplacingPercentEscapes, "name was not legal UTF8");
result = CFStringGetLength(unescapedPath);
CFRelease(unescapedPath);
CFURLCreateStringByReplacingPercentEscapes:
CFRelease(escapedPath);
CFURLCopyPath:
CFRelease(absoluteURL);
CFURLCopyAbsoluteURL:
return ( result );
}
static Boolean GetComponentName(
CFURLRef urlRef,
CFIndex parentPathLength,
char *uri,
char *componentName)
{
Boolean result;
CFStringRef uriString;
CFURLRef uriURL;
CFStringRef uriName;
result = FALSE;
uriString = CFStringCreateWithCString(kCFAllocatorDefault, uri, kCFStringEncodingUTF8);
require(uriString != NULL, CFStringCreateWithCString);
uriURL = CFURLCreateWithString(kCFAllocatorDefault, uriString, urlRef);
if (uriURL != NULL) {
CFRelease(uriString);
}
else {
CFStringRef unEscapedString;
CFStringRef reEscapedString;
unEscapedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding (kCFAllocatorDefault, uriString, NULL, kCFStringEncodingUTF8);
CFRelease(uriString);
require(unEscapedString != NULL, CFURLCreateWithString);
reEscapedString = CFURLCreateStringByAddingPercentEscapes (kCFAllocatorDefault, unEscapedString, NULL, NULL, kCFStringEncodingUTF8);
CFRelease(unEscapedString);
require(reEscapedString != NULL, CFURLCreateWithString);
uriURL = CFURLCreateWithString(kCFAllocatorDefault, reEscapedString, urlRef);
CFRelease(reEscapedString);
require(uriURL != NULL, CFURLCreateWithString);
}
if ( GetNormalizedPathLength(uriURL) > parentPathLength ) {
uriName = CFURLCopyLastPathComponent(uriURL);
require_string(uriName != NULL, CFURLCopyLastPathComponent, "name was not legal UTF8");
if ( CFStringGetCString(uriName, componentName, MAXNAMLEN + 1, kCFStringEncodingUTF8) ) {
result = TRUE;
}
else {
debug_string("could not get child name (too long?)");
}
CFRelease(uriName);
}
else {
}
CFURLCopyLastPathComponent:
CFRelease(uriURL);
CFURLCreateWithString:
CFStringCreateWithCString:
return ( result );
}
int parse_opendir(
UInt8 *xmlp,
CFIndex xmlp_len,
CFURLRef urlRef,
uid_t uid,
struct node_entry *parent_node)
{
CFDataRef xml_dataref;
CFXMLParserCallBacks callbacks =
{
0, parser_opendir_create, parser_opendir_add, parser_opendir_end, parser_resolve, NULL
};
webdav_parse_opendir_struct_t opendir_struct;
webdav_parse_opendir_element_t *element_ptr, *prev_element_ptr;
CFXMLParserContext context =
{
0, &opendir_struct, NULL, NULL, NULL
};
CFXMLParserRef parser;
int error;
ssize_t size;
struct webdav_dirent dir_data[2];
CFIndex parentPathLength;
error = 0;
opendir_struct.head = opendir_struct.tail = NULL;
opendir_struct.error = 0;
require(ftruncate(parent_node->file_fd, 0) == 0, ftruncate);
require(lseek(parent_node->file_fd, 0, SEEK_SET) == 0, lseek);
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlp, xmlp_len, kCFAllocatorNull);
require(xml_dataref != NULL, CFDataCreateWithBytesNoCopy);
parser = CFXMLParserCreate(kCFAllocatorDefault, xml_dataref, NULL, kCFXMLParserNoOptions, kCFXMLNodeCurrentVersion, &callbacks, &context);
require(parser != NULL, CFXMLParserCreate);
require((CFXMLParserParse(parser) == true) && (opendir_struct.error == 0), CFXMLParserParse);
if ( !NODE_IS_DELETED(parent_node) )
{
bzero(dir_data, sizeof(dir_data));
dir_data[0].d_ino = parent_node->fileid;
dir_data[0].d_reclen = sizeof(struct webdav_dirent);
dir_data[0].d_type = DT_DIR;
dir_data[0].d_namlen = 1;
dir_data[0].d_name[0] = '.';
dir_data[1].d_ino =
(dir_data[0].d_ino == WEBDAV_ROOTFILEID) ? WEBDAV_ROOTPARENTFILEID : parent_node->parent->fileid;
dir_data[1].d_reclen = sizeof(struct webdav_dirent);
dir_data[1].d_type = DT_DIR;
dir_data[1].d_namlen = 2;
dir_data[1].d_name[0] = '.';
dir_data[1].d_name[1] = '.';
size = write(parent_node->file_fd, dir_data, sizeof(struct webdav_dirent) * 2);
require(size == (sizeof(struct webdav_dirent) * 2), write_dot_dotdot);
}
parentPathLength = GetNormalizedPathLength(urlRef);
(void) nodecache_invalidate_directory_node_time(parent_node);
for (element_ptr = opendir_struct.head; element_ptr != NULL; element_ptr = element_ptr->next)
{
char namebuffer[MAXNAMLEN + 1];
struct stat statbuf;
element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length] = '\0';
if ( GetComponentName(urlRef, parentPathLength, element_ptr->dir_data.d_name, namebuffer) )
{
struct node_entry *element_node;
size_t name_len;
name_len = strlen(namebuffer);
error = nodecache_get_node(parent_node, name_len, namebuffer, TRUE, FALSE,
element_ptr->dir_data.d_type == DT_DIR ? WEBDAV_DIR_TYPE : WEBDAV_FILE_TYPE, &element_node);
if (error)
{
debug_string("nodecache_get_node failed");
continue;
}
bcopy(element_node->name, element_ptr->dir_data.d_name, element_node->name_length);
element_ptr->dir_data.d_name[element_node->name_length] = '\0';
element_ptr->dir_data.d_namlen = element_node->name_length;
element_ptr->dir_data.d_ino = element_node->fileid;
bzero(&statbuf, sizeof(struct stat));
statbuf.st_dev = 0;
statbuf.st_nlink = 1;
statbuf.st_uid = UNKNOWNUID;
statbuf.st_gid = UNKNOWNUID;
statbuf.st_rdev = 0;
statbuf.st_blksize = WEBDAV_IOSIZE;
statbuf.st_flags = 0;
statbuf.st_gen = 0;
statbuf.st_atimespec = statbuf.st_mtimespec = statbuf.st_ctimespec = element_ptr->stattime;
if (element_ptr->dir_data.d_type == DT_DIR)
{
statbuf.st_mode = S_IFDIR | S_IRWXU;
statbuf.st_size = WEBDAV_DIR_SIZE;
element_ptr->appledoubleheadervalid = FALSE;
}
else
{
statbuf.st_mode = S_IFREG | S_IRWXU;
statbuf.st_size = element_ptr->statsize;
element_ptr->appledoubleheadervalid =
(element_ptr->appledoubleheadervalid && (element_ptr->statsize == APPLEDOUBLEHEADER_LENGTH));
}
statbuf.st_blocks = ((statbuf.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
statbuf.st_ino = element_node->fileid;
(void) nodecache_add_attributes(element_node, uid, &statbuf,
element_ptr->appledoubleheadervalid ? element_ptr->appledoubleheader : NULL);
size = write(parent_node->file_fd, (void *)&element_ptr->dir_data, element_ptr->dir_data.d_reclen);
require(size == element_ptr->dir_data.d_reclen, write_element);
}
else
{
struct node_entry *temp_node;
(void) nodecache_get_node(parent_node, 0, NULL, TRUE, TRUE, WEBDAV_DIR_TYPE, &temp_node);
bzero(&statbuf, sizeof(struct stat));
statbuf.st_dev = 0;
statbuf.st_nlink = 1;
statbuf.st_uid = UNKNOWNUID;
statbuf.st_gid = UNKNOWNUID;
statbuf.st_rdev = 0;
statbuf.st_blksize = WEBDAV_IOSIZE;
statbuf.st_flags = 0;
statbuf.st_gen = 0;
statbuf.st_atimespec = statbuf.st_mtimespec = statbuf.st_ctimespec = element_ptr->stattime;
statbuf.st_mode = S_IFDIR | S_IRWXU;
statbuf.st_size = WEBDAV_DIR_SIZE;
statbuf.st_blocks = ((statbuf.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
statbuf.st_ino = parent_node->fileid;
(void) nodecache_add_attributes(parent_node, uid, &statbuf, NULL);
}
}
(void) nodecache_delete_invalid_directory_nodes(parent_node);
element_ptr = opendir_struct.head;
while (element_ptr)
{
prev_element_ptr = element_ptr;
element_ptr = element_ptr->next;
free(prev_element_ptr);
}
CFRelease(parser);
CFRelease(xml_dataref);
return ( 0 );
write_element:
element_ptr = opendir_struct.head;
while (element_ptr)
{
prev_element_ptr = element_ptr;
element_ptr = element_ptr->next;
free(prev_element_ptr);
}
write_dot_dotdot:
(void) ftruncate(parent_node->file_fd, 0);
CFXMLParserParse:
CFRelease(parser);
CFXMLParserCreate:
CFRelease(xml_dataref);
CFDataCreateWithBytesNoCopy:
lseek:
ftruncate:
return ( EIO );
}
int parse_file_count(const UInt8 *xmlp, CFIndex xmlp_len, int *file_count)
{
CFXMLParserCallBacks callbacks =
{
0, parser_file_count_create, parser_add, parser_end, parser_resolve, NULL
};
CFXMLParserContext context =
{
0, file_count, NULL, NULL, NULL
};
CFDataRef xml_dataref;
CFXMLParserRef parser;
*file_count = 0;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
if ( xml_dataref != NULL )
{
parser = CFXMLParserCreate(kCFAllocatorDefault, xml_dataref, NULL, kCFXMLParserNoOptions,
kCFXMLNodeCurrentVersion, &callbacks, &context);
if ( parser != NULL )
{
CFXMLParserParse(parser);
CFRelease(parser);
}
CFRelease(xml_dataref);
}
return ( 0 );
}
int parse_stat(const UInt8 *xmlp, CFIndex xmlp_len, struct stat *statbuf)
{
CFXMLParserCallBacks callbacks =
{
0, parser_stat_create, parser_stat_add, parser_end, parser_resolve, NULL
};
CFXMLParserContext context =
{
0, statbuf, NULL, NULL, NULL
};
CFDataRef xml_dataref;
CFXMLParserRef parser;
bzero((void *)statbuf, sizeof(struct stat));
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlp,
xmlp_len, kCFAllocatorNull);
if ( xml_dataref != NULL )
{
parser = CFXMLParserCreate(kCFAllocatorDefault, xml_dataref, NULL, kCFXMLParserNoOptions,
kCFXMLNodeCurrentVersion, &callbacks, &context);
if ( parser != NULL )
{
CFXMLParserParse(parser);
CFRelease(parser);
}
CFRelease(xml_dataref);
}
statbuf->st_dev = 0;
statbuf->st_nlink = 1;
statbuf->st_uid = UNKNOWNUID;
statbuf->st_gid = UNKNOWNUID;
statbuf->st_rdev = 0;
statbuf->st_blksize = WEBDAV_IOSIZE;
statbuf->st_flags = 0;
statbuf->st_gen = 0;
statbuf->st_atimespec = statbuf->st_ctimespec = statbuf->st_mtimespec;
if ( S_ISDIR(statbuf->st_mode) )
{
statbuf->st_mode |= S_IRWXU;
statbuf->st_size = WEBDAV_DIR_SIZE;
}
else
{
statbuf->st_mode = S_IFREG | S_IRWXU;
}
statbuf->st_blocks = ((statbuf->st_size + S_BLKSIZE - 1) / S_BLKSIZE);
return ( 0 );
}
int parse_statfs(const UInt8 *xmlp, CFIndex xmlp_len, struct statfs *statfsbuf)
{
CFXMLParserCallBacks callbacks =
{
0, parser_statfs_create, parser_statfs_add, parser_end, parser_resolve, NULL
};
struct webdav_quotas quotas;
CFXMLParserContext context =
{
0, "as, NULL, NULL, NULL
};
CFDataRef xml_dataref;
CFXMLParserRef parser;
bzero((void *)statfsbuf, sizeof(struct statfs));
bzero((void *)"as, sizeof(struct webdav_quotas));
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlp,
xmlp_len, kCFAllocatorNull);
if ( xml_dataref != NULL )
{
parser = CFXMLParserCreate(kCFAllocatorDefault, xml_dataref, NULL, kCFXMLParserNoOptions,
kCFXMLNodeCurrentVersion, &callbacks, &context);
if ( parser != NULL )
{
CFXMLParserParse(parser);
CFRelease(parser);
}
CFRelease(xml_dataref);
}
if ( quotas.use_bytes_values )
{
uint64_t total_bytes;
uint64_t total_blocks;
uint32_t bsize;
total_bytes = quotas.quota_available_bytes + quotas.quota_used_bytes;
if ( (total_bytes >= quotas.quota_available_bytes) && (total_bytes >= quotas.quota_used_bytes) )
{
bsize = S_BLKSIZE / 2;
do
{
bsize *= 2;
total_blocks = ((total_bytes + bsize - 1) / bsize);
} while ( total_blocks > LONG_MAX );
statfsbuf->f_bsize = bsize;
#ifdef __LP64__
statfsbuf->f_blocks = total_blocks;
statfsbuf->f_bavail = statfsbuf->f_bfree = quotas.quota_available_bytes / bsize;
#else
statfsbuf->f_blocks = (long)total_blocks;
statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota_available_bytes / bsize);
#endif
}
}
else
{
if ( (quotas.quota != 0) && (quotas.quota > quotas.quotaused) )
{
#ifdef __LP64__
statfsbuf->f_bavail = statfsbuf->f_bfree = (quotas.quota - quotas.quotaused);
#else
statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota - quotas.quotaused);
#endif
}
else
{
statfsbuf->f_bavail = statfsbuf->f_bfree = 0;
}
#ifdef __LP64__
statfsbuf->f_blocks = quotas.quota;
#else
statfsbuf->f_blocks = (long)quotas.quota;
#endif
statfsbuf->f_bsize = S_BLKSIZE;
}
statfsbuf->f_iosize = WEBDAV_IOSIZE;
return ( 0 );
}
int parse_lock(const UInt8 *xmlp, CFIndex xmlp_len, char **locktoken)
{
CFXMLParserCallBacks callbacks =
{
0, parser_lock_create, parser_add, parser_end, parser_resolve, NULL
};
webdav_parse_lock_struct_t lock_struct;
CFXMLParserContext context =
{
0, &lock_struct, NULL, NULL, NULL
};
CFDataRef xml_dataref;
CFXMLParserRef parser;
lock_struct.context = 0;
lock_struct.locktoken = NULL;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlp,
xmlp_len, kCFAllocatorNull);
if ( xml_dataref != NULL )
{
parser = CFXMLParserCreate(kCFAllocatorDefault, xml_dataref, NULL, kCFXMLParserNoOptions,
kCFXMLNodeCurrentVersion, &callbacks, &context);
if ( parser != NULL )
{
CFXMLParserParse(parser);
CFRelease(parser);
}
CFRelease(xml_dataref);
}
*locktoken = (char *)lock_struct.locktoken;
if (*locktoken == NULL)
{
debug_string("error parsing lock token");
}
return ( 0 );
}
int parse_cachevalidators(const UInt8 *xmlp, CFIndex xmlp_len, time_t *last_modified, char **entity_tag)
{
CFXMLParserCallBacks callbacks =
{
0, parser_cachevalidators_create, parser_cachevalidators_add, parser_end, parser_resolve, NULL
};
struct webdav_parse_cachevalidators_struct cachevalidators_struct;
CFXMLParserContext context =
{
0, &cachevalidators_struct, NULL, NULL, NULL
};
CFDataRef xml_dataref;
CFXMLParserRef parser;
cachevalidators_struct.last_modified = 0;
cachevalidators_struct.entity_tag = NULL;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
if ( xml_dataref != NULL )
{
parser = CFXMLParserCreate(kCFAllocatorDefault, xml_dataref, NULL, kCFXMLParserNoOptions,
kCFXMLNodeCurrentVersion, &callbacks, &context);
if ( parser != NULL )
{
CFXMLParserParse(parser);
CFRelease(parser);
}
CFRelease(xml_dataref);
}
if ( cachevalidators_struct.last_modified != 0 )
{
*last_modified = cachevalidators_struct.last_modified;
}
else
{
time(last_modified);
}
*entity_tag = cachevalidators_struct.entity_tag;
return ( 0 );
}