1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "webdavd.h"
25
26#include <CoreFoundation/CoreFoundation.h>
27#include <sys/dirent.h>
28#include <unistd.h>
29#include <stdlib.h>
30#include <libxml/parser.h>
31#include <libxml/xmlmemory.h>
32#include "webdav_parse.h"
33#include "webdav_cache.h"
34#include "webdav_network.h"
35#include "LogMessage.h"
36
37extern long long
38strtoq(const char *, char **, int);
39
40/*****************************************************************************/
41
42/* local function prototypes */
43
44static int from_base64(const char *base64str, unsigned char *outBuffer, size_t *lengthptr);
45static void parser_file_count_create(void *ctx,
46									 const xmlChar *localname,
47									 const xmlChar *prefix,
48									 const xmlChar *URI,
49									 int nb_namespaces,
50									 const xmlChar **namespaces,
51									 int nb_attributes,
52									 int nb_defaulted,
53									 const xmlChar **attributes);
54static void parser_stat_create(void *ctx,
55							   const xmlChar *localname,
56							   const xmlChar *prefix,
57							   const xmlChar *URI,
58							   int nb_namespaces,
59							   const xmlChar **namespaces,
60							   int nb_attributes,
61							   int nb_defaulted,
62							   const xmlChar **attributes);
63static void parser_statfs_create(void *ctx,
64								 const xmlChar *localname,
65								 const xmlChar *prefix,
66								 const xmlChar *URI,
67								 int nb_namespaces,
68								 const xmlChar **namespaces,
69								 int nb_attributes,
70								 int nb_defaulted,
71								 const xmlChar **attributes);
72static void parser_lock_create(void *ctx,
73							   const xmlChar *localname,
74							   const xmlChar *prefix,
75							   const xmlChar *URI,
76							   int nb_namespaces,
77							   const xmlChar **namespaces,
78							   int nb_attributes,
79							   int nb_defaulted,
80							   const xmlChar **attributes);
81void parser_opendir_create (void *ctx,
82							const xmlChar *localname,
83							const xmlChar *prefix,
84							const xmlChar *URI,
85							int nb_namespaces,
86							const xmlChar **namespaces,
87							int nb_attributes,
88							int nb_defaulted,
89							const xmlChar **attributes);
90static void parser_lock_add(void *ctx, const xmlChar *localname, int length);
91static void parser_stat_add(void *ctx, const xmlChar *localname, int length);
92static void parser_statfs_add(void *ctx, const xmlChar *localname, int length);
93void parser_opendir_add(void *ctx, const xmlChar *localname, int length);
94void parse_stat_end(void *ctx,
95					const xmlChar *localname,
96					const xmlChar *prefix,
97					const xmlChar *URI);
98void parser_statfs_end(void *ctx,
99					   const xmlChar *localname,
100					   const xmlChar *prefix,
101					   const xmlChar *URI);
102void parser_lock_end(void *ctx,
103					 const xmlChar *localname,
104					 const xmlChar *prefix,
105					 const xmlChar *URI);
106void parser_opendir_end(void *ctx,
107						const xmlChar *localname,
108						const xmlChar *prefix,
109						const xmlChar *URI);
110void parser_file_count_end(void *ctx,
111						   const xmlChar *localname,
112						   const xmlChar *prefix,
113						   const xmlChar *URI);
114void parser_cachevalidators_end(void *ctx,
115								const xmlChar *localname,
116								const xmlChar *prefix,
117								const xmlChar *URI);
118void parser_multistatus_end(void *ctx,
119							const xmlChar *localname,
120							const xmlChar *prefix,
121							const xmlChar *URI);
122/*****************************************************************************/
123/* The from_base64 function decodes a base64 encoded c-string into outBuffer.
124 * The outBuffer's size is *lengthptr. The actual number of bytes decoded into
125 * outBuffer is also returned in *lengthptr. If outBuffer is large enough to
126 * decode the base64 string and if the base64 encoding is valid, from_base64()
127 * returns 0; otherwise -1 is returned. Note that outBuffer is just an array of
128 * bytes... it is not a c-string.
129 */
130static int from_base64(const char *base64str, unsigned char *outBuffer, size_t *lengthptr)
131{
132	char			decodedChar;
133	unsigned long	base64Length;
134	unsigned char	*eightBitByte;
135	unsigned char	sixBitEncoding[4];
136	unsigned short	encodingIndex;
137	int				endOfData;
138	const char		*equalPtr;
139	const char		*base64CharPtr;
140	const char		*base64EndPtr;
141
142	/* Determine the length of the base64 input string.
143	 * This also catches illegal '=' characters within a base64 string.
144	 */
145
146	base64Length = 0;
147
148	/* is there an '=' character? */
149	equalPtr = strchr(base64str, '=');
150	if ( equalPtr != NULL )
151	{
152		/* yes -- then it must be the last character of an octet, or
153		 * it must be the next to last character of an octet followed
154		 * by another '=' character */
155		switch ( (equalPtr - base64str) % 4 )
156		{
157			case 0:
158			case 1:
159				/* invalid encoding */
160				goto error_exit;
161				break;
162
163			case 2:
164				if ( equalPtr[1] != '=' )
165				{
166					/* invalid encoding */
167					goto error_exit;
168				}
169				base64Length = (equalPtr - base64str) + 2;
170				*lengthptr += 2;	/* adjust for padding */
171				break;
172
173			case 3:
174				base64Length = (equalPtr - base64str) + 1;
175				*lengthptr += 1;	/* adjust for padding */
176				break;
177		}
178	}
179	else
180	{
181		base64Length = strlen(base64str);
182	}
183
184	/* Make sure outBuffer is big enough */
185	if ( *lengthptr < ((base64Length / 4) * 3) )
186	{
187		/* outBuffer is too small */
188		goto error_exit;
189	}
190
191	/* Make sure length is a multiple of 4 */
192	if ( (base64Length % 4) != 0 )
193	{
194		/* invalid encoding */
195		goto error_exit;
196	}
197
198	/* OK -- */
199	eightBitByte = outBuffer;
200	encodingIndex = 0;
201	endOfData = FALSE;
202	base64EndPtr = (char *)((unsigned long)base64str + base64Length);
203	base64CharPtr = base64str;
204	while ( base64CharPtr < base64EndPtr )
205	{
206		decodedChar = *base64CharPtr++;
207
208		if ( (decodedChar >= 'A') && (decodedChar <= 'Z') )
209		{
210			decodedChar = decodedChar - 'A';
211		}
212		else if ( (decodedChar >= 'a') && (decodedChar <= 'z') )
213		{
214			decodedChar = decodedChar - 'a' + 26;
215		}
216		else if ( (decodedChar >= '0') && (decodedChar <= '9') )
217		{
218			decodedChar = decodedChar - '0' + 52;
219		}
220		else if ( decodedChar == '+' )
221		{
222			decodedChar = 62;
223		}
224		else if ( decodedChar == '/' )
225		{
226			decodedChar = 63;
227		}
228		else if ( decodedChar == '=' ) /* end of base64 encoding */
229		{
230			endOfData = TRUE;
231		}
232		else
233		{
234			/* invalid character */
235			goto error_exit;
236		}
237
238		if ( endOfData )
239		{
240			/* make sure there's no more looping */
241			base64CharPtr = base64EndPtr;
242		}
243		else
244		{
245			sixBitEncoding[encodingIndex] = (unsigned char)decodedChar;
246			++encodingIndex;
247		}
248
249		if ( (encodingIndex == 4) || endOfData)
250		{
251			/* convert four 6-bit characters into three 8-bit bytes */
252
253			/* always get first byte */
254			*eightBitByte++ =
255			(sixBitEncoding[0] << 2) | ((sixBitEncoding[1] & 0x30) >> 4);
256			if ( encodingIndex >= 3 )
257			{
258				/* get second byte only if encodingIndex is 3 or 4 */
259				*eightBitByte++ =
260				((sixBitEncoding[1] & 0x0F) << 4) | ((sixBitEncoding[2] & 0x3C) >> 2);
261				if ( encodingIndex == 4 )
262				{
263					/* get third byte only if encodingIndex is 4 */
264					*eightBitByte++ =
265					((sixBitEncoding[2] & 0x03) << 6) | (sixBitEncoding[3] & 0x3F);
266				}
267			}
268
269			/* reset encodingIndex */
270			encodingIndex = 0;
271		}
272	}
273
274	/* return the number of bytes in outBuffer and no error */
275	*lengthptr = eightBitByte - outBuffer;
276	return ( 0 );
277
278error_exit:
279	/* return 0 bytes in outBuffer and an error */
280	*lengthptr = 0;
281	return ( -1 );
282}
283/*****************************************************************************/
284
285void parse_stat_end(void *ctx,
286					const xmlChar *localname,
287					const xmlChar *prefix,
288					const xmlChar *URI)
289{
290	#pragma unused(localname,prefix,URI)
291	struct webdav_stat_attr* text_ptr = (struct webdav_stat_attr*)ctx;
292	text_ptr->start = false;
293}
294/*****************************************************************************/
295
296void parser_file_count_end(void *ctx,
297						   const xmlChar *localname,
298						   const xmlChar *prefix,
299						   const xmlChar *URI)
300{
301	#pragma unused(ctx,localname,prefix,URI)
302}
303/*****************************************************************************/
304
305void parser_statfs_end(void *ctx,
306					   const xmlChar *localname,
307					   const xmlChar *prefix,
308					   const xmlChar *URI)
309{
310	#pragma unused(localname,prefix,URI)
311	struct webdav_quotas* text_ptr = (struct webdav_quotas*)ctx;
312	text_ptr->start = false;
313}
314/*****************************************************************************/
315
316void parser_lock_end(void *ctx,
317					 const xmlChar *localname,
318					 const xmlChar *prefix,
319					 const xmlChar *URI)
320{
321	#pragma unused(localname,prefix,URI)
322	webdav_parse_lock_struct_t *lock_struct = (webdav_parse_lock_struct_t *)ctx;
323	lock_struct->start = false;
324}
325/*****************************************************************************/
326
327void parser_opendir_end(void *ctx,
328						const xmlChar *localname,
329						const xmlChar *prefix,
330						const xmlChar *URI)
331{
332	#pragma unused(localname,prefix,URI)
333	webdav_parse_opendir_struct_t * struct_ptr = (webdav_parse_opendir_struct_t *)ctx;
334	struct_ptr->start = false;
335}
336/*****************************************************************************/
337
338void parser_cachevalidators_end(void *ctx,
339								const xmlChar *localname,
340								const xmlChar *prefix,
341								const xmlChar *URI)
342{
343	#pragma unused(localname,prefix,URI)
344	struct webdav_parse_cachevalidators_struct *cachevalidators_struct = (struct webdav_parse_cachevalidators_struct *)ctx;
345	cachevalidators_struct->start = false;
346}
347/*****************************************************************************/
348
349void parser_multistatus_end(void *ctx,
350							const xmlChar *localname,
351							const xmlChar *prefix,
352							const xmlChar *URI)
353{
354	#pragma unused(localname,prefix,URI)
355	webdav_parse_multistatus_list_t * struct_ptr = (webdav_parse_multistatus_list_t *)ctx;
356	struct_ptr->start = false;
357}
358/*****************************************************************************/
359static webdav_parse_opendir_element_t *create_opendir_element(void)
360{
361	webdav_parse_opendir_element_t *element_ptr;
362
363	element_ptr = malloc(sizeof(webdav_parse_opendir_element_t));
364	if (!element_ptr)
365		return (NULL);
366
367	bzero(element_ptr, sizeof(webdav_parse_opendir_element_t));
368	element_ptr->dir_data.d_type = DT_REG;
369	element_ptr->seen_href = FALSE;
370	element_ptr->seen_response_end = FALSE;
371	element_ptr->dir_data.d_namlen = 0;
372	element_ptr->dir_data.d_name_URI_length = 0;
373	element_ptr->dir_data.d_reclen = sizeof(struct webdav_dirent);
374	element_ptr->next = NULL;
375	return (element_ptr);
376}
377/*****************************************************************************/
378void parser_opendir_create (void *ctx,
379							const xmlChar *localname,
380							const xmlChar *prefix,
381							const xmlChar *URI,
382							int nb_namespaces,
383							const xmlChar **namespaces,
384							int nb_attributes,
385							int nb_defaulted,
386							const xmlChar **attributes)
387{
388	#pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes)
389	webdav_parse_opendir_element_t * element_ptr = NULL;
390	webdav_parse_opendir_element_t * list_ptr;
391	webdav_parse_opendir_struct_t * struct_ptr = (webdav_parse_opendir_struct_t *)ctx;
392	CFStringRef nodeString;
393	struct_ptr->start=true;
394	nodeString =  CFStringCreateWithCString (kCFAllocatorDefault,
395											 (const char *)localname,
396											 kCFStringEncodingUTF8
397											 );
398	/* See if this is the resource type.  If it is, malloc a webdav_parse_opendir_element_t element and add it to the list.*/
399
400	if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
401	{
402		element_ptr = struct_ptr->tail;
403
404		if ( (element_ptr != NULL) && (element_ptr->seen_href == FALSE))
405		{
406			// <rdar://problem/4173444>
407			// This is a placeholder (<D:propstat> & children came before the <D:href>)
408			element_ptr->seen_href = TRUE;
409			struct_ptr->id = WEBDAV_OPENDIR_ELEMENT;
410			struct_ptr->data_ptr = (void *)element_ptr;
411		}
412		else
413		{
414			// Create the new href element
415			element_ptr = create_opendir_element();
416			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
417
418			element_ptr->seen_href = TRUE;
419
420			if (struct_ptr->head == NULL)
421			{
422				struct_ptr->head = element_ptr;
423			}
424			else
425			{
426				list_ptr = struct_ptr->tail;
427				list_ptr->next = element_ptr;
428			}
429			struct_ptr->tail = element_ptr;
430
431			struct_ptr->id = WEBDAV_OPENDIR_ELEMENT;
432			struct_ptr->data_ptr = (void *)element_ptr;
433		}
434	}	/* end if href */
435	else if (((CFStringCompare(nodeString, CFSTR("collection"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
436	{
437		/* If we have a collection property, we should normally have an
438		 * element ptr in the context already. But this is not always the
439		 * case (see <rdar://problem/4173444>).
440		 */
441		element_ptr = struct_ptr->tail;
442
443		// ignore the last <D:href> if we've already seen <D:/response>
444		if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE))
445			element_ptr = NULL;
446
447		if (element_ptr == NULL)
448		{
449			// <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload
450			//
451			// The <D:href> element might appear after the <D:propstat>. To handle this
452			// case we simply create a placeholder opendir element.
453			element_ptr = create_opendir_element();
454			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
455
456			if (struct_ptr->head == NULL)
457			{
458				struct_ptr->head = element_ptr;
459			}
460			else
461			{
462				list_ptr = struct_ptr->tail;
463				list_ptr->next = element_ptr;
464			}
465			struct_ptr->tail = element_ptr;
466		}
467
468		element_ptr->dir_data.d_type = DT_DIR;
469
470		/* Not interested in child of collection element. We can
471		 * and should free the return_ptr in this case.
472		 */
473		struct_ptr->id = WEBDAV_OPENDIR_IGNORE;
474		struct_ptr->data_ptr = NULL;
475	}	/* end if collection */
476	else if (((CFStringCompare(nodeString, CFSTR("getcontentlength"), kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
477	{
478		/* If we have size then mark up the element pointer so that the
479		 * child will know to parse the upcoming text size and store it.
480		 */
481		element_ptr = struct_ptr->tail;
482
483		// ignore the last <D:href> if we've already seen <D:/response>
484		if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE))
485			element_ptr = NULL;
486
487		if (element_ptr == NULL)
488		{
489			// <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload
490			//
491			// The <D:href> element might appear after the <D:propstat>. To handle this
492			// case we simply create a placeholder opendir element.
493			element_ptr = create_opendir_element();
494			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
495
496			if (struct_ptr->head == NULL)
497			{
498				struct_ptr->head = element_ptr;
499			}
500			else
501			{
502				list_ptr = struct_ptr->tail;
503				list_ptr->next = element_ptr;
504			}
505			struct_ptr->tail = element_ptr;
506		}
507
508		struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_LENGTH;
509		struct_ptr->data_ptr = (void *)element_ptr;
510	}	/* end if length */
511	else if (((CFStringCompare(nodeString, CFSTR("getlastmodified"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
512	{
513		/* If we have size then mark up the element pointer so that the
514		 * child will know to parse the upcoming text size and store it.
515		 */
516		element_ptr = struct_ptr->tail;
517
518		// ignore the last <D:href> if we've already seen <D:/response>
519		if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE))
520			element_ptr = NULL;
521
522		if (element_ptr == NULL)
523		{
524			// <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload
525			//
526			// The <D:href> element might appear after the <D:propstat>. To handle this
527			// case we simply create a placeholder opendir element.
528			element_ptr = create_opendir_element();
529			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
530
531			if (struct_ptr->head == NULL)
532			{
533				struct_ptr->head = element_ptr;
534			}
535			else
536			{
537				list_ptr = struct_ptr->tail;
538				list_ptr->next = element_ptr;
539			}
540			struct_ptr->tail = element_ptr;
541		}
542
543		struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_MODDATE;
544		struct_ptr->data_ptr = (void *)element_ptr;
545	}	/* end if modified */
546	else if (((CFStringCompare(nodeString, CFSTR("creationdate"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
547	{
548		/* If we have size then mark up the element pointer so that the
549		 * child will know to parse the upcoming text size and store it.
550		 */
551		element_ptr = struct_ptr->tail;
552
553		// ignore the last <D:href> if we've already seen <D:/response>
554		if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE))
555			element_ptr = NULL;
556
557		if (element_ptr == NULL)
558		{
559			// <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload
560			//
561			// The <D:href> element might appear after the <D:propstat>. To handle this
562			// case we simply create a placeholder opendir element.
563			element_ptr = create_opendir_element();
564			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
565
566			if (struct_ptr->head == NULL)
567			{
568				struct_ptr->head = element_ptr;
569			}
570			else
571			{
572				list_ptr = struct_ptr->tail;
573				list_ptr->next = element_ptr;
574			}
575			struct_ptr->tail = element_ptr;
576		}
577
578		struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_CREATEDATE;
579		struct_ptr->data_ptr = (void *)element_ptr;
580	}	/* end if createdate */
581	else if (((CFStringCompare(nodeString, CFSTR("appledoubleheader"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
582	{
583		/* If we have size then mark up the element pointer so that the
584		 * child will know to parse the upcoming text size and store it.
585		 */
586		syslog(LOG_ERR, "in the create opendir appledoubleheaderfield\n");
587		element_ptr = struct_ptr->tail;
588
589		// ignore the last <D:href> if we've already seen <D:/response>
590		if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE))
591			element_ptr = NULL;
592
593		if (element_ptr == NULL)
594		{
595			// <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload
596			//
597			// The <D:href> element might appear after the <D:propstat>. To handle this
598			// case we simply create a placeholder opendir element.
599			element_ptr = create_opendir_element();
600			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
601
602			if (struct_ptr->head == NULL)
603			{
604				struct_ptr->head = element_ptr;
605			}
606			else
607			{
608				list_ptr = struct_ptr->tail;
609				list_ptr->next = element_ptr;
610			}
611			struct_ptr->tail = element_ptr;
612		}
613
614		struct_ptr->id = WEBDAV_OPENDIR_APPLEDOUBLEHEADER;
615		struct_ptr->data_ptr = (void *)element_ptr;
616	}	/* end if appledoubleheader */
617	else if (((CFStringCompare(nodeString, CFSTR("response"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
618	{
619		struct_ptr->id = WEBDAV_OPENDIR_ELEMENT_RESPONSE;
620		struct_ptr->data_ptr = (void *)NULL;
621	}
622	else {
623		struct_ptr->id = WEBDAV_OPENDIR_IGNORE;
624		struct_ptr->data_ptr = (void *)NULL;
625	}
626
627malloc_element_ptr:
628	syslog(LOG_DEBUG,"malloc failed\n");
629
630
631}
632/*****************************************************************************/
633static webdav_parse_multistatus_element_t *
634create_multistatus_element(void)
635{
636	webdav_parse_multistatus_element_t *element_ptr;
637
638	element_ptr = malloc(sizeof(webdav_parse_multistatus_element_t));
639	if (!element_ptr)
640		return (NULL);
641
642	bzero(element_ptr, sizeof(webdav_parse_multistatus_element_t));
643	element_ptr->statusCode = WEBDAV_MULTISTATUS_INVALID_STATUS;
644	element_ptr->name_len = 0;
645	element_ptr->name[0] = 0;
646	element_ptr->seen_href = FALSE;
647	element_ptr->seen_response_end = FALSE;
648	element_ptr->next = NULL;
649	return (element_ptr);
650}
651/*****************************************************************************/
652
653static void parser_file_count_create(void *ctx,
654									 const xmlChar *localname,
655									 const xmlChar *prefix,
656									 const xmlChar *URI,
657									 int nb_namespaces,
658									 const xmlChar **namespaces,
659									 int nb_attributes,
660									 int nb_defaulted,
661									 const xmlChar **attributes)
662{
663	#pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes)
664	CFStringRef nodeString;
665	nodeString =  CFStringCreateWithCString (kCFAllocatorDefault,
666											 (const char *)localname,
667											 kCFStringEncodingUTF8
668											 );
669	if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
670	{
671		++(*((int *)ctx));
672	}
673	if(nodeString)
674		CFRelease(nodeString);
675}
676/*****************************************************************************/
677
678static void parser_stat_create(void *ctx,
679							   const xmlChar *localname,
680							   const xmlChar *prefix,
681							   const xmlChar *URI,
682							   int nb_namespaces,
683							   const xmlChar **namespaces,
684							   int nb_attributes,
685							   int nb_defaulted,
686							   const xmlChar **attributes)
687{
688	#pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes)
689	struct webdav_stat_attr* text_ptr = (struct webdav_stat_attr*)ctx;
690	text_ptr->data = (void *)WEBDAV_STATFS_IGNORE;
691	text_ptr->start = true;
692	CFStringRef nodeString;
693	nodeString =  CFStringCreateWithCString (kCFAllocatorDefault,
694											 (const char *)localname,
695											 kCFStringEncodingUTF8
696											 );
697	/* See if this is a type we are interested in  If it is, we'll return
698	 the appropriate constant */
699
700	if (((CFStringCompare(nodeString, CFSTR("getcontentlength"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
701	{
702		text_ptr->data = (void *)WEBDAV_STAT_LENGTH;
703	}
704	else
705	{
706		if (((CFStringCompare(nodeString, CFSTR("getlastmodified"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
707		{
708			text_ptr->data = (void *)WEBDAV_STAT_MODDATE;
709		}
710		else if (((CFStringCompare(nodeString, CFSTR("creationdate"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
711		{
712			text_ptr->data = (void *)WEBDAV_STAT_CREATEDATE;
713		}
714		else
715		{
716			if (((CFStringCompare(nodeString, CFSTR("collection"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
717			{
718				/* It's a collection so set the type as VDIR */
719				((struct stat *)ctx)->st_mode = S_IFDIR;
720			}	/* end if collection */
721		}	/* end of if-else mod date */
722	}	/* end if-else length*/
723	if(nodeString)
724		CFRelease(nodeString);
725}
726/*****************************************************************************/
727
728static void parser_statfs_create(void *ctx,
729								 const xmlChar *localname,
730								 const xmlChar *prefix,
731								 const xmlChar *URI,
732								 int nb_namespaces,
733								 const xmlChar **namespaces,
734								 int nb_attributes,
735								 int nb_defaulted,
736								 const xmlChar **attributes)
737{
738	#pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes)
739	struct webdav_quotas* text_ptr = (struct webdav_quotas*)ctx;
740	text_ptr->data = (void *)WEBDAV_STATFS_IGNORE;
741	text_ptr->start = true;
742	CFStringRef nodeString;
743	nodeString =  CFStringCreateWithCString (kCFAllocatorDefault,
744											 (const char *)localname,
745											 kCFStringEncodingUTF8
746											 );
747	/* See if this is a type we are interested in  If it is, we'll return
748	 the appropriate constant */
749
750	/* handle the "quota-available-bytes" and "quota-used-bytes" properties in the "DAV:" namespace */
751	if (((CFStringCompare(nodeString, CFSTR("quota-available-bytes"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
752	{
753		text_ptr->data = (void *)WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES;
754	}
755	else if (((CFStringCompare(nodeString, CFSTR("quota-used-bytes"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
756	{
757		text_ptr->data = (void *)WEBDAV_STATFS_QUOTA_USED_BYTES;
758	}
759	/* handle the deprecated "quota" and "quotaused" properties in the "DAV:" namespace */
760	else if (((CFStringCompare(nodeString, CFSTR("quota"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
761	{
762		text_ptr->data = (void *)WEBDAV_STATFS_QUOTA;
763	}
764	else if (((CFStringCompare(nodeString, CFSTR("quotaused"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
765	{
766		text_ptr->data = (void *)WEBDAV_STATFS_QUOTAUSED;
767	}
768
769	if(nodeString)
770		CFRelease(nodeString);
771}
772
773/*****************************************************************************/
774
775static void parser_lock_create(void *ctx,
776							   const xmlChar *localname,
777							   const xmlChar *prefix,
778							   const xmlChar *URI,
779							   int nb_namespaces,
780							   const xmlChar **namespaces,
781							   int nb_attributes,
782							   int nb_defaulted,
783							   const xmlChar **attributes)
784{
785	#pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes)
786	webdav_parse_lock_struct_t *lock_struct = (webdav_parse_lock_struct_t *)ctx;
787	CFStringRef nodeString = NULL;
788	nodeString =  CFStringCreateWithCString (kCFAllocatorDefault,
789											 (const char *)localname,
790											 kCFStringEncodingUTF8
791											 );
792	/* See if this is a type we are interested in  If it is, we'll return
793	 the appropriate constant */
794	if (((CFStringCompare(nodeString, CFSTR("locktoken"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
795	{
796		lock_struct->context = WEBDAV_LOCK_TOKEN;
797	}
798	else
799	{
800		if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
801		{
802			if (lock_struct->context == WEBDAV_LOCK_TOKEN)
803			{
804				lock_struct->context = WEBDAV_LOCK_HREF;
805			}
806			else
807			{
808				lock_struct->context = 0;
809			}
810		}
811	}	/* end if-else locktoken*/
812	if(nodeString)
813		CFRelease(nodeString);
814}
815/*****************************************************************************/
816static void parser_cachevalidators_create(void *ctx,
817										  const xmlChar *localname,
818										  const xmlChar *prefix,
819										  const xmlChar *URI,
820										  int nb_namespaces,
821										  const xmlChar **namespaces,
822										  int nb_attributes,
823										  int nb_defaulted,
824										  const xmlChar **attributes)
825{
826	#pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes)
827	struct webdav_parse_cachevalidators_struct* text_ptr = (struct webdav_parse_cachevalidators_struct*)ctx;
828	CFStringRef nodeString = NULL;
829	text_ptr->start = true;
830	nodeString =  CFStringCreateWithCString (kCFAllocatorDefault,
831											 (const char *)localname,
832											 kCFStringEncodingUTF8
833											 );
834	/* See if this is a type we are interested in  If it is, we'll return
835	 the appropriate constant */
836	if (((CFStringCompare(nodeString, CFSTR("getlastmodified"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
837	{
838		text_ptr->data = (void *)WEBDAV_CACHEVALIDATORS_MODDATE;
839	}
840	else if (((CFStringCompare(nodeString, CFSTR("getetag"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
841	{
842		text_ptr->data = (void *)WEBDAV_CACHEVALIDATORS_ETAG;
843	}
844	if(nodeString)
845		CFRelease(nodeString);
846}
847
848/*****************************************************************************/
849
850static void parser_multistatus_create(void *ctx,
851									  const xmlChar *localname,
852									  const xmlChar *prefix,
853									  const xmlChar *URI,
854									  int nb_namespaces,
855									  const xmlChar **namespaces,
856									  int nb_attributes,
857									  int nb_defaulted,
858									  const xmlChar **attributes)
859{
860	#pragma unused(prefix,URI,nb_namespaces,namespaces,nb_attributes,nb_defaulted,attributes)
861	webdav_parse_multistatus_element_t * element_ptr = NULL;
862	webdav_parse_multistatus_element_t * list_ptr = NULL;
863	webdav_parse_multistatus_list_t * struct_ptr = (webdav_parse_multistatus_list_t *)ctx;
864	CFStringRef nodeString = NULL;
865	struct_ptr->start = true;
866	nodeString =  CFStringCreateWithCString (kCFAllocatorDefault,
867											 (const char *)localname,
868											 kCFStringEncodingUTF8
869											 );
870	/* See if this is the resource type.  If it is, malloc a webdav_parse_opendir_element_t element and
871	 add it to the list. */
872	if (((CFStringCompare(nodeString, CFSTR("href"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
873	{
874		element_ptr = struct_ptr->tail;
875
876		if ( (element_ptr != NULL) && (element_ptr->seen_href == FALSE))
877		{
878			// <rdar://problem/4173444>
879			// This is a placeholder (<D:propstat> & children came before the <D:href>)
880			element_ptr->seen_href = TRUE;
881			struct_ptr->id = WEBDAV_MULTISTATUS_ELEMENT;
882			struct_ptr->data_ptr = (void *)element_ptr;
883		}
884		else
885		{
886			// Create the new href element
887			element_ptr = create_multistatus_element();
888			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
889
890			element_ptr->seen_href = TRUE;
891
892			if (struct_ptr->head == NULL)
893			{
894				struct_ptr->head = element_ptr;
895			}
896			else
897			{
898				list_ptr = struct_ptr->tail;
899				list_ptr->next = element_ptr;
900			}
901			struct_ptr->tail = element_ptr;
902
903			struct_ptr->id = WEBDAV_MULTISTATUS_ELEMENT;
904			struct_ptr->data_ptr = (void *)element_ptr;
905		}
906	}	/* end if href */
907	else if (((CFStringCompare(nodeString, CFSTR("status"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
908	{
909		/* If we have status then mark up the element pointer so that the
910		 * add callback will know to parse the upcoming text size and store it.
911		 */
912		element_ptr = struct_ptr->tail;
913
914		// ignore the last <D:href> if we've already seen <D:/response>
915		if ( (element_ptr != NULL) && (element_ptr->seen_response_end == TRUE))
916			element_ptr = NULL;
917
918		if (element_ptr == NULL)
919		{
920			// <rdar://problem/4173444> WebDAV filesystem bug parsing PROPFIND payload
921			//
922			// The <D:href> element might appear after the <D:propstat>. To handle this
923			// case we simply create a placeholder opendir element.
924			element_ptr = create_multistatus_element();
925			require_action(element_ptr != NULL, malloc_element_ptr, struct_ptr->error = ENOMEM);
926
927			if (struct_ptr->head == NULL)
928			{
929				struct_ptr->head = element_ptr;
930			}
931			else
932			{
933				list_ptr = struct_ptr->tail;
934				list_ptr->next = element_ptr;
935			}
936			struct_ptr->tail = element_ptr;
937		}
938
939		struct_ptr->id = WEBDAV_MULTISTATUS_STATUS;
940		struct_ptr->data_ptr = (void *)element_ptr;
941	}	/* end if length */
942	else if (((CFStringCompare(nodeString, CFSTR("response"),kCFCompareCaseInsensitive)) == kCFCompareEqualTo))
943	{
944		struct_ptr->id = WEBDAV_MULTISTATUS_RESPONSE;
945		struct_ptr->data_ptr = (void *)NULL;
946	}
947	else {
948		struct_ptr->id = WEBDAV_MULTISTATUS_IGNORE;
949		struct_ptr->data_ptr = (void *)NULL;
950
951	}
952
953malloc_element_ptr:
954	syslog(LOG_INFO,"malloc failed\n");
955
956	if(nodeString)
957		CFRelease(nodeString);
958}
959/*****************************************************************************/
960void parser_opendir_add(void *ctx, const xmlChar *localname, int length)
961{
962
963	webdav_parse_opendir_element_t * element_ptr;
964	webdav_parse_opendir_text_t * text_ptr = NULL;
965	webdav_parse_opendir_struct_t * parent_ptr = (webdav_parse_opendir_struct_t *)ctx;
966	char * ampPointer = NULL;
967	char* str_ptr = NULL;
968	char *ep;
969
970	text_ptr = malloc(sizeof(webdav_parse_opendir_text_t));
971	bzero(text_ptr,sizeof(webdav_parse_opendir_text_t));
972	text_ptr->size = (CFIndex)length;
973	memcpy(text_ptr->name,localname,length);
974	/* If the parent is one of our returned directory elements, and if this is a
975	 * text element, than copy the text into the name buffer provided we have room */
976	if(parent_ptr->start == true)
977	{
978		switch (parent_ptr->id)
979		{
980			case WEBDAV_OPENDIR_ELEMENT:
981				element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
982				/* TO Handle a special case for ampersand */
983				ampPointer = strstr((const char*) localname,"amp;");
984				if(ampPointer) {
985					char * literalPtr = strchr((const char*) localname,'<');
986					int totalLength = (int)(literalPtr - (char*)localname);
987					if(totalLength >= (length+5)) {
988						str_ptr = (char*)malloc(totalLength+1);
989						memset(str_ptr,0,totalLength+1);
990						memcpy(str_ptr,localname,totalLength);
991						ampPointer = strstr((const char*) str_ptr,"amp;");
992						/* To handle multiple ampersands*/
993						while(ampPointer) {
994							ampPointer+=4;
995							memcpy(&text_ptr->name[length],"&",1);
996							memcpy(&text_ptr->name[length+1],ampPointer,totalLength-length-4-1);
997							text_ptr->size = totalLength-4;
998							ampPointer = strstr((const char*)text_ptr->name,"amp;");
999							length++;
1000						}
1001						free(str_ptr);
1002					}
1003				}
1004				/* make sure the complete name will fit in the structure */
1005				if ((element_ptr->dir_data.d_name_URI_length + text_ptr->size) <=
1006					((unsigned int)sizeof(element_ptr->dir_data.d_name) - 1))
1007				{
1008					bcopy(text_ptr->name,
1009						  &element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length],
1010						  text_ptr->size);
1011					element_ptr->dir_data.d_name_URI_length += (uint32_t)text_ptr->size;
1012				}
1013				else
1014				{
1015					debug_string("URI too long");
1016					parent_ptr->error = ENAMETOOLONG;
1017				}
1018				break;
1019
1020			case WEBDAV_OPENDIR_ELEMENT_LENGTH:
1021				element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
1022				element_ptr->statsize = strtoq((const char *)text_ptr->name, &ep, 10);
1023				break;
1024
1025			case WEBDAV_OPENDIR_ELEMENT_MODDATE:
1026				element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
1027				element_ptr->stattime.tv_sec = DateBytesToTime(text_ptr->name, strlen((const char *)text_ptr->name));
1028				if (element_ptr->stattime.tv_sec == -1)
1029				{
1030					element_ptr->stattime.tv_sec = 0;
1031				}
1032				element_ptr->stattime.tv_nsec = 0;
1033				break;
1034
1035			case WEBDAV_OPENDIR_ELEMENT_CREATEDATE:
1036				// First try ISO8601
1037				element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
1038				element_ptr->createtime.tv_sec = ISO8601ToTime(text_ptr->name, strlen((const char *)text_ptr->name));
1039
1040				if (element_ptr->createtime.tv_sec == -1) {
1041					// Try RFC 850, RFC 1123
1042					element_ptr->createtime.tv_sec = DateBytesToTime(text_ptr->name, strlen((const char *)text_ptr->name));
1043
1044				}
1045
1046				if (element_ptr->createtime.tv_sec == -1)
1047				{
1048					element_ptr->createtime.tv_sec = 0;
1049				}
1050				element_ptr->createtime.tv_nsec = 0;
1051				break;
1052
1053			case WEBDAV_OPENDIR_APPLEDOUBLEHEADER:
1054			{
1055				size_t	len = APPLEDOUBLEHEADER_LENGTH;
1056
1057				element_ptr = (webdav_parse_opendir_element_t *)parent_ptr->data_ptr;
1058				from_base64((const char *)text_ptr->name, (unsigned char *)element_ptr->appledoubleheader, &len);
1059				if (len == APPLEDOUBLEHEADER_LENGTH)
1060				{
1061					element_ptr->appledoubleheadervalid = TRUE;
1062				}
1063			}
1064				break;
1065
1066			default:
1067				break;
1068		}	/* end of switch statement */
1069		parent_ptr->start = false;
1070	}/* end of if it is our text element */
1071	free(text_ptr);
1072}
1073
1074/*****************************************************************************/
1075
1076static void parser_lock_add(void *ctx, const xmlChar *localname, int length)
1077{
1078	webdav_parse_lock_struct_t *lock_struct = (webdav_parse_lock_struct_t *)ctx;
1079	UInt8 *text_ptr = malloc(length);
1080	bzero(text_ptr,length);
1081	memcpy(text_ptr,localname,length);
1082	UInt8* ch = NULL;
1083
1084	if (lock_struct->context == WEBDAV_LOCK_HREF)
1085	{
1086		lock_struct->context = 0; /* clear the context so the next time through it's not in WEBDAV_LOCK_HREF */
1087
1088		/* Since the context is set to WEBDAV_LOCK_HREF, we have
1089		 * found the token and we have found the href indicating
1090		 * that the locktoken is coming so squirrel it away.
1091		 */
1092
1093		/* null terminate the string */
1094		text_ptr[length] = '\0';
1095
1096		// Trim trailing whitespace
1097		ch = &text_ptr[length-1];
1098		while (ch > text_ptr) {
1099			if (isspace(*ch))
1100				*ch-- = '\0';
1101			else
1102				break;
1103		}
1104
1105		lock_struct->locktoken = (char *)text_ptr;
1106
1107	}
1108}
1109/*****************************************************************************/
1110
1111static void parser_stat_add(void *ctx, const xmlChar *localname, int length)
1112{
1113	UInt8 *text_ptr = (UInt8*) malloc(length);
1114	bzero(text_ptr,length);
1115	memcpy(text_ptr,localname,length);
1116	struct webdav_stat_attr *statbuf = (struct webdav_stat_attr *)ctx;
1117	struct webdav_stat_attr*parent = (struct webdav_stat_attr*)ctx;
1118	char *ep;
1119	/*
1120	 * If the context reflects one of our properties then the localname must
1121	 * be the text pointer with the data we want
1122	 */
1123	if(parent->start==true)
1124	{
1125		switch ((uintptr_t)parent->data)
1126		{
1127			case WEBDAV_STAT_LENGTH:
1128				/* the text pointer is the length in bytes so put it in the stat buffer */
1129				if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE))
1130				{
1131					statbuf->attr_stat.st_size = strtoq((const char *)text_ptr, &ep, 10);
1132				}
1133				else
1134				{
1135					/* if we got a string set to NULL we could not fit the value in our buffer, return invalid value */
1136					statbuf->attr_stat.st_size = -1LL;
1137				}
1138				break;
1139
1140			case WEBDAV_STAT_MODDATE:
1141				/* the text pointer is the date so translate it and put it into the stat buffer */
1142
1143				if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE))
1144				{
1145					statbuf->attr_stat.st_mtimespec.tv_sec = DateBytesToTime(text_ptr, length);
1146					if (statbuf->attr_stat.st_mtimespec.tv_sec == -1)
1147					{
1148						statbuf->attr_stat.st_mtimespec.tv_sec = 0;
1149					}
1150					statbuf->attr_stat.st_mtimespec.tv_nsec = 0;
1151				}
1152				else
1153				{
1154					/* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1155				}
1156				break;
1157
1158			case WEBDAV_STAT_CREATEDATE:
1159				/* the text pointer is the date so translate it and put it into the stat buffer */
1160
1161				if (text_ptr && (text_ptr != (UInt8 *)WEBDAV_STAT_IGNORE))
1162				{
1163					// First try ISO8601
1164					statbuf->attr_create_time.tv_sec = ISO8601ToTime(text_ptr, length);
1165
1166					if (statbuf->attr_create_time.tv_sec == -1) {
1167						// Try RFC 850, RFC 1123
1168						statbuf->attr_create_time.tv_sec = DateBytesToTime(text_ptr, length);
1169					}
1170
1171					if (statbuf->attr_create_time.tv_sec == -1)
1172					{
1173						statbuf->attr_create_time.tv_sec = 0;
1174					}
1175					statbuf->attr_create_time.tv_nsec = 0;
1176				}
1177				else
1178				{
1179					/* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1180				}
1181				break;
1182
1183			default:
1184				break;
1185		}
1186		parent->start = false;
1187	}
1188	free(text_ptr);
1189}
1190
1191/*****************************************************************************/
1192
1193static void parser_statfs_add(void *ctx, const xmlChar *localname, int length)
1194{
1195	char *text_ptr = (char*) malloc(length);
1196	bzero(text_ptr,length);
1197	memcpy(text_ptr,(char*)localname,length);
1198
1199	struct webdav_quotas *quotas = (struct webdav_quotas *)ctx;
1200	struct webdav_quotas* parent = (struct webdav_quotas*)ctx;
1201	char *ep;
1202	/*
1203	 * If the parent reflects one of our properties than the localname must
1204	 * be the text pointer with the data we want
1205	 */
1206	if(parent->start == true)
1207	{
1208		switch ((uintptr_t)parent->data)
1209		{
1210			case WEBDAV_STATFS_QUOTA_AVAILABLE_BYTES:
1211				/* the text pointer is the data so put it in the quotas buffer */
1212				if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
1213				{
1214					quotas->quota_available_bytes = strtouq(text_ptr, &ep, 10);
1215					quotas->use_bytes_values = TRUE;
1216				}
1217				else
1218				{
1219					/* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1220				}
1221				break;
1222
1223			case WEBDAV_STATFS_QUOTA_USED_BYTES:
1224				/* the text pointer is the data so put it in the quotas buffer */
1225				if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
1226				{
1227					quotas->quota_used_bytes = strtouq(text_ptr, &ep, 10);
1228				}
1229				else
1230				{
1231					/* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1232				}
1233				break;
1234
1235			case WEBDAV_STATFS_QUOTA:
1236				/* the text pointer is the data so put it in the quotas buffer */
1237				if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
1238				{
1239					quotas->quota = strtouq(text_ptr, &ep, 10);
1240				}
1241				else
1242				{
1243					/* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1244				}
1245				break;
1246
1247			case WEBDAV_STATFS_QUOTAUSED:
1248				/* the text pointer is the data so put it in the quotas buffer */
1249				if (text_ptr && (text_ptr != (char *)WEBDAV_STATFS_IGNORE))
1250				{
1251					quotas->quotaused = strtouq(text_ptr, &ep, 10);
1252				}
1253				else
1254				{
1255					/* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1256				}
1257				break;
1258
1259			default:
1260				break;
1261		}
1262		parent->start = false;
1263	}
1264	free(text_ptr);
1265}
1266
1267/*****************************************************************************/
1268
1269static void parser_cachevalidators_add(void *ctx, const xmlChar *localname, int length)
1270{
1271	struct webdav_parse_cachevalidators_struct *cachevalidators_struct = (struct webdav_parse_cachevalidators_struct *)ctx;
1272
1273	/*
1274	 * If the parent reflects one of our properties than the child must
1275	 * be the text pointer with the data we want
1276	 */if(cachevalidators_struct->start == true)
1277	 {
1278		 switch ((uintptr_t)cachevalidators_struct->data)
1279		 {
1280			 case WEBDAV_CACHEVALIDATORS_MODDATE:
1281				 /* the text pointer is the date so translate it and get it into last_modified */
1282				 if (localname && (localname != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE))
1283				 {
1284					 cachevalidators_struct->last_modified = DateBytesToTime(localname, length);
1285				 }
1286				 else
1287				 {
1288					 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1289				 }
1290				 break;
1291
1292			 case WEBDAV_CACHEVALIDATORS_ETAG:
1293				 /* the text pointer is the etag so copy it into last_modified */
1294				 if (localname && (localname != (UInt8 *)WEBDAV_CACHEVALIDATORS_IGNORE))
1295				 {
1296					 size_t	len = length + 1;
1297
1298					 cachevalidators_struct->entity_tag = malloc(len);
1299					 if ( cachevalidators_struct->entity_tag != NULL )
1300					 {
1301						 strlcpy(cachevalidators_struct->entity_tag, (const char *)localname, len);
1302					 }
1303				 }
1304				 else
1305				 {
1306					 /* if we got a string set to NULL we could not fit the value in our buffer, do nothing */
1307				 }
1308				 break;
1309
1310			 default:
1311				 break;
1312		 }
1313		 cachevalidators_struct->start = false;
1314	 }
1315
1316}
1317
1318/*****************************************************************************/
1319
1320static void parser_multistatus_add(void *ctx, const xmlChar *localname, int length)
1321{
1322	webdav_parse_multistatus_element_t * element_ptr;
1323	webdav_parse_multistatus_list_t * parent_ptr = (webdav_parse_multistatus_list_t *)ctx;
1324	webdav_parse_multistatus_text_t * text_ptr = NULL;
1325	webdav_parse_multistatus_list_t * struct_ptr = (webdav_parse_multistatus_list_t *)ctx;
1326	char *ep, *ch, *endPtr;
1327	int errnum;
1328	/* If the parent is one of our returned directory elements, and if this is a
1329	 * text element, than copy the text into the name buffer provided we have room */
1330	text_ptr = malloc(sizeof(webdav_parse_multistatus_text_t));
1331	bzero(text_ptr,sizeof(webdav_parse_multistatus_text_t));
1332	text_ptr->size = (CFIndex)length;
1333	memcpy(text_ptr->name,localname,length);
1334
1335	if(parent_ptr->start == true)
1336	{
1337		switch (parent_ptr->id)
1338		{
1339			case WEBDAV_MULTISTATUS_ELEMENT:
1340				element_ptr = (webdav_parse_multistatus_element_t *)parent_ptr->data_ptr;
1341
1342				/* make sure the complete name will fit in the structure */
1343				if ( (text_ptr->size) < (CFIndex)(sizeof(element_ptr->name)))
1344				{
1345					bcopy(text_ptr->name, &element_ptr->name, text_ptr->size);
1346					element_ptr->name_len += (uint32_t)text_ptr->size;
1347					element_ptr->name[element_ptr->name_len] = 0;
1348				}
1349				else
1350				{
1351					debug_string("URI too long");
1352					struct_ptr->error = ENAMETOOLONG;
1353				}
1354				break;
1355
1356			case WEBDAV_MULTISTATUS_STATUS:
1357				/* the text pointer is status code so put it in the element statusCode field */
1358				element_ptr = (webdav_parse_multistatus_element_t *)parent_ptr->data_ptr;
1359				ch = (char *)text_ptr->name;
1360				endPtr = ch + text_ptr->size;
1361
1362				// Find the 'H' of "HTTP/1.1 XXX Description"
1363				while (ch < endPtr) {
1364					if (*ch == 'H')
1365						break;
1366					ch++;
1367				}
1368
1369				// Did we overshoot to the end?
1370				if (ch >= endPtr) {
1371					debug_string("Cannot parse Status line, HTTP string not found");
1372					struct_ptr->error = EIO;
1373					break;
1374				}
1375
1376				// Now find the space inbetween "HTTP/1.1" and the numeric status code
1377				while (ch < endPtr) {
1378					if (*ch == ' ') {
1379						ch++;
1380						break;
1381					}
1382					ch++;
1383				}
1384
1385				// Did we overshoot to the end?
1386				if (ch >= endPtr) {
1387					debug_string("Cannot parse Status line, numeric status code missing");
1388					struct_ptr->error = EIO;
1389					break;
1390				}
1391
1392				// Now convert status code string to a ulong
1393				errno = 0;
1394				element_ptr->statusCode = (UInt32)strtoul((const char *)ch, &ep, 10);
1395				errnum = errno;
1396
1397				// See if the conversion was successful
1398				if ((errnum != 0) || (ep == ch)) {
1399					// nothing was converted
1400					debug_string("Cannot parse Status line, could not convert numeric status code");
1401					struct_ptr->error = EIO;
1402				}
1403				break;
1404
1405			default:
1406				break;
1407		}	/* end of switch statement */
1408		parent_ptr->start = false;
1409	}/* end of if it is our text element */
1410	free(text_ptr);
1411}
1412
1413/*****************************************************************************/
1414
1415/*
1416 * GetNormalizedPathLength returns the length of the absolute path (with percent
1417 * escapes removed) portion of a CFURLRef.
1418 */
1419static CFIndex GetNormalizedPathLength(CFURLRef anURL)
1420{
1421	CFURLRef absoluteURL;
1422	CFStringRef escapedPath;
1423	CFStringRef unescapedPath;
1424	CFIndex result;
1425
1426	result = 0;
1427	absoluteURL = CFURLCopyAbsoluteURL(anURL);
1428	require(absoluteURL != NULL, CFURLCopyAbsoluteURL);
1429
1430	escapedPath = CFURLCopyPath(absoluteURL);
1431	require(escapedPath != NULL, CFURLCopyPath);
1432
1433	unescapedPath = CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, escapedPath, CFSTR(""));
1434	require_string(unescapedPath != NULL, CFURLCreateStringByReplacingPercentEscapes, "name was not legal UTF8");
1435
1436	result = CFStringGetLength(unescapedPath);
1437
1438	CFRelease(unescapedPath);
1439
1440CFURLCreateStringByReplacingPercentEscapes:
1441
1442	CFRelease(escapedPath);
1443
1444CFURLCopyPath:
1445
1446	CFRelease(absoluteURL);
1447
1448CFURLCopyAbsoluteURL:
1449
1450	return ( result );
1451}
1452
1453/*****************************************************************************/
1454
1455/*
1456 * GetComponentName determines if the URI combined with the parent URL is a
1457 * child of the parent or is the parent itself, and if it is a child, extracts
1458 * the child's component name.
1459 *
1460 * GetComponentName returns TRUE if the URI is for a child, or returns FALSE if
1461 * the URI is the parent itself. The componentName buffer will contain the child's
1462 * component name if the result is TRUE.
1463 *
1464 * The parentPathLength parameter allows this routine to determine child/parent
1465 * status without comparing the path strings.
1466 */
1467static Boolean GetComponentName(	/* <- TRUE if http URI was not parent and component name was returned */
1468								CFURLRef urlRef,				/* -> the parent directory's URL  */
1469								CFIndex parentPathLength,		/* -> the parent directory's percent decoded path length */
1470								char *uri,						/* -> the http URI from the WebDAV server */
1471								char *componentName)			/* <-> point to buffer of MAXNAMLEN + 1 bytes where URI's LastPathComponent is returned if result it TRUE */
1472{
1473	Boolean result;
1474	CFStringRef uriString;				/* URI as CFString */
1475	CFURLRef uriURL;					/* URI converted to full URL */
1476	CFStringRef uriName;				/* URI's LastPathComponent as CFString */
1477
1478	result = FALSE;
1479
1480	/* create a CFString from the c-string containing URI */
1481	uriString = CFStringCreateWithCString(kCFAllocatorDefault, uri, kCFStringEncodingUTF8);
1482	require(uriString != NULL, CFStringCreateWithCString);
1483
1484	/* create a CFURL from the URI CFString and the parent URL */
1485	uriURL = CFURLCreateWithString(kCFAllocatorDefault, uriString, urlRef);
1486	if (uriURL != NULL) {
1487		/* CFURLCreateWithString worked fine, so release uriString and keep going */
1488		CFRelease(uriString);
1489	}
1490	else {
1491		/* Fix for Windows servers.  They tend to send over names that have a space in them instead of the correct %20.  This causes
1492		 CFURLCreateWithString to fail.  Since it might be a partially escaped URL string, try to unescape the string, then re-escape it */
1493		CFStringRef unEscapedString;		/* URI as CFString with un-escaped chars */
1494		CFStringRef reEscapedString;		/* URI as CFString with re-escaped chars */
1495
1496		unEscapedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding (kCFAllocatorDefault, uriString, NULL, kCFStringEncodingUTF8);
1497		CFRelease(uriString);
1498		require(unEscapedString != NULL, CFURLCreateWithString);
1499
1500		reEscapedString = CFURLCreateStringByAddingPercentEscapes (kCFAllocatorDefault, unEscapedString, NULL, NULL, kCFStringEncodingUTF8);
1501		CFRelease(unEscapedString);
1502		require(reEscapedString != NULL, CFURLCreateWithString);
1503
1504		/* try again to create a CFURL from the reEscapedString and the parent URL */
1505		uriURL = CFURLCreateWithString(kCFAllocatorDefault, reEscapedString, urlRef);
1506		CFRelease(reEscapedString);
1507		require(uriURL != NULL, CFURLCreateWithString);
1508	}
1509
1510	/* see if this is the parent or a child */
1511	if ( GetNormalizedPathLength(uriURL) > parentPathLength ) {
1512		/* this is a child */
1513
1514		/* get the child's name */
1515		uriName = CFURLCopyLastPathComponent(uriURL);
1516		require_string(uriName != NULL, CFURLCopyLastPathComponent, "name was not legal UTF8");
1517
1518		if ( CFStringGetCString(uriName, componentName, MAXNAMLEN + 1, kCFStringEncodingUTF8) ) {
1519			/* we have the child name */
1520			result = TRUE;
1521		}
1522		else {
1523			debug_string("could not get child name (too long?)");
1524		}
1525		CFRelease(uriName);
1526	}
1527	else {
1528		/* this is the parent, skip it */
1529	}
1530
1531CFURLCopyLastPathComponent:
1532
1533	CFRelease(uriURL);
1534
1535CFURLCreateWithString:
1536CFStringCreateWithCString:
1537
1538	return ( result );
1539}
1540
1541/*****************************************************************************/
1542
1543int parse_opendir(UInt8 *xmlp,					/* -> xml data returned by PROPFIND with depth of 1 */
1544				  CFIndex xmlp_len,				/* -> length of xml data */
1545				  CFURLRef urlRef,				/* -> the CFURL to the parent directory */
1546				  uid_t uid,						/* -> uid of the user making the request */
1547				  struct node_entry *parent_node)	/* -> pointer to the parent directory's node_entry */
1548{
1549	int error = 0;
1550	ssize_t size = 0;
1551	struct webdav_dirent dir_data[2];
1552	CFIndex parentPathLength;
1553	webdav_parse_opendir_struct_t opendir_struct;
1554	webdav_parse_opendir_element_t *element_ptr, *prev_element_ptr;
1555	opendir_struct.head = opendir_struct.tail = NULL;
1556	opendir_struct.error = 0;
1557
1558	xmlSAXHandler sh;
1559    memset(&sh,0,sizeof(sh));
1560    sh.startElementNs = parser_opendir_create;
1561    sh.characters = parser_opendir_add;
1562	sh.endElementNs = parser_opendir_end;
1563    sh.initialized = XML_SAX2_MAGIC;
1564
1565	/* truncate the file, and reset the file pointer to 0 */
1566	require(ftruncate(parent_node->file_fd, 0) == 0, ftruncate);
1567	require(lseek(parent_node->file_fd, 0, SEEK_SET) == 0, lseek);
1568
1569	int result = xmlSAXUserParseMemory( &sh,&opendir_struct,(char*)xmlp,(int)xmlp_len);
1570	require(result == 0, ParserCreate);
1571	/* parse the XML -- exit now if error during parse */
1572
1573	/* if the directory is not deleted, write "." and ".."  */
1574	if ( !NODE_IS_DELETED(parent_node) )
1575	{
1576		bzero(dir_data, sizeof(dir_data));
1577
1578		dir_data[0].d_ino = parent_node->fileid;
1579		dir_data[0].d_reclen = sizeof(struct webdav_dirent);
1580		dir_data[0].d_type = DT_DIR;
1581		dir_data[0].d_namlen = 1;
1582		dir_data[0].d_name[0] = '.';
1583
1584		dir_data[1].d_ino =
1585		(dir_data[0].d_ino == WEBDAV_ROOTFILEID) ? WEBDAV_ROOTPARENTFILEID : parent_node->parent->fileid;
1586		dir_data[1].d_reclen = sizeof(struct webdav_dirent);
1587		dir_data[1].d_type = DT_DIR;
1588		dir_data[1].d_namlen = 2;
1589		dir_data[1].d_name[0] = '.';
1590		dir_data[1].d_name[1] = '.';
1591
1592		size = write(parent_node->file_fd, dir_data, sizeof(struct webdav_dirent) * 2);
1593		require(size == (sizeof(struct webdav_dirent) * 2), write_dot_dotdot);
1594	}
1595
1596	/*
1597	 * Important: the xml we get back from the server includes the info
1598	 * on the parent directory as well as all of its children.
1599	 *
1600	 * The elements returned by PROPFIND contain http URI. So, give a parent URL of
1601	 * http://host/parent/, the responses could be:
1602	 *		absolute URL:	http://host/parent/child
1603	 *		absolute path:	/parent/child
1604	 *		relative path:	child
1605	 * So, if all URLs are normalized to an absolute path with percent escapes
1606	 * removed, then the children will always be longer than the parent.
1607	 */
1608
1609	/* get the parent directory's path length */
1610	parentPathLength = GetNormalizedPathLength(urlRef);
1611
1612	/* invalidate any children nodes -- they'll be marked valid by nodecache_get_node */
1613	(void) nodecache_invalidate_directory_node_time(parent_node);
1614
1615	/* look at the list of elements */
1616	for (element_ptr = opendir_struct.head; element_ptr != NULL; element_ptr = element_ptr->next)
1617	{
1618		char namebuffer[MAXNAMLEN + 1];
1619		struct webdav_stat_attr statbuf;
1620
1621		// Skip any placeholder that never saw a matching <D:href> element
1622		if (element_ptr->seen_href == FALSE)
1623			continue;
1624
1625		/* make element_ptr->dir_data.d_name a cstring */
1626		element_ptr->dir_data.d_name[element_ptr->dir_data.d_name_URI_length] = '\0';
1627		//syslog(LOG_ERR,"element_ptr->dir_data.d_name is %s\n",element_ptr->dir_data.d_name);
1628		/* get the component name if this element is not the parent */
1629		if ( GetComponentName(urlRef, parentPathLength, element_ptr->dir_data.d_name, namebuffer) )
1630		{
1631			/* this is a child */
1632			struct node_entry *element_node;
1633			size_t name_len;
1634
1635			name_len = strlen(namebuffer);
1636			//syslog(LOG_ERR,"namebuffer is %s\n",namebuffer);
1637			/* get (or create) a cache node for this element */
1638			error = nodecache_get_node(parent_node, name_len, namebuffer, TRUE, FALSE,
1639									   element_ptr->dir_data.d_type == DT_DIR ? WEBDAV_DIR_TYPE : WEBDAV_FILE_TYPE, &element_node);
1640			if (error)
1641			{
1642				debug_string("nodecache_get_node failed");
1643				continue;
1644			}
1645			/* move just the element name over element_ptr->dir_data.d_name */
1646			bcopy(element_node->name, element_ptr->dir_data.d_name, element_node->name_length);
1647
1648			element_ptr->dir_data.d_name[element_node->name_length] = '\0';
1649			element_ptr->dir_data.d_namlen = element_node->name_length;
1650
1651			/* set the file number */
1652			element_ptr->dir_data.d_ino = element_node->fileid;
1653			//syslog(LOG_ERR,"element_node->fileid : %d\n",element_node->fileid);
1654			/*
1655			 * Prepare to cache this element's attributes, since it's
1656			 * highly likely a stat will follow reading the directory.
1657			 */
1658
1659			bzero(&statbuf, sizeof(struct webdav_stat_attr));
1660
1661			/* the first thing to do is fill in the fields we cannot get from the server. */
1662			statbuf.attr_stat.st_dev = 0;
1663			/* Why 1 for st_nlink?
1664			 * Getting the real link count for directories is expensive.
1665			 * Setting it to 1 lets FTS(3) (and other utilities that assume
1666			 * 1 means a file system doesn't support link counts) work.
1667			 */
1668			statbuf.attr_stat.st_nlink = 1;
1669			statbuf.attr_stat.st_uid = UNKNOWNUID;
1670			statbuf.attr_stat.st_gid = UNKNOWNUID;
1671			statbuf.attr_stat.st_rdev = 0;
1672			statbuf.attr_stat.st_blksize = WEBDAV_IOSIZE;
1673			statbuf.attr_stat.st_flags = 0;
1674			statbuf.attr_stat.st_gen = 0;
1675
1676			/* set all times to the last modified time since we cannot get the other times */
1677			statbuf.attr_stat.st_atimespec = statbuf.attr_stat.st_mtimespec = statbuf.attr_stat.st_ctimespec = element_ptr->stattime;
1678
1679			/* set create time if we have it */
1680			if (element_ptr->createtime.tv_sec)
1681				statbuf.attr_create_time = element_ptr->createtime;
1682			//syslog(LOG_ERR,"element_ptr->dir_data.d_type : %d\n",element_ptr->dir_data.d_type);
1683			if (element_ptr->dir_data.d_type == DT_DIR)
1684			{
1685				statbuf.attr_stat.st_mode = S_IFDIR | S_IRWXU;
1686				statbuf.attr_stat.st_size = WEBDAV_DIR_SIZE;
1687				/* appledoubleheadervalid is never valid for directories */
1688				element_ptr->appledoubleheadervalid = FALSE;
1689			}
1690			else
1691			{
1692				statbuf.attr_stat.st_mode = S_IFREG | S_IRWXU;
1693				statbuf.attr_stat.st_size = element_ptr->statsize;
1694				/* appledoubleheadervalid is valid for files only if the server
1695				 * returned the appledoubleheader property and file size is
1696				 * the size of the appledoubleheader (APPLEDOUBLEHEADER_LENGTH bytes).
1697				 */
1698				element_ptr->appledoubleheadervalid =
1699				(element_ptr->appledoubleheadervalid && (element_ptr->statsize == APPLEDOUBLEHEADER_LENGTH));
1700				//syslog(LOG_ERR, "element_ptr->appledoubleheadervalid %d",element_ptr->appledoubleheadervalid);
1701			}
1702
1703			/* calculate number of S_BLKSIZE blocks */
1704			statbuf.attr_stat.st_blocks = ((statbuf.attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
1705
1706			/* set the fileid in statbuf*/
1707			statbuf.attr_stat.st_ino = element_node->fileid;
1708
1709			/* Now cache the stat structure (ignoring errors) */
1710			(void) nodecache_add_attributes(element_node, uid, &statbuf,
1711											element_ptr->appledoubleheadervalid ? element_ptr->appledoubleheader : NULL);
1712
1713			/* Complete the task of getting the regular name into the dirent */
1714
1715			size = write(parent_node->file_fd, (void *)&element_ptr->dir_data, element_ptr->dir_data.d_reclen);
1716			require(size == element_ptr->dir_data.d_reclen, write_element);
1717		}
1718		else
1719		{
1720			struct node_entry *temp_node;
1721			/* it was the parent */
1722
1723			/* we are reading this directory, so mark it "recent" */
1724			(void) nodecache_get_node(parent_node, 0, NULL, TRUE, TRUE, WEBDAV_DIR_TYPE, &temp_node);
1725
1726			/*
1727			 * Prepare to cache this element's attributes, since it's
1728			 * highly likely a stat will follow reading the directory.
1729			 */
1730
1731			bzero(&statbuf, sizeof(struct webdav_stat_attr));
1732
1733			/* the first thing to do is fill in the fields we cannot get from the server. */
1734			statbuf.attr_stat.st_dev = 0;
1735			/* Why 1 for st_nlink?
1736			 * Getting the real link count for directories is expensive.
1737			 * Setting it to 1 lets FTS(3) (and other utilities that assume
1738			 * 1 means a file system doesn't support link counts) work.
1739			 */
1740			statbuf.attr_stat.st_nlink = 1;
1741			statbuf.attr_stat.st_uid = UNKNOWNUID;
1742			statbuf.attr_stat.st_gid = UNKNOWNUID;
1743			statbuf.attr_stat.st_rdev = 0;
1744			statbuf.attr_stat.st_blksize = WEBDAV_IOSIZE;
1745			statbuf.attr_stat.st_flags = 0;
1746			statbuf.attr_stat.st_gen = 0;
1747
1748			/* set all times to the last modified time since we cannot get the other times */
1749			statbuf.attr_stat.st_atimespec = statbuf.attr_stat.st_mtimespec = statbuf.attr_stat.st_ctimespec = element_ptr->stattime;
1750
1751			/* set create time if we have it */
1752			if (element_ptr->createtime.tv_sec)
1753				statbuf.attr_create_time = element_ptr->createtime;
1754
1755			statbuf.attr_stat.st_mode = S_IFDIR | S_IRWXU;
1756			statbuf.attr_stat.st_size = WEBDAV_DIR_SIZE;
1757
1758			/* calculate number of S_BLKSIZE blocks */
1759			statbuf.attr_stat.st_blocks = ((statbuf.attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
1760
1761			/* set the fileid in statbuf*/
1762			statbuf.attr_stat.st_ino = parent_node->fileid;
1763
1764			/* Now cache the stat structure (ignoring errors) */
1765			(void) nodecache_add_attributes(parent_node, uid, &statbuf, NULL);
1766		}
1767	}	/* for element_ptr */
1768
1769	/* delete any children nodes that are still invalid */
1770	(void) nodecache_delete_invalid_directory_nodes(parent_node);
1771
1772	/* free any elements allocated */
1773	element_ptr = opendir_struct.head;
1774	while (element_ptr)
1775	{
1776		prev_element_ptr = element_ptr;
1777		element_ptr = element_ptr->next;
1778		free(prev_element_ptr);
1779	}
1780
1781	return ( 0 );
1782
1783	/**********************/
1784
1785write_element:
1786	/* free any elements allocated */
1787	element_ptr = opendir_struct.head;
1788	while (element_ptr)
1789	{
1790		prev_element_ptr = element_ptr;
1791		element_ptr = element_ptr->next;
1792		free(prev_element_ptr);
1793	}
1794write_dot_dotdot:
1795	/* directory is in unknown condition - erase whatever is there */
1796	(void) ftruncate(parent_node->file_fd, 0);
1797ParserCreate:
1798lseek:
1799ftruncate:
1800	return ( EIO );
1801}
1802
1803/*****************************************************************************/
1804webdav_parse_multistatus_list_t *
1805parse_multi_status(
1806				   UInt8 *xmlp,					/* -> xml data returned by PROPFIND with depth of 1 */
1807				   CFIndex xmlp_len)				/* -> length of xml data */
1808{
1809	webdav_parse_multistatus_list_t *multistatus_list;
1810
1811	multistatus_list = malloc(sizeof(webdav_parse_multistatus_list_t));
1812	require(multistatus_list != NULL, malloc_list);
1813
1814	multistatus_list->error = 0;
1815	multistatus_list->head = NULL;
1816	multistatus_list->tail = NULL;
1817
1818	xmlSAXHandler sh;
1819    memset(&sh,0,sizeof(sh));
1820    sh.startElementNs = parser_multistatus_create;
1821    sh.characters = parser_multistatus_add;
1822	sh.endElementNs = parser_multistatus_end;
1823    sh.initialized = XML_SAX2_MAGIC;
1824
1825	int result = xmlSAXUserParseMemory( &sh,multistatus_list,(char*)xmlp,(int)xmlp_len);
1826	require(result == 0, ParserCreate);
1827
1828ParserCreate:
1829malloc_list:
1830	return (multistatus_list);
1831
1832	return ( 0 );
1833}
1834
1835
1836/*****************************************************************************/
1837
1838int parse_file_count(const UInt8 *xmlp, CFIndex xmlp_len, int *file_count)
1839{
1840	xmlSAXHandler sh;
1841    memset(&sh,0,sizeof(sh));
1842    sh.startElementNs = parser_file_count_create;
1843	sh.endElementNs = parser_file_count_end;
1844    sh.initialized = XML_SAX2_MAGIC;
1845	*file_count = 0;
1846
1847	xmlSAXUserParseMemory( &sh,file_count,(char*)xmlp,(int)xmlp_len);
1848
1849	return ( 0 );
1850}
1851
1852/*****************************************************************************/
1853
1854int parse_stat(const UInt8 *xmlp, CFIndex xmlp_len, struct webdav_stat_attr *statbuf)
1855{
1856	xmlSAXHandler sh;
1857    memset(&sh,0,sizeof(sh));
1858    sh.startElementNs = parser_stat_create;
1859	sh.endElementNs = parse_stat_end;
1860    sh.characters = parser_stat_add;
1861    sh.initialized = XML_SAX2_MAGIC;
1862	bzero((void *)statbuf, sizeof(struct webdav_stat_attr));
1863
1864	xmlSAXUserParseMemory( &sh,statbuf,(char*)xmlp,(int)xmlp_len);
1865	/* Coming back from the parser:
1866	 *   statbuf->attr_stat_info.attr_stat.st_mode will be 0 or will have S_IFDIR set if the object is a directory.
1867	 *   statbuf->attr_stat_info.attr_stat.st_mtimespec will be 0 or will have the last modified time.
1868	 *   statbuf->attr_stat_info.attr_create_time will be 0 or the file creation time
1869	 *   statbuf->attr_stat_info.attr_stat.st_size will be set to the resource size if the object is a file.
1870	 *
1871	 * So, the first thing to do is fill in the fields we cannot get from the server.
1872	 */
1873
1874	statbuf->attr_stat.st_dev = 0;
1875	/* Why 1 for st_nlink?
1876	 * Getting the real link count for directories is expensive.
1877	 * Setting it to 1 lets FTS(3) (and other utilities that assume
1878	 * 1 means a file system doesn't support link counts) work.
1879	 */
1880	statbuf->attr_stat.st_nlink = 1;
1881	statbuf->attr_stat.st_uid = UNKNOWNUID;
1882	statbuf->attr_stat.st_gid = UNKNOWNUID;
1883	statbuf->attr_stat.st_rdev = 0;
1884	statbuf->attr_stat.st_blksize = WEBDAV_IOSIZE;
1885	statbuf->attr_stat.st_flags = 0;
1886	statbuf->attr_stat.st_gen = 0;
1887
1888	/* set last accessed and changed times to last modified time since we cannot get them */
1889	statbuf->attr_stat.st_atimespec = statbuf->attr_stat.st_ctimespec = statbuf->attr_stat.st_mtimespec;
1890
1891	/* was this a directory? */
1892	if ( S_ISDIR(statbuf->attr_stat.st_mode) )
1893	{
1894		/* yes - add the directory access permissions */
1895		statbuf->attr_stat.st_mode |= S_IRWXU;
1896		/* fake up the directory size */
1897		statbuf->attr_stat.st_size = WEBDAV_DIR_SIZE;
1898	}
1899	else
1900	{
1901		/* no - mark it as a regular file and set the file access permissions
1902		 * (for now, everything is either a file or a directory)
1903		 */
1904		statbuf->attr_stat.st_mode = S_IFREG | S_IRWXU;
1905	}
1906
1907	/* calculate number of S_BLKSIZE blocks */
1908	statbuf->attr_stat.st_blocks = ((statbuf->attr_stat.st_size + S_BLKSIZE - 1) / S_BLKSIZE);
1909
1910	return ( 0 );
1911}
1912
1913/*****************************************************************************/
1914
1915int parse_statfs(const UInt8 *xmlp, CFIndex xmlp_len, struct statfs *statfsbuf)
1916{
1917	xmlSAXHandler sh;
1918    memset(&sh,0,sizeof(sh));
1919    sh.startElementNs = parser_statfs_create;
1920    sh.characters = parser_statfs_add;
1921	sh.endElementNs = parser_statfs_end;
1922    sh.initialized = XML_SAX2_MAGIC;
1923
1924	struct webdav_quotas quotas;
1925	bzero((void *)statfsbuf, sizeof(struct statfs));
1926	bzero((void *)&quotas, sizeof(struct webdav_quotas));
1927	xmlSAXUserParseMemory( &sh,&quotas,(char*)xmlp,(int)xmlp_len);
1928	/* were the IETF quota properties returned? */
1929	if ( quotas.use_bytes_values )
1930	{
1931		uint64_t total_bytes;
1932		uint64_t total_blocks;
1933		uint32_t bsize;
1934
1935		/* calculate the total bytes (available + used) */
1936		total_bytes = quotas.quota_available_bytes + quotas.quota_used_bytes;
1937		if ( (total_bytes >= quotas.quota_available_bytes) && (total_bytes >= quotas.quota_used_bytes) )
1938		{
1939			/*
1940			 * calculate the smallest file system block size (bsize) that's a
1941			 * multiple of S_BLKSIZE, is >= S_BLKSIZE, and is <= LONG_MAX
1942			 */
1943			bsize = S_BLKSIZE / 2;
1944			do
1945			{
1946				bsize *= 2;
1947				total_blocks = ((total_bytes + bsize - 1) / bsize);
1948			} while ( total_blocks > LONG_MAX );
1949
1950			/* stuff the results into statfsbuf */
1951			statfsbuf->f_bsize = bsize;
1952#ifdef __LP64__
1953			statfsbuf->f_blocks = total_blocks;
1954			statfsbuf->f_bavail = statfsbuf->f_bfree = quotas.quota_available_bytes / bsize;
1955#else
1956			statfsbuf->f_blocks = (long)total_blocks;
1957			statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota_available_bytes / bsize);
1958#endif
1959		}
1960		/* else we were handed values we cannot represent so leave statfsbuf zeroed (no quota support for this file system) */
1961	}
1962	else
1963	{
1964		/* use the deprecated quota and quotaused if they were returned */
1965		if ( (quotas.quota != 0) && (quotas.quota > quotas.quotaused) )
1966		{
1967#ifdef __LP64__
1968			statfsbuf->f_bavail = statfsbuf->f_bfree = (quotas.quota - quotas.quotaused);
1969#else
1970			statfsbuf->f_bavail = statfsbuf->f_bfree = (long)(quotas.quota - quotas.quotaused);
1971#endif
1972		}
1973		else
1974		{
1975			statfsbuf->f_bavail = statfsbuf->f_bfree = 0;
1976		}
1977#ifdef __LP64__
1978		statfsbuf->f_blocks = quotas.quota;
1979#else
1980		statfsbuf->f_blocks = (long)quotas.quota;
1981#endif
1982		statfsbuf->f_bsize = S_BLKSIZE;
1983	}
1984
1985	statfsbuf->f_iosize = WEBDAV_IOSIZE;
1986
1987	return ( 0 );
1988}
1989
1990/*****************************************************************************/
1991
1992int parse_lock(const UInt8 *xmlp, CFIndex xmlp_len, char **locktoken)
1993{
1994	xmlSAXHandler sh;
1995    memset(&sh,0,sizeof(sh));
1996    sh.startElementNs = parser_lock_create;
1997	sh.characters = parser_lock_add;
1998	sh.endElementNs = parser_lock_end;
1999    sh.initialized = XML_SAX2_MAGIC;
2000	webdav_parse_lock_struct_t lock_struct;
2001	lock_struct.context = 0;
2002	lock_struct.locktoken = NULL;	/* NULL coming into this function */
2003
2004	xmlSAXUserParseMemory( &sh,&lock_struct,(char*)xmlp,(int)xmlp_len);
2005
2006	*locktoken = (char *)lock_struct.locktoken;
2007	if (*locktoken == NULL)
2008	{
2009		debug_string("error parsing lock token");
2010	}
2011
2012	return ( 0 );
2013}
2014
2015/*****************************************************************************/
2016
2017int parse_cachevalidators(const UInt8 *xmlp, CFIndex xmlp_len, time_t *last_modified, char **entity_tag)
2018{
2019	xmlSAXHandler sh;
2020    memset(&sh,0,sizeof(sh));
2021    sh.startElementNs = parser_cachevalidators_create;
2022	sh.characters = parser_cachevalidators_add;
2023	sh.endElementNs = parser_cachevalidators_end;
2024    sh.initialized = XML_SAX2_MAGIC;
2025	struct webdav_parse_cachevalidators_struct cachevalidators_struct;
2026	/* results if parser fails or values are not found */
2027	cachevalidators_struct.last_modified = 0;
2028	cachevalidators_struct.entity_tag = NULL;
2029
2030	xmlSAXUserParseMemory( &sh,&cachevalidators_struct,(char*)xmlp,(int)xmlp_len);
2031
2032	if ( cachevalidators_struct.last_modified != 0 )
2033	{
2034		*last_modified = cachevalidators_struct.last_modified;
2035	}
2036	else
2037	{
2038		time(last_modified);
2039	}
2040	*entity_tag = cachevalidators_struct.entity_tag;
2041
2042	return ( 0 );
2043}
2044
2045/*****************************************************************************/
2046