Unit test stuff on new contact fields.
[libgcal.git] / src / gcont.c
blobef8a835ebe9f3c01d862998d181440f1b139f512
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 gcontact.c
32 * @author Adenilson Cavalcanti
33 * @date Fri May 30 15:30:35 2008
35 * @brief Base file for google contacts service access library.
37 * \todo:
38 * - for some firewalls, X-HTTP-Method-Override: DELETE can be required
41 #include <string.h>
42 #include "internal_gcal.h"
43 #include "gcontact.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,
49 void *data)
52 size_t size = count * chunk_size;
53 struct gcal_resource *gcal_ptr = (struct gcal_resource *)data;
54 char *ptr_tmp;
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);
60 if (!ptr_tmp) {
61 if (gcal_ptr->fout_log)
62 fprintf(gcal_ptr->fout_log,
63 "write_cb: Failed relloc!\n");
64 goto exit;
67 gcal_ptr->buffer = ptr_tmp;
70 memcpy(gcal_ptr->buffer + gcal_ptr->previous_length, ptr, size);
71 gcal_ptr->previous_length += size;
73 exit:
74 return size;
77 struct gcal_contact *gcal_get_all_contacts(struct gcal_resource *gcalobj,
78 size_t *length)
81 int result = -1;
82 size_t i = 0;
83 struct gcal_contact *ptr_res = NULL;
85 if (!gcalobj)
86 goto exit;
88 if (!gcalobj->buffer || !gcalobj->has_xml)
89 goto exit;
91 gcalobj->document = build_dom_document(gcalobj->buffer);
92 if (!gcalobj->document)
93 goto exit;
95 result = get_entries_number(gcalobj->document);
96 if (result == -1)
97 goto cleanup;
99 ptr_res = malloc(sizeof(struct gcal_contact) * result);
100 if (!ptr_res)
101 goto cleanup;
102 memset(ptr_res, 0, sizeof(struct gcal_contact) * result);
104 *length = 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);
112 if (result == -1) {
113 free(ptr_res);
114 ptr_res = NULL;
115 goto cleanup;
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,
126 ptr_res[i].photo,
127 write_cb_binary,
128 "GData-Version: 3.0");
129 ptr_res[i].photo_data = malloc(sizeof(char) *
130 gcalobj->length);
131 if (!ptr_res[i].photo_data)
132 goto exit;
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");
142 goto exit;
144 cleanup:
145 clean_dom_document(gcalobj->document);
146 gcalobj->document = NULL;
148 exit:
150 return ptr_res;
153 static void clean_string(char *ptr_str)
155 if (ptr_str)
156 free(ptr_str);
159 static void clean_multi_string(char **ptr_str, int n)
161 int i;
163 if (ptr_str) {
164 for (i = 0; i < n; i++)
165 if (ptr_str[i])
166 free(ptr_str[i]);
167 free(ptr_str);
171 void gcal_init_contact(struct gcal_contact *contact)
173 if (!contact)
174 return;
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;
219 if (!contact)
220 return;
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);
232 /* Extra fields */
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);
254 do {
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;
270 do {
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)
286 size_t i = 0;
287 if (!contacts)
288 return;
290 for (; i < length; ++i)
291 gcal_destroy_contact((contacts + i));
293 free(contacts);
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))
304 return result;
306 result = xmlcontact_create(contact, &xml_contact, &length);
307 if (result == -1)
308 goto exit;
310 /* Mounts URL */
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);
315 if (!buffer)
316 goto cleanup;
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);
323 if (result)
324 goto cleanup;
326 /* Copy raw XML */
327 if (gcalobj->store_xml_entry) {
328 if (contact->common.xml)
329 free(contact->common.xml);
330 if (!(contact->common.xml = strdup(gcalobj->buffer)))
331 goto cleanup;
334 /* Parse buffer and create the new contact object */
335 if (!updated)
336 goto cleanup;
337 result = -2;
338 gcalobj->document = build_dom_document(gcalobj->buffer);
339 if (!gcalobj->document)
340 goto cleanup;
342 /* There is only one 'entry' in the buffer */
343 gcal_init_contact(updated);
344 result = extract_all_contacts(gcalobj->document, updated, 1);
345 if (result == -1)
346 goto xmlclean;
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 */
353 "If-Match: *",
354 PUT, "Content-Type: image/*",
355 GCAL_DEFAULT_ANSWER);
356 if (result)
357 goto cleanup;
360 result = 0;
362 xmlclean:
363 clean_dom_document(gcalobj->document);
364 gcalobj->document = NULL;
366 cleanup:
367 if (xml_contact)
368 free(xml_contact);
369 if (buffer)
370 free(buffer);
372 exit:
373 return result;
377 int gcal_delete_contact(struct gcal_resource *gcalobj,
378 struct gcal_contact *contact)
380 int result = -1, length;
381 char *h_auth;
383 if (!contact || !gcalobj)
384 goto exit;
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);
392 if (!h_auth)
393 goto exit;
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 */
400 "If-Match: *",
401 h_auth,
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);
408 if (h_auth)
409 free(h_auth);
411 exit:
413 return result;
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))
425 goto exit;
427 result = xmlcontact_create(contact, &xml_contact, &length);
428 if (result == -1)
429 goto exit;
431 result = up_entry(xml_contact, strlen(xml_contact), gcalobj,
432 contact->common.edit_uri,
433 /* Google Data API 2.0 requires ETag */
434 "If-Match: *",
435 PUT, NULL, GCAL_DEFAULT_ANSWER);
436 if (result)
437 goto cleanup;
439 /* Copy raw XML */
440 if (gcalobj->store_xml_entry) {
441 if (contact->common.xml)
442 free(contact->common.xml);
443 if (!(contact->common.xml = strdup(gcalobj->buffer)))
444 goto cleanup;
447 /* Parse buffer and create the new contact object */
448 if (!updated)
449 goto cleanup;
450 result = -2;
451 gcalobj->document = build_dom_document(gcalobj->buffer);
452 if (!gcalobj->document)
453 goto cleanup;
455 /* There is only one 'entry' in the buffer */
456 gcal_init_contact(updated);
457 result = extract_all_contacts(gcalobj->document, updated, 1);
458 if (result == -1)
459 goto xmlclean;
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 */
466 "If-Match: *",
467 PUT, "Content-Type: image/*",
468 GCAL_DEFAULT_ANSWER);
469 if (result)
470 goto cleanup;
474 result = 0;
477 xmlclean:
478 clean_dom_document(gcalobj->document);
479 gcalobj->document = NULL;
481 cleanup:
483 if (xml_contact)
484 free(xml_contact);
486 exit:
487 return result;