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(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
);
113 static char *get_edit(xmlNode
*a_node
)
115 xmlNode
*cur_node
= NULL
;
117 xmlChar
*attr
= NULL
, *uri
= NULL
;
119 for (cur_node
= a_node
; cur_node
; cur_node
= cur_node
->next
) {
120 if (xmlHasProp(cur_node
, "rel")) {
121 attr
= xmlGetProp(cur_node
, "rel");
123 if (!strcmp(attr
, "edit")) {
124 uri
= xmlGetProp(cur_node
, "href");
126 result
= strdup(uri
);
137 result
= get_edit(cur_node
->children
);
146 int get_edit_url(char *data
, int length
, char **url
)
149 xmlNode
*root_element
= NULL
;
153 doc
= xmlReadMemory(data
, length
, "noname.xml", NULL
, 0);
157 root_element
= xmlDocGetRootElement(doc
);
158 *url
= get_edit(root_element
);
169 int get_edit_etag(char *data
, int length
, char **url
)
172 xmlNode
*root_element
= NULL
;
176 doc
= xmlReadMemory(data
, length
, "noname.xml", NULL
, 0);
180 root_element
= xmlDocGetRootElement(doc
);
181 *url
= get_etag_attribute(root_element
);
193 dom_document
*build_dom_document(char *xml_data
)
195 dom_document
*ptr
= NULL
;
199 if (build_doc_tree(&ptr
, xml_data
)) {
200 fprintf(stderr
, "build_dom_document: failed doc parse");
215 void clean_dom_document(dom_document
*doc
)
218 clean_doc_tree(&doc
);
222 int get_entries_number(dom_document
*doc
)
226 fprintf(stderr
, "get_entries_number: null document!");
230 result
= atom_entries(doc
);
235 int extract_all_entries(dom_document
*doc
,
236 struct gcal_event
*data_extract
, int length
)
240 xmlXPathObject
*xpath_obj
= NULL
;
243 /* get the entry node list */
244 xpath_obj
= atom_get_entries(doc
);
247 nodes
= xpath_obj
->nodesetval
;
251 if (length
!= nodes
->nodeNr
) {
252 fprintf(stderr
, "extract_all_entries: Size mismatch!");
256 /* extract the fields */
257 for (i
= 0; i
< length
; ++i
) {
258 result
= atom_extract_data(nodes
->nodeTab
[i
], &data_extract
[i
]);
266 xmlXPathFreeObject(xpath_obj
);
272 int xmlentry_create(struct gcal_event
*entry
, char **xml_entry
, int *length
)
276 xmlNode
*root
, *node
;
278 xmlChar
*xml_str
= NULL
;
280 doc
= xmlNewDoc(BAD_CAST
"1.0");
281 root
= xmlNewNode(NULL
, BAD_CAST
"entry");
286 xmlSetProp(root
, BAD_CAST
"xmlns", BAD_CAST atom_href
);
287 /* Google Data API 2.0 requires ETag to edit an entry */
288 if (entry
->common
.etag
)
289 xmlSetProp(root
, BAD_CAST
"gd:etag",
290 BAD_CAST entry
->common
.etag
);
291 ns
= xmlNewNs(root
, BAD_CAST gd_href
, BAD_CAST
"gd");
293 xmlDocSetRootElement(doc
, root
);
296 /* entry ID, only if the 'entry' is already existant (i.e. the user
297 * of library just got one entry result from a request from
300 if (entry
->common
.id
) {
301 node
= xmlNewNode(NULL
, "id");
304 xmlNodeAddContent(node
, entry
->common
.id
);
305 xmlAddChild(root
, node
);
308 /* category element */
309 node
= xmlNewNode(NULL
, "category");
312 xmlSetProp(node
, BAD_CAST
"scheme", BAD_CAST scheme_href
);
313 xmlSetProp(node
, BAD_CAST
"term", BAD_CAST term_href_cal
);
314 xmlAddChild(root
, node
);
317 node
= xmlNewNode(NULL
, "title");
320 xmlSetProp(node
, BAD_CAST
"type", BAD_CAST
"text");
321 xmlNodeAddContent(node
, entry
->common
.title
);
322 xmlAddChild(root
, node
);
324 /* content element */
325 node
= xmlNewNode(NULL
, "content");
328 xmlSetProp(node
, BAD_CAST
"type", BAD_CAST
"text");
329 xmlNodeAddContent(node
, entry
->content
);
330 xmlAddChild(root
, node
);
332 /* entry edit URL, only if the 'entry' is already existant.
334 if (entry
->common
.edit_uri
) {
335 node
= xmlNewNode(NULL
, "link");
338 xmlSetProp(node
, BAD_CAST
"rel", BAD_CAST
"edit");
339 xmlSetProp(node
, BAD_CAST
"type",
340 BAD_CAST
"application/atom+xml");
341 xmlSetProp(node
, BAD_CAST
"href",
342 BAD_CAST entry
->common
.edit_uri
);
343 xmlAddChild(root
, node
);
349 node
= xmlNewNode(ns
, "transparency");
352 xmlSetProp(node
, BAD_CAST
"value",
353 BAD_CAST
"http://schemas.google.com/g/2005#event.opaque");
354 xmlAddChild(root
, node
);
357 node
= xmlNewNode(ns
, "eventStatus");
360 xmlSetProp(node
, BAD_CAST
"value",
361 BAD_CAST
"http://schemas.google.com/g/2005#event.confirmed");
362 xmlAddChild(root
, node
);
367 node
= xmlNewNode(ns
, "where");
370 xmlSetProp(node
, BAD_CAST
"valueString", BAD_CAST entry
->where
);
371 xmlAddChild(root
, node
);
375 node
= xmlNewNode(ns
, "when");
379 xmlSetProp(node
, BAD_CAST
"startTime",
380 BAD_CAST entry
->dt_start
);
382 xmlSetProp(node
, BAD_CAST
"endTime", BAD_CAST entry
->dt_end
);
383 xmlAddChild(root
, node
);
386 xmlDocDumpMemory(doc
, &xml_str
, length
);
387 /* xmlDocDumpMemory doesn't include the last 0 in the returned size */
390 if ((*xml_entry
= strdup(xml_str
)))
407 int extract_all_contacts(dom_document
*doc
,
408 struct gcal_contact
*data_extract
, int length
)
411 /* The logic of this function is the same of 'extract_all_entries'
412 * but I can't find a way to share code without having a common
413 * type for contact/calendar and registering a callback which
414 * would accept both types as a valid parameter and parse the
415 * DOM outputing a vector of contacts/entries.
418 xmlXPathObject
*xpath_obj
= NULL
;
421 /* get the contact node list */
422 xpath_obj
= atom_get_entries(doc
);
425 nodes
= xpath_obj
->nodesetval
;
429 if (length
!= nodes
->nodeNr
) {
430 /* FIXME: don't print to terminal! */
431 fprintf(stderr
, "extract_all_contacts: Size mismatch!\n");
435 /* extract the fields */
436 for (i
= 0; i
< length
; ++i
) {
437 result
= atom_extract_contact(nodes
->nodeTab
[i
],
447 xmlXPathFreeObject(xpath_obj
);
453 int xmlcontact_create(struct gcal_contact
*contact
, char **xml_contact
,
456 /* XXX: this function is pretty much a copy of 'xmlentry_create'
457 * some code could be shared if I provided a common type between
458 * contact X calendar.
462 struct gcal_structured_subvalues
*this_structured_entry
;
463 int set_structured_entry
= 0;
465 xmlNode
*root
= NULL
;
466 xmlNode
*node
= NULL
;
467 xmlNode
*node2
= NULL
;
468 xmlNode
*child
= NULL
;
471 xmlChar
*xml_str
= NULL
;
473 const char * rel_prefix
= "http://schemas.google.com/g/2005#";
475 doc
= xmlNewDoc(BAD_CAST
"1.0");
476 root
= xmlNewNode(NULL
, BAD_CAST
"atom:entry");
481 xmlSetProp(root
, BAD_CAST
"xmlns:atom", BAD_CAST atom_href
);
482 /* Google Data API 2.0 requires ETag to edit an entry */
483 if (contact
->common
.etag
)
484 xmlSetProp(root
, BAD_CAST
"gd:etag",
485 BAD_CAST contact
->common
.etag
);
487 ns
= xmlNewNs(root
, BAD_CAST gd_href
, BAD_CAST
"gd");
489 /* Google contact group */
490 ns2
= xmlNewNs(root
, BAD_CAST gContact_href
, BAD_CAST
"gContact");
492 xmlDocSetRootElement(doc
, root
);
494 /* category element */
495 node
= xmlNewNode(NULL
, "category");
498 xmlSetProp(node
, BAD_CAST
"scheme", BAD_CAST scheme_href
);
499 xmlSetProp(node
, BAD_CAST
"term", BAD_CAST term_href_cont
);
500 xmlAddChild(root
, node
);
502 /* entry ID, only if the 'contact' is already existant (i.e. the user
503 * of library just got one contact result from a request from
506 if (contact
->common
.id
) {
507 node
= xmlNewNode(NULL
, "id");
510 xmlNodeAddContent(node
, contact
->common
.id
);
511 xmlAddChild(root
, node
);
514 /* Sets contact structured name (Google API 3.0) */
515 if (contact
->structured_name_nr
) {
516 set_structured_entry
= 0;
517 for (this_structured_entry
= contact
->structured_name
;
518 this_structured_entry
!= NULL
;
519 this_structured_entry
= this_structured_entry
->next_field
) {
520 if ((this_structured_entry
->field_value
!= NULL
)) {
521 if( !set_structured_entry
) {
522 if (!(node
= xmlNewNode(ns
, "name")))
524 set_structured_entry
= 1;
527 if (!(child
= xmlNewNode(ns
, BAD_CAST this_structured_entry
->field_key
)))
529 xmlNodeAddContent(child
, BAD_CAST this_structured_entry
->field_value
);
530 xmlAddChild(node
, child
);
534 if( set_structured_entry
)
535 xmlAddChild(root
, node
);
536 } else if (contact
->common
.title
) {
537 node
= xmlNewNode(NULL
, "gd:name");
540 node2
= xmlNewNode(NULL
, "gd:fullName");
541 xmlNodeAddContent(node2
, contact
->common
.title
);
542 xmlAddChild(node
, node2
);
543 xmlAddChild(root
, node
);
546 /* entry edit URL, only if the 'entry' is already existant.
548 if (contact
->common
.edit_uri
) {
549 node
= xmlNewNode(NULL
, "link");
552 xmlSetProp(node
, BAD_CAST
"rel", BAD_CAST
"edit");
553 xmlSetProp(node
, BAD_CAST
"type",
554 BAD_CAST
"application/atom+xml");
555 xmlSetProp(node
, BAD_CAST
"href",
556 BAD_CAST contact
->common
.edit_uri
);
557 xmlAddChild(root
, node
);
561 /* email addresses */
562 if (contact
->emails_nr
> 0) {
563 for (i
= 0; i
< contact
->emails_nr
; i
++) {
564 if (!(node
= xmlNewNode(ns
, "email")))
566 temp
= (char *)malloc((strlen(contact
->emails_type
[i
])+strlen(rel_prefix
)+1) * sizeof(char));
567 strcpy(temp
, rel_prefix
);
568 strcat(temp
, contact
->emails_type
[i
]);
569 xmlSetProp(node
, BAD_CAST
"rel",
571 xmlSetProp(node
, BAD_CAST
"address",
572 BAD_CAST contact
->emails_field
[i
]);
573 if (i
== contact
->pref_email
)
574 xmlSetProp(node
, BAD_CAST
"primary",
576 xmlAddChild(root
, node
);
581 /* Here begin extra fields */
582 if (contact
->content
) {
583 node
= xmlNewNode(NULL
, "atom:content");
586 xmlSetProp(node
, BAD_CAST
"type", BAD_CAST
"text");
587 xmlNodeAddContent(node
, contact
->content
);
588 xmlAddChild(root
, node
);
591 if (contact
->nickname
) {
592 node
= xmlNewNode(NULL
, "gContact:nickname");
595 xmlNodeAddContent(node
, contact
->nickname
);
596 xmlAddChild(root
, node
);
599 if (contact
->homepage
) {
600 if (!(node
= xmlNewNode(NULL
, "gContact:website")))
602 xmlSetProp(node
, BAD_CAST
"rel", BAD_CAST
"home-page");
603 xmlSetProp(node
, BAD_CAST
"href", BAD_CAST contact
->homepage
);
604 xmlAddChild(root
, node
);
608 if (!(node
= xmlNewNode(NULL
, "gContact:website")))
610 xmlSetProp(node
, BAD_CAST
"rel", BAD_CAST
"blog");
611 xmlSetProp(node
, BAD_CAST
"href", BAD_CAST contact
->blog
);
612 xmlAddChild(root
, node
);
615 /* organization (it has 2 subelements: orgName, orgTitle) */
616 if (contact
->org_name
|| contact
->org_title
) {
617 if (!(node
= xmlNewNode(ns
, "organization")))
619 xmlSetProp(node
, BAD_CAST
"rel",
620 BAD_CAST
"http://schemas.google.com/g/2005#other");
622 if (contact
->org_name
) {
623 if (!(child
= xmlNewNode(ns
, "orgName")))
625 xmlNodeAddContent(child
, contact
->org_name
);
626 xmlAddChild(node
, child
);
630 if (contact
->org_title
) {
631 if (!(child
= xmlNewNode(ns
, "orgTitle")))
633 xmlNodeAddContent(child
, contact
->org_title
);
634 xmlAddChild(node
, child
);
637 xmlAddChild(root
, node
);
640 if (contact
->occupation
) {
641 node
= xmlNewNode(NULL
, "gContact:occupation");
644 xmlNodeAddContent(node
, contact
->occupation
);
645 xmlAddChild(root
, node
);
648 /* Get phone numbers */
649 if (contact
->phone_numbers_nr
> 0) {
650 for (i
= 0; i
< contact
->phone_numbers_nr
; i
++) {
651 if (!(node
= xmlNewNode(ns
, "phoneNumber")))
653 /* TODO: support user setting phone type */
655 temp
= (char *)malloc((strlen(contact
->phone_numbers_type
[i
])+strlen(rel_prefix
)+1) * sizeof(char));
656 strcpy(temp
, rel_prefix
);
657 strcat(temp
, contact
->phone_numbers_type
[i
]);
658 xmlSetProp(node
, BAD_CAST
"rel",
661 xmlNodeAddContent(node
, contact
->phone_numbers_field
[i
]);
662 xmlAddChild(root
, node
);
668 if (contact
->im_nr
> 0) {
669 for (i
= 0; i
< contact
->im_nr
; i
++) {
670 if (!(node
= xmlNewNode(ns
, "im")))
672 temp
= (char *)malloc((strlen(contact
->im_type
[i
])+strlen(rel_prefix
)+1) * sizeof(char));
673 strcpy(temp
, rel_prefix
);
674 strcat(temp
, contact
->im_type
[i
]);
675 xmlSetProp(node
, BAD_CAST
"rel",
677 temp
= (char *)malloc((strlen(contact
->im_protocol
[i
])+strlen(rel_prefix
)+1) * sizeof(char));
678 strcpy(temp
, rel_prefix
);
679 strcat(temp
, contact
->im_protocol
[i
]);
680 xmlSetProp(node
, BAD_CAST
"protocol",
682 xmlSetProp(node
, BAD_CAST
"address",
683 BAD_CAST contact
->im_address
[i
]);
684 if (i
== contact
->im_pref
)
685 xmlSetProp(node
, BAD_CAST
"primary",
687 xmlAddChild(root
, node
);
692 /* Sets contact structured postal addressees (Google API 3.0) */
693 /* TODO: move this to another function (identation is looking bad) */
694 if (contact
->structured_address_nr
> 0) {
695 for (i
= 0; i
< contact
->structured_address_nr
; i
++) {
696 set_structured_entry
= 0;
697 for (this_structured_entry
= contact
->structured_address
;
698 this_structured_entry
!= NULL
;
699 this_structured_entry
= this_structured_entry
->next_field
) {
700 if (this_structured_entry
->field_value
&&
701 this_structured_entry
->field_key
&&
702 (this_structured_entry
->field_typenr
== i
)) {
703 if (!set_structured_entry
) {
704 if (!(node
= xmlNewNode(ns
, "structuredPostalAddress")))
706 // TODO: support user settting address type
707 temp
= (char *)malloc((strlen(contact
->structured_address_type
[i
])+strlen(rel_prefix
)+2) * sizeof(char));
708 strcpy(temp
, rel_prefix
);
709 strcat(temp
, contact
->structured_address_type
[i
]);
710 xmlSetProp(node
, BAD_CAST
"rel", BAD_CAST temp
);
711 set_structured_entry
= 1;
715 if (!(child
= xmlNewNode(ns
, BAD_CAST this_structured_entry
->field_key
)))
717 xmlNodeAddContent(child
, BAD_CAST this_structured_entry
->field_value
);
718 if (i
== contact
->structured_address_pref
)
719 xmlSetProp(node
, BAD_CAST
"primary",
721 xmlAddChild(node
, child
);
725 if (set_structured_entry
)
726 xmlAddChild(root
, node
);
728 } else if (contact
->post_address
) {
729 node
= xmlNewNode(NULL
, "gd:structuredPostalAddress");
732 node2
= xmlNewNode(NULL
, "gd:formattedAddress");
733 xmlNodeAddContent(node2
, contact
->post_address
);
734 xmlAddChild(node
, node2
);
735 xmlAddChild(root
, node
);
738 /* Google group membership info */
739 if (contact
->groupMembership_nr
> 0) {
740 for (i
= 0; i
< contact
->groupMembership_nr
; i
++) {
741 if (!(node
= xmlNewNode(ns2
, "groupMembershipInfo")))
743 xmlSetProp(node
, BAD_CAST
"deleted",
745 xmlSetProp(node
, BAD_CAST
"href",
746 BAD_CAST contact
->groupMembership
[i
]);
747 xmlAddChild(root
, node
);
752 if (contact
->birthday
) {
753 /*if (!(node = xmlNewNode(NULL, BAD_CAST "gContact:birthday")))
755 xmlSetProp(node, BAD_CAST "xmlns", BAD_CAST "http://schemas.google.com/contact/2008");*/
756 if (!(node
= xmlNewNode(NULL
, "gContact:birthday")))
758 xmlSetProp(node
, BAD_CAST
"when", BAD_CAST contact
->birthday
);
759 xmlAddChild(root
, node
);
762 /* TODO: implement missing fields (which ones? geo location?)
765 xmlDocDumpMemory(doc
, &xml_str
, length
);
766 /* xmlDocDumpMemory doesn't include the last 0 in the returned size */
769 if ((*xml_contact
= strdup(xml_str
)))