Reporting calendar events to opensync when doing slow sync.
[gdataplugin.git] / src / gcalendar.c
blobc207be529fceb90d1a16331a9ff7b398a5a8531e
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 /* TODO: report back contacts to opensync */
277 plgdata->xslt_ctx_gcont->xml_str;
280 } else {
281 /* TODO: write getchanges */
282 fprintf(stderr, "\n\t\tgcont: Its a fast sync!\n");
285 exit:
286 ++counter;
287 osync_context_report_success(ctx);
288 return;
290 cleanup:
291 osync_error_unref(&error);
292 osync_xmlformat_unref(&xmlformat);
294 error:
295 ++counter;
296 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, msg);
299 static void gc_commit_change(void *data, OSyncPluginInfo *info,
300 OSyncContext *ctx, OSyncChange *change)
302 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx, change);
305 switch (osync_change_get_changetype(change)) {
306 case OSYNC_CHANGE_TYPE_ADDED:
307 cmd = "add";
308 arg = NULL;
309 break;
310 case OSYNC_CHANGE_TYPE_MODIFIED:
311 cmd = "edit";
312 arg = hash;
313 break;
314 case OSYNC_CHANGE_TYPE_DELETED:
315 cmd = "delete";
316 arg = hash;
317 break;
318 default:
319 osync_context_report_error(ctx, OSYNC_ERROR_NOT_SUPPORTED, "Unknown change type");
320 goto error;
321 break;
325 osync_context_report_success(ctx);
327 osync_trace(TRACE_EXIT, "%s", __func__);
328 return;
331 static void gc_sync_done(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
333 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
334 struct gc_plgdata *plgdata = NULL;
335 int result;
337 plgdata = data;
338 result = get_mili_timestamp(plgdata->timestamp, TIMESTAMP_MAX_SIZE,
339 plgdata->timezone);
340 osync_anchor_update(plgdata->anchor_path, "gdata", plgdata->timestamp);
343 static void gc_disconnect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
345 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
346 struct gc_plgdata *plgdata = data;
348 osync_context_report_success(ctx);
349 osync_trace(TRACE_EXIT, "%s", __func__);
352 static void gc_finalize(void *data)
354 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
355 struct gc_plgdata *plgdata = data;
357 free_plg(plgdata);
358 osync_trace(TRACE_EXIT, "%s", __func__);
361 static void *gc_initialize(OSyncPlugin *plugin,
362 OSyncPluginInfo *info,
363 OSyncError **error)
365 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, plugin, info, error);
366 struct gc_plgdata *plgdata;
367 OSyncPluginConfig *config;
368 OSyncPluginAuthentication *auth;
369 OSyncPluginAdvancedOption *advanced;
370 OSyncList *resources;
371 OSyncList *r;
372 const char *objtype, *tmp;
373 int i, numobjs;
375 plgdata = osync_try_malloc0(sizeof(struct gc_plgdata), error);
376 config = osync_plugin_info_get_config(info);
377 if ((!plgdata) || (!config)) {
378 osync_error_set(error, OSYNC_ERROR_GENERIC,
379 "Unable to get config data.");
380 goto error_freeplg;
383 advanced = osync_plugin_config_get_advancedoption_value_by_name(config, "xslt");
384 if (!advanced) {
385 fprintf(stderr, "Cannot locate xslt config!\n");
386 goto error_freeplg;
389 if (!(plgdata->xslt_path = strdup(osync_plugin_advancedoption_get_value(advanced))))
390 goto error_freeplg;
392 /* TODO: add timezone in configuration */
393 if (!(plgdata->timestamp = malloc(TIMESTAMP_MAX_SIZE)))
394 goto error_freeplg;
395 plgdata->timezone = NULL;
397 resources = osync_plugin_config_get_resources(config);
398 numobjs = osync_plugin_info_num_objtypes(info);
400 for (i = 1, r = resources; r; r = r->next, i++) {
401 fprintf(stderr, "field: %s\n", osync_plugin_resource_get_objtype(r->data));
402 if (!(strcmp(osync_plugin_resource_get_objtype(r->data), "event")))
403 if (!(plgdata->calendar = gcal_new(GCALENDAR)))
404 goto error_freeplg;
405 else {
406 /* fprintf(stderr, "\tcreated calendar obj!\n"); */
407 gcal_set_store_xml(plgdata->calendar, 1);
410 if (!(strcmp(osync_plugin_resource_get_objtype(r->data), "contact")))
411 if (!(plgdata->contacts = gcal_new(GCONTACT)))
412 goto error_freeplg;
413 else {
414 /* fprintf(stderr, "\tcreated contact obj!\n"); */
415 gcal_set_store_xml(plgdata->contacts, 1);
421 /* TODO: how works resource policy? For while, copy everything... */
422 for (i = 0; i < numobjs; i++) {
424 if (!plgdata->username) {
425 auth = osync_plugin_config_get_authentication(config);
426 tmp = osync_plugin_authentication_get_username(auth);
427 if (!tmp)
428 goto error_freeplg;
429 else
430 if (!(plgdata->username = strdup(tmp)))
431 goto error_freeplg;
435 if (!plgdata->password) {
436 tmp = osync_plugin_authentication_get_password(auth);
437 if (!tmp)
438 goto error_freeplg;
439 else
440 if (!(plgdata->password = strdup(tmp)))
441 goto error_freeplg;
444 /* TODO: get proxy/calendar title/resources/etc */
448 OSyncObjTypeSinkFunctions functions;
449 memset(&functions, 0, sizeof(functions));
450 functions.connect = gc_connect;
451 functions.get_changes = gc_get_changes;
452 functions.commit = gc_commit_change;
453 functions.disconnect = gc_disconnect;
454 functions.sync_done = gc_sync_done;
457 if (plgdata->calendar) {
458 fprintf(stderr, "\n\n\tcreating calendar sink...\n");
459 OSyncFormatEnv *formatenv1 = osync_plugin_info_get_format_env(info);
460 plgdata->gcal_format = osync_format_env_find_objformat(formatenv1, "xmlformat-event");
461 if (!plgdata->gcal_format)
462 goto error_freeplg;
463 osync_objformat_ref(plgdata->gcal_format);
465 plgdata->gcal_sink = osync_objtype_sink_new("event", error);
466 if (!plgdata->gcal_sink)
467 goto error_freeplg;
469 osync_objtype_sink_set_functions(plgdata->gcal_sink, functions, plgdata);
470 osync_plugin_info_add_objtype(info, plgdata->gcal_sink);
474 if (plgdata->contacts) {
475 fprintf(stderr, "\n\n\tcreating contact sink...\n");
476 OSyncFormatEnv *formatenv2 = osync_plugin_info_get_format_env(info);
477 plgdata->gcont_format = osync_format_env_find_objformat(formatenv2, "xmlformat-contact");
478 if (!plgdata->gcont_format)
479 goto error_freeplg;
480 osync_objformat_ref(plgdata->gcont_format);
482 plgdata->gcont_sink = osync_objtype_sink_new("contact", error);
483 if (!plgdata->gcont_sink)
484 goto error_freeplg;
486 osync_objtype_sink_set_functions(plgdata->gcont_sink, functions, plgdata);
487 osync_plugin_info_add_objtype(info, plgdata->gcont_sink);
492 plgdata->anchor_path = g_strdup_printf("%s/anchor.db",
493 osync_plugin_info_get_configdir(info));
494 if (!(plgdata->anchor_path))
495 goto error_freeplg;
496 else
497 fprintf(stderr, "\tanchor: %s\n", plgdata->anchor_path);
499 if (plgdata->calendar)
500 if (!(plgdata->xslt_ctx_gcal = xslt_new()))
501 goto error_freeplg;
502 else
503 fprintf(stderr, "\tsucceed creating xslt_gcal!\n");
505 if (plgdata->contacts)
506 if (!(plgdata->xslt_ctx_gcont = xslt_new()))
507 goto error_freeplg;
508 else
509 fprintf(stderr, "\tsucceed creating xslt_gcont!\n");
511 osync_trace(TRACE_EXIT, "%s", __func__);
513 return plgdata;
515 error_freeplg:
516 if (plgdata)
517 free_plg(plgdata);
518 out:
519 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
520 return NULL;
523 static osync_bool gc_discover(void *data, OSyncPluginInfo *info, OSyncError **error)
525 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, error);
527 struct gc_plgdata *plgdata = data;
529 if (plgdata->calendar)
530 osync_objtype_sink_set_available(plgdata->gcal_sink, TRUE);
531 if (plgdata->contacts)
532 osync_objtype_sink_set_available(plgdata->gcont_sink, TRUE);
534 OSyncVersion *version = osync_version_new(error);
535 osync_version_set_plugin(version, "google-data");
536 osync_plugin_info_set_version(info, version);
537 osync_version_unref(version);
539 osync_trace(TRACE_EXIT, "%s", __func__);
540 return TRUE;
543 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
545 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, env, error);
546 OSyncPlugin *plugin = osync_plugin_new(error);
547 if (!plugin)
548 goto error;
550 osync_plugin_set_name(plugin, "google-data");
551 osync_plugin_set_longname(plugin, "Google calendar/plugin");
552 osync_plugin_set_description(plugin, "Google calendar and contacts plugin");
554 osync_plugin_set_initialize(plugin, gc_initialize);
555 osync_plugin_set_finalize(plugin, gc_finalize);
556 osync_plugin_set_discover(plugin, gc_discover);
558 osync_plugin_env_register_plugin(env, plugin);
559 osync_plugin_unref(plugin);
561 osync_trace(TRACE_EXIT, "%s", __func__);
562 return TRUE;
564 error:
565 osync_trace(TRACE_EXIT_ERROR, "Unable to register: %s", osync_error_print(error));
566 osync_error_unref(error);
567 return FALSE;
570 int get_version(void)
572 return 1;