Reporting contacts entries to opensync when doing slow sync.
[gdataplugin.git] / src / gcalendar.c
blobf1480e6fbcf72b2ffea9593784ce378b32910490
1 /** Google Calendar plugin
3 * Copyright (c) 2006 Eduardo Pereira Habkost <ehabkost@raisama.net>
4 * Copyright (c) 2008 Adenilson Cavalcanti da Silva <adenilson.silva@indt.org.br>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA
23 /* TODO:
24 * - remove all fprintf's and use osync_trace(TRACE_INTERNAL, ...) instead
25 * - getchanges: convert gdata XML to osync using 'xslt_transform'
26 * - getchanges: report all entries to opensync
27 * - getchanges: implement getupdated for fast sync
28 * - test if it works inside opensync (all tests for while are running with 'osyncplugin'
29 * helper tool)
30 * - commitchanges: well... write the code
31 * - review code for leaks (I'm not sure if I'm using opensync API correctly...)
35 #include <opensync/opensync.h>
36 #include <opensync/opensync-plugin.h>
37 #include <opensync/opensync-helper.h>
38 #include <opensync/opensync-merger.h>
39 #include <opensync/opensync-format.h>
40 #include <opensync/opensync-data.h>
41 #include <opensync/opensync-version.h>
43 #include <glib.h>
45 #include <libxml/tree.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sys/types.h>
51 #include <signal.h>
52 #include <sys/wait.h>
53 #include <gcal_status.h>
54 #include <gcalendar.h>
55 #include <gcontact.h>
56 #include "xslt_aux.h"
58 struct gc_plgdata
60 char *url;
61 char *username;
62 char *password;
63 char *anchor_path;
64 char *timestamp;
65 char *timezone;
66 char *xslt_path;
67 /* libgcal resources */
68 gcal_t calendar;
69 gcal_t contacts;
70 struct gcal_event_array all_events;
71 struct gcal_contact_array all_contacts;
72 /* calendar sink/format */
73 OSyncObjTypeSink *gcal_sink;
74 OSyncObjFormat *gcal_format;
75 /* contact sink/format */
76 OSyncObjTypeSink *gcont_sink;
77 OSyncObjFormat *gcont_format;
78 /* XSLT context resource struct */
79 struct xslt_resources *xslt_ctx_gcal;
80 struct xslt_resources *xslt_ctx_gcont;
83 static void free_plg(struct gc_plgdata *plgdata)
85 if (plgdata->calendar) {
86 gcal_delete(plgdata->calendar);
87 gcal_cleanup_events(&(plgdata->all_events));
89 if (plgdata->contacts) {
90 gcal_delete(plgdata->contacts);
91 gcal_cleanup_contacts(&(plgdata->all_contacts));
94 if (plgdata->xslt_path)
95 free(plgdata->xslt_path);
96 if (plgdata->xslt_ctx_gcal)
97 xslt_delete(plgdata->xslt_ctx_gcal);
98 if (plgdata->xslt_ctx_gcont)
99 xslt_delete(plgdata->xslt_ctx_gcont);
100 if (plgdata->timestamp)
101 free(plgdata->timestamp);
102 if (plgdata->timezone)
103 free(plgdata->timezone);
104 if (plgdata->url)
105 xmlFree(plgdata->url);
106 if (plgdata->username)
107 xmlFree(plgdata->username);
108 if (plgdata->password)
109 xmlFree(plgdata->password);
110 if (plgdata->gcal_sink)
111 osync_objtype_sink_unref(plgdata->gcal_sink);
112 if (plgdata->gcal_format)
113 osync_objformat_unref(plgdata->gcal_format);
114 if (plgdata->gcont_sink)
115 osync_objtype_sink_unref(plgdata->gcont_sink);
116 if (plgdata->gcont_format)
117 osync_objformat_unref(plgdata->gcont_format);
118 g_free(plgdata);
121 /** Run gchelper and return the file descriptors for its stdin/stdout
124 osync_bool run_helper(struct gc_plgdata *plgdata, const char *operation,
125 const char *arg, int *in, int *out, pid_t *ppid,
126 OSyncError **error)
129 osync_bool result = FALSE;
130 /* TODO: add calls to libgcal */
131 exit:
132 return result;
135 static void gc_connect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
137 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
138 static int counter = 0;
139 int result;
140 struct gc_plgdata *plgdata = data;
141 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
142 OSyncError *error = NULL;
143 char buffer[512];
145 if ((plgdata->calendar) && (counter == 0)) {
146 result = gcal_get_authentication(plgdata->calendar, plgdata->username,
147 plgdata->password);
148 ++counter;
149 if (result == -1)
150 goto error;
152 snprintf(buffer, sizeof(buffer) - 1, "%s/gcal2osync.xslt",
153 plgdata->xslt_path);
154 if ((result = xslt_initialize(plgdata->xslt_ctx_gcal, buffer)))
155 goto error;
156 fprintf(stderr, "\ndone calendar: %s\n", buffer);
159 if (((plgdata->contacts) && (counter == 1)) ||
160 ((plgdata->gcont_sink) && (!plgdata->gcal_sink))) {
161 result = gcal_get_authentication(plgdata->contacts, plgdata->username,
162 plgdata->password);
163 counter++;
164 if (result == -1)
165 goto error;
167 snprintf(buffer, sizeof(buffer) - 1, "%s/gcont2osync.xslt",
168 plgdata->xslt_path);
169 if ((result = xslt_initialize(plgdata->xslt_ctx_gcont, buffer)))
170 goto error;
171 fprintf(stderr, "\ndone contact: %s\n", buffer);
174 osync_context_report_success(ctx);
175 osync_trace(TRACE_EXIT, "%s", __func__);
176 return;
178 error:
179 fprintf(stderr, "\nGood bye, cruel world...\n");
180 osync_context_report_osyncerror(ctx, &error);
183 static void gc_get_changes(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
185 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
186 static int counter = 0;
187 struct gc_plgdata *plgdata = data;
188 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
189 OSyncError *error = NULL;
190 OSyncXMLFormat *xmlformat;
191 OSyncData *odata = NULL;
192 OSyncChange *chg = NULL;
193 int result = 0, i;
194 char *timestamp = NULL, *msg, *raw_xml = NULL;
195 gcal_event event;
196 gcal_contact contact;
198 timestamp = osync_anchor_retrieve(plgdata->anchor_path, "gdata");
199 if (timestamp)
200 fprintf(stderr, "timestamp is: %s\n", timestamp);
201 else
202 fprintf(stderr, "first sync!\n");
204 if ((plgdata->gcal_sink) && (counter == 0)) {
205 if (osync_objtype_sink_get_slowsync(plgdata->gcal_sink)) {
206 fprintf(stderr, "\n\t\tgcal: Client asked for slow syncing...\n");
207 result = gcal_get_events(plgdata->calendar, &(plgdata->all_events));
208 if (result) {
209 msg = "Failed getting events!";
210 goto error;
213 fprintf(stderr, "\tgot then all!\n");
214 for (i = 0; i < plgdata->all_events.length; ++i) {
215 event = gcal_event_element(&(plgdata->all_events), i);
216 if (!event)
217 goto error;
218 raw_xml = gcal_event_get_xml(event);
219 if ((result = xslt_transform(plgdata->xslt_ctx_gcal,
220 raw_xml)))
221 goto error;
223 raw_xml = plgdata->xslt_ctx_gcal->xml_str;
224 xmlformat = osync_xmlformat_parse(raw_xml,
225 strlen(raw_xml),
226 &error);
227 if (!xmlformat)
228 goto error;
230 osync_xmlformat_sort(xmlformat);
231 odata = osync_data_new((char *)xmlformat,
232 osync_xmlformat_size(),
233 plgdata->gcal_format, &error);
234 if (!odata)
235 goto cleanup;
237 if (!(chg = osync_change_new(&error)))
238 goto cleanup;
239 osync_data_set_objtype(odata, osync_objtype_sink_get_name(plgdata->gcal_sink));
240 osync_change_set_data(chg, odata);
241 osync_data_unref(odata);
243 osync_change_set_uid(chg, gcal_event_get_id(event));
244 osync_change_set_changetype(chg, OSYNC_CHANGE_TYPE_ADDED);
245 osync_context_report_change(ctx, chg);
246 osync_change_unref(chg);
250 } else {
251 /* TODO: write getchanges */
252 fprintf(stderr, "\n\t\tgcal: Its a fast sync!\n");
257 if (((plgdata->gcont_sink) && (counter == 1)) ||
258 ((plgdata->gcont_sink) && (!plgdata->gcal_sink)))
259 if (osync_objtype_sink_get_slowsync(plgdata->gcont_sink)) {
260 fprintf(stderr, "\n\t\tgcont: Client asked for slow syncing...\n");
261 result = gcal_get_contacts(plgdata->contacts, &(plgdata->all_contacts));
262 if (result) {
263 msg = "Failed getting contacts!";
264 goto error;
267 fprintf(stderr, "\tgot then all!\n");
268 for (i = 0; i < plgdata->all_contacts.length; ++i) {
269 contact = gcal_contact_element(&(plgdata->all_contacts), i);
270 if (!contact)
271 goto error;
272 raw_xml = gcal_contact_get_xml(contact);
273 if ((result = xslt_transform(plgdata->xslt_ctx_gcont,
274 raw_xml)))
275 goto error;
276 raw_xml = plgdata->xslt_ctx_gcal->xml_str;
277 xmlformat = osync_xmlformat_parse(raw_xml,
278 strlen(raw_xml),
279 &error);
280 if (!xmlformat)
281 goto error;
283 osync_xmlformat_sort(xmlformat);
284 odata = osync_data_new((char *)xmlformat,
285 osync_xmlformat_size(),
286 plgdata->gcont_format, &error);
287 if (!odata)
288 goto cleanup;
290 if (!(chg = osync_change_new(&error)))
291 goto cleanup;
292 osync_data_set_objtype(odata, osync_objtype_sink_get_name(plgdata->gcont_sink));
293 osync_change_set_data(chg, odata);
294 osync_data_unref(odata);
296 osync_change_set_uid(chg, gcal_contact_get_id(contact));
297 osync_change_set_changetype(chg, OSYNC_CHANGE_TYPE_ADDED);
298 osync_context_report_change(ctx, chg);
299 osync_change_unref(chg);
303 } else {
304 /* TODO: write getchanges */
305 fprintf(stderr, "\n\t\tgcont: Its a fast sync!\n");
308 exit:
309 ++counter;
310 osync_context_report_success(ctx);
311 return;
313 cleanup:
314 osync_error_unref(&error);
315 osync_xmlformat_unref(&xmlformat);
317 error:
318 ++counter;
319 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, msg);
322 static void gc_commit_change(void *data, OSyncPluginInfo *info,
323 OSyncContext *ctx, OSyncChange *change)
325 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx, change);
328 switch (osync_change_get_changetype(change)) {
329 case OSYNC_CHANGE_TYPE_ADDED:
330 cmd = "add";
331 arg = NULL;
332 break;
333 case OSYNC_CHANGE_TYPE_MODIFIED:
334 cmd = "edit";
335 arg = hash;
336 break;
337 case OSYNC_CHANGE_TYPE_DELETED:
338 cmd = "delete";
339 arg = hash;
340 break;
341 default:
342 osync_context_report_error(ctx, OSYNC_ERROR_NOT_SUPPORTED, "Unknown change type");
343 goto error;
344 break;
348 osync_context_report_success(ctx);
350 osync_trace(TRACE_EXIT, "%s", __func__);
351 return;
354 static void gc_sync_done(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
356 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
357 struct gc_plgdata *plgdata = NULL;
358 int result;
360 plgdata = data;
361 result = get_mili_timestamp(plgdata->timestamp, TIMESTAMP_MAX_SIZE,
362 plgdata->timezone);
363 osync_anchor_update(plgdata->anchor_path, "gdata", plgdata->timestamp);
366 static void gc_disconnect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
368 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
369 struct gc_plgdata *plgdata = data;
371 osync_context_report_success(ctx);
372 osync_trace(TRACE_EXIT, "%s", __func__);
375 static void gc_finalize(void *data)
377 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
378 struct gc_plgdata *plgdata = data;
380 free_plg(plgdata);
381 osync_trace(TRACE_EXIT, "%s", __func__);
384 static void *gc_initialize(OSyncPlugin *plugin,
385 OSyncPluginInfo *info,
386 OSyncError **error)
388 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, plugin, info, error);
389 struct gc_plgdata *plgdata;
390 OSyncPluginConfig *config;
391 OSyncPluginAuthentication *auth;
392 OSyncPluginAdvancedOption *advanced;
393 OSyncList *resources;
394 OSyncList *r;
395 const char *objtype, *tmp;
396 int i, numobjs;
398 plgdata = osync_try_malloc0(sizeof(struct gc_plgdata), error);
399 config = osync_plugin_info_get_config(info);
400 if ((!plgdata) || (!config)) {
401 osync_error_set(error, OSYNC_ERROR_GENERIC,
402 "Unable to get config data.");
403 goto error_freeplg;
406 advanced = osync_plugin_config_get_advancedoption_value_by_name(config, "xslt");
407 if (!advanced) {
408 fprintf(stderr, "Cannot locate xslt config!\n");
409 goto error_freeplg;
412 if (!(plgdata->xslt_path = strdup(osync_plugin_advancedoption_get_value(advanced))))
413 goto error_freeplg;
415 /* TODO: add timezone in configuration */
416 if (!(plgdata->timestamp = malloc(TIMESTAMP_MAX_SIZE)))
417 goto error_freeplg;
418 plgdata->timezone = NULL;
420 resources = osync_plugin_config_get_resources(config);
421 numobjs = osync_plugin_info_num_objtypes(info);
423 for (i = 1, r = resources; r; r = r->next, i++) {
424 fprintf(stderr, "field: %s\n", osync_plugin_resource_get_objtype(r->data));
425 if (!(strcmp(osync_plugin_resource_get_objtype(r->data), "event")))
426 if (!(plgdata->calendar = gcal_new(GCALENDAR)))
427 goto error_freeplg;
428 else {
429 /* fprintf(stderr, "\tcreated calendar obj!\n"); */
430 gcal_set_store_xml(plgdata->calendar, 1);
433 if (!(strcmp(osync_plugin_resource_get_objtype(r->data), "contact")))
434 if (!(plgdata->contacts = gcal_new(GCONTACT)))
435 goto error_freeplg;
436 else {
437 /* fprintf(stderr, "\tcreated contact obj!\n"); */
438 gcal_set_store_xml(plgdata->contacts, 1);
444 /* TODO: how works resource policy? For while, copy everything... */
445 for (i = 0; i < numobjs; i++) {
447 if (!plgdata->username) {
448 auth = osync_plugin_config_get_authentication(config);
449 tmp = osync_plugin_authentication_get_username(auth);
450 if (!tmp)
451 goto error_freeplg;
452 else
453 if (!(plgdata->username = strdup(tmp)))
454 goto error_freeplg;
458 if (!plgdata->password) {
459 tmp = osync_plugin_authentication_get_password(auth);
460 if (!tmp)
461 goto error_freeplg;
462 else
463 if (!(plgdata->password = strdup(tmp)))
464 goto error_freeplg;
467 /* TODO: get proxy/calendar title/resources/etc */
471 OSyncObjTypeSinkFunctions functions;
472 memset(&functions, 0, sizeof(functions));
473 functions.connect = gc_connect;
474 functions.get_changes = gc_get_changes;
475 functions.commit = gc_commit_change;
476 functions.disconnect = gc_disconnect;
477 functions.sync_done = gc_sync_done;
480 if (plgdata->calendar) {
481 fprintf(stderr, "\n\n\tcreating calendar sink...\n");
482 OSyncFormatEnv *formatenv1 = osync_plugin_info_get_format_env(info);
483 plgdata->gcal_format = osync_format_env_find_objformat(formatenv1, "xmlformat-event");
484 if (!plgdata->gcal_format)
485 goto error_freeplg;
486 osync_objformat_ref(plgdata->gcal_format);
488 plgdata->gcal_sink = osync_objtype_sink_new("event", error);
489 if (!plgdata->gcal_sink)
490 goto error_freeplg;
492 osync_objtype_sink_set_functions(plgdata->gcal_sink, functions, plgdata);
493 osync_plugin_info_add_objtype(info, plgdata->gcal_sink);
497 if (plgdata->contacts) {
498 fprintf(stderr, "\n\n\tcreating contact sink...\n");
499 OSyncFormatEnv *formatenv2 = osync_plugin_info_get_format_env(info);
500 plgdata->gcont_format = osync_format_env_find_objformat(formatenv2, "xmlformat-contact");
501 if (!plgdata->gcont_format)
502 goto error_freeplg;
503 osync_objformat_ref(plgdata->gcont_format);
505 plgdata->gcont_sink = osync_objtype_sink_new("contact", error);
506 if (!plgdata->gcont_sink)
507 goto error_freeplg;
509 osync_objtype_sink_set_functions(plgdata->gcont_sink, functions, plgdata);
510 osync_plugin_info_add_objtype(info, plgdata->gcont_sink);
515 plgdata->anchor_path = g_strdup_printf("%s/anchor.db",
516 osync_plugin_info_get_configdir(info));
517 if (!(plgdata->anchor_path))
518 goto error_freeplg;
519 else
520 fprintf(stderr, "\tanchor: %s\n", plgdata->anchor_path);
522 if (plgdata->calendar)
523 if (!(plgdata->xslt_ctx_gcal = xslt_new()))
524 goto error_freeplg;
525 else
526 fprintf(stderr, "\tsucceed creating xslt_gcal!\n");
528 if (plgdata->contacts)
529 if (!(plgdata->xslt_ctx_gcont = xslt_new()))
530 goto error_freeplg;
531 else
532 fprintf(stderr, "\tsucceed creating xslt_gcont!\n");
534 osync_trace(TRACE_EXIT, "%s", __func__);
536 return plgdata;
538 error_freeplg:
539 if (plgdata)
540 free_plg(plgdata);
541 out:
542 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
543 return NULL;
546 static osync_bool gc_discover(void *data, OSyncPluginInfo *info, OSyncError **error)
548 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, error);
550 struct gc_plgdata *plgdata = data;
552 if (plgdata->calendar)
553 osync_objtype_sink_set_available(plgdata->gcal_sink, TRUE);
554 if (plgdata->contacts)
555 osync_objtype_sink_set_available(plgdata->gcont_sink, TRUE);
557 OSyncVersion *version = osync_version_new(error);
558 osync_version_set_plugin(version, "google-data");
559 osync_plugin_info_set_version(info, version);
560 osync_version_unref(version);
562 osync_trace(TRACE_EXIT, "%s", __func__);
563 return TRUE;
566 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
568 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, env, error);
569 OSyncPlugin *plugin = osync_plugin_new(error);
570 if (!plugin)
571 goto error;
573 osync_plugin_set_name(plugin, "google-data");
574 osync_plugin_set_longname(plugin, "Google calendar/plugin");
575 osync_plugin_set_description(plugin, "Google calendar and contacts plugin");
577 osync_plugin_set_initialize(plugin, gc_initialize);
578 osync_plugin_set_finalize(plugin, gc_finalize);
579 osync_plugin_set_discover(plugin, gc_discover);
581 osync_plugin_env_register_plugin(env, plugin);
582 osync_plugin_unref(plugin);
584 osync_trace(TRACE_EXIT, "%s", __func__);
585 return TRUE;
587 error:
588 osync_trace(TRACE_EXIT_ERROR, "Unable to register: %s", osync_error_print(error));
589 osync_error_unref(error);
590 return FALSE;
593 int get_version(void)
595 return 1;