Added/fixed support for some fields.
[libgcal.git] / src / gcont.c
blobfc2bcded47b2aaad3b61ef5fbe34d82280299982
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_postal_address *)malloc(sizeof(struct gcal_structured_postal_address));
178 contact->structured_address->address_field_key = NULL;
179 contact->structured_address->address_field_value = NULL;
180 contact->structured_address->next_address_field = NULL;
182 contact->common.store_xml = 0;
183 contact->common.id = contact->common.updated = NULL;
184 contact->common.title = contact->common.xml = NULL;
185 contact->common.edit_uri = contact->common.etag = NULL;
186 contact->emails_field = contact->emails_type = NULL;
187 contact->emails_nr = contact->pref_email = 0;
188 contact->content = NULL;
189 contact->org_name = contact->org_title = contact->im = NULL;
190 contact->phone_numbers_field = contact->phone_numbers_type = NULL;
191 contact->phone_numbers_nr = contact->groupMembership_nr = 0;
192 contact->post_address = NULL;
193 contact->groupMembership = NULL;
194 contact->homepage = NULL;
195 contact->blog = NULL;
196 contact->photo = contact->photo_data = NULL;
197 contact->photo_length = 0;
198 contact->birthday = NULL;
201 void gcal_destroy_contact(struct gcal_contact *contact)
203 struct gcal_structured_postal_address *temp_structured_address = contact->structured_address;
204 if (!contact)
205 return;
207 clean_string(contact->common.id);
208 clean_string(contact->common.updated);
209 clean_string(contact->common.title);
210 clean_string(contact->common.edit_uri);
211 clean_string(contact->common.etag);
212 clean_multi_string(contact->emails_field, contact->emails_nr);
213 clean_multi_string(contact->emails_type, contact->emails_nr);
214 contact->emails_nr = contact->pref_email = 0;
215 clean_string(contact->common.xml);
217 /* Extra fields */
218 clean_string(contact->content);
219 clean_string(contact->org_name);
220 clean_string(contact->org_title);
221 clean_string(contact->im);
222 clean_multi_string(contact->phone_numbers_field, contact->phone_numbers_nr);
223 clean_multi_string(contact->phone_numbers_type, contact->phone_numbers_nr);
224 clean_multi_string(contact->groupMembership, contact->groupMembership_nr);
225 contact->phone_numbers_nr = contact->groupMembership_nr = 0;
226 clean_string(contact->post_address);
227 clean_string(contact->homepage);
228 clean_string(contact->blog);
229 clean_string(contact->photo);
230 clean_string(contact->photo_data);
231 contact->photo_length = 0;
232 clean_string(contact->birthday);
234 while (temp_structured_address != NULL)
236 struct gcal_structured_postal_address *next_structured_address = temp_structured_address->next_address_field;
237 clean_string(temp_structured_address->address_field_value);
238 clean_string(temp_structured_address->address_field_key);
239 free(temp_structured_address);
240 temp_structured_address = next_structured_address;
244 void gcal_destroy_contacts(struct gcal_contact *contacts, size_t length)
247 size_t i = 0;
248 if (!contacts)
249 return;
251 for (; i < length; ++i)
252 gcal_destroy_contact((contacts + i));
254 free(contacts);
257 int gcal_create_contact(struct gcal_resource *gcalobj,
258 struct gcal_contact *contact,
259 struct gcal_contact *updated)
261 int result = -1, length;
262 char *xml_contact = NULL, *buffer;
264 if ((!contact) || (!gcalobj))
265 return result;
267 result = xmlcontact_create(contact, &xml_contact, &length);
268 if (result == -1)
269 goto exit;
271 /* Mounts URL */
272 length = sizeof(GCONTACT_START) + sizeof(GCONTACT_END) +
273 strlen(gcalobj->user) + sizeof(GCAL_DELIMITER) +
274 strlen(gcalobj->domain) + 1;
275 buffer = (char *) malloc(length);
276 if (!buffer)
277 goto cleanup;
278 snprintf(buffer, length - 1, "%s%s%s%s%s", GCONTACT_START,
279 gcalobj->user, GCAL_DELIMITER, gcalobj->domain, GCONTACT_END);
281 result = up_entry(xml_contact, strlen(xml_contact), gcalobj,
282 buffer, NULL, POST, NULL, GCAL_EDIT_ANSWER);
283 if (result)
284 goto cleanup;
286 /* Copy raw XML */
287 if (gcalobj->store_xml_entry) {
288 if (contact->common.xml)
289 free(contact->common.xml);
290 if (!(contact->common.xml = strdup(gcalobj->buffer)))
291 goto cleanup;
294 /* Parse buffer and create the new contact object */
295 if (!updated)
296 goto cleanup;
297 result = -2;
298 gcalobj->document = build_dom_document(gcalobj->buffer);
299 if (!gcalobj->document)
300 goto cleanup;
302 /* There is only one 'entry' in the buffer */
303 gcal_init_contact(updated);
304 result = extract_all_contacts(gcalobj->document, updated, 1);
305 if (result == -1)
306 goto xmlclean;
308 /* Adding photo is the same as an edit operation */
309 if (contact->photo_length) {
310 result = up_entry(contact->photo_data, contact->photo_length,
311 gcalobj, updated->photo, NULL,
312 PUT, "Content-Type: image/*",
313 GCAL_DEFAULT_ANSWER);
314 if (result)
315 goto cleanup;
319 result = 0;
322 xmlclean:
323 clean_dom_document(gcalobj->document);
324 gcalobj->document = NULL;
326 cleanup:
327 if (xml_contact)
328 free(xml_contact);
329 if (buffer)
330 free(buffer);
332 exit:
333 return result;
337 int gcal_delete_contact(struct gcal_resource *gcalobj,
338 struct gcal_contact *contact)
340 int result = -1, length;
341 char *h_auth;
343 if (!contact || !gcalobj)
344 goto exit;
346 /* Must cleanup HTTP buffer between requests */
347 clean_buffer(gcalobj);
349 /* TODO: add X-HTTP header */
350 length = strlen(gcalobj->auth) + sizeof(HEADER_GET) + 1;
351 h_auth = (char *) malloc(length);
352 if (!h_auth)
353 goto exit;
354 snprintf(h_auth, length - 1, "%s%s", HEADER_GET, gcalobj->auth);
356 curl_easy_setopt(gcalobj->curl, CURLOPT_CUSTOMREQUEST, "DELETE");
357 result = http_post(gcalobj, contact->common.edit_uri,
358 "Content-Type: application/atom+xml",
359 /* Google Data API 2.0 requires ETag */
360 "If-Match: *",
361 h_auth,
362 NULL, NULL, 0, GCAL_DEFAULT_ANSWER,
363 "GData-Version: 3.0");
365 /* Restores curl context to previous standard mode */
366 curl_easy_setopt(gcalobj->curl, CURLOPT_CUSTOMREQUEST, NULL);
368 if (h_auth)
369 free(h_auth);
371 exit:
373 return result;
376 int gcal_edit_contact(struct gcal_resource *gcalobj,
377 struct gcal_contact *contact,
378 struct gcal_contact *updated)
381 int result = -1, length;
382 char *xml_contact = NULL;
384 if ((!contact) || (!gcalobj))
385 goto exit;
387 result = xmlcontact_create(contact, &xml_contact, &length);
388 if (result == -1)
389 goto exit;
391 result = up_entry(xml_contact, strlen(xml_contact), gcalobj,
392 contact->common.edit_uri,
393 /* Google Data API 2.0 requires ETag */
394 "If-Match: *",
395 PUT, NULL, GCAL_DEFAULT_ANSWER);
396 if (result)
397 goto cleanup;
399 /* Copy raw XML */
400 if (gcalobj->store_xml_entry) {
401 if (contact->common.xml)
402 free(contact->common.xml);
403 if (!(contact->common.xml = strdup(gcalobj->buffer)))
404 goto cleanup;
407 /* Parse buffer and create the new contact object */
408 if (!updated)
409 goto cleanup;
410 result = -2;
411 gcalobj->document = build_dom_document(gcalobj->buffer);
412 if (!gcalobj->document)
413 goto cleanup;
415 /* There is only one 'entry' in the buffer */
416 gcal_init_contact(updated);
417 result = extract_all_contacts(gcalobj->document, updated, 1);
418 if (result == -1)
419 goto xmlclean;
421 /* Adding photo is the same as an edit operation */
422 if (contact->photo_length) {
423 result = up_entry(contact->photo_data, contact->photo_length,
424 gcalobj, updated->photo,
425 /* Google Data API 2.0 requires ETag */
426 "If-Match: *",
427 PUT, "Content-Type: image/*",
428 GCAL_DEFAULT_ANSWER);
429 if (result)
430 goto cleanup;
434 result = 0;
437 xmlclean:
438 clean_dom_document(gcalobj->document);
439 gcalobj->document = NULL;
441 cleanup:
443 if (xml_contact)
444 free(xml_contact);
446 exit:
447 return result;