Removing calls for python helper (code compiles now).
[gdataplugin.git] / src / gcalendar.c
blobd24180aedb7a4b45dc8bb516b484c504451ddfd9
1 /** Google Calendar plugin
3 * Copyright (c) 2006 Eduardo Pereira Habkost <ehabkost@raisama.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA
22 #include <opensync/opensync.h>
23 #include <opensync/opensync-plugin.h>
24 #include <opensync/opensync-helper.h>
25 #include <opensync/opensync-merger.h>
26 #include <opensync/opensync-format.h>
27 #include <opensync/opensync-data.h>
28 #include <opensync/opensync-version.h>
30 #include <glib.h>
32 #include <libxml/tree.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <signal.h>
39 #include <sys/wait.h>
40 #include <gcal_status.h>
41 #include <gcalendar.h>
43 struct gc_plgdata
45 char *url;
46 char *username;
47 char *password;
48 gcal_t gcal;
49 struct gcal_event_array all_events;
50 OSyncHashTable *hashtable;
51 OSyncObjTypeSink *sink;
52 OSyncObjFormat *objformat;
55 static void free_plg(struct gc_plgdata *plgdata)
57 if (plgdata->url)
58 xmlFree(plgdata->url);
59 if (plgdata->username)
60 xmlFree(plgdata->username);
61 if (plgdata->password)
62 xmlFree(plgdata->password);
63 if (plgdata->hashtable)
64 osync_hashtable_free(plgdata->hashtable);
65 if (plgdata->sink)
66 osync_objtype_sink_unref(plgdata->sink);
67 if (plgdata->objformat)
68 osync_objformat_unref(plgdata->objformat);
69 g_free(plgdata);
72 /** Run gchelper and return the file descriptors for its stdin/stdout
75 osync_bool run_helper(struct gc_plgdata *plgdata, const char *operation,
76 const char *arg, int *in, int *out, pid_t *ppid,
77 OSyncError **error)
80 osync_bool result = FALSE;
81 /* TODO: add calls to libgcal */
82 exit:
83 return result;
86 char *gc_get_cfgvalue(xmlNode *cfg, const char *name)
88 xmlNode *c;
89 for (c = cfg->xmlChildrenNode; c; c = c->next) {
90 if (!xmlStrcmp(c->name, (const xmlChar*)name))
91 return (char*)xmlNodeGetContent(c);
93 return NULL;
96 osync_bool gc_parse_config(struct gc_plgdata *plgdata, const char *cfg, OSyncError **error)
98 xmlNode *node;
99 xmlDoc *doc;
100 osync_bool ret = FALSE;
102 doc = xmlParseMemory(cfg, strlen(cfg) + 1);
103 if (!doc) {
104 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't parse configuration");
105 goto out;
108 node = xmlDocGetRootElement(doc);
109 if (!node || xmlStrcmp(node->name, (const xmlChar*)"config")) {
110 osync_error_set(error, OSYNC_ERROR_GENERIC, "Invalid configuration");
111 goto out_freedoc;
114 /*TODO: Make this more user-friendly: allow the URL to be omitted
115 * by the user and build it automatically from the username
117 plgdata->url = gc_get_cfgvalue(node, "url");
118 plgdata->username = gc_get_cfgvalue(node, "username");
119 /*FIXME: We need an opensync API for getting info from the user,
120 * such as passwords
122 plgdata->password = gc_get_cfgvalue(node, "password");
124 if (!plgdata->url || !plgdata->username || !plgdata->password) {
125 osync_error_set(error, OSYNC_ERROR_GENERIC, "Invalid configuration");
126 goto error_freedata;
129 ret = TRUE;
131 out_freedoc:
132 xmlFreeDoc(doc);
133 out:
134 return ret;
136 error_freedata:
137 xmlFree(plgdata->url);
138 xmlFree(plgdata->username);
139 xmlFree(plgdata->password);
140 goto out_freedoc;
143 static void gc_connect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
145 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
147 struct gc_plgdata *plgdata = data;
148 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
149 OSyncError *error = NULL;
151 char *tablepath = g_strdup_printf("%s/hashtable.db", osync_plugin_info_get_configdir(info));
153 plgdata->hashtable = osync_hashtable_new(tablepath, osync_objtype_sink_get_name(sink), &error);
154 g_free(tablepath);
156 if (!plgdata->hashtable) {
157 osync_context_report_osyncerror(ctx, &error);
158 return;
161 osync_context_report_success(ctx);
163 osync_trace(TRACE_EXIT, "%s", __func__);
166 static void gc_get_changes(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
168 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
170 struct gc_plgdata *plgdata = data;
171 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
172 OSyncError *error = NULL;
174 int output;
175 int pid;
176 FILE *out;
177 char sizeline[1024];
178 int status;
180 /* Flush internal reports of hashtable to determin deleted entries. */
181 osync_hashtable_reset_reports(plgdata->hashtable);
183 if (osync_objtype_sink_get_slowsync(sink)) {
184 if (!osync_hashtable_slowsync(plgdata->hashtable, &error)) {
185 osync_context_report_osyncerror(ctx, &error);
186 goto error;
190 if (!run_helper(plgdata, "get_all", NULL,
191 NULL, &output, &pid, &error)) {
192 osync_context_report_osyncerror(ctx, &error);
193 goto error;
196 out = fdopen(output, "r");
197 if (!out) {
198 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't open helper output");
199 close(output);
200 goto error_fdopen;
203 while (fgets(sizeline, sizeof(sizeline), out)) {
204 int size, uidsize, hashsize;
205 char *xmldata, *uid, *hash;
207 if (sscanf(sizeline, "%d %d %d", &size, &uidsize, &hashsize) < 3) {
208 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Invalid size line from helper");
209 goto error_size;
212 xmldata = malloc(size);
213 if (!xmldata) {
214 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
215 goto error_xmldata;
218 uid = malloc(uidsize + 1);
219 if (!uid) {
220 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
221 goto error_uid;
224 hash = malloc(hashsize + 1);
225 if (!hash) {
226 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
227 goto error_hash;
230 if (fread(xmldata, size, 1, out) < 1) {
231 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
232 goto error_xml;
235 if (fread(uid, uidsize, 1, out) < 1) {
236 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
237 goto error_xml;
239 uid[uidsize] = '\0';
241 if (fread(hash, hashsize, 1, out) < 1) {
242 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
243 goto error_xml;
245 hash[hashsize] = '\0';
247 OSyncXMLFormat *doc = osync_xmlformat_parse(xmldata, size, &error);
248 if (!doc) {
249 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Invalid XML data from helper");
250 osync_error_unref(&error);
251 goto error_xml;
253 /* osync_merge_merge() seems to like its input sorted... */
254 osync_xmlformat_sort(doc);
256 OSyncData *odata = osync_data_new((char *)doc, osync_xmlformat_size(), plgdata->objformat, &error);
257 if (!odata) {
258 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
259 osync_error_unref(&error);
260 /* osync_data_new() does not increase the reference count of
261 its 'data' member, but osync_data_unref() will decrease it,
262 so this is the only case where 'doc' has to be unreferenced
263 manually */
264 osync_xmlformat_unref(doc);
265 goto error_xml;
268 OSyncChange *chg = osync_change_new(&error);
269 if (!chg) {
270 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
271 osync_error_unref(&error);
272 goto error_chg;
275 osync_change_set_uid(chg, uid);
276 osync_change_set_data(chg, odata);
277 osync_data_unref(odata);
278 osync_change_set_objtype(chg, osync_objtype_sink_get_name(sink));
279 osync_change_set_hash(chg, hash);
281 OSyncChangeType type = osync_hashtable_get_changetype(plgdata->hashtable,
282 chg);
283 osync_change_set_changetype(chg, type);
284 osync_hashtable_report(plgdata->hashtable, uid);
286 if (osync_change_get_changetype(chg) != OSYNC_CHANGE_TYPE_UNMODIFIED) {
287 osync_context_report_change(ctx, chg);
288 osync_hashtable_update_hash(plgdata->hashtable, osync_change_get_changetype(chg), uid, hash);
291 osync_change_unref(chg);
292 free(hash);
293 free(uid);
294 free(xmldata);
296 /* end of loop */
297 continue;
299 /* error handling in the loop */
300 error_chg:
301 osync_data_unref(odata);
302 error_xml:
303 free(hash);
304 error_hash:
305 free(uid);
306 error_uid:
307 free(xmldata);
308 goto error_xmldata;
311 char **uids = osync_hashtable_get_deleted(plgdata->hashtable);
312 int i;
313 for (i = 0; uids[i]; i++) {
314 OSyncChange *change = osync_change_new(&error);
315 if (!change) {
316 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "ERROR is: %s", osync_error_print(&error));
317 goto error_fdopen;
319 OSyncData *data = osync_data_new(NULL, 0, plgdata->objformat, &error);
320 if (!data) {
321 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "ERROR is: %s", osync_error_print(&error));
322 goto error_fdopen;
324 osync_data_set_objtype(data, "event");
326 osync_change_set_data(change, data);
327 osync_change_set_changetype(change, OSYNC_CHANGE_TYPE_DELETED);
328 osync_change_set_uid(change, uids[i]);
329 osync_context_report_change(ctx, change);
330 osync_hashtable_update_hash(plgdata->hashtable, OSYNC_CHANGE_TYPE_DELETED, uids[i], NULL);
331 osync_change_unref(change);
332 osync_data_unref(data);
333 g_free(uids[i]);
335 g_free(uids);
337 fclose(out);
338 waitpid(pid, &status, 0);
340 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
341 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Helper exited abnormally");
342 goto error;
345 osync_context_report_success(ctx);
347 osync_trace(TRACE_EXIT, "%s", __func__);
348 return;
350 error_xmldata:
351 error_size:
352 fclose(out);
353 error_fdopen:
354 kill(pid, SIGTERM);
355 waitpid(pid, NULL, 0);
356 error:
357 osync_trace(TRACE_EXIT, "%s", __func__);
358 return;
361 static void gc_commit_change(void *data, OSyncPluginInfo *info,
362 OSyncContext *ctx, OSyncChange *change)
364 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx, change);
366 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
367 struct gc_plgdata *plgdata = data;
369 pid_t pid;
370 int input, output;
371 OSyncError *error = NULL;
372 const char *cmd;
373 const char *arg;
374 FILE *out;
375 int status;
376 char sizeline[1024];
377 char *hash;
379 hash = osync_hashtable_get_hash(plgdata->hashtable, osync_change_get_uid(change));
381 switch (osync_change_get_changetype(change)) {
382 case OSYNC_CHANGE_TYPE_ADDED:
383 cmd = "add";
384 arg = NULL;
385 break;
386 case OSYNC_CHANGE_TYPE_MODIFIED:
387 cmd = "edit";
388 arg = hash;
389 break;
390 case OSYNC_CHANGE_TYPE_DELETED:
391 cmd = "delete";
392 arg = hash;
393 break;
394 default:
395 osync_context_report_error(ctx, OSYNC_ERROR_NOT_SUPPORTED, "Unknown change type");
396 goto error;
397 break;
400 if (!run_helper(plgdata, cmd, arg,
401 &input, &output, &pid, &error)) {
402 osync_context_report_osyncerror(ctx, &error);
403 goto error;
406 g_free(hash);
408 out = fdopen(output, "r");
409 if (!out) {
410 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't open helper output");
411 close(output);
412 goto error_fdopen;
415 switch (osync_change_get_changetype(change)) {
416 case OSYNC_CHANGE_TYPE_ADDED:
417 case OSYNC_CHANGE_TYPE_MODIFIED:
419 OSyncXMLFormat *doc = (OSyncXMLFormat *)osync_data_get_data_ptr(osync_change_get_data(change));
420 int size;
421 char *buffer;
423 osync_xmlformat_assemble(doc, &buffer, &size);
424 osync_trace(TRACE_INTERNAL, "input to helper:\n%s", buffer);
425 if (write(input, buffer, size) < size) {
426 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't write data to helper");
427 close(input);
428 g_free(buffer);
429 goto error_write;
432 close(input);
433 g_free(buffer);
435 int xmlsize, uidsize, hashsize;
436 char *xmldata, *uid, *hash;
438 if (!fgets(sizeline, sizeof(sizeline), out)) {
439 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't read from helper");
440 goto error_read;
443 if (sscanf(sizeline, "%d %d %d", &xmlsize, &uidsize, &hashsize) < 3) {
444 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Invalid size line from helper");
445 goto error_size;
448 xmldata = malloc(xmlsize);
449 if (!xmldata) {
450 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
451 goto error_xmldata;
454 uid = malloc(uidsize + 1);
455 if (!uid) {
456 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
457 goto error_uid;
460 hash = malloc(hashsize + 1);
461 if (!hash) {
462 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
463 goto error_hash;
466 if (fread(xmldata, xmlsize, 1, out) < 1) {
467 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
468 goto error_readxml;
472 if (fread(uid, uidsize, 1, out) < 1) {
473 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
474 goto error_readxml;
476 uid[uidsize] = '\0';
478 if (fread(hash, hashsize, 1, out) < 1) {
479 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
480 goto error_readxml;
482 hash[hashsize] = '\0';
484 /* Done writing. Update UID and hash */
485 osync_change_set_uid(change, uid);
486 osync_change_set_hash(change, hash);
488 free(hash);
489 free(uid);
490 free(xmldata);
492 break;
494 /* Error handling */
495 error_readxml:
496 free(hash);
497 error_hash:
498 free(uid);
499 error_uid:
500 free(xmldata);
501 error_xmldata:
502 error_size:
503 break;
505 break;
506 case OSYNC_CHANGE_TYPE_DELETED:
507 close(input);
508 break;
509 default:
510 g_assert_not_reached();
513 osync_hashtable_update_hash(plgdata->hashtable,
514 osync_change_get_changetype(change),
515 osync_change_get_uid(change),
516 osync_change_get_hash(change));
518 fclose(out);
520 waitpid(pid, &status, 0);
522 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
523 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Helper exited abnormally");
524 goto error;
527 osync_context_report_success(ctx);
529 osync_trace(TRACE_EXIT, "%s", __func__);
530 return;
532 error_read:
533 error_write:
534 fclose(out);
535 error_fdopen:
536 kill(pid, SIGTERM);
537 waitpid(pid, NULL, 0);
538 error:
539 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
540 return;
543 static void gc_disconnect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
545 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
546 struct gc_plgdata *plgdata = data;
548 osync_hashtable_free(plgdata->hashtable);
549 plgdata->hashtable = NULL;
551 osync_context_report_success(ctx);
552 osync_trace(TRACE_EXIT, "%s", __func__);
555 static void gc_finalize(void *data)
557 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
558 struct gc_plgdata *plgdata = data;
560 free_plg(plgdata);
561 osync_trace(TRACE_EXIT, "%s", __func__);
564 static void *gc_initialize(OSyncPlugin *plugin,
565 OSyncPluginInfo *info,
566 OSyncError **error)
568 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, plugin, info, error);
569 struct gc_plgdata *plgdata;
570 const char *cfg;
572 plgdata = osync_try_malloc0(sizeof(struct gc_plgdata), error);
573 if (!plgdata)
574 goto out;
576 cfg = osync_plugin_info_get_config(info);
577 if (!cfg) {
578 osync_error_set(error, OSYNC_ERROR_GENERIC,
579 "Unable to get config data.");
580 goto error_freeplg;
583 if (!gc_parse_config(plgdata, cfg, error))
584 goto error_freeplg;
586 OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
587 plgdata->objformat = osync_format_env_find_objformat(formatenv, "xmlformat-event");
588 if (!plgdata->objformat)
589 goto error_freeplg;
590 osync_objformat_ref(plgdata->objformat);
592 plgdata->sink = osync_objtype_sink_new("event", error);
593 if (!plgdata->sink)
594 goto error_freeplg;
596 osync_objtype_sink_add_objformat(plgdata->sink, "xmlformat-event");
598 OSyncObjTypeSinkFunctions functions;
599 memset(&functions, 0, sizeof(functions));
600 functions.connect = gc_connect;
601 functions.disconnect = gc_disconnect;
602 functions.get_changes = gc_get_changes;
603 functions.commit = gc_commit_change;
605 osync_objtype_sink_set_functions(plgdata->sink, functions, plgdata);
606 osync_plugin_info_add_objtype(info, plgdata->sink);
608 osync_trace(TRACE_EXIT, "%s", __func__);
610 return plgdata;
612 error_freeplg:
613 free_plg(plgdata);
614 out:
615 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
616 return NULL;
619 static osync_bool gc_discover(void *data, OSyncPluginInfo *info, OSyncError **error)
621 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, error);
623 struct gc_plgdata *plgdata = data;
625 osync_objtype_sink_set_available(plgdata->sink, TRUE);
627 OSyncVersion *version = osync_version_new(error);
628 osync_version_set_plugin(version, "google-calendar");
629 osync_plugin_info_set_version(info, version);
630 osync_version_unref(version);
632 osync_trace(TRACE_EXIT, "%s", __func__);
633 return TRUE;
636 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
638 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, env, error);
639 OSyncPlugin *plugin = osync_plugin_new(error);
640 if (!plugin)
641 goto error;
643 osync_plugin_set_name(plugin, "google-calendar");
644 osync_plugin_set_longname(plugin, "Google Calendar");
645 osync_plugin_set_description(plugin, "Google Calendar plugin");
646 osync_plugin_set_config_type(plugin, OSYNC_PLUGIN_NEEDS_CONFIGURATION);
648 osync_plugin_set_initialize(plugin, gc_initialize);
649 osync_plugin_set_finalize(plugin, gc_finalize);
650 osync_plugin_set_discover(plugin, gc_discover);
652 osync_plugin_env_register_plugin(env, plugin);
653 osync_plugin_unref(plugin);
655 osync_trace(TRACE_EXIT, "%s", __func__);
656 return TRUE;
658 error:
659 osync_trace(TRACE_EXIT_ERROR, "Unable to register: %s", osync_error_print(error));
660 osync_error_unref(error);
661 return FALSE;
664 int get_version(void)
666 return 1;