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>
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
)
58 gcal_delete(plgdata
->gcal
);
60 xmlFree(plgdata
->url
);
61 if (plgdata
->username
)
62 xmlFree(plgdata
->username
);
63 if (plgdata
->password
)
64 xmlFree(plgdata
->password
);
66 osync_objtype_sink_unref(plgdata
->sink
);
67 if (plgdata
->objformat
)
68 osync_objformat_unref(plgdata
->objformat
);
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
,
80 osync_bool result
= FALSE
;
81 /* TODO: add calls to libgcal */
86 char *gc_get_cfgvalue(xmlNode
*cfg
, const char *name
)
89 for (c
= cfg
->xmlChildrenNode
; c
; c
= c
->next
) {
90 if (!xmlStrcmp(c
->name
, (const xmlChar
*)name
))
91 return (char*)xmlNodeGetContent(c
);
96 osync_bool
gc_parse_config(struct gc_plgdata
*plgdata
, const char *cfg
, OSyncError
**error
)
100 osync_bool ret
= FALSE
;
102 doc
= xmlParseMemory(cfg
, strlen(cfg
) + 1);
104 osync_error_set(error
, OSYNC_ERROR_GENERIC
, "Couldn't parse configuration");
108 node
= xmlDocGetRootElement(doc
);
109 if (!node
|| xmlStrcmp(node
->name
, (const xmlChar
*)"config")) {
110 osync_error_set(error
, OSYNC_ERROR_GENERIC
, "Invalid configuration");
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,
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");
137 xmlFree(plgdata
->url
);
138 xmlFree(plgdata
->username
);
139 xmlFree(plgdata
->password
);
143 static void gc_connect(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
145 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 plgdata
->gcal
= gcal_new(GCALENDAR
);
153 result
= gcal_get_authentication(plgdata
->gcal
, plgdata
->username
,
155 if ((!plgdata
->gcal
) || (result
== -1)) {
156 osync_context_report_osyncerror(ctx
, &error
);
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
;
180 if (!run_helper(plgdata
, "get_all", NULL
,
181 NULL
, &output
, &pid
, &error
)) {
182 osync_context_report_osyncerror(ctx
, &error
);
186 out
= fdopen(output
, "r");
188 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Couldn't open helper output");
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");
202 xmldata
= malloc(size
);
204 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
208 uid
= malloc(uidsize
+ 1);
210 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
214 hash
= malloc(hashsize
+ 1);
216 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
220 if (fread(xmldata
, size
, 1, out
) < 1) {
221 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
225 if (fread(uid
, uidsize
, 1, out
) < 1) {
226 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
231 if (fread(hash
, hashsize
, 1, out
) < 1) {
232 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
235 hash
[hashsize
] = '\0';
237 OSyncXMLFormat
*doc
= osync_xmlformat_parse(xmldata
, size
, &error
);
239 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Invalid XML data from helper");
240 osync_error_unref(&error
);
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
);
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
254 osync_xmlformat_unref(doc
);
258 OSyncChange
*chg
= osync_change_new(&error
);
260 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
261 osync_error_unref(&error
);
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
);
287 /* error handling in the loop */
289 osync_data_unref(odata
);
299 /* TODO: report deleted entries */
303 waitpid(pid
, &status
, 0);
305 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
306 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Helper exited abnormally");
310 osync_context_report_success(ctx
);
312 osync_trace(TRACE_EXIT
, "%s", __func__
);
320 waitpid(pid
, NULL
, 0);
322 osync_trace(TRACE_EXIT
, "%s", __func__
);
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
;
336 OSyncError
*error
= NULL
;
344 switch (osync_change_get_changetype(change
)) {
345 case OSYNC_CHANGE_TYPE_ADDED
:
349 case OSYNC_CHANGE_TYPE_MODIFIED
:
353 case OSYNC_CHANGE_TYPE_DELETED
:
358 osync_context_report_error(ctx
, OSYNC_ERROR_NOT_SUPPORTED
, "Unknown change type");
363 if (!run_helper(plgdata
, cmd
, arg
,
364 &input
, &output
, &pid
, &error
)) {
365 osync_context_report_osyncerror(ctx
, &error
);
371 out
= fdopen(output
, "r");
373 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Couldn't open helper output");
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));
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");
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");
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");
412 xmldata
= malloc(xmlsize
);
414 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
418 uid
= malloc(uidsize
+ 1);
420 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
424 hash
= malloc(hashsize
+ 1);
426 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "No memory");
430 if (fread(xmldata
, xmlsize
, 1, out
) < 1) {
431 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
436 if (fread(uid
, uidsize
, 1, out
) < 1) {
437 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
442 if (fread(hash
, hashsize
, 1, out
) < 1) {
443 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Error reading xml data from helper");
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
);
470 case OSYNC_CHANGE_TYPE_DELETED
:
474 g_assert_not_reached();
479 waitpid(pid
, &status
, 0);
481 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
482 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, "Helper exited abnormally");
486 osync_context_report_success(ctx
);
488 osync_trace(TRACE_EXIT
, "%s", __func__
);
496 waitpid(pid
, NULL
, 0);
498 osync_trace(TRACE_EXIT_ERROR
, "%s: %s", __func__
, osync_error_print(&error
));
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
;
517 osync_trace(TRACE_EXIT
, "%s", __func__
);
520 static void *gc_initialize(OSyncPlugin
*plugin
,
521 OSyncPluginInfo
*info
,
524 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, plugin
, info
, error
);
525 struct gc_plgdata
*plgdata
;
528 plgdata
= osync_try_malloc0(sizeof(struct gc_plgdata
), error
);
532 cfg
= osync_plugin_info_get_config(info
);
534 osync_error_set(error
, OSYNC_ERROR_GENERIC
,
535 "Unable to get config data.");
539 if (!gc_parse_config(plgdata
, cfg
, error
))
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
)
546 osync_objformat_ref(plgdata
->objformat
);
548 plgdata
->sink
= osync_objtype_sink_new("event", error
);
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__
);
572 osync_trace(TRACE_EXIT_ERROR
, "%s: %s", __func__
, osync_error_print(error
));
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__
);
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
);
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__
);
616 osync_trace(TRACE_EXIT_ERROR
, "Unable to register: %s", osync_error_print(error
));
617 osync_error_unref(error
);
621 int get_version(void)