Some unit test regarding the new fields.
[libgcal.git] / src / gcont.c
blob5dc53a174cc20f482862d37db74ebd1e39deca1e
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;
96 result = get_entries_number(gcalobj->document);
97 if (result == -1)
98 goto cleanup;
100 ptr_res = malloc(sizeof(struct gcal_contact) * result);
101 if (!ptr_res)
102 goto cleanup;
103 memset(ptr_res, 0, sizeof(struct gcal_contact) * result);
105 *length = result;
106 for (i = 0; i < *length; ++i) {
107 gcal_init_contact((ptr_res + i));
108 if (gcalobj->store_xml_entry)
109 (ptr_res + i)->common.store_xml = 1;
112 result = extract_all_contacts(gcalobj->document, ptr_res, *length);
113 if (result == -1) {
114 free(ptr_res);
115 ptr_res = NULL;
116 goto cleanup;
119 /* Check contacts with photo and download the pictures */
120 for (i = 0; i < *length; ++i){
121 if (ptr_res[i].photo_length) {
122 if (gcalobj->fout_log)
123 fprintf(gcalobj->fout_log,
124 "contact with photo!\n");
126 result = get_follow_redirection(gcalobj,
127 ptr_res[i].photo,
128 write_cb_binary,
129 "GData-Version: 3.0");
130 ptr_res[i].photo_data = malloc(sizeof(char) *
131 gcalobj->length);
132 if (!ptr_res[i].photo_data)
133 goto exit;
134 ptr_res[i].photo_length = gcalobj->length;
135 memcpy(ptr_res[i].photo_data, gcalobj->buffer,
136 ptr_res[i].photo_length);
138 clean_buffer(gcalobj);
140 } else if (gcalobj->fout_log)
141 fprintf(gcalobj->fout_log, "contact without photo!\n");
143 goto exit;
145 cleanup:
146 clean_dom_document(gcalobj->document);
147 gcalobj->document = NULL;
149 exit:
151 return ptr_res;
154 static void clean_string(char *ptr_str)
156 if (ptr_str)
157 free(ptr_str);
160 static void clean_multi_string(char **ptr_str, int n)
162 int i;
164 if (ptr_str) {
165 for (i = 0; i < n; i++)
166 if (ptr_str[i])
167 free(ptr_str[i]);
168 free(ptr_str);
172 void gcal_init_contact(struct gcal_contact *contact)
174 if (!contact)
175 return;
177 contact->structured_address = (struct gcal_structured_subvalues *)malloc(
178 sizeof(struct gcal_structured_subvalues));
179 contact->structured_address->field_typenr = 0;
180 contact->structured_address->field_key = NULL;
181 contact->structured_address->field_value = NULL;
182 contact->structured_address->next_field = NULL;
183 contact->structured_address_nr = 0;
184 contact->structured_address_type = NULL;
186 contact->structured_name = (struct gcal_structured_subvalues *)malloc(
187 sizeof(struct gcal_structured_subvalues));
188 contact->structured_name->field_typenr = 0;
189 contact->structured_name->field_key = NULL;
190 contact->structured_name->field_value = NULL;
191 contact->structured_name->next_field = NULL;
193 contact->common.store_xml = 0;
194 contact->common.id = contact->common.updated = NULL;
195 contact->common.title = contact->common.xml = NULL;
196 contact->common.edit_uri = contact->common.etag = NULL;
197 contact->emails_field = contact->emails_type = NULL;
198 contact->emails_nr = contact->pref_email = 0;
199 contact->content = NULL;
200 contact->nickname = NULL;
201 contact->occupation = NULL;
202 contact->org_name = contact->org_title = contact->im = NULL;
203 contact->phone_numbers_field = contact->phone_numbers_type = NULL;
204 contact->phone_numbers_nr = contact->groupMembership_nr = 0;
205 contact->post_address = NULL;
206 contact->groupMembership = NULL;
207 contact->homepage = NULL;
208 contact->blog = NULL;
209 contact->photo = contact->photo_data = NULL;
210 contact->photo_length = 0;
211 contact->birthday = NULL;
214 void gcal_destroy_contact(struct gcal_contact *contact)
216 struct gcal_structured_subvalues *temp_structured_entry;
218 if (!contact)
219 return;
221 clean_string(contact->common.id);
222 clean_string(contact->common.updated);
223 clean_string(contact->common.title);
224 clean_string(contact->common.edit_uri);
225 clean_string(contact->common.etag);
226 clean_multi_string(contact->emails_field, contact->emails_nr);
227 clean_multi_string(contact->emails_type, contact->emails_nr);
228 contact->emails_nr = contact->pref_email = 0;
229 clean_string(contact->common.xml);
231 /* Extra fields */
232 clean_string(contact->content);
233 clean_string(contact->nickname);
234 clean_string(contact->occupation);
235 clean_string(contact->org_name);
236 clean_string(contact->org_title);
237 clean_string(contact->im);
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_string(contact->post_address);
243 clean_string(contact->homepage);
244 clean_string(contact->blog);
245 clean_string(contact->photo);
246 clean_string(contact->photo_data);
247 contact->photo_length = 0;
248 clean_string(contact->birthday);
250 do {
251 temp_structured_entry = contact->structured_address;
252 if (temp_structured_entry) {
253 temp_structured_entry->field_typenr = 0;
254 clean_string(temp_structured_entry->field_key);
255 clean_string(temp_structured_entry->field_value);
256 contact->structured_address = temp_structured_entry->next_field;
257 free(temp_structured_entry);
259 } while (contact->structured_address);
261 clean_multi_string(contact->structured_address_type, contact->structured_address_nr);
262 contact->structured_address_nr = 0;
264 do {
265 temp_structured_entry = contact->structured_name;
266 if (temp_structured_entry) {
267 temp_structured_entry->field_typenr = 0;
268 clean_string(temp_structured_entry->field_key);
269 clean_string(temp_structured_entry->field_value);
270 contact->structured_name = temp_structured_entry->next_field;
271 free(temp_structured_entry);
273 } while (contact->structured_name);
276 void gcal_destroy_contacts(struct gcal_contact *contacts, size_t length)
279 size_t i = 0;
280 if (!contacts)
281 return;
283 for (; i < length; ++i)
284 gcal_destroy_contact((contacts + i));
286 free(contacts);
289 int gcal_create_contact(struct gcal_resource *gcalobj,
290 struct gcal_contact *contact,
291 struct gcal_contact *updated)
293 int result = -1, length;
294 char *xml_contact = NULL, *buffer;
296 if ((!contact) || (!gcalobj))
297 return result;
299 result = xmlcontact_create(contact, &xml_contact, &length);
300 if (result == -1)
301 goto exit;
303 /* Mounts URL */
304 length = sizeof(GCONTACT_START) + sizeof(GCONTACT_END) +
305 strlen(gcalobj->user) + sizeof(GCAL_DELIMITER) +
306 strlen(gcalobj->domain) + 1;
307 buffer = (char *) malloc(length);
308 if (!buffer)
309 goto cleanup;
311 snprintf(buffer, length - 1, "%s%s%s%s%s", GCONTACT_START,
312 gcalobj->user, GCAL_DELIMITER, gcalobj->domain, GCONTACT_END);
314 result = up_entry(xml_contact, strlen(xml_contact), gcalobj,
315 buffer, NULL, POST, NULL, GCAL_EDIT_ANSWER);
316 if (result)
317 goto cleanup;
319 /* Copy raw XML */
320 if (gcalobj->store_xml_entry) {
321 if (contact->common.xml)
322 free(contact->common.xml);
323 if (!(contact->common.xml = strdup(gcalobj->buffer)))
324 goto cleanup;
327 /* Parse buffer and create the new contact object */
328 if (!updated)
329 goto cleanup;
330 result = -2;
331 gcalobj->document = build_dom_document(gcalobj->buffer);
332 if (!gcalobj->document)
333 goto cleanup;
335 /* There is only one 'entry' in the buffer */
336 gcal_init_contact(updated);
337 result = extract_all_contacts(gcalobj->document, updated, 1);
338 if (result == -1)
339 goto xmlclean;
341 /* Adding photo is the same as an edit operation */
342 if (contact->photo_length) {
343 result = up_entry(contact->photo_data, contact->photo_length,
344 gcalobj, updated->photo,
345 /* Google Data API 2.0 requires ETag */
346 "If-Match: *",
347 PUT, "Content-Type: image/*",
348 GCAL_DEFAULT_ANSWER);
349 if (result)
350 goto cleanup;
353 result = 0;
355 xmlclean:
356 clean_dom_document(gcalobj->document);
357 gcalobj->document = NULL;
359 cleanup:
360 if (xml_contact)
361 free(xml_contact);
362 if (buffer)
363 free(buffer);
365 exit:
366 return result;
370 int gcal_delete_contact(struct gcal_resource *gcalobj,
371 struct gcal_contact *contact)
373 int result = -1, length;
374 char *h_auth;
376 if (!contact || !gcalobj)
377 goto exit;
379 /* Must cleanup HTTP buffer between requests */
380 clean_buffer(gcalobj);
382 /* TODO: add X-HTTP header */
383 length = strlen(gcalobj->auth) + sizeof(HEADER_GET) + 1;
384 h_auth = (char *) malloc(length);
385 if (!h_auth)
386 goto exit;
387 snprintf(h_auth, length - 1, "%s%s", HEADER_GET, gcalobj->auth);
389 curl_easy_setopt(gcalobj->curl, CURLOPT_CUSTOMREQUEST, "DELETE");
390 result = http_post(gcalobj, contact->common.edit_uri,
391 "Content-Type: application/atom+xml",
392 /* Google Data API 2.0 requires ETag */
393 "If-Match: *",
394 h_auth,
395 NULL, NULL, 0, GCAL_DEFAULT_ANSWER,
396 "GData-Version: 3.0");
398 /* Restores curl context to previous standard mode */
399 curl_easy_setopt(gcalobj->curl, CURLOPT_CUSTOMREQUEST, NULL);
401 if (h_auth)
402 free(h_auth);
404 exit:
406 return result;
409 int gcal_edit_contact(struct gcal_resource *gcalobj,
410 struct gcal_contact *contact,
411 struct gcal_contact *updated)
414 int result = -1, length;
415 char *xml_contact = NULL;
417 if ((!contact) || (!gcalobj))
418 goto exit;
420 result = xmlcontact_create(contact, &xml_contact, &length);
421 if (result == -1)
422 goto exit;
424 result = up_entry(xml_contact, strlen(xml_contact), gcalobj,
425 contact->common.edit_uri,
426 /* Google Data API 2.0 requires ETag */
427 "If-Match: *",
428 PUT, NULL, GCAL_DEFAULT_ANSWER);
429 if (result)
430 goto cleanup;
432 /* Copy raw XML */
433 if (gcalobj->store_xml_entry) {
434 if (contact->common.xml)
435 free(contact->common.xml);
436 if (!(contact->common.xml = strdup(gcalobj->buffer)))
437 goto cleanup;
440 /* Parse buffer and create the new contact object */
441 if (!updated)
442 goto cleanup;
443 result = -2;
444 gcalobj->document = build_dom_document(gcalobj->buffer);
445 if (!gcalobj->document)
446 goto cleanup;
448 /* There is only one 'entry' in the buffer */
449 gcal_init_contact(updated);
450 result = extract_all_contacts(gcalobj->document, updated, 1);
451 if (result == -1)
452 goto xmlclean;
454 /* Adding photo is the same as an edit operation */
455 if (contact->photo_length) {
456 result = up_entry(contact->photo_data, contact->photo_length,
457 gcalobj, updated->photo,
458 /* Google Data API 2.0 requires ETag */
459 "If-Match: *",
460 PUT, "Content-Type: image/*",
461 GCAL_DEFAULT_ANSWER);
462 if (result)
463 goto cleanup;
467 result = 0;
470 xmlclean:
471 clean_dom_document(gcalobj->document);
472 gcalobj->document = NULL;
474 cleanup:
476 if (xml_contact)
477 free(xml_contact);
479 exit:
480 return result;