2 Copyright (c) 2008 Instituto Nokia de Tecnologia
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.
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"
45 #include <libxml/tree.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
55 /** libxml DOM document structure pointer */
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
;
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");
79 result
= get(cur_node
->children
);
89 int get_the_url(const char *data
, int length
, char **url
)
92 xmlNode
*root_element
= NULL
;
96 doc
= xmlReadMemory(data
, length
, "noname.xml", NULL
, 0);
100 root_element
= xmlDocGetRootElement(doc
);
101 *url
= get(root_element
);
112 static char *get_edit(xmlNode
*a_node
)
114 xmlNode
*cur_node
= 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");
122 if (!strcmp(attr
, "edit")) {
123 uri
= xmlGetProp(cur_node
, "href");
125 result
= strdup(uri
);
136 result
= get_edit(cur_node
->children
);
145 int get_edit_url(const char *data
, int length
, char **url
)
148 xmlNode
*root_element
= NULL
;
152 doc
= xmlReadMemory(data
, length
, "noname.xml", NULL
, 0);
156 root_element
= xmlDocGetRootElement(doc
);
157 *url
= get_edit(root_element
);
167 int get_edit_etag(const char *data
, int length
, char **url
)
170 xmlNode
*root_element
= NULL
;
174 doc
= xmlReadMemory(data
, length
, "noname.xml", NULL
, 0);
178 root_element
= xmlDocGetRootElement(doc
);
179 *url
= get_etag_attribute(root_element
);
190 dom_document
*build_dom_document(const char *xml_data
)
192 dom_document
*ptr
= NULL
;
196 if (build_doc_tree(&ptr
, xml_data
)) {
197 fprintf(stderr
, "build_dom_document: failed doc parse");
212 void clean_dom_document(dom_document
*doc
)
215 clean_doc_tree(&doc
);
219 int get_entries_number(dom_document
*doc
)
223 fprintf(stderr
, "get_entries_number: null document!");
227 result
= atom_entries(doc
);
232 int extract_all_entries(dom_document
*doc
,
233 struct gcal_event
*data_extract
, int length
)
237 xmlXPathObject
*xpath_obj
= NULL
;
240 /* get the entry node list */
241 xpath_obj
= atom_get_entries(doc
);
244 nodes
= xpath_obj
->nodesetval
;
248 if (length
!= nodes
->nodeNr
) {
249 fprintf(stderr
, "extract_all_entries: Size mismatch!");
253 /* extract the fields */
254 for (i
= 0; i
< length
; ++i
) {
255 result
= atom_extract_data(nodes
->nodeTab
[i
], &data_extract
[i
]);
263 xmlXPathFreeObject(xpath_obj
);
269 int xmlentry_create(const struct gcal_event
*entry
, char **xml_entry
, int *length
)
273 xmlNode
*root
, *node
;
275 xmlChar
*xml_str
= NULL
;
277 doc
= xmlNewDoc(BAD_CAST
"1.0");
278 root
= xmlNewNode(NULL
, BAD_CAST
"entry");
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
297 if (entry
->common
.id
) {
298 node
= xmlNewNode(NULL
, "id");
301 xmlNodeAddContent(node
, entry
->common
.id
);
302 xmlAddChild(root
, node
);
305 /* category element */
306 node
= xmlNewNode(NULL
, "category");
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
);
314 node
= xmlNewNode(NULL
, "title");
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");
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");
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
);
346 node
= xmlNewNode(ns
, "transparency");
349 xmlSetProp(node
, BAD_CAST
"value",
350 BAD_CAST
"http://schemas.google.com/g/2005#event.opaque");
351 xmlAddChild(root
, node
);
354 node
= xmlNewNode(ns
, "eventStatus");
357 xmlSetProp(node
, BAD_CAST
"value",
358 BAD_CAST
"http://schemas.google.com/g/2005#event.confirmed");
359 xmlAddChild(root
, node
);
364 node
= xmlNewNode(ns
, "where");
367 xmlSetProp(node
, BAD_CAST
"valueString", BAD_CAST entry
->where
);
368 xmlAddChild(root
, node
);
372 node
= xmlNewNode(ns
, "when");
376 xmlSetProp(node
, BAD_CAST
"startTime",
377 BAD_CAST entry
->dt_start
);
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 */
387 if ((*xml_entry
= strdup(xml_str
)))
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.
415 xmlXPathObject
*xpath_obj
= NULL
;
418 /* get the contact node list */
419 xpath_obj
= atom_get_entries(doc
);
422 nodes
= xpath_obj
->nodesetval
;
426 if (length
!= nodes
->nodeNr
) {
427 /* FIXME: don't print to terminal! */
428 fprintf(stderr
, "extract_all_contacts: Size mismatch!\n");
432 /* extract the fields */
433 for (i
= 0; i
< length
; ++i
) {
434 result
= atom_extract_contact(nodes
->nodeTab
[i
],
444 xmlXPathFreeObject(xpath_obj
);
450 int xmlcontact_create(struct gcal_contact
*contact
, char **xml_contact
,
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.
459 struct gcal_structured_subvalues
*this_structured_entry
;
460 int set_structured_entry
= 0;
462 xmlNode
*root
= NULL
;
463 xmlNode
*node
= NULL
;
464 xmlNode
*node2
= NULL
;
465 xmlNode
*child
= NULL
;
468 xmlChar
*xml_str
= NULL
;
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");
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");
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
503 if (contact
->common
.id
) {
504 node
= xmlNewNode(NULL
, "id");
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")))
521 set_structured_entry
= 1;
524 if (!(child
= xmlNewNode(ns
, BAD_CAST this_structured_entry
->field_key
)))
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");
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");
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")))
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",
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",
573 xmlAddChild(root
, node
);
578 /* Here begin extra fields */
579 if (contact
->content
) {
580 node
= xmlNewNode(NULL
, "atom:content");
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");
592 xmlNodeAddContent(node
, contact
->nickname
);
593 xmlAddChild(root
, node
);
596 if (contact
->homepage
) {
597 if (!(node
= xmlNewNode(NULL
, "gContact:website")))
599 xmlSetProp(node
, BAD_CAST
"rel", BAD_CAST
"home-page");
600 xmlSetProp(node
, BAD_CAST
"href", BAD_CAST contact
->homepage
);
601 xmlAddChild(root
, node
);
605 if (!(node
= xmlNewNode(NULL
, "gContact:website")))
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")))
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")))
622 xmlNodeAddContent(child
, contact
->org_name
);
623 xmlAddChild(node
, child
);
627 if (contact
->org_title
) {
628 if (!(child
= xmlNewNode(ns
, "orgTitle")))
630 xmlNodeAddContent(child
, contact
->org_title
);
631 xmlAddChild(node
, child
);
634 xmlAddChild(root
, node
);
637 if (contact
->occupation
) {
638 node
= xmlNewNode(NULL
, "gContact:occupation");
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")))
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",
658 xmlNodeAddContent(node
, contact
->phone_numbers_field
[i
]);
659 xmlAddChild(root
, node
);
665 if (contact
->im_nr
> 0) {
666 for (i
= 0; i
< contact
->im_nr
; i
++) {
667 if (!(node
= xmlNewNode(ns
, "im")))
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",
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",
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",
684 xmlAddChild(root
, node
);
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")))
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;
712 if (!(child
= xmlNewNode(ns
, BAD_CAST this_structured_entry
->field_key
)))
714 xmlNodeAddContent(child
, BAD_CAST this_structured_entry
->field_value
);
715 if (i
== contact
->structured_address_pref
)
716 xmlSetProp(node
, BAD_CAST
"primary",
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");
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")))
740 xmlSetProp(node
, BAD_CAST
"deleted",
742 xmlSetProp(node
, BAD_CAST
"href",
743 BAD_CAST contact
->groupMembership
[i
]);
744 xmlAddChild(root
, node
);
749 if (contact
->birthday
) {
750 /*if (!(node = xmlNewNode(NULL, BAD_CAST "gContact:birthday")))
752 xmlSetProp(node, BAD_CAST "xmlns", BAD_CAST "http://schemas.google.com/contact/2008");*/
753 if (!(node
= xmlNewNode(NULL
, "gContact:birthday")))
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 */
766 if ((*xml_contact
= strdup(xml_str
)))