Setting gcal objects to store the raw XML for entries (I'm going to use
[gdataplugin.git] / src / gcalendar.c
blob95059bb712f906f9214d62781123699374486e2f
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>
42 #include <gcontact.h>
44 struct gc_plgdata
46 char *url;
47 char *username;
48 char *password;
49 char *anchor_path;
50 char *timestamp;
51 char *timezone;
52 gcal_t calendar;
53 gcal_t contacts;
54 /* TODO: add a anchor here */
55 struct gcal_event_array all_events;
56 struct gcal_contact_array all_contacts;
57 /* calendar sink/format */
58 OSyncObjTypeSink *gcal_sink;
59 OSyncObjFormat *gcal_format;
60 /* contact sink/format */
61 OSyncObjTypeSink *gcont_sink;
62 OSyncObjFormat *gcont_format;
66 static void free_plg(struct gc_plgdata *plgdata)
68 if (plgdata->calendar) {
69 gcal_delete(plgdata->calendar);
70 gcal_cleanup_events(&(plgdata->all_events));
72 if (plgdata->contacts) {
73 gcal_delete(plgdata->contacts);
74 gcal_cleanup_contacts(&(plgdata->all_contacts));
77 if (plgdata->timestamp)
78 free(plgdata->timestamp);
79 if (plgdata->timezone)
80 free(plgdata->timezone);
81 if (plgdata->url)
82 xmlFree(plgdata->url);
83 if (plgdata->username)
84 xmlFree(plgdata->username);
85 if (plgdata->password)
86 xmlFree(plgdata->password);
87 if (plgdata->gcal_sink)
88 osync_objtype_sink_unref(plgdata->gcal_sink);
89 if (plgdata->gcal_format)
90 osync_objformat_unref(plgdata->gcal_format);
91 if (plgdata->gcont_sink)
92 osync_objtype_sink_unref(plgdata->gcont_sink);
93 if (plgdata->gcont_format)
94 osync_objformat_unref(plgdata->gcont_format);
95 g_free(plgdata);
98 /** Run gchelper and return the file descriptors for its stdin/stdout
101 osync_bool run_helper(struct gc_plgdata *plgdata, const char *operation,
102 const char *arg, int *in, int *out, pid_t *ppid,
103 OSyncError **error)
106 osync_bool result = FALSE;
107 /* TODO: add calls to libgcal */
108 exit:
109 return result;
112 static void gc_connect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
114 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
116 int result;
117 struct gc_plgdata *plgdata = data;
118 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
119 OSyncError *error = NULL;
121 if (plgdata->calendar) {
122 result = gcal_get_authentication(plgdata->calendar, plgdata->username,
123 plgdata->password);
124 if (result == -1) {
125 osync_context_report_osyncerror(ctx, &error);
126 return;
130 if (plgdata->contacts) {
131 result = gcal_get_authentication(plgdata->contacts, plgdata->username,
132 plgdata->password);
133 if (result == -1) {
134 osync_context_report_osyncerror(ctx, &error);
135 return;
139 osync_context_report_success(ctx);
141 osync_trace(TRACE_EXIT, "%s", __func__);
144 static void gc_get_changes(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
146 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
148 struct gc_plgdata *plgdata = data;
149 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
150 OSyncError *error = NULL;
151 int result;
152 char *timestamp = NULL;
154 timestamp = osync_anchor_retrieve(plgdata->anchor_path, "gdata");
155 if (timestamp)
156 fprintf(stderr, "timestamp is: %s\n", timestamp);
157 else
158 fprintf(stderr, "first sync!\n");
160 if (plgdata->gcal_sink)
161 if (osync_objtype_sink_get_slowsync(plgdata->gcal_sink)) {
162 /* TODO: write getall */
163 fprintf(stderr, "\n\t\tgcal: Client asked for slow syncing...\n");
164 } else {
165 /* TODO: write getchanges */
166 fprintf(stderr, "\n\t\tgcal: Its a fast sync!\n");
169 if (plgdata->gcont_sink)
170 if (osync_objtype_sink_get_slowsync(plgdata->gcont_sink)) {
171 /* TODO: write getall */
172 fprintf(stderr, "\n\t\tgcont: Client asked for slow syncing...\n");
173 } else {
174 /* TODO: write getchanges */
175 fprintf(stderr, "\n\t\tgcont: Its a fast sync!\n");
178 osync_context_report_success(ctx);
182 static void gc_commit_change(void *data, OSyncPluginInfo *info,
183 OSyncContext *ctx, OSyncChange *change)
185 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx, change);
187 OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
188 struct gc_plgdata *plgdata = data;
190 pid_t pid;
191 int input, output;
192 OSyncError *error = NULL;
193 const char *cmd;
194 const char *arg;
195 FILE *out;
196 int status;
197 char sizeline[1024];
198 char *hash;
200 switch (osync_change_get_changetype(change)) {
201 case OSYNC_CHANGE_TYPE_ADDED:
202 cmd = "add";
203 arg = NULL;
204 break;
205 case OSYNC_CHANGE_TYPE_MODIFIED:
206 cmd = "edit";
207 arg = hash;
208 break;
209 case OSYNC_CHANGE_TYPE_DELETED:
210 cmd = "delete";
211 arg = hash;
212 break;
213 default:
214 osync_context_report_error(ctx, OSYNC_ERROR_NOT_SUPPORTED, "Unknown change type");
215 goto error;
216 break;
219 if (!run_helper(plgdata, cmd, arg,
220 &input, &output, &pid, &error)) {
221 osync_context_report_osyncerror(ctx, &error);
222 goto error;
225 g_free(hash);
227 out = fdopen(output, "r");
228 if (!out) {
229 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't open helper output");
230 close(output);
231 goto error_fdopen;
234 switch (osync_change_get_changetype(change)) {
235 case OSYNC_CHANGE_TYPE_ADDED:
236 case OSYNC_CHANGE_TYPE_MODIFIED:
238 /* TODO: get data using updated function */
239 OSyncXMLFormat *doc = NULL;//(OSyncXMLFormat *)osync_data_get_data_ptr(osync_change_get_data(change));
240 int size;
241 char *buffer;
243 osync_xmlformat_assemble(doc, &buffer, &size);
244 osync_trace(TRACE_INTERNAL, "input to helper:\n%s", buffer);
245 if (write(input, buffer, size) < size) {
246 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't write data to helper");
247 close(input);
248 g_free(buffer);
249 goto error_write;
252 close(input);
253 g_free(buffer);
255 int xmlsize, uidsize, hashsize;
256 char *xmldata, *uid, *hash;
258 if (!fgets(sizeline, sizeof(sizeline), out)) {
259 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't read from helper");
260 goto error_read;
263 if (sscanf(sizeline, "%d %d %d", &xmlsize, &uidsize, &hashsize) < 3) {
264 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Invalid size line from helper");
265 goto error_size;
268 xmldata = malloc(xmlsize);
269 if (!xmldata) {
270 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
271 goto error_xmldata;
274 uid = malloc(uidsize + 1);
275 if (!uid) {
276 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
277 goto error_uid;
280 hash = malloc(hashsize + 1);
281 if (!hash) {
282 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "No memory");
283 goto error_hash;
286 if (fread(xmldata, xmlsize, 1, out) < 1) {
287 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
288 goto error_readxml;
292 if (fread(uid, uidsize, 1, out) < 1) {
293 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
294 goto error_readxml;
296 uid[uidsize] = '\0';
298 if (fread(hash, hashsize, 1, out) < 1) {
299 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Error reading xml data from helper");
300 goto error_readxml;
302 hash[hashsize] = '\0';
304 /* Done writing. Update UID and hash */
305 osync_change_set_uid(change, uid);
306 osync_change_set_hash(change, hash);
308 free(hash);
309 free(uid);
310 free(xmldata);
312 break;
314 /* Error handling */
315 error_readxml:
316 free(hash);
317 error_hash:
318 free(uid);
319 error_uid:
320 free(xmldata);
321 error_xmldata:
322 error_size:
323 break;
325 break;
326 case OSYNC_CHANGE_TYPE_DELETED:
327 close(input);
328 break;
329 default:
330 g_assert_not_reached();
333 fclose(out);
335 waitpid(pid, &status, 0);
337 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
338 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Helper exited abnormally");
339 goto error;
342 osync_context_report_success(ctx);
344 osync_trace(TRACE_EXIT, "%s", __func__);
345 return;
347 error_read:
348 error_write:
349 fclose(out);
350 error_fdopen:
351 kill(pid, SIGTERM);
352 waitpid(pid, NULL, 0);
353 error:
354 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
355 return;
358 static void gc_sync_done(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
360 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
361 struct gc_plgdata *plgdata = NULL;
362 int result;
364 plgdata = data;
365 result = get_mili_timestamp(plgdata->timestamp, TIMESTAMP_MAX_SIZE,
366 plgdata->timezone);
367 osync_anchor_update(plgdata->anchor_path, "gdata", plgdata->timestamp);
370 static void gc_disconnect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
372 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, info, ctx);
373 struct gc_plgdata *plgdata = data;
375 osync_context_report_success(ctx);
376 osync_trace(TRACE_EXIT, "%s", __func__);
379 static void gc_finalize(void *data)
381 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
382 struct gc_plgdata *plgdata = data;
384 free_plg(plgdata);
385 osync_trace(TRACE_EXIT, "%s", __func__);
388 static void *gc_initialize(OSyncPlugin *plugin,
389 OSyncPluginInfo *info,
390 OSyncError **error)
392 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, plugin, info, error);
393 struct gc_plgdata *plgdata;
394 OSyncPluginConfig *config;
395 OSyncPluginAuthentication *auth;
396 OSyncList *resources;
397 OSyncList *r;
398 const char *objtype, *tmp;
399 int i, numobjs;
401 plgdata = osync_try_malloc0(sizeof(struct gc_plgdata), error);
402 config = osync_plugin_info_get_config(info);
403 if ((!plgdata) || (!config)) {
404 osync_error_set(error, OSYNC_ERROR_GENERIC,
405 "Unable to get config data.");
406 goto error_freeplg;
409 /* TODO: add timezone in configuration */
410 if (!(plgdata->timestamp = malloc(TIMESTAMP_MAX_SIZE)))
411 goto error_freeplg;
412 plgdata->timezone = NULL;
414 resources = osync_plugin_config_get_resources(config);
415 numobjs = osync_plugin_info_num_objtypes(info);
417 for (i = 1, r = resources; r; r = r->next, i++) {
418 if (!(strcmp(osync_plugin_resource_get_objtype(r->data), "calendar")))
419 if (!(plgdata->calendar = gcal_new(GCALENDAR)))
420 goto error_freeplg;
421 else
422 gcal_set_store_xml(plgdata->calendar, 1);
424 if (!(strcmp(osync_plugin_resource_get_objtype(r->data), "contact")))
425 if (!(plgdata->contacts = gcal_new(GCONTACT)))
426 goto error_freeplg;
427 else
428 gcal_set_store_xml(plgdata->contacts, 1);
433 /* TODO: how works resource policy? For while, copy everything... */
434 for (i = 0; i < numobjs; i++) {
436 if (!plgdata->username) {
437 auth = osync_plugin_config_get_authentication(config);
438 tmp = osync_plugin_authentication_get_username(auth);
439 if (!tmp)
440 goto error_freeplg;
441 else
442 if (!(plgdata->username = strdup(tmp)))
443 goto error_freeplg;
447 if (!plgdata->password) {
448 tmp = osync_plugin_authentication_get_password(auth);
449 if (!tmp)
450 goto error_freeplg;
451 else
452 if (!(plgdata->password = strdup(tmp)))
453 goto error_freeplg;
456 /* TODO: get proxy/calendar title/resources/etc */
460 OSyncObjTypeSinkFunctions functions;
461 memset(&functions, 0, sizeof(functions));
462 functions.connect = gc_connect;
463 functions.get_changes = gc_get_changes;
464 functions.commit = gc_commit_change;
465 functions.disconnect = gc_disconnect;
466 functions.sync_done = gc_sync_done;
469 if (plgdata->calendar) {
470 fprintf(stderr, "\n\n\tcreating calendar sink...\n");
471 OSyncFormatEnv *formatenv1 = osync_plugin_info_get_format_env(info);
472 plgdata->gcal_format = osync_format_env_find_objformat(formatenv1, "xmlformat-event");
473 if (!plgdata->gcal_format)
474 goto error_freeplg;
475 osync_objformat_ref(plgdata->gcal_format);
477 plgdata->gcal_sink = osync_objtype_sink_new("event", error);
478 if (!plgdata->gcal_sink)
479 goto error_freeplg;
481 osync_objtype_sink_set_functions(plgdata->gcal_sink, functions, plgdata);
482 osync_plugin_info_add_objtype(info, plgdata->gcal_sink);
486 if (plgdata->contacts) {
487 fprintf(stderr, "\n\n\tcreating contact sink...\n");
488 OSyncFormatEnv *formatenv2 = osync_plugin_info_get_format_env(info);
489 plgdata->gcont_format = osync_format_env_find_objformat(formatenv2, "xmlformat-contact");
490 if (!plgdata->gcont_format)
491 goto error_freeplg;
492 osync_objformat_ref(plgdata->gcont_format);
494 plgdata->gcont_sink = osync_objtype_sink_new("contact", error);
495 if (!plgdata->gcont_sink)
496 goto error_freeplg;
498 osync_objtype_sink_set_functions(plgdata->gcont_sink, functions, plgdata);
499 osync_plugin_info_add_objtype(info, plgdata->gcont_sink);
504 plgdata->anchor_path = g_strdup_printf("%sanchor.db",
505 osync_plugin_info_get_configdir(info));
506 if (!(plgdata->anchor_path))
507 goto error_freeplg;
508 else
509 fprintf(stderr, "\tanchor: %s\n", plgdata->anchor_path);
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;