#include <CoreFoundation/CoreFoundation.h>
#include <string.h>
#include <sys/vnode.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include "webdavd.h"
#include "webdav_parse.h"
#include "webdav_memcache.h"
#include "webdav_inode.h"
#include "fetch.h"
#include "pathnames.h"
#include "../webdav_fs.kextproj/webdav_fs.kmodproj/vnops.h"
#include "../webdav_fs.kextproj/webdav_fs.kmodproj/webdav.h"
extern time_t parse_http_date(char *string);
static void *parser_lookup_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context);
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 void *parser_lookup_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
webdav_parse_lookup_element_t * element_ptr = NULL;
webdav_parse_lookup_element_t * list_ptr;
webdav_parse_lookup_struct_t * struct_ptr = (webdav_parse_lookup_struct_t *)context;
CFRange comparison_range;
#ifdef DEBUG_PARSE
char buffer[256];
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
fprintf(stderr, "Element Type");
break;
case kCFXMLNodeTypeDocument:
fprintf(stderr, "Document Type");
break;
case kCFXMLNodeTypeWhitespace:
fprintf(stderr, "WhiteSpace");
break;
case kCFXMLNodeTypeText:
fprintf(stderr, "TextType");
break;
default:
fprintf(stderr, "Unknown Type");
}
CFStringGetCString(CFXMLNodeGetString(node), buffer, (CFIndex)256, kCFStringEncodingUTF8);
fprintf(stderr, ": Element string is: %s\n", buffer);
#endif
if (CFXMLNodeGetTypeCode(node) == kCFXMLNodeTypeElement)
{
CFStringRef str = CFXMLNodeGetString(node);
comparison_range = CFStringFind(str, CFSTR(":"), 0);
comparison_range.location++;
comparison_range.length = CFStringGetLength(str) - comparison_range.location;
if ((CFStringCompareWithOptions(str, CFSTR("resourcetype"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo)
{
element_ptr = malloc(sizeof(webdav_parse_lookup_element_t));
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_lookup_create: malloc element_ptr %d\n", (int)element_ptr);
#endif
if (!element_ptr)
{
syslog(LOG_ERR, "parser_lookup_create: element_ptr could not be allocated");
return (NULL);
}
element_ptr->file_type = WEBDAV_FILE_TYPE;
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(str, CFSTR("collection"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo)
{
element_ptr = struct_ptr->tail;
if (element_ptr)
{
element_ptr->file_type = WEBDAV_DIR_TYPE;
}
else
{
syslog(LOG_ERR, "parser_lookup_create: element_ptr was NULL");
return (NULL);
}
}
}
}
if (element_ptr)
{
return ((void *)element_ptr);
}
else
{
return ((void *)struct_ptr);
}
}
#define SUBSTITUTE_PHYSICAL_ENTITY(node, text_ptr, size) \
{ \
if (CFXMLNodeGetTypeCode(node) == kCFXMLNodeTypeEntityReference) \
{ \
if (!strcmp(text_ptr, "amp")) \
strcpy(text_ptr, "&"); \
else if (!strcmp(text_ptr, "lt")) \
strcpy(text_ptr, "<"); \
else if (!strcmp(text_ptr, "gt")) \
strcpy(text_ptr, ">"); \
else if (!strcmp(text_ptr, "apos")) \
strcpy(text_ptr, "'"); \
else if (!strcmp(text_ptr, "quot")) \
strcpy(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 = CFXMLNodeGetString(node);
#ifdef DEBUG_PARSE
char buffer[257];
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
fprintf(stderr, "Element Type");
break;
case kCFXMLNodeTypeDocument:
fprintf(stderr, "Document Type");
break;
case kCFXMLNodeTypeWhitespace:
fprintf(stderr, "WhiteSpace");
break;
case kCFXMLNodeTypeText:
fprintf(stderr, "TextType");
break;
default:
fprintf(stderr, "Unknown Type");
}
CFStringGetCString(CFXMLNodeGetString(node), buffer, (CFIndex)256, kCFStringEncodingUTF8);
fprintf(stderr, ": Element string is: %s\n", buffer);
#endif
return_ptr = malloc(sizeof(webdav_parse_opendir_return_t));
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_opendir_create: malloc return_ptr %d\n", (int)return_ptr);
#endif
if (!return_ptr)
{
syslog(LOG_ERR, "parser_opendir_create: return_ptr could not be allocated");
struct_ptr->error = ENOMEM;
return (NULL);
}
return_ptr->id = WEBDAV_OPENDIR_IGNORE;
return_ptr->data_ptr = (void *)NULL;
return_value = (void *)return_ptr;
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
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))
{
element_ptr = malloc(sizeof(webdav_parse_opendir_element_t));
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_opendir_create: malloc element_ptr %d\n", (int)element_ptr);
#endif
if (!element_ptr)
{
#ifdef DEBUG_PARSE
fprintf(stderr,
"parser_opendir_create: free return_ptr on failed element_ptr malloc %d\n",
(int)return_ptr);
#endif
syslog(LOG_ERR, "parser_opendir_create: element_ptr could not be allocated");
free(return_ptr);
struct_ptr->error = ENOMEM;
return (NULL);
}
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 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;
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_opendir_create: free return_ptr on collection %d\n",
(int)return_ptr);
#endif
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));
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_opendir_create: malloc text_ptr %d\n", (int)text_ptr);
#endif
if (!text_ptr)
{
#ifdef DEBUG_PARSE
fprintf(stderr,
"parser_opendir_create: free return_ptr on failed text_ptr malloc %d\n",
(int)return_ptr);
#endif
syslog(LOG_ERR, "parser_opendir_create: text_ptr could not be allocated");
free(return_ptr);
struct_ptr->error = ENOMEM;
return (NULL);
}
if (CFStringGetBytes(nodeString, CFRangeMake(0, CFStringGetLength(nodeString)),
kCFStringEncodingUTF8, 0, 0, text_ptr->name,
sizeof(text_ptr->name) - 1, &text_ptr->size) != CFStringGetLength(nodeString))
{
#ifdef DEBUG_PARSE
fprintf(stderr, "Get Length Says %d\n", (int)CFStringGetLength(nodeString));
fprintf(stderr, "Get Bytes Says %d\n",
(int)CFStringGetBytes(nodeString, CFRangeMake(0,
CFStringGetLength(nodeString)), kCFStringEncodingUTF8, 0, 0,
text_ptr->name, sizeof(text_ptr->name) - 1, &text_ptr->size));
fprintf(stderr, "parser_opendir_create: free return ptr on name too long %d\n",
(int)return_ptr);
#endif
syslog(LOG_ERR, "parser_opendir_create: name too long");
free(return_ptr);
struct_ptr->error = ENAMETOOLONG;
return (NULL);
}
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;
default:
break;
}
return (return_value);
}
static void *parser_file_count_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
CFRange comparison_range;
#ifdef DEBUG_PARSE
char buffer[257];
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
fprintf(stderr, "Element Type");
break;
case kCFXMLNodeTypeDocument:
fprintf(stderr, "Document Type");
break;
case kCFXMLNodeTypeWhitespace:
fprintf(stderr, "WhiteSpace");
break;
case kCFXMLNodeTypeText:
fprintf(stderr, "TextType");
break;
default:
fprintf(stderr, "Unknown Type");
}
CFStringGetCString(CFXMLNodeGetString(node), buffer, (CFIndex)256, kCFStringEncodingUTF8);
fprintf(stderr, ": Element string is: %s\n", buffer);
#endif
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
{
CFStringRef str = CFXMLNodeGetString(node);
comparison_range = CFStringFind(str, CFSTR(":"), 0);
comparison_range.location++;
comparison_range.length = CFStringGetLength(str) - comparison_range.location;
if (((CFStringCompareWithOptions(str, CFSTR("href"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
*((int *)context) += 1;
}
break;
}
default:
break;
}
return ((void *)1);
}
static void *parser_stat_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
void *return_val = (void *)WEBDAV_STAT_IGNORE;
char *text_ptr;
size_t size;
CFRange comparison_range;
CFStringRef nodeString = CFXMLNodeGetString(node);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
comparison_range.location++;
comparison_range.length = CFStringGetLength(nodeString) - 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 vattr *)context)->va_type = VDIR;
}
}
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
text_ptr = malloc(WEBDAV_MAX_STAT_SIZE);
if (CFStringGetBytes(nodeString, CFRangeMake(0, CFStringGetLength(nodeString)),
kCFStringEncodingUTF8, 0, 0, text_ptr,
WEBDAV_MAX_STAT_SIZE - 1, &size) != CFStringGetLength(nodeString))
{
syslog(LOG_ERR, "parser_stat_create: name too long or malformed");
free(text_ptr);
return_val = NULL;
}
else
{
text_ptr[size] = '\0';
SUBSTITUTE_PHYSICAL_ENTITY(node, text_ptr, size);
return_val = (void *)text_ptr;
}
default:
break;
}
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;
char *text_ptr;
size_t size;
CFRange comparison_range;
CFStringRef nodeString = CFXMLNodeGetString(node);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
comparison_range.location++;
comparison_range.length = CFStringGetLength(nodeString) - comparison_range.location;
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(WEBDAV_MAX_STATFS_SIZE);
if (CFStringGetBytes(nodeString, CFRangeMake(0, CFStringGetLength(nodeString)),
kCFStringEncodingUTF8, 0, 0, text_ptr,
WEBDAV_MAX_STATFS_SIZE - 1, &size) != CFStringGetLength(nodeString))
{
syslog(LOG_ERR, "parser_statfs_create: name too long");
free(text_ptr);
return_val = NULL;
}
else
{
text_ptr[size] = '\0';
SUBSTITUTE_PHYSICAL_ENTITY(node, text_ptr, size);
return_val = (void *)text_ptr;
}
break;
default:
break;
}
return (return_val);
}
static void *parser_lock_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser)
void *return_val = (void *)WEBDAV_LOCK_CONTINUE;
char *text_ptr;
size_t text_size, string_size;
CFRange comparison_range;
CFStringRef nodeString = CFXMLNodeGetString(node);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
comparison_range.location++;
comparison_range.length = CFStringGetLength(nodeString) - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("locktoken"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
*((char **)context) = (char *)WEBDAV_LOCK_TOKEN;
}
else
{
if (((CFStringCompareWithOptions(nodeString, CFSTR("href"), comparison_range,
kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
if (*((char **)context) == (char *)WEBDAV_LOCK_TOKEN)
{
*((char **)context) = (char *)WEBDAV_LOCK_HREF;
}
else
{
*((char **)context) = (char *)NULL;
}
}
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
if (*((char **)context) == (char *)WEBDAV_LOCK_HREF)
{
text_size = CFStringGetLength(nodeString);
text_ptr = malloc(text_size + 1);
if ((size_t)CFStringGetBytes(nodeString, CFRangeMake(0, text_size), kCFStringEncodingUTF8,
0, 0, text_ptr, WEBDAV_MAX_STAT_SIZE - 1,
&string_size) != text_size)
{
syslog(LOG_ERR, "parser_lock_create: name too long");
free(text_ptr);
return_val = NULL;
}
else
{
text_ptr[string_size] = '\0';
SUBSTITUTE_PHYSICAL_ENTITY(node, text_ptr, string_size);
*((char **)context) = text_ptr;
return_val = NULL;
}
}
default:
break;
}
return (return_val);
}
static void *parser_getlastmodified_create(CFXMLParserRef parser, CFXMLNodeRef node, void *context)
{
#pragma unused(parser, context)
void *return_val = (void *)WEBDAV_GETLASTMODIFIED_IGNORE;
char *text_ptr;
size_t text_size;
size_t size;
CFRange comparison_range;
CFStringRef nodeString = CFXMLNodeGetString(node);
switch (CFXMLNodeGetTypeCode(node))
{
case kCFXMLNodeTypeElement:
comparison_range = CFStringFind(nodeString, CFSTR(":"), 0);
comparison_range.location++;
comparison_range.length = CFStringGetLength(nodeString) - comparison_range.location;
if (((CFStringCompareWithOptions(nodeString, CFSTR("getlastmodified"),
comparison_range, kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
{
return_val = (void *)WEBDAV_GETLASTMODIFIED_MODDATE;
}
break;
case kCFXMLNodeTypeEntityReference:
case kCFXMLNodeTypeText:
case kCFXMLNodeTypeCDATASection:
text_size = CFStringGetLength(nodeString);
text_ptr = malloc(text_size + 1);
if ((size_t)CFStringGetBytes(nodeString, CFRangeMake(0, text_size),
kCFStringEncodingUTF8, 0, 0, text_ptr,
text_size, &size) != text_size)
{
syslog(LOG_ERR, "parser_getlastmodified_create: name too long or malformed");
free(text_ptr);
return_val = NULL;
}
else
{
text_ptr[size] = '\0';
SUBSTITUTE_PHYSICAL_ENTITY(node, text_ptr, size);
return_val = (void *)text_ptr;
}
default:
break;
}
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) <=
(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 += text_ptr->size;
}
else
{
syslog(LOG_ERR, "parser_opendir_add: 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(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 = parse_http_date(text_ptr->name);
element_ptr->stattime.tv_nsec = 0;
if (element_ptr->stattime.tv_sec == -1)
{
element_ptr->stattime.tv_sec = 0;
}
break;
case WEBDAV_OPENDIR_APPLEDOUBLEHEADER:
{
size_t len = APPLEDOUBLEHEADER_LENGTH;
element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
from_base64(text_ptr->name, element_ptr->appledoubleheader, &len);
if (len == APPLEDOUBLEHEADER_LENGTH)
{
element_ptr->appledoubleheadervalid = TRUE;
}
}
break;
default:
break;
}
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_opendir_add: free text_ptr %d\n", (int)text_ptr);
#endif
free(text_ptr);
}
}
static void parser_stat_add(CFXMLParserRef parser, void *parent, void *child, void *context)
{
#pragma unused(parser)
char *text_ptr = (char *)child;
struct vattr *statbuf = (struct vattr *)context;
char *ep;
switch ((int)parent)
{
case WEBDAV_STAT_LENGTH:
if (text_ptr && (text_ptr != (char *)WEBDAV_STAT_IGNORE))
{
statbuf->va_size = strtoq(text_ptr, &ep, 10);
free(text_ptr);
}
else
{
statbuf->va_size = -1LL;
}
break;
case WEBDAV_STAT_MODDATE:
if (text_ptr && (text_ptr != (char *)WEBDAV_STAT_IGNORE))
{
statbuf->va_mtime.tv_sec = parse_http_date(text_ptr);
if (statbuf->va_mtime.tv_sec == -1)
{
statbuf->va_mtime.tv_sec = 0;
}
statbuf->va_mtime.tv_nsec = 0;
statbuf->va_atime = statbuf->va_mtime;
statbuf->va_ctime = statbuf->va_mtime;
free(text_ptr);
}
else
{
}
break;
default:
if (text_ptr &&
text_ptr != (char *)WEBDAV_STAT_IGNORE &&
text_ptr != (char *)WEBDAV_STAT_LENGTH &&
text_ptr != (char *)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 statfs *statfsbuf = (struct statfs *)context;
char *ep;
switch ((int)parent)
{
case WEBDAV_STATFS_QUOTA:
if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
{
statfsbuf->f_blocks = strtoq(text_ptr, &ep, 10);
free(text_ptr);
}
else
{
statfsbuf->f_blocks = 0;
}
break;
case WEBDAV_STATFS_QUOTAUSED:
if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
{
statfsbuf->f_bavail = statfsbuf->f_bfree = strtoq(text_ptr, &ep, 10);
free(text_ptr);
}
else
{
statfsbuf->f_bavail = statfsbuf->f_bfree = 0;
}
break;
default:
if (text_ptr &&
text_ptr != (char *)WEBDAV_STATFS_IGNORE &&
text_ptr != (char *)WEBDAV_STAT_LENGTH &&
text_ptr != (char *)WEBDAV_STAT_MODDATE)
{
free(text_ptr);
}
break;
}
return;
}
static void parser_getlastmodified_add(CFXMLParserRef parser, void *parent, void *child, void *context)
{
#pragma unused(parser)
char *text_ptr = (char *)child;
time_t *last_modified = (time_t *)context;
switch ((int)parent)
{
case WEBDAV_GETLASTMODIFIED_MODDATE:
if (text_ptr && (text_ptr != (char *)WEBDAV_GETLASTMODIFIED_IGNORE))
{
*last_modified = parse_http_date(text_ptr);
free(text_ptr);
}
else
{
}
break;
default:
if (text_ptr &&
text_ptr != (char *)WEBDAV_GETLASTMODIFIED_IGNORE &&
text_ptr != (char *)WEBDAV_GETLASTMODIFIED_MODDATE)
{
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)
{
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_opendir_end: free return_ptr %d\n", (int)my_element);
#endif
free(my_element);
}
return;
}
static CFDataRef parser_resolve(CFXMLParserRef parser, CFXMLExternalID *extID, void *context)
{
#pragma unused(parser, extID, context)
return (NULL);
}
int parse_lookup(char *xmlp, int xmlp_len, webdav_filetype_t *a_file_type)
{
CFDataRef xml_dataref;
CFXMLParserCallBacks callbacks =
{
0, parser_lookup_create, parser_add, parser_end, parser_resolve, NULL
};
webdav_parse_lookup_struct_t lookup_struct;
webdav_parse_lookup_element_t * element_ptr;
CFXMLParserOptions options = kCFXMLParserNoOptions;
CFXMLParserContext context =
{
0, &lookup_struct, NULL, NULL, NULL
};
CFXMLParserRef parser;
CFURLRef fake_url;
int error = 0;
lookup_struct.head = lookup_struct.tail = NULL;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
fake_url = CFURLCreateWithString(NULL, CFSTR("fakeurl"), NULL);
parser = CFXMLParserCreate(kCFAllocatorSystemDefault, xml_dataref, fake_url, options,
kCFXMLNodeCurrentVersion, &callbacks, &context);
CFXMLParserParse(parser);
element_ptr = lookup_struct.head;
if (!element_ptr)
{
syslog(LOG_ERR, "parser_lookup: element_ptr is NULL");
error = EFTYPE;
*a_file_type = 0;
}
else
{
*a_file_type = element_ptr->file_type;
if (element_ptr->next != NULL)
{
syslog(LOG_ERR, "parser_lookup: element_ptr->next != NULL");
error = 1;
}
#ifdef DEBUG_PARSE
fprintf(stderr, "parser_lookup: free element_ptr %d\n", (int)element_ptr);
#endif
free(element_ptr);
}
CFRelease(parser);
CFRelease(xml_dataref);
CFRelease(fake_url);
return (error);
}
int parse_opendir(char *xmlp, int xmlp_len, int fd, char *dir_ref, char *hostname, uid_t uid)
{
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;
CFXMLParserOptions options = kCFXMLParserNoOptions;
CFXMLParserContext context =
{
0, &opendir_struct, NULL, NULL, NULL
};
CFXMLParserRef parser;
CFURLRef fake_url;
int error = 0;
int ignore_error;
ssize_t size;
char *name_ptr;
char *decoded_dir_ref;
char *after_dir_ref_hostname = NULL;
char *after_hostname = NULL;
char *cache_uri;
char *temp_uri;
u_int32_t last_char;
int name_len = 0;
struct dirent dir_data[2];
struct vattr statstruct;
opendir_struct.head = opendir_struct.tail = NULL;
opendir_struct.error = 0;
bzero(&statstruct, sizeof(statstruct));
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
fake_url = CFURLCreateWithString(NULL, CFSTR("fakeurl"), NULL);
parser = CFXMLParserCreate(kCFAllocatorSystemDefault, xml_dataref, fake_url, options,
kCFXMLNodeCurrentVersion, &callbacks, &context);
CFXMLParserParse(parser);
CFRelease(parser);
CFRelease(xml_dataref);
CFRelease(fake_url);
if (strncmp(dir_ref, _WEBDAVPREFIX, strlen(_WEBDAVPREFIX)) == 0)
{
decoded_dir_ref = percent_decode(dir_ref);
if (!decoded_dir_ref)
{
return ENOMEM;
}
}
else
{
after_dir_ref_hostname = dir_ref;
error = reconstruct_url(hostname, dir_ref, &temp_uri);
if (error)
{
return ENOMEM;
}
decoded_dir_ref = percent_decode(temp_uri);
if (!decoded_dir_ref)
{
free(temp_uri);
return ENOMEM;
}
else
{
free(temp_uri);
}
}
cache_uri = &decoded_dir_ref[strlen(_WEBDAVPREFIX)];
after_dir_ref_hostname = &cache_uri[strlen(_WEBDAVPREFIX)];
after_dir_ref_hostname = strchr(after_dir_ref_hostname, '/');
bzero(dir_data, sizeof(dir_data));
dir_data[0].d_namlen = 1;
dir_data[0].d_reclen = sizeof(struct dirent);
dir_data[0].d_name[0] = '.';
error = webdav_get_inode(cache_uri, strlen(cache_uri), TRUE, &dir_data[0].d_fileno);
if (error)
{
goto free_decoded_dir_ref;
}
dir_data[0].d_type = DT_DIR;
dir_data[1].d_namlen = 2;
dir_data[1].d_reclen = sizeof(struct dirent);
dir_data[1].d_name[0] = '.';
dir_data[1].d_name[1] = '.';
if (dir_data[0].d_fileno == WEBDAV_ROOTFILEID)
{
dir_data[1].d_fileno = 2;
}
else
{
if (after_dir_ref_hostname && strchr(after_dir_ref_hostname, '/'))
{
error = webdav_get_inode(cache_uri, (unsigned int)(strrchr(cache_uri, '/') - cache_uri),
TRUE, &dir_data[1].d_fileno);
if (error)
{
goto free_decoded_dir_ref;
}
}
else
{
dir_data[1].d_fileno = WEBDAV_ROOTFILEID;
}
}
dir_data[1].d_type = DT_DIR;
size = write(fd, (void *)dir_data, (sizeof(struct dirent)) * 2);
if (size != (sizeof(struct dirent)) * 2)
{
if (size == -1)
{
syslog(LOG_ERR, "parser_opendir: write() . & ..: %s", strerror(errno));
error = errno;
}
else
{
syslog(LOG_ERR, "parser_opendir: write() . & .. was short");
error = EIO;
}
}
element_ptr = opendir_struct.head;
if (!element_ptr && !opendir_struct.error)
{
error = 0;
goto free_decoded_dir_ref;
}
else
{
while (element_ptr)
{
element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length] = '\0';
if (element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length - 1] == '/')
{
--element_ptr->dir_data.d_name_URI_length;
element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length] = '\0';
}
percent_decode_in_place(element_ptr->dir_data.d_name);
element_ptr->dir_data.d_name_URI_length = strlen(element_ptr->dir_data.d_name);
last_char = element_ptr->dir_data.d_name_URI_length;
after_hostname = element_ptr->dir_data.d_name;
if (!(strncmp(after_hostname, _WEBDAVPREFIX, strlen(_WEBDAVPREFIX))))
{
after_hostname = &(after_hostname[strlen(_WEBDAVPREFIX)]);
after_hostname = strchr(after_hostname, '/');
}
if ((!after_hostname) ||
(after_dir_ref_hostname && (!(strncmp(after_hostname, after_dir_ref_hostname,
strlen(after_hostname))))))
{
}
else
{
if (element_ptr->dir_data.d_type == DT_DIR)
{
statstruct.va_type = VDIR;
statstruct.va_size = WEBDAV_DIR_SIZE;
element_ptr->appledoubleheadervalid = FALSE;
}
else
{
statstruct.va_type = VREG;
statstruct.va_size = element_ptr->statsize;
element_ptr->appledoubleheadervalid =
(element_ptr->appledoubleheadervalid && (element_ptr->statsize == APPLEDOUBLEHEADER_LENGTH));
}
statstruct.va_bytes = ((statstruct.va_size + S_BLKSIZE - 1) / S_BLKSIZE) * S_BLKSIZE;
statstruct.va_atime = statstruct.va_mtime = statstruct.va_ctime =
element_ptr->stattime;
name_ptr = strrchr(element_ptr->dir_data.d_name, '/');
if (name_ptr)
{
name_len = (int)(((int)element_ptr->dir_data.d_name + last_char) -
(((int)name_ptr) + 1));
if (name_len > MAXNAMLEN)
{
syslog(LOG_ERR, "parser_opendir: URI too long");
error = ENAMETOOLONG;
goto free_decoded_dir_ref;
}
}
if (element_ptr->dir_data.d_name_URI_length && element_ptr->dir_data.d_name[0] == '/')
{
char *full_url;
if (strncmp(dir_ref, _WEBDAVPREFIX, strlen(_WEBDAVPREFIX)) == 0)
{
size_t tmp_name_len = (strlen(dir_ref) + (name_ptr ? strlen(name_ptr) : 0));
full_url = malloc(tmp_name_len);
if (full_url)
{
strncpy(full_url, &dir_ref[strlen(_WEBDAVPREFIX)], tmp_name_len);
if (name_ptr)
{
full_url = strncat(full_url, name_ptr, tmp_name_len);
}
}
else
{
syslog(LOG_ERR, "parser_opendir: full_url could not be allocated");
error = ENOMEM;
goto free_decoded_dir_ref;
}
cache_uri = full_url;
error = webdav_get_inode(cache_uri, strlen(cache_uri), TRUE,
(u_int32_t *) & statstruct.va_fileid);
if (error)
{
goto free_decoded_dir_ref;
}
ignore_error = webdav_memcache_insert(uid, cache_uri, &gmemcache_header,
&statstruct,
element_ptr->appledoubleheadervalid ? element_ptr->appledoubleheader: NULL);
free(full_url);
}
else
{
ignore_error = reconstruct_url(hostname, element_ptr->dir_data.d_name,
&full_url);
if (!ignore_error)
{
cache_uri = &full_url[strlen(_WEBDAVPREFIX)];
error = webdav_get_inode(cache_uri, strlen(cache_uri), TRUE,
(u_int32_t *) & statstruct.va_fileid);
if (error)
{
free(full_url);
goto free_decoded_dir_ref;
}
ignore_error = webdav_memcache_insert(uid, cache_uri, &gmemcache_header,
&statstruct,
element_ptr->appledoubleheadervalid ? element_ptr->appledoubleheader: NULL);
free(full_url);
}
}
}
else
{
cache_uri = &element_ptr->dir_data.d_name[strlen(_WEBDAVPREFIX)];
error = webdav_get_inode(cache_uri, strlen(cache_uri), TRUE,
(u_int32_t *) & statstruct.va_fileid);
if (error)
{
goto free_decoded_dir_ref;
}
ignore_error = webdav_memcache_insert(uid, cache_uri, &gmemcache_header,
&statstruct,
element_ptr->appledoubleheadervalid ? element_ptr->appledoubleheader: NULL);
}
if (name_ptr)
{
bcopy(name_ptr + 1, element_ptr->dir_data.d_name, (size_t)name_len);
element_ptr->dir_data.d_name[name_len] = '\0';
element_ptr->dir_data.d_namlen = name_len;
element_ptr->dir_data.d_fileno = statstruct.va_fileid;
}
size = write(fd, (void *) & element_ptr->dir_data, element_ptr->dir_data.d_reclen);
if (size != element_ptr->dir_data.d_reclen)
{
if (size == -1)
{
syslog(LOG_ERR, "parser_opendir: write(): %s", strerror(errno));
error = errno;
}
else
{
syslog(LOG_ERR, "parser_opendir: write() was short");
error = EIO;
}
}
}
prev_element_ptr = element_ptr;
element_ptr = element_ptr->next;
#ifdef DEBUG_PARSE
fprintf(stderr, "parse_opendir: freeing previous element ptr %d\n", (int)prev_element_ptr);
#endif
free(prev_element_ptr);
}
}
if (opendir_struct.error)
{
error = opendir_struct.error;
}
free_decoded_dir_ref:
free(decoded_dir_ref);
return (error);
}
int parse_file_count(char *xmlp, int xmlp_len, int *file_count)
{
CFDataRef xml_dataref;
CFXMLParserCallBacks callbacks =
{
0, parser_file_count_create, parser_add, parser_end, parser_resolve, NULL
};
CFXMLParserOptions options = kCFXMLParserNoOptions;
CFXMLParserContext context =
{
0, file_count, NULL, NULL, NULL
};
CFXMLParserRef parser;
CFURLRef fake_url;
int error = 0;
*file_count = 0;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
fake_url = CFURLCreateWithString(NULL, CFSTR("fakeurl"), NULL);
parser = CFXMLParserCreate(kCFAllocatorSystemDefault, xml_dataref, fake_url, options,
kCFXMLNodeCurrentVersion, &callbacks, &context);
CFXMLParserParse(parser);
CFRelease(parser);
CFRelease(xml_dataref);
CFRelease(fake_url);
return (error);
}
int parse_stat(char *xmlp, int xmlp_len, const char *orig_uri, struct vattr *statbuf, uid_t uid)
{
CFDataRef xml_dataref;
CFXMLParserCallBacks callbacks =
{
0, parser_stat_create, parser_stat_add, parser_end, parser_resolve, NULL
};
CFXMLParserContext context =
{
0, statbuf, NULL, NULL, NULL
};
CFXMLParserOptions options = kCFXMLParserNoOptions;
CFXMLParserRef parser;
CFURLRef fake_url;
int error = 0;
int ignore_error;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
fake_url = CFURLCreateWithString(NULL, CFSTR("fakeurl"), NULL);
bzero((void *)statbuf, sizeof(struct vattr));
parser = CFXMLParserCreate(kCFAllocatorSystemDefault, xml_dataref, fake_url, options,
kCFXMLNodeCurrentVersion, &callbacks, &context);
CFXMLParserParse(parser);
CFRelease(parser);
CFRelease(xml_dataref);
CFRelease(fake_url);
if (statbuf->va_type == VDIR)
{
statbuf->va_size = WEBDAV_DIR_SIZE;
}
else
{
statbuf->va_type = VREG;
}
if (statbuf->va_size)
{
statbuf->va_bytes = ((statbuf->va_size + S_BLKSIZE - 1) / S_BLKSIZE) * S_BLKSIZE;
}
if (strncmp(orig_uri, _WEBDAVPREFIX, strlen(_WEBDAVPREFIX)) == 0)
{
orig_uri += strlen(_WEBDAVPREFIX);
}
error = webdav_get_inode(orig_uri, strlen(orig_uri), TRUE,
(u_int32_t *) & statbuf->va_fileid);
if (error)
{
return (error);
}
ignore_error = webdav_memcache_insert(uid, (const char *)orig_uri, &gmemcache_header,
statbuf, NULL);
return (error);
}
int parse_statfs(char *xmlp, int xmlp_len, struct statfs *statfsbuf)
{
CFDataRef xml_dataref;
CFXMLParserCallBacks callbacks =
{
0, parser_statfs_create, parser_statfs_add, parser_end, parser_resolve, NULL
};
CFXMLParserContext context =
{
0, statfsbuf, NULL, NULL, NULL
};
CFXMLParserOptions options = kCFXMLParserNoOptions;
CFXMLParserRef parser;
CFURLRef fake_url;
int error = 0;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
fake_url = CFURLCreateWithString(NULL, CFSTR("fakeurl"), NULL);
bzero((void *)statfsbuf, sizeof(struct statfs));
parser = CFXMLParserCreate(kCFAllocatorSystemDefault, xml_dataref, fake_url, options,
kCFXMLNodeCurrentVersion, &callbacks, &context);
CFXMLParserParse(parser);
CFRelease(parser);
CFRelease(xml_dataref);
CFRelease(fake_url);
if (statfsbuf->f_blocks && (statfsbuf->f_blocks > statfsbuf->f_bfree))
{
statfsbuf->f_bavail = statfsbuf->f_bfree = (statfsbuf->f_blocks - statfsbuf->f_bfree);
}
else
{
statfsbuf->f_bavail = statfsbuf->f_bfree = 0;
}
return (error);
}
int parse_lock(char *xmlp, int xmlp_len, char **locktoken)
{
CFDataRef xml_dataref;
CFXMLParserCallBacks callbacks =
{
0, parser_lock_create, parser_add, parser_end, parser_resolve, NULL
};
CFXMLParserOptions options = kCFXMLParserNoOptions;
CFXMLParserContext context =
{
0, locktoken, NULL, NULL, NULL
};
CFXMLParserRef parser;
CFURLRef fake_url;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
fake_url = CFURLCreateWithString(NULL, CFSTR("fakeurl"), NULL);
parser = CFXMLParserCreate(kCFAllocatorSystemDefault, xml_dataref, fake_url, options,
kCFXMLNodeCurrentVersion, &callbacks, &context);
CFXMLParserParse(parser);
CFRelease(parser);
CFRelease(xml_dataref);
CFRelease(fake_url);
if (*locktoken == (char *)WEBDAV_LOCK_TOKEN || *locktoken == (char *)WEBDAV_LOCK_HREF)
{
syslog(LOG_ERR, "parse_lock: error parsing lock token");
*locktoken = NULL;
}
return (0);
}
int parse_getlastmodified(char *xmlp, int xmlp_len, time_t *last_modified)
{
CFDataRef xml_dataref;
CFXMLParserCallBacks callbacks =
{
0, parser_getlastmodified_create, parser_getlastmodified_add, parser_end, parser_resolve, NULL
};
CFXMLParserContext context =
{
0, last_modified, NULL, NULL, NULL
};
CFXMLParserOptions options = kCFXMLParserNoOptions;
CFXMLParserRef parser;
CFURLRef fake_url;
xml_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, xmlp,
(CFIndex)xmlp_len, kCFAllocatorNull);
fake_url = CFURLCreateWithString(NULL, CFSTR("fakeurl"), NULL);
parser = CFXMLParserCreate(kCFAllocatorSystemDefault, xml_dataref, fake_url, options,
kCFXMLNodeCurrentVersion, &callbacks, &context);
CFXMLParserParse(parser);
CFRelease(parser);
CFRelease(xml_dataref);
CFRelease(fake_url);
return (0);
}