Added header guards for C++
[libgcal/libgcal-cdf.git] / src / gcal_parser.c
blobee4902286e203c015a297372d30695eea812c502
1 /*
2 Copyright (c) 2008 Instituto Nokia de Tecnologia
3 All rights reserved.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13 * Neither the name of the INdT nor the names of its contributors
14 may be used to endorse or promote products derived from this software
15 without specific prior written permission.
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
30 /**
31 * @file gcal_parser.h
32 * @author Adenilson Cavalcanti da Silva <adenilson.silva@indt.org.br>
33 * @date Mon Mar 31 11:17:02 2008
35 * @brief A thin layer over \ref atom_parser.h, so I can plug another
36 * XML parser to libgcal if required.
37 * It creates a DOM document from libgcal atom stream and provides functions
38 * wrappers to extract data.
41 #include "gcal_parser.h"
42 #include "atom_parser.h"
43 #include "xml_aux.h"
45 #include <libxml/tree.h>
46 #include <string.h>
48 char scheme_href[] = "http://schemas.google.com/g/2005#kind";
49 char term_href_cal[] = "http://schemas.google.com/g/2005#event";
50 char term_href_cont[] = "http://schemas.google.com/contact/2008#contact";
51 /** A thin wrapper around libxml document structure
54 struct dom_document {
55 /** libxml DOM document structure pointer */
56 xmlDoc *document;
59 /* REMARK: this function is recursive, I'm not completely sure if this
60 * is a good idea (i.e. for small devices).
62 static char *get(xmlNode *a_node)
64 xmlNode *cur_node = NULL;
65 char *result = NULL;
66 xmlChar *uri = NULL;
68 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
69 if (xmlHasProp(cur_node, "HREF")) {
70 uri = xmlGetProp(cur_node, "HREF");
71 if (uri) {
72 result = strdup(uri);
73 xmlFree(uri);
74 goto exit;
79 result = get(cur_node->children);
80 if (result)
81 goto exit;
84 exit:
85 return result;
89 int get_the_url(const char *data, int length, char **url)
91 xmlDoc *doc = NULL;
92 xmlNode *root_element = NULL;
93 int result = -1;
95 *url = NULL;
96 doc = xmlReadMemory(data, length, "noname.xml", NULL, 0);
97 if (!doc)
98 goto exit;
100 root_element = xmlDocGetRootElement(doc);
101 *url = get(root_element);
102 if (*url)
103 result = 0;
105 xmlFreeDoc(doc);
107 exit:
108 return result;
112 static char *get_edit(xmlNode *a_node)
114 xmlNode *cur_node = NULL;
115 char *result = NULL;
116 xmlChar *attr = NULL, *uri = NULL;
118 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
119 if (xmlHasProp(cur_node, "rel")) {
120 attr = xmlGetProp(cur_node, "rel");
121 if (attr) {
122 if (!strcmp(attr, "edit")) {
123 uri = xmlGetProp(cur_node, "href");
124 if (uri)
125 result = strdup(uri);
126 xmlFree(attr);
127 xmlFree(uri);
128 goto exit;
131 xmlFree(attr);
136 result = get_edit(cur_node->children);
137 if (result)
138 goto exit;
141 exit:
142 return result;
145 int get_edit_url(const char *data, int length, char **url)
147 xmlDoc *doc = NULL;
148 xmlNode *root_element = NULL;
149 int result = -1;
151 *url = NULL;
152 doc = xmlReadMemory(data, length, "noname.xml", NULL, 0);
153 if (!doc)
154 goto exit;
156 root_element = xmlDocGetRootElement(doc);
157 *url = get_edit(root_element);
158 if (*url)
159 result = 0;
161 xmlFreeDoc(doc);
163 exit:
164 return result;
167 int get_edit_etag(const char *data, int length, char **url)
169 xmlDoc *doc = NULL;
170 xmlNode *root_element = NULL;
171 int result = -1;
173 *url = NULL;
174 doc = xmlReadMemory(data, length, "noname.xml", NULL, 0);
175 if (!doc)
176 goto exit;
178 root_element = xmlDocGetRootElement(doc);
179 *url = get_etag_attribute(root_element);
180 if (*url)
181 result = 0;
183 xmlFreeDoc(doc);
185 exit:
186 return result;
190 dom_document *build_dom_document(const char *xml_data)
192 dom_document *ptr = NULL;
193 if (!xml_data)
194 goto exit;
196 if (build_doc_tree(&ptr, xml_data)) {
197 fprintf(stderr, "build_dom_document: failed doc parse");
198 goto cleanup;
201 goto exit;
203 cleanup:
204 if (ptr)
205 free(ptr);
207 exit:
208 return ptr;
212 void clean_dom_document(dom_document *doc)
214 if (doc)
215 clean_doc_tree(&doc);
219 int get_entries_number(dom_document *doc)
221 int result = -1;
222 if (!doc) {
223 fprintf(stderr, "get_entries_number: null document!");
224 goto exit;
227 result = atom_entries(doc);
228 exit:
229 return result;
232 int extract_all_entries(dom_document *doc,
233 struct gcal_event *data_extract, int length)
236 int result = -1, i;
237 xmlXPathObject *xpath_obj = NULL;
238 xmlNodeSet *nodes;
240 /* get the entry node list */
241 xpath_obj = atom_get_entries(doc);
242 if (!xpath_obj)
243 goto exit;
244 nodes = xpath_obj->nodesetval;
245 if (!nodes)
246 goto exit;
248 if (length != nodes->nodeNr) {
249 fprintf(stderr, "extract_all_entries: Size mismatch!");
250 goto cleanup;
253 /* extract the fields */
254 for (i = 0; i < length; ++i) {
255 result = atom_extract_data(nodes->nodeTab[i], &data_extract[i]);
256 if (result == -1)
257 goto cleanup;
260 result = 0;
262 cleanup:
263 xmlXPathFreeObject(xpath_obj);
265 exit:
266 return result;
269 int xmlentry_create(const struct gcal_event *entry, char **xml_entry, int *length)
271 int result = -1;
272 xmlDoc *doc = NULL;
273 xmlNode *root, *node;
274 xmlNs *ns;
275 xmlChar *xml_str = NULL;
277 doc = xmlNewDoc(BAD_CAST "1.0");
278 root = xmlNewNode(NULL, BAD_CAST "entry");
280 if (!doc || !root)
281 goto exit;
283 xmlSetProp(root, BAD_CAST "xmlns", BAD_CAST atom_href);
284 /* Google Data API 2.0 requires ETag to edit an entry */
285 if (entry->common.etag)
286 xmlSetProp(root, BAD_CAST "gd:etag",
287 BAD_CAST entry->common.etag);
288 ns = xmlNewNs(root, BAD_CAST gd_href, BAD_CAST "gd");
290 xmlDocSetRootElement(doc, root);
293 /* entry ID, only if the 'entry' is already existant (i.e. the user
294 * of library just got one entry result from a request from
295 * server).
297 if (entry->common.id) {
298 node = xmlNewNode(NULL, "id");
299 if (!node)
300 goto cleanup;
301 xmlNodeAddContent(node, entry->common.id);
302 xmlAddChild(root, node);
305 /* category element */
306 node = xmlNewNode(NULL, "category");
307 if (!node)
308 goto cleanup;
309 xmlSetProp(node, BAD_CAST "scheme", BAD_CAST scheme_href);
310 xmlSetProp(node, BAD_CAST "term", BAD_CAST term_href_cal);
311 xmlAddChild(root, node);
313 /* title element */
314 node = xmlNewNode(NULL, "title");
315 if (!node)
316 goto cleanup;
317 xmlSetProp(node, BAD_CAST "type", BAD_CAST "text");
318 xmlNodeAddContent(node, entry->common.title);
319 xmlAddChild(root, node);
321 /* content element */
322 node = xmlNewNode(NULL, "content");
323 if (!node)
324 goto cleanup;
325 xmlSetProp(node, BAD_CAST "type", BAD_CAST "text");
326 xmlNodeAddContent(node, entry->content);
327 xmlAddChild(root, node);
329 /* entry edit URL, only if the 'entry' is already existant.
331 if (entry->common.edit_uri) {
332 node = xmlNewNode(NULL, "link");
333 if (!node)
334 goto cleanup;
335 xmlSetProp(node, BAD_CAST "rel", BAD_CAST "edit");
336 xmlSetProp(node, BAD_CAST "type",
337 BAD_CAST "application/atom+xml");
338 xmlSetProp(node, BAD_CAST "href",
339 BAD_CAST entry->common.edit_uri);
340 xmlAddChild(root, node);
345 /* transparency */
346 node = xmlNewNode(ns, "transparency");
347 if (!node)
348 goto cleanup;
349 xmlSetProp(node, BAD_CAST "value",
350 BAD_CAST "http://schemas.google.com/g/2005#event.opaque");
351 xmlAddChild(root, node);
353 /* event status */
354 node = xmlNewNode(ns, "eventStatus");
355 if (!node)
356 goto cleanup;
357 xmlSetProp(node, BAD_CAST "value",
358 BAD_CAST "http://schemas.google.com/g/2005#event.confirmed");
359 xmlAddChild(root, node);
362 /* where */
363 if (entry->where) {
364 node = xmlNewNode(ns, "where");
365 if (!node)
366 goto cleanup;
367 xmlSetProp(node, BAD_CAST "valueString", BAD_CAST entry->where);
368 xmlAddChild(root, node);
371 /* when */
372 node = xmlNewNode(ns, "when");
373 if (!node)
374 goto cleanup;
375 if (entry->dt_start)
376 xmlSetProp(node, BAD_CAST "startTime",
377 BAD_CAST entry->dt_start);
378 if (entry->dt_end)
379 xmlSetProp(node, BAD_CAST "endTime", BAD_CAST entry->dt_end);
380 xmlAddChild(root, node);
383 xmlDocDumpMemory(doc, &xml_str, length);
384 /* xmlDocDumpMemory doesn't include the last 0 in the returned size */
385 ++(*length);
386 if (xml_str)
387 if ((*xml_entry = strdup(xml_str)))
388 result = 0;
390 cleanup:
392 if (xml_str)
393 xmlFree(xml_str);
395 if (doc)
396 xmlFreeDoc(doc);
398 exit:
400 return result;
404 int extract_all_contacts(dom_document *doc,
405 struct gcal_contact *data_extract, int length)
408 /* The logic of this function is the same of 'extract_all_entries'
409 * but I can't find a way to share code without having a common
410 * type for contact/calendar and registering a callback which
411 * would accept both types as a valid parameter and parse the
412 * DOM outputing a vector of contacts/entries.
414 int result = -1, i;
415 xmlXPathObject *xpath_obj = NULL;
416 xmlNodeSet *nodes;
418 /* get the contact node list */
419 xpath_obj = atom_get_entries(doc);
420 if (!xpath_obj)
421 goto exit;
422 nodes = xpath_obj->nodesetval;
423 if (!nodes)
424 goto exit;
426 if (length != nodes->nodeNr) {
427 /* FIXME: don't print to terminal! */
428 fprintf(stderr, "extract_all_contacts: Size mismatch!\n");
429 goto cleanup;
432 /* extract the fields */
433 for (i = 0; i < length; ++i) {
434 result = atom_extract_contact(nodes->nodeTab[i],
435 &data_extract[i]);
437 if (result == -1)
438 goto cleanup;
441 result = 0;
443 cleanup:
444 xmlXPathFreeObject(xpath_obj);
446 exit:
447 return result;
450 int xmlcontact_create(struct gcal_contact *contact, char **xml_contact,
451 int *length)
453 /* XXX: this function is pretty much a copy of 'xmlentry_create'
454 * some code could be shared if I provided a common type between
455 * contact X calendar.
457 int result = -1;
458 int i;
459 struct gcal_structured_subvalues *this_structured_entry;
460 int set_structured_entry = 0;
461 xmlDoc *doc = NULL;
462 xmlNode *root = NULL;
463 xmlNode *node = NULL;
464 xmlNode *node2 = NULL;
465 xmlNode *child = NULL;
466 xmlNs *ns;
467 xmlNs *ns2;
468 xmlChar *xml_str = NULL;
469 char *temp;
470 const char * rel_prefix = "http://schemas.google.com/g/2005#";
472 doc = xmlNewDoc(BAD_CAST "1.0");
473 root = xmlNewNode(NULL, BAD_CAST "atom:entry");
475 if (!doc || !root)
476 goto exit;
478 xmlSetProp(root, BAD_CAST "xmlns:atom", BAD_CAST atom_href);
479 /* Google Data API 2.0 requires ETag to edit an entry */
480 if (contact->common.etag)
481 xmlSetProp(root, BAD_CAST "gd:etag",
482 BAD_CAST contact->common.etag);
484 ns = xmlNewNs(root, BAD_CAST gd_href, BAD_CAST "gd");
486 /* Google contact group */
487 ns2 = xmlNewNs(root, BAD_CAST gContact_href, BAD_CAST "gContact");
489 xmlDocSetRootElement(doc, root);
491 /* category element */
492 node = xmlNewNode(NULL, "category");
493 if (!node)
494 goto cleanup;
495 xmlSetProp(node, BAD_CAST "scheme", BAD_CAST scheme_href);
496 xmlSetProp(node, BAD_CAST "term", BAD_CAST term_href_cont);
497 xmlAddChild(root, node);
499 /* entry ID, only if the 'contact' is already existant (i.e. the user
500 * of library just got one contact result from a request from
501 * server).
503 if (contact->common.id) {
504 node = xmlNewNode(NULL, "id");
505 if (!node)
506 goto cleanup;
507 xmlNodeAddContent(node, contact->common.id);
508 xmlAddChild(root, node);
511 /* Sets contact structured name (Google API 3.0) */
512 if (contact->structured_name_nr) {
513 set_structured_entry = 0;
514 for (this_structured_entry = contact->structured_name;
515 this_structured_entry != NULL;
516 this_structured_entry = this_structured_entry->next_field) {
517 if ((this_structured_entry->field_value != NULL)) {
518 if( !set_structured_entry ) {
519 if (!(node = xmlNewNode(ns, "name")))
520 goto cleanup;
521 set_structured_entry = 1;
524 if (!(child = xmlNewNode(ns, BAD_CAST this_structured_entry->field_key)))
525 goto cleanup;
526 xmlNodeAddContent(child, BAD_CAST this_structured_entry->field_value);
527 xmlAddChild(node, child);
531 if( set_structured_entry )
532 xmlAddChild(root, node);
533 } else if (contact->common.title) {
534 node = xmlNewNode(NULL, "gd:name");
535 if (!node)
536 goto cleanup;
537 node2 = xmlNewNode(NULL, "gd:fullName");
538 xmlNodeAddContent(node2, contact->common.title);
539 xmlAddChild(node, node2);
540 xmlAddChild(root, node);
543 /* entry edit URL, only if the 'entry' is already existant.
545 if (contact->common.edit_uri) {
546 node = xmlNewNode(NULL, "link");
547 if (!node)
548 goto cleanup;
549 xmlSetProp(node, BAD_CAST "rel", BAD_CAST "edit");
550 xmlSetProp(node, BAD_CAST "type",
551 BAD_CAST "application/atom+xml");
552 xmlSetProp(node, BAD_CAST "href",
553 BAD_CAST contact->common.edit_uri);
554 xmlAddChild(root, node);
558 /* email addresses */
559 if (contact->emails_nr > 0) {
560 for (i = 0; i < contact->emails_nr; i++) {
561 if (!(node = xmlNewNode(ns, "email")))
562 goto cleanup;
563 temp = (char *)malloc((strlen(contact->emails_type[i])+strlen(rel_prefix)+1) * sizeof(char));
564 strcpy(temp, rel_prefix);
565 strcat(temp, contact->emails_type[i]);
566 xmlSetProp(node, BAD_CAST "rel",
567 BAD_CAST temp);
568 xmlSetProp(node, BAD_CAST "address",
569 BAD_CAST contact->emails_field[i]);
570 if (i == contact->pref_email)
571 xmlSetProp(node, BAD_CAST "primary",
572 BAD_CAST "true");
573 xmlAddChild(root, node);
574 free(temp);
578 /* Here begin extra fields */
579 if (contact->content) {
580 node = xmlNewNode(NULL, "atom:content");
581 if (!node)
582 goto cleanup;
583 xmlSetProp(node, BAD_CAST "type", BAD_CAST "text");
584 xmlNodeAddContent(node, contact->content);
585 xmlAddChild(root, node);
588 if (contact->nickname) {
589 node = xmlNewNode(NULL, "gContact:nickname");
590 if (!node)
591 goto cleanup;
592 xmlNodeAddContent(node, contact->nickname);
593 xmlAddChild(root, node);
596 if (contact->homepage) {
597 if (!(node = xmlNewNode(NULL, "gContact:website")))
598 goto cleanup;
599 xmlSetProp(node, BAD_CAST "rel", BAD_CAST "home-page");
600 xmlSetProp(node, BAD_CAST "href", BAD_CAST contact->homepage);
601 xmlAddChild(root, node);
604 if (contact->blog) {
605 if (!(node = xmlNewNode(NULL, "gContact:website")))
606 goto cleanup;
607 xmlSetProp(node, BAD_CAST "rel", BAD_CAST "blog");
608 xmlSetProp(node, BAD_CAST "href", BAD_CAST contact->blog);
609 xmlAddChild(root, node);
612 /* organization (it has 2 subelements: orgName, orgTitle) */
613 if (contact->org_name || contact->org_title) {
614 if (!(node = xmlNewNode(ns, "organization")))
615 goto cleanup;
616 xmlSetProp(node, BAD_CAST "rel",
617 BAD_CAST "http://schemas.google.com/g/2005#other");
619 if (contact->org_name) {
620 if (!(child = xmlNewNode(ns, "orgName")))
621 goto cleanup;
622 xmlNodeAddContent(child, contact->org_name);
623 xmlAddChild(node, child);
627 if (contact->org_title) {
628 if (!(child = xmlNewNode(ns, "orgTitle")))
629 goto cleanup;
630 xmlNodeAddContent(child, contact->org_title);
631 xmlAddChild(node, child);
634 xmlAddChild(root, node);
637 if (contact->occupation) {
638 node = xmlNewNode(NULL, "gContact:occupation");
639 if (!node)
640 goto cleanup;
641 xmlNodeAddContent(node, contact->occupation);
642 xmlAddChild(root, node);
645 /* Get phone numbers */
646 if (contact->phone_numbers_nr > 0) {
647 for (i = 0; i < contact->phone_numbers_nr; i++) {
648 if (!(node = xmlNewNode(ns, "phoneNumber")))
649 goto cleanup;
650 /* TODO: support user setting phone type */
652 temp = (char *)malloc((strlen(contact->phone_numbers_type[i])+strlen(rel_prefix)+1) * sizeof(char));
653 strcpy(temp, rel_prefix);
654 strcat(temp, contact->phone_numbers_type[i]);
655 xmlSetProp(node, BAD_CAST "rel",
656 BAD_CAST temp);
658 xmlNodeAddContent(node, contact->phone_numbers_field[i]);
659 xmlAddChild(root, node);
660 free(temp);
664 /* im addresses */
665 if (contact->im_nr > 0) {
666 for (i = 0; i < contact->im_nr; i++) {
667 if (!(node = xmlNewNode(ns, "im")))
668 goto cleanup;
669 temp = (char *)malloc((strlen(contact->im_type[i])+strlen(rel_prefix)+1) * sizeof(char));
670 strcpy(temp, rel_prefix);
671 strcat(temp, contact->im_type[i]);
672 xmlSetProp(node, BAD_CAST "rel",
673 BAD_CAST temp);
674 temp = (char *)malloc((strlen(contact->im_protocol[i])+strlen(rel_prefix)+1) * sizeof(char));
675 strcpy(temp, rel_prefix);
676 strcat(temp, contact->im_protocol[i]);
677 xmlSetProp(node, BAD_CAST "protocol",
678 BAD_CAST temp);
679 xmlSetProp(node, BAD_CAST "address",
680 BAD_CAST contact->im_address[i]);
681 if (i == contact->im_pref)
682 xmlSetProp(node, BAD_CAST "primary",
683 BAD_CAST "true");
684 xmlAddChild(root, node);
685 free(temp);
689 /* Sets contact structured postal addressees (Google API 3.0) */
690 /* TODO: move this to another function (identation is looking bad) */
691 if (contact->structured_address_nr > 0) {
692 for (i = 0; i < contact->structured_address_nr; i++) {
693 set_structured_entry = 0;
694 for (this_structured_entry = contact->structured_address;
695 this_structured_entry != NULL;
696 this_structured_entry = this_structured_entry->next_field) {
697 if (this_structured_entry->field_value &&
698 this_structured_entry->field_key &&
699 (this_structured_entry->field_typenr == i)) {
700 if (!set_structured_entry) {
701 if (!(node = xmlNewNode(ns, "structuredPostalAddress")))
702 goto cleanup;
703 // TODO: support user settting address type
704 temp = (char *)malloc((strlen(contact->structured_address_type[i])+strlen(rel_prefix)+2) * sizeof(char));
705 strcpy(temp, rel_prefix);
706 strcat(temp, contact->structured_address_type[i]);
707 xmlSetProp(node, BAD_CAST "rel", BAD_CAST temp);
708 set_structured_entry = 1;
709 free(temp);
712 if (!(child = xmlNewNode(ns, BAD_CAST this_structured_entry->field_key)))
713 goto cleanup;
714 xmlNodeAddContent(child, BAD_CAST this_structured_entry->field_value);
715 if (i == contact->structured_address_pref)
716 xmlSetProp(node, BAD_CAST "primary",
717 BAD_CAST "true");
718 xmlAddChild(node, child);
722 if (set_structured_entry)
723 xmlAddChild(root, node);
725 } else if (contact->post_address) {
726 node = xmlNewNode(NULL, "gd:structuredPostalAddress");
727 if (!node)
728 goto cleanup;
729 node2 = xmlNewNode(NULL, "gd:formattedAddress");
730 xmlNodeAddContent(node2, contact->post_address);
731 xmlAddChild(node, node2);
732 xmlAddChild(root, node);
735 /* Google group membership info */
736 if (contact->groupMembership_nr > 0) {
737 for (i = 0; i < contact->groupMembership_nr; i++) {
738 if (!(node = xmlNewNode(ns2, "groupMembershipInfo")))
739 goto cleanup;
740 xmlSetProp(node, BAD_CAST "deleted",
741 BAD_CAST "false");
742 xmlSetProp(node, BAD_CAST "href",
743 BAD_CAST contact->groupMembership[i]);
744 xmlAddChild(root, node);
748 /* birthday */
749 if (contact->birthday) {
750 /*if (!(node = xmlNewNode(NULL, BAD_CAST "gContact:birthday")))
751 goto cleanup;
752 xmlSetProp(node, BAD_CAST "xmlns", BAD_CAST "http://schemas.google.com/contact/2008");*/
753 if (!(node = xmlNewNode(NULL, "gContact:birthday")))
754 goto cleanup;
755 xmlSetProp(node, BAD_CAST "when", BAD_CAST contact->birthday);
756 xmlAddChild(root, node);
759 /* TODO: implement missing fields (which ones? geo location?)
762 xmlDocDumpMemory(doc, &xml_str, length);
763 /* xmlDocDumpMemory doesn't include the last 0 in the returned size */
764 ++(*length);
765 if (xml_str)
766 if ((*xml_contact = strdup(xml_str)))
767 result = 0;
768 cleanup:
770 if (xml_str)
771 xmlFree(xml_str);
773 if (doc)
774 xmlFreeDoc(doc);
776 exit:
778 return result;