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.
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
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>
32 #include <libxml/tree.h>
37 #include <sys/types.h>
40 #include <gcal_status.h>
41 #include <gcalendar.h>
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
);
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
);
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
,
106 osync_bool result
= FALSE
;
107 /* TODO: add calls to libgcal */
112 static void gc_connect(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
114 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
);
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
,
125 osync_context_report_osyncerror(ctx
, &error
);
130 if (plgdata
->contacts
) {
131 result
= gcal_get_authentication(plgdata
->contacts
, plgdata
->username
,
134 osync_context_report_osyncerror(ctx
, &error
);
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
;
152 char *timestamp
= NULL
;
154 timestamp
= osync_anchor_retrieve(plgdata
->anchor_path
, "gdata");
156 fprintf(stderr
, "timestamp is: %s\n", timestamp
);
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");
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");
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
;
192 OSyncError
*error
= NULL
;
200 switch (osync_change_get_changetype(change
)) {
201 case OSYNC_CHANGE_TYPE_ADDED
:
205 case OSYNC_CHANGE_TYPE_MODIFIED
:
209 case OSYNC_CHANGE_TYPE_DELETED
:
214 osync_context_report_error(ctx
, OSYNC_ERROR_NOT_SUPPORTED
, "Unknown change type");
219 if (!run_helper(plgdata
, cmd
, arg
,
220 &input
, &output
, &pid
, &error
)) {
221 osync_context_report_osyncerror(ctx
, &error
);
227 out
= fdopen(output
, "r");
229 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Couldn't open helper output");
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));
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");
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");
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");
268 xmldata
= malloc(xmlsize
);
270 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
274 uid
= malloc(uidsize
+ 1);
276 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
280 hash
= malloc(hashsize
+ 1);
282 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
286 if (fread(xmldata
, xmlsize
, 1, out
) < 1) {
287 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
292 if (fread(uid
, uidsize
, 1, out
) < 1) {
293 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
298 if (fread(hash
, hashsize
, 1, out
) < 1) {
299 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
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
);
326 case OSYNC_CHANGE_TYPE_DELETED
:
330 g_assert_not_reached();
335 waitpid(pid
, &status
, 0);
337 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
338 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Helper exited abnormally");
342 osync_context_report_success(ctx
);
344 osync_trace(TRACE_EXIT
, "%s", __func__
);
352 waitpid(pid
, NULL
, 0);
354 osync_trace(TRACE_EXIT_ERROR
, "%s: %s", __func__
, osync_error_print(&error
));
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
;
365 result
= get_mili_timestamp(plgdata
->timestamp
, TIMESTAMP_MAX_SIZE
,
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
;
385 osync_trace(TRACE_EXIT
, "%s", __func__
);
388 static void *gc_initialize(OSyncPlugin
*plugin
,
389 OSyncPluginInfo
*info
,
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
;
398 const char *objtype
, *tmp
;
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.");
409 /* TODO: add timezone in configuration */
410 if (!(plgdata
->timestamp
= malloc(TIMESTAMP_MAX_SIZE
)))
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
)))
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
)))
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
);
442 if (!(plgdata
->username
= strdup(tmp
)))
447 if (!plgdata
->password
) {
448 tmp
= osync_plugin_authentication_get_password(auth
);
452 if (!(plgdata
->password
= strdup(tmp
)))
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
)
475 osync_objformat_ref(plgdata
->gcal_format
);
477 plgdata
->gcal_sink
= osync_objtype_sink_new("event", error
);
478 if (!plgdata
->gcal_sink
)
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
)
492 osync_objformat_ref(plgdata
->gcont_format
);
494 plgdata
->gcont_sink
= osync_objtype_sink_new("contact", error
);
495 if (!plgdata
->gcont_sink
)
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
))
509 fprintf(stderr
, "\tanchor: %s\n", plgdata
->anchor_path
);
511 osync_trace(TRACE_EXIT
, "%s", __func__
);
519 osync_trace(TRACE_EXIT_ERROR
, "%s: %s", __func__
, osync_error_print(error
));
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__
);
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
);
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__
);
565 osync_trace(TRACE_EXIT_ERROR
, "Unable to register: %s", osync_error_print(error
));
566 osync_error_unref(error
);
570 int get_version(void)