1 /** Google Calendar plugin
3 * Copyright (c) 2006 Eduardo Pereira Habkost <ehabkost@raisama.net>
4 * Copyright (c) 2008 Adenilson Cavalcanti da Silva <adenilson.silva@indt.org.br>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * - remove all fprintf's and use osync_trace(TRACE_INTERNAL, ...) instead
25 * - getchanges: convert gdata XML to osync using 'xslt_transform'
26 * - getchanges: report all entries to opensync
27 * - getchanges: implement getupdated for fast sync
28 * - test if it works inside opensync (all tests for while are running with 'osyncplugin'
30 * - commitchanges: well... write the code
31 * - review code for leaks (I'm not sure if I'm using opensync API correctly...)
35 #include <opensync/opensync.h>
36 #include <opensync/opensync-plugin.h>
37 #include <opensync/opensync-helper.h>
38 #include <opensync/opensync-merger.h>
39 #include <opensync/opensync-format.h>
40 #include <opensync/opensync-data.h>
41 #include <opensync/opensync-version.h>
45 #include <libxml/tree.h>
50 #include <sys/types.h>
53 #include <gcal_status.h>
54 #include <gcalendar.h>
67 /* libgcal resources */
70 struct gcal_event_array all_events
;
71 struct gcal_contact_array all_contacts
;
72 /* calendar sink/format */
73 OSyncObjTypeSink
*gcal_sink
;
74 OSyncObjFormat
*gcal_format
;
75 /* contact sink/format */
76 OSyncObjTypeSink
*gcont_sink
;
77 OSyncObjFormat
*gcont_format
;
78 /* XSLT context resource struct */
79 struct xslt_resources
*xslt_ctx_gcal
;
80 struct xslt_resources
*xslt_ctx_gcont
;
83 static void free_plg(struct gc_plgdata
*plgdata
)
85 if (plgdata
->calendar
) {
86 gcal_delete(plgdata
->calendar
);
87 gcal_cleanup_events(&(plgdata
->all_events
));
89 if (plgdata
->contacts
) {
90 gcal_delete(plgdata
->contacts
);
91 gcal_cleanup_contacts(&(plgdata
->all_contacts
));
94 if (plgdata
->xslt_path
)
95 free(plgdata
->xslt_path
);
96 if (plgdata
->xslt_ctx_gcal
)
97 xslt_delete(plgdata
->xslt_ctx_gcal
);
98 if (plgdata
->xslt_ctx_gcont
)
99 xslt_delete(plgdata
->xslt_ctx_gcont
);
100 if (plgdata
->timestamp
)
101 free(plgdata
->timestamp
);
102 if (plgdata
->timezone
)
103 free(plgdata
->timezone
);
105 xmlFree(plgdata
->url
);
106 if (plgdata
->username
)
107 xmlFree(plgdata
->username
);
108 if (plgdata
->password
)
109 xmlFree(plgdata
->password
);
110 if (plgdata
->gcal_sink
)
111 osync_objtype_sink_unref(plgdata
->gcal_sink
);
112 if (plgdata
->gcal_format
)
113 osync_objformat_unref(plgdata
->gcal_format
);
114 if (plgdata
->gcont_sink
)
115 osync_objtype_sink_unref(plgdata
->gcont_sink
);
116 if (plgdata
->gcont_format
)
117 osync_objformat_unref(plgdata
->gcont_format
);
121 /** Run gchelper and return the file descriptors for its stdin/stdout
124 osync_bool
run_helper(struct gc_plgdata
*plgdata
, const char *operation
,
125 const char *arg
, int *in
, int *out
, pid_t
*ppid
,
129 osync_bool result
= FALSE
;
130 /* TODO: add calls to libgcal */
135 static void gc_connect(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
137 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
);
138 static int counter
= 0;
140 struct gc_plgdata
*plgdata
= data
;
141 OSyncObjTypeSink
*sink
= osync_plugin_info_get_sink(info
);
142 OSyncError
*error
= NULL
;
145 if ((plgdata
->calendar
) && (counter
== 0)) {
146 result
= gcal_get_authentication(plgdata
->calendar
, plgdata
->username
,
152 snprintf(buffer
, sizeof(buffer
) - 1, "%s/gcal2osync.xslt",
154 if ((result
= xslt_initialize(plgdata
->xslt_ctx_gcal
, buffer
)))
156 fprintf(stderr
, "\ndone calendar: %s\n", buffer
);
159 if (((plgdata
->contacts
) && (counter
== 1)) ||
160 ((plgdata
->gcont_sink
) && (!plgdata
->gcal_sink
))) {
161 result
= gcal_get_authentication(plgdata
->contacts
, plgdata
->username
,
167 snprintf(buffer
, sizeof(buffer
) - 1, "%s/gcont2osync.xslt",
169 if ((result
= xslt_initialize(plgdata
->xslt_ctx_gcont
, buffer
)))
171 fprintf(stderr
, "\ndone contact: %s\n", buffer
);
174 osync_context_report_success(ctx
);
175 osync_trace(TRACE_EXIT
, "%s", __func__
);
179 fprintf(stderr
, "\nGood bye, cruel world...\n");
180 osync_context_report_osyncerror(ctx
, &error
);
183 static void gc_get_changes(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
185 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
);
186 static int counter
= 0;
187 struct gc_plgdata
*plgdata
= data
;
188 OSyncObjTypeSink
*sink
= osync_plugin_info_get_sink(info
);
189 OSyncError
*error
= NULL
;
190 OSyncXMLFormat
*xmlformat
;
191 OSyncData
*odata
= NULL
;
192 OSyncChange
*chg
= NULL
;
194 char *timestamp
= NULL
, *msg
, *raw_xml
= NULL
;
196 gcal_contact contact
;
198 timestamp
= osync_anchor_retrieve(plgdata
->anchor_path
, "gdata");
200 fprintf(stderr
, "timestamp is: %s\n", timestamp
);
202 fprintf(stderr
, "first sync!\n");
204 if ((plgdata
->gcal_sink
) && (counter
== 0)) {
205 if (osync_objtype_sink_get_slowsync(plgdata
->gcal_sink
)) {
206 fprintf(stderr
, "\n\t\tgcal: Client asked for slow syncing...\n");
207 result
= gcal_get_events(plgdata
->calendar
, &(plgdata
->all_events
));
209 msg
= "Failed getting events!";
213 fprintf(stderr
, "\tgot then all!\n");
214 for (i
= 0; i
< plgdata
->all_events
.length
; ++i
) {
215 event
= gcal_event_element(&(plgdata
->all_events
), i
);
218 raw_xml
= gcal_event_get_xml(event
);
219 if ((result
= xslt_transform(plgdata
->xslt_ctx_gcal
,
223 raw_xml
= plgdata
->xslt_ctx_gcal
->xml_str
;
224 xmlformat
= osync_xmlformat_parse(raw_xml
,
230 osync_xmlformat_sort(xmlformat
);
231 odata
= osync_data_new((char *)xmlformat
,
232 osync_xmlformat_size(),
233 plgdata
->gcal_format
, &error
);
237 if (!(chg
= osync_change_new(&error
)))
239 osync_data_set_objtype(odata
, osync_objtype_sink_get_name(plgdata
->gcal_sink
));
240 osync_change_set_data(chg
, odata
);
241 osync_data_unref(odata
);
243 osync_change_set_uid(chg
, gcal_event_get_id(event
));
244 osync_change_set_changetype(chg
, OSYNC_CHANGE_TYPE_ADDED
);
245 osync_context_report_change(ctx
, chg
);
246 osync_change_unref(chg
);
251 /* TODO: write getchanges */
252 fprintf(stderr
, "\n\t\tgcal: Its a fast sync!\n");
257 if (((plgdata
->gcont_sink
) && (counter
== 1)) ||
258 ((plgdata
->gcont_sink
) && (!plgdata
->gcal_sink
)))
259 if (osync_objtype_sink_get_slowsync(plgdata
->gcont_sink
)) {
260 fprintf(stderr
, "\n\t\tgcont: Client asked for slow syncing...\n");
261 result
= gcal_get_contacts(plgdata
->contacts
, &(plgdata
->all_contacts
));
263 msg
= "Failed getting contacts!";
267 fprintf(stderr
, "\tgot then all!\n");
268 for (i
= 0; i
< plgdata
->all_contacts
.length
; ++i
) {
269 contact
= gcal_contact_element(&(plgdata
->all_contacts
), i
);
272 raw_xml
= gcal_contact_get_xml(contact
);
273 if ((result
= xslt_transform(plgdata
->xslt_ctx_gcont
,
276 /* TODO: report back contacts to opensync */
277 plgdata
->xslt_ctx_gcont
->xml_str
;
281 /* TODO: write getchanges */
282 fprintf(stderr
, "\n\t\tgcont: Its a fast sync!\n");
287 osync_context_report_success(ctx
);
291 osync_error_unref(&error
);
292 osync_xmlformat_unref(&xmlformat
);
296 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, msg
);
299 static void gc_commit_change(void *data
, OSyncPluginInfo
*info
,
300 OSyncContext
*ctx
, OSyncChange
*change
)
302 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
, change
);
305 switch (osync_change_get_changetype(change)) {
306 case OSYNC_CHANGE_TYPE_ADDED:
310 case OSYNC_CHANGE_TYPE_MODIFIED:
314 case OSYNC_CHANGE_TYPE_DELETED:
319 osync_context_report_error(ctx, OSYNC_ERROR_NOT_SUPPORTED, "Unknown change type");
325 osync_context_report_success(ctx
);
327 osync_trace(TRACE_EXIT
, "%s", __func__
);
331 static void gc_sync_done(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
333 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
);
334 struct gc_plgdata
*plgdata
= NULL
;
338 result
= get_mili_timestamp(plgdata
->timestamp
, TIMESTAMP_MAX_SIZE
,
340 osync_anchor_update(plgdata
->anchor_path
, "gdata", plgdata
->timestamp
);
343 static void gc_disconnect(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
345 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
);
346 struct gc_plgdata
*plgdata
= data
;
348 osync_context_report_success(ctx
);
349 osync_trace(TRACE_EXIT
, "%s", __func__
);
352 static void gc_finalize(void *data
)
354 osync_trace(TRACE_ENTRY
, "%s(%p)", __func__
, data
);
355 struct gc_plgdata
*plgdata
= data
;
358 osync_trace(TRACE_EXIT
, "%s", __func__
);
361 static void *gc_initialize(OSyncPlugin
*plugin
,
362 OSyncPluginInfo
*info
,
365 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, plugin
, info
, error
);
366 struct gc_plgdata
*plgdata
;
367 OSyncPluginConfig
*config
;
368 OSyncPluginAuthentication
*auth
;
369 OSyncPluginAdvancedOption
*advanced
;
370 OSyncList
*resources
;
372 const char *objtype
, *tmp
;
375 plgdata
= osync_try_malloc0(sizeof(struct gc_plgdata
), error
);
376 config
= osync_plugin_info_get_config(info
);
377 if ((!plgdata
) || (!config
)) {
378 osync_error_set(error
, OSYNC_ERROR_GENERIC
,
379 "Unable to get config data.");
383 advanced
= osync_plugin_config_get_advancedoption_value_by_name(config
, "xslt");
385 fprintf(stderr
, "Cannot locate xslt config!\n");
389 if (!(plgdata
->xslt_path
= strdup(osync_plugin_advancedoption_get_value(advanced
))))
392 /* TODO: add timezone in configuration */
393 if (!(plgdata
->timestamp
= malloc(TIMESTAMP_MAX_SIZE
)))
395 plgdata
->timezone
= NULL
;
397 resources
= osync_plugin_config_get_resources(config
);
398 numobjs
= osync_plugin_info_num_objtypes(info
);
400 for (i
= 1, r
= resources
; r
; r
= r
->next
, i
++) {
401 fprintf(stderr
, "field: %s\n", osync_plugin_resource_get_objtype(r
->data
));
402 if (!(strcmp(osync_plugin_resource_get_objtype(r
->data
), "event")))
403 if (!(plgdata
->calendar
= gcal_new(GCALENDAR
)))
406 /* fprintf(stderr, "\tcreated calendar obj!\n"); */
407 gcal_set_store_xml(plgdata
->calendar
, 1);
410 if (!(strcmp(osync_plugin_resource_get_objtype(r
->data
), "contact")))
411 if (!(plgdata
->contacts
= gcal_new(GCONTACT
)))
414 /* fprintf(stderr, "\tcreated contact obj!\n"); */
415 gcal_set_store_xml(plgdata
->contacts
, 1);
421 /* TODO: how works resource policy? For while, copy everything... */
422 for (i
= 0; i
< numobjs
; i
++) {
424 if (!plgdata
->username
) {
425 auth
= osync_plugin_config_get_authentication(config
);
426 tmp
= osync_plugin_authentication_get_username(auth
);
430 if (!(plgdata
->username
= strdup(tmp
)))
435 if (!plgdata
->password
) {
436 tmp
= osync_plugin_authentication_get_password(auth
);
440 if (!(plgdata
->password
= strdup(tmp
)))
444 /* TODO: get proxy/calendar title/resources/etc */
448 OSyncObjTypeSinkFunctions functions
;
449 memset(&functions
, 0, sizeof(functions
));
450 functions
.connect
= gc_connect
;
451 functions
.get_changes
= gc_get_changes
;
452 functions
.commit
= gc_commit_change
;
453 functions
.disconnect
= gc_disconnect
;
454 functions
.sync_done
= gc_sync_done
;
457 if (plgdata
->calendar
) {
458 fprintf(stderr
, "\n\n\tcreating calendar sink...\n");
459 OSyncFormatEnv
*formatenv1
= osync_plugin_info_get_format_env(info
);
460 plgdata
->gcal_format
= osync_format_env_find_objformat(formatenv1
, "xmlformat-event");
461 if (!plgdata
->gcal_format
)
463 osync_objformat_ref(plgdata
->gcal_format
);
465 plgdata
->gcal_sink
= osync_objtype_sink_new("event", error
);
466 if (!plgdata
->gcal_sink
)
469 osync_objtype_sink_set_functions(plgdata
->gcal_sink
, functions
, plgdata
);
470 osync_plugin_info_add_objtype(info
, plgdata
->gcal_sink
);
474 if (plgdata
->contacts
) {
475 fprintf(stderr
, "\n\n\tcreating contact sink...\n");
476 OSyncFormatEnv
*formatenv2
= osync_plugin_info_get_format_env(info
);
477 plgdata
->gcont_format
= osync_format_env_find_objformat(formatenv2
, "xmlformat-contact");
478 if (!plgdata
->gcont_format
)
480 osync_objformat_ref(plgdata
->gcont_format
);
482 plgdata
->gcont_sink
= osync_objtype_sink_new("contact", error
);
483 if (!plgdata
->gcont_sink
)
486 osync_objtype_sink_set_functions(plgdata
->gcont_sink
, functions
, plgdata
);
487 osync_plugin_info_add_objtype(info
, plgdata
->gcont_sink
);
492 plgdata
->anchor_path
= g_strdup_printf("%s/anchor.db",
493 osync_plugin_info_get_configdir(info
));
494 if (!(plgdata
->anchor_path
))
497 fprintf(stderr
, "\tanchor: %s\n", plgdata
->anchor_path
);
499 if (plgdata
->calendar
)
500 if (!(plgdata
->xslt_ctx_gcal
= xslt_new()))
503 fprintf(stderr
, "\tsucceed creating xslt_gcal!\n");
505 if (plgdata
->contacts
)
506 if (!(plgdata
->xslt_ctx_gcont
= xslt_new()))
509 fprintf(stderr
, "\tsucceed creating xslt_gcont!\n");
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)