Removing all hashtable references and commenting out outdated functions.
[gdataplugin.git] / src / gcalendar.c
blobb42b83430b28f6527aebc3bfc3692fb8a45d0ddc
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 /* TODO: add a anchor here */
50 struct gcal_event_array all_events;
51 OSyncObjTypeSink *sink;
52 OSyncObjFormat *objformat;
55 static void free_plg(struct gc_plgdata *plgdata)
57 if (plgdata->gcal)
58 gcal_delete(plgdata->gcal);
59 if (plgdata->url)
60 xmlFree(plgdata->url);
61 if (plgdata->username)
62 xmlFree(plgdata->username);
63 if (plgdata->password)
64 xmlFree(plgdata->password);
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 int result;
148 struct gc_plgdata *plgdata = data;
149 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
150 OSyncError *error = NULL;
152 plgdata->gcal = gcal_new(GCALENDAR);
153 result = gcal_get_authentication(plgdata->gcal, plgdata->username,
154 plgdata->password);
155 if ((!plgdata->gcal) || (result == -1)) {
156 osync_context_report_osyncerror(ctx, &error);
157 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 if (!run_helper(plgdata, "get_all", NULL,
181 NULL, &output, &pid, &error)) {
182 osync_context_report_osyncerror(ctx, &error);
183 goto error;
186 out = fdopen(output, "r");
187 if (!out) {
188 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't open helper output");
189 close(output);
190 goto error_fdopen;
193 while (fgets(sizeline, sizeof(sizeline), out)) {
194 int size, uidsize, hashsize;
195 char *xmldata, *uid, *hash;
197 if (sscanf(sizeline, "%d %d %d", &size, &uidsize, &hashsize) < 3) {
198 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Invalid size line from helper");
199 goto error_size;
202 xmldata = malloc(size);
203 if (!xmldata) {
204 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
205 goto error_xmldata;
208 uid = malloc(uidsize + 1);
209 if (!uid) {
210 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
211 goto error_uid;
214 hash = malloc(hashsize + 1);
215 if (!hash) {
216 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
217 goto error_hash;
220 if (fread(xmldata, size, 1, out) < 1) {
221 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
222 goto error_xml;
225 if (fread(uid, uidsize, 1, out) < 1) {
226 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
227 goto error_xml;
229 uid[uidsize] = '\0';
231 if (fread(hash, hashsize, 1, out) < 1) {
232 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
233 goto error_xml;
235 hash[hashsize] = '\0';
237 OSyncXMLFormat *doc = osync_xmlformat_parse(xmldata, size, &error);
238 if (!doc) {
239 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Invalid XML data from helper");
240 osync_error_unref(&error);
241 goto error_xml;
243 /* osync_merge_merge() seems to like its input sorted... */
244 osync_xmlformat_sort(doc);
246 OSyncData *odata = osync_data_new((char *)doc, osync_xmlformat_size(), plgdata->objformat, &error);
247 if (!odata) {
248 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
249 osync_error_unref(&error);
250 /* osync_data_new() does not increase the reference count of
251 its 'data' member, but osync_data_unref() will decrease it,
252 so this is the only case where 'doc' has to be unreferenced
253 manually */
254 osync_xmlformat_unref(doc);
255 goto error_xml;
258 OSyncChange *chg = osync_change_new(&error);
259 if (!chg) {
260 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
261 osync_error_unref(&error);
262 goto error_chg;
265 osync_change_set_uid(chg, uid);
266 osync_change_set_data(chg, odata);
267 osync_data_unref(odata);
268 osync_change_set_objtype(chg, osync_objtype_sink_get_name(sink));
269 osync_change_set_hash(chg, hash);
271 /* TODO: put logic here to report correctly the changes */
272 OSyncChangeType type = OSYNC_CHANGE_TYPE_MODIFIED;
273 osync_change_set_changetype(chg, type);
275 if (osync_change_get_changetype(chg) != OSYNC_CHANGE_TYPE_UNMODIFIED) {
276 osync_context_report_change(ctx, chg);
279 osync_change_unref(chg);
280 free(hash);
281 free(uid);
282 free(xmldata);
284 /* end of loop */
285 continue;
287 /* error handling in the loop */
288 error_chg:
289 osync_data_unref(odata);
290 error_xml:
291 free(hash);
292 error_hash:
293 free(uid);
294 error_uid:
295 free(xmldata);
296 goto error_xmldata;
299 /* TODO: report deleted entries */
302 fclose(out);
303 waitpid(pid, &status, 0);
305 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
306 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Helper exited abnormally");
307 goto error;
310 osync_context_report_success(ctx);
312 osync_trace(TRACE_EXIT, "%s", __func__);
313 return;
315 error_xmldata:
316 error_size:
317 fclose(out);
318 error_fdopen:
319 kill(pid, SIGTERM);
320 waitpid(pid, NULL, 0);
321 error:
322 osync_trace(TRACE_EXIT, "%s", __func__);
323 return;
326 static void gc_commit_change(void *data, OSyncPluginInfo *info,
327 OSyncContext *ctx, OSyncChange *change)
329 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx, change);
331 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
332 struct gc_plgdata *plgdata = data;
334 pid_t pid;
335 int input, output;
336 OSyncError *error = NULL;
337 const char *cmd;
338 const char *arg;
339 FILE *out;
340 int status;
341 char sizeline[1024];
342 char *hash;
344 switch (osync_change_get_changetype(change)) {
345 case OSYNC_CHANGE_TYPE_ADDED:
346 cmd = "add";
347 arg = NULL;
348 break;
349 case OSYNC_CHANGE_TYPE_MODIFIED:
350 cmd = "edit";
351 arg = hash;
352 break;
353 case OSYNC_CHANGE_TYPE_DELETED:
354 cmd = "delete";
355 arg = hash;
356 break;
357 default:
358 osync_context_report_error(ctx, OSYNC_ERROR_NOT_SUPPORTED, "Unknown change type");
359 goto error;
360 break;
363 if (!run_helper(plgdata, cmd, arg,
364 &input, &output, &pid, &error)) {
365 osync_context_report_osyncerror(ctx, &error);
366 goto error;
369 g_free(hash);
371 out = fdopen(output, "r");
372 if (!out) {
373 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't open helper output");
374 close(output);
375 goto error_fdopen;
378 switch (osync_change_get_changetype(change)) {
379 case OSYNC_CHANGE_TYPE_ADDED:
380 case OSYNC_CHANGE_TYPE_MODIFIED:
382 /* TODO: get data using updated function */
383 OSyncXMLFormat *doc = NULL;//(OSyncXMLFormat *)osync_data_get_data_ptr(osync_change_get_data(change));
384 int size;
385 char *buffer;
387 osync_xmlformat_assemble(doc, &buffer, &size);
388 osync_trace(TRACE_INTERNAL, "input to helper:\n%s", buffer);
389 if (write(input, buffer, size) < size) {
390 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't write data to helper");
391 close(input);
392 g_free(buffer);
393 goto error_write;
396 close(input);
397 g_free(buffer);
399 int xmlsize, uidsize, hashsize;
400 char *xmldata, *uid, *hash;
402 if (!fgets(sizeline, sizeof(sizeline), out)) {
403 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't read from helper");
404 goto error_read;
407 if (sscanf(sizeline, "%d %d %d", &xmlsize, &uidsize, &hashsize) < 3) {
408 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Invalid size line from helper");
409 goto error_size;
412 xmldata = malloc(xmlsize);
413 if (!xmldata) {
414 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
415 goto error_xmldata;
418 uid = malloc(uidsize + 1);
419 if (!uid) {
420 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
421 goto error_uid;
424 hash = malloc(hashsize + 1);
425 if (!hash) {
426 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
427 goto error_hash;
430 if (fread(xmldata, xmlsize, 1, out) < 1) {
431 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
432 goto error_readxml;
436 if (fread(uid, uidsize, 1, out) < 1) {
437 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
438 goto error_readxml;
440 uid[uidsize] = '\0';
442 if (fread(hash, hashsize, 1, out) < 1) {
443 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
444 goto error_readxml;
446 hash[hashsize] = '\0';
448 /* Done writing. Update UID and hash */
449 osync_change_set_uid(change, uid);
450 osync_change_set_hash(change, hash);
452 free(hash);
453 free(uid);
454 free(xmldata);
456 break;
458 /* Error handling */
459 error_readxml:
460 free(hash);
461 error_hash:
462 free(uid);
463 error_uid:
464 free(xmldata);
465 error_xmldata:
466 error_size:
467 break;
469 break;
470 case OSYNC_CHANGE_TYPE_DELETED:
471 close(input);
472 break;
473 default:
474 g_assert_not_reached();
477 fclose(out);
479 waitpid(pid, &status, 0);
481 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
482 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Helper exited abnormally");
483 goto error;
486 osync_context_report_success(ctx);
488 osync_trace(TRACE_EXIT, "%s", __func__);
489 return;
491 error_read:
492 error_write:
493 fclose(out);
494 error_fdopen:
495 kill(pid, SIGTERM);
496 waitpid(pid, NULL, 0);
497 error:
498 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
499 return;
502 static void gc_disconnect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
504 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
505 struct gc_plgdata *plgdata = data;
507 osync_context_report_success(ctx);
508 osync_trace(TRACE_EXIT, "%s", __func__);
511 static void gc_finalize(void *data)
513 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
514 struct gc_plgdata *plgdata = data;
516 free_plg(plgdata);
517 osync_trace(TRACE_EXIT, "%s", __func__);
520 static void *gc_initialize(OSyncPlugin *plugin,
521 OSyncPluginInfo *info,
522 OSyncError **error)
524 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, plugin, info, error);
525 struct gc_plgdata *plgdata;
526 const char *cfg;
528 plgdata = osync_try_malloc0(sizeof(struct gc_plgdata), error);
529 if (!plgdata)
530 goto out;
532 cfg = osync_plugin_info_get_config(info);
533 if (!cfg) {
534 osync_error_set(error, OSYNC_ERROR_GENERIC,
535 "Unable to get config data.");
536 goto error_freeplg;
539 if (!gc_parse_config(plgdata, cfg, error))
540 goto error_freeplg;
542 OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
543 plgdata->objformat = osync_format_env_find_objformat(formatenv, "xmlformat-event");
544 if (!plgdata->objformat)
545 goto error_freeplg;
546 osync_objformat_ref(plgdata->objformat);
548 plgdata->sink = osync_objtype_sink_new("event", error);
549 if (!plgdata->sink)
550 goto error_freeplg;
552 /* Add object format somehow */
553 //osync_objtype_sink_add_objformat(plgdata->sink, "xmlformat-event");
555 OSyncObjTypeSinkFunctions functions;
556 memset(&functions, 0, sizeof(functions));
557 functions.connect = gc_connect;
558 functions.disconnect = gc_disconnect;
559 functions.get_changes = gc_get_changes;
560 functions.commit = gc_commit_change;
562 osync_objtype_sink_set_functions(plgdata->sink, functions, plgdata);
563 osync_plugin_info_add_objtype(info, plgdata->sink);
565 osync_trace(TRACE_EXIT, "%s", __func__);
567 return plgdata;
569 error_freeplg:
570 free_plg(plgdata);
571 out:
572 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
573 return NULL;
576 static osync_bool gc_discover(void *data, OSyncPluginInfo *info, OSyncError **error)
578 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, error);
580 struct gc_plgdata *plgdata = data;
582 osync_objtype_sink_set_available(plgdata->sink, TRUE);
584 OSyncVersion *version = osync_version_new(error);
585 osync_version_set_plugin(version, "google-calendar");
586 osync_plugin_info_set_version(info, version);
587 osync_version_unref(version);
589 osync_trace(TRACE_EXIT, "%s", __func__);
590 return TRUE;
593 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
595 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, env, error);
596 OSyncPlugin *plugin = osync_plugin_new(error);
597 if (!plugin)
598 goto error;
600 osync_plugin_set_name(plugin, "google-calendar");
601 osync_plugin_set_longname(plugin, "Google Calendar");
602 osync_plugin_set_description(plugin, "Google Calendar plugin");
603 osync_plugin_set_config_type(plugin, OSYNC_PLUGIN_NEEDS_CONFIGURATION);
605 osync_plugin_set_initialize(plugin, gc_initialize);
606 osync_plugin_set_finalize(plugin, gc_finalize);
607 osync_plugin_set_discover(plugin, gc_discover);
609 osync_plugin_env_register_plugin(env, plugin);
610 osync_plugin_unref(plugin);
612 osync_trace(TRACE_EXIT, "%s", __func__);
613 return TRUE;
615 error:
616 osync_trace(TRACE_EXIT_ERROR, "Unable to register: %s", osync_error_print(error));
617 osync_error_unref(error);
618 return FALSE;
621 int get_version(void)
623 return 1;