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
33 * @date Fri May 30 15:30:35 2008
35 * @brief Base file for google contacts service access library.
38 * - for some firewalls, X-HTTP-Method-Override: DELETE can be required
42 #include "internal_gcal.h"
44 #include "gcal_parser.h"
45 #include "msvc_hacks.h"
48 static size_t write_cb_binary(void *ptr
, size_t count
, size_t chunk_size
,
52 size_t size
= count
* chunk_size
;
53 struct gcal_resource
*gcal_ptr
= (struct gcal_resource
*)data
;
56 if (size
> (gcal_ptr
->length
- gcal_ptr
->previous_length
- 1)) {
57 gcal_ptr
->length
= gcal_ptr
->length
+ size
+ 1;
58 ptr_tmp
= realloc(gcal_ptr
->buffer
, gcal_ptr
->length
);
61 if (gcal_ptr
->fout_log
)
62 fprintf(gcal_ptr
->fout_log
,
63 "write_cb: Failed relloc!\n");
67 gcal_ptr
->buffer
= ptr_tmp
;
70 memcpy(gcal_ptr
->buffer
+ gcal_ptr
->previous_length
, ptr
, size
);
71 gcal_ptr
->previous_length
+= size
;
77 struct gcal_contact
*gcal_get_all_contacts(struct gcal_resource
*gcalobj
,
83 struct gcal_contact
*ptr_res
= NULL
;
88 if (!gcalobj
->buffer
|| !gcalobj
->has_xml
)
91 gcalobj
->document
= build_dom_document(gcalobj
->buffer
);
92 if (!gcalobj
->document
)
95 result
= get_entries_number(gcalobj
->document
);
99 ptr_res
= malloc(sizeof(struct gcal_contact
) * result
);
102 memset(ptr_res
, 0, sizeof(struct gcal_contact
) * result
);
105 for (i
= 0; i
< *length
; ++i
) {
106 gcal_init_contact((ptr_res
+ i
));
107 if (gcalobj
->store_xml_entry
)
108 (ptr_res
+ i
)->common
.store_xml
= 1;
111 result
= extract_all_contacts(gcalobj
->document
, ptr_res
, *length
);
118 /* Check contacts with photo and download the pictures */
119 for (i
= 0; i
< *length
; ++i
){
120 if (ptr_res
[i
].photo_length
) {
121 if (gcalobj
->fout_log
)
122 fprintf(gcalobj
->fout_log
,
123 "contact with photo!\n");
125 result
= get_follow_redirection(gcalobj
,
128 "GData-Version: 3.0");
129 ptr_res
[i
].photo_data
= malloc(sizeof(char) *
131 if (!ptr_res
[i
].photo_data
)
133 ptr_res
[i
].photo_length
= gcalobj
->length
;
134 memcpy(ptr_res
[i
].photo_data
, gcalobj
->buffer
,
135 ptr_res
[i
].photo_length
);
137 clean_buffer(gcalobj
);
139 } else if (gcalobj
->fout_log
)
140 fprintf(gcalobj
->fout_log
, "contact without photo!\n");
145 clean_dom_document(gcalobj
->document
);
146 gcalobj
->document
= NULL
;
153 static void clean_string(char *ptr_str
)
159 static void clean_multi_string(char **ptr_str
, int n
)
164 for (i
= 0; i
< n
; i
++)
171 void gcal_init_contact(struct gcal_contact
*contact
)
176 contact
->structured_address
= (struct gcal_structured_subvalues
*)malloc(
177 sizeof(struct gcal_structured_subvalues
));
178 contact
->structured_address
->field_typenr
= 0;
179 contact
->structured_address
->field_key
= NULL
;
180 contact
->structured_address
->field_value
= NULL
;
181 contact
->structured_address
->next_field
= NULL
;
182 contact
->structured_address_nr
= contact
->structured_address_pref
= 0;
183 contact
->structured_address_type
= NULL
;
185 contact
->structured_name
= (struct gcal_structured_subvalues
*)malloc(
186 sizeof(struct gcal_structured_subvalues
));
187 contact
->structured_name
->field_typenr
= 0;
188 contact
->structured_name
->field_key
= NULL
;
189 contact
->structured_name
->field_value
= NULL
;
190 contact
->structured_name
->next_field
= NULL
;
192 contact
->common
.store_xml
= 0;
193 contact
->common
.id
= contact
->common
.updated
= NULL
;
194 contact
->common
.title
= contact
->common
.xml
= NULL
;
195 contact
->common
.edit_uri
= contact
->common
.etag
= NULL
;
196 contact
->emails_field
= contact
->emails_type
= NULL
;
197 contact
->emails_nr
= contact
->pref_email
= 0;
198 contact
->content
= NULL
;
199 contact
->nickname
= NULL
;
200 contact
->occupation
= NULL
;
201 contact
->org_name
= contact
->org_title
= NULL
;
202 contact
->phone_numbers_field
= contact
->phone_numbers_type
= NULL
;
203 contact
->phone_numbers_nr
= contact
->groupMembership_nr
= 0;
204 contact
->im_protocol
= contact
->im_address
= contact
->im_type
= NULL
;
205 contact
->im_nr
= contact
->im_pref
= 0;
206 contact
->post_address
= NULL
;
207 contact
->groupMembership
= NULL
;
208 contact
->homepage
= NULL
;
209 contact
->blog
= NULL
;
210 contact
->photo
= contact
->photo_data
= NULL
;
211 contact
->photo_length
= 0;
212 contact
->birthday
= NULL
;
215 void gcal_destroy_contact(struct gcal_contact
*contact
)
217 struct gcal_structured_subvalues
*temp_structured_entry
;
222 clean_string(contact
->common
.id
);
223 clean_string(contact
->common
.updated
);
224 clean_string(contact
->common
.title
);
225 clean_string(contact
->common
.edit_uri
);
226 clean_string(contact
->common
.etag
);
227 clean_multi_string(contact
->emails_field
, contact
->emails_nr
);
228 clean_multi_string(contact
->emails_type
, contact
->emails_nr
);
229 contact
->emails_nr
= contact
->pref_email
= 0;
230 clean_string(contact
->common
.xml
);
233 clean_string(contact
->content
);
234 clean_string(contact
->nickname
);
235 clean_string(contact
->occupation
);
236 clean_string(contact
->org_name
);
237 clean_string(contact
->org_title
);
238 clean_multi_string(contact
->phone_numbers_field
, contact
->phone_numbers_nr
);
239 clean_multi_string(contact
->phone_numbers_type
, contact
->phone_numbers_nr
);
240 clean_multi_string(contact
->groupMembership
, contact
->groupMembership_nr
);
241 contact
->phone_numbers_nr
= contact
->groupMembership_nr
= 0;
242 clean_multi_string(contact
->im_protocol
, contact
->im_nr
);
243 clean_multi_string(contact
->im_address
, contact
->im_nr
);
244 clean_multi_string(contact
->im_type
, contact
->im_nr
);
245 contact
->im_nr
= contact
->im_pref
= 0;
246 clean_string(contact
->post_address
);
247 clean_string(contact
->homepage
);
248 clean_string(contact
->blog
);
249 clean_string(contact
->photo
);
250 clean_string(contact
->photo_data
);
251 contact
->photo_length
= 0;
252 clean_string(contact
->birthday
);
255 temp_structured_entry
= contact
->structured_address
;
256 if (temp_structured_entry
) {
257 temp_structured_entry
->field_typenr
= 0;
258 clean_string(temp_structured_entry
->field_key
);
259 clean_string(temp_structured_entry
->field_value
);
260 contact
->structured_address
= temp_structured_entry
->next_field
;
261 free(temp_structured_entry
);
263 } while (contact
->structured_address
);
264 free(contact
->structured_address
);
266 clean_multi_string(contact
->structured_address_type
, contact
->structured_address_nr
);
267 contact
->structured_address_nr
= 0;
268 contact
->structured_address_pref
= 0;
271 temp_structured_entry
= contact
->structured_name
;
272 if (temp_structured_entry
) {
273 temp_structured_entry
->field_typenr
= 0;
274 clean_string(temp_structured_entry
->field_key
);
275 clean_string(temp_structured_entry
->field_value
);
276 contact
->structured_name
= temp_structured_entry
->next_field
;
277 free(temp_structured_entry
);
279 } while (contact
->structured_name
);
280 free(contact
->structured_name
);
283 void gcal_destroy_contacts(struct gcal_contact
*contacts
, size_t length
)
290 for (; i
< length
; ++i
)
291 gcal_destroy_contact((contacts
+ i
));
296 int gcal_create_contact(struct gcal_resource
*gcalobj
,
297 struct gcal_contact
*contact
,
298 struct gcal_contact
*updated
)
300 int result
= -1, length
;
301 char *xml_contact
= NULL
, *buffer
;
303 if ((!contact
) || (!gcalobj
))
306 result
= xmlcontact_create(contact
, &xml_contact
, &length
);
311 length
= sizeof(GCONTACT_START
) + sizeof(GCONTACT_END
) +
312 strlen(gcalobj
->user
) + sizeof(GCAL_DELIMITER
) +
313 strlen(gcalobj
->domain
) + 1;
314 buffer
= (char *) malloc(length
);
318 snprintf(buffer
, length
- 1, "%s%s%s%s%s", GCONTACT_START
,
319 gcalobj
->user
, GCAL_DELIMITER
, gcalobj
->domain
, GCONTACT_END
);
321 result
= up_entry(xml_contact
, strlen(xml_contact
), gcalobj
,
322 buffer
, NULL
, POST
, NULL
, GCAL_EDIT_ANSWER
);
327 if (gcalobj
->store_xml_entry
) {
328 if (contact
->common
.xml
)
329 free(contact
->common
.xml
);
330 if (!(contact
->common
.xml
= strdup(gcalobj
->buffer
)))
334 /* Parse buffer and create the new contact object */
338 gcalobj
->document
= build_dom_document(gcalobj
->buffer
);
339 if (!gcalobj
->document
)
342 /* There is only one 'entry' in the buffer */
343 gcal_init_contact(updated
);
344 result
= extract_all_contacts(gcalobj
->document
, updated
, 1);
348 /* Adding photo is the same as an edit operation */
349 if (contact
->photo_length
) {
350 result
= up_entry(contact
->photo_data
, contact
->photo_length
,
351 gcalobj
, updated
->photo
,
352 /* Google Data API 2.0 requires ETag */
354 PUT
, "Content-Type: image/*",
355 GCAL_DEFAULT_ANSWER
);
363 clean_dom_document(gcalobj
->document
);
364 gcalobj
->document
= NULL
;
377 int gcal_delete_contact(struct gcal_resource
*gcalobj
,
378 struct gcal_contact
*contact
)
380 int result
= -1, length
;
383 if (!contact
|| !gcalobj
)
386 /* Must cleanup HTTP buffer between requests */
387 clean_buffer(gcalobj
);
389 /* TODO: add X-HTTP header */
390 length
= strlen(gcalobj
->auth
) + sizeof(HEADER_GET
) + 1;
391 h_auth
= (char *) malloc(length
);
394 snprintf(h_auth
, length
- 1, "%s%s", HEADER_GET
, gcalobj
->auth
);
396 curl_easy_setopt(gcalobj
->curl
, CURLOPT_CUSTOMREQUEST
, "DELETE");
397 result
= http_post(gcalobj
, contact
->common
.edit_uri
,
398 "Content-Type: application/atom+xml",
399 /* Google Data API 2.0 requires ETag */
402 NULL
, NULL
, 0, GCAL_DEFAULT_ANSWER
,
403 "GData-Version: 3.0");
405 /* Restores curl context to previous standard mode */
406 curl_easy_setopt(gcalobj
->curl
, CURLOPT_CUSTOMREQUEST
, NULL
);
416 int gcal_edit_contact(struct gcal_resource
*gcalobj
,
417 struct gcal_contact
*contact
,
418 struct gcal_contact
*updated
)
421 int result
= -1, length
;
422 char *xml_contact
= NULL
;
424 if ((!contact
) || (!gcalobj
))
427 result
= xmlcontact_create(contact
, &xml_contact
, &length
);
431 result
= up_entry(xml_contact
, strlen(xml_contact
), gcalobj
,
432 contact
->common
.edit_uri
,
433 /* Google Data API 2.0 requires ETag */
435 PUT
, NULL
, GCAL_DEFAULT_ANSWER
);
440 if (gcalobj
->store_xml_entry
) {
441 if (contact
->common
.xml
)
442 free(contact
->common
.xml
);
443 if (!(contact
->common
.xml
= strdup(gcalobj
->buffer
)))
447 /* Parse buffer and create the new contact object */
451 gcalobj
->document
= build_dom_document(gcalobj
->buffer
);
452 if (!gcalobj
->document
)
455 /* There is only one 'entry' in the buffer */
456 gcal_init_contact(updated
);
457 result
= extract_all_contacts(gcalobj
->document
, updated
, 1);
461 /* Adding photo is the same as an edit operation */
462 if (contact
->photo_length
) {
463 result
= up_entry(contact
->photo_data
, contact
->photo_length
,
464 gcalobj
, updated
->photo
,
465 /* Google Data API 2.0 requires ETag */
467 PUT
, "Content-Type: image/*",
468 GCAL_DEFAULT_ANSWER
);
478 clean_dom_document(gcalobj
->document
);
479 gcalobj
->document
= NULL
;