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 raw_xml
= plgdata
->xslt_ctx_gcal
->xml_str
;
277 xmlformat
= osync_xmlformat_parse(raw_xml
,
283 osync_xmlformat_sort(xmlformat
);
284 odata
= osync_data_new((char *)xmlformat
,
285 osync_xmlformat_size(),
286 plgdata
->gcont_format
, &error
);
290 if (!(chg
= osync_change_new(&error
)))
292 osync_data_set_objtype(odata
, osync_objtype_sink_get_name(plgdata
->gcont_sink
));
293 osync_change_set_data(chg
, odata
);
294 osync_data_unref(odata
);
296 osync_change_set_uid(chg
, gcal_contact_get_id(contact
));
297 osync_change_set_changetype(chg
, OSYNC_CHANGE_TYPE_ADDED
);
298 osync_context_report_change(ctx
, chg
);
299 osync_change_unref(chg
);
304 /* TODO: write getchanges */
305 fprintf(stderr
, "\n\t\tgcont: Its a fast sync!\n");
310 osync_context_report_success(ctx
);
314 osync_error_unref(&error
);
315 osync_xmlformat_unref(&xmlformat
);
319 osync_context_report_error(ctx
, OSYNC_ERROR_GENERIC
, msg
);
322 static void gc_commit_change(void *data
, OSyncPluginInfo
*info
,
323 OSyncContext
*ctx
, OSyncChange
*change
)
325 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
, change
);
328 switch (osync_change_get_changetype(change)) {
329 case OSYNC_CHANGE_TYPE_ADDED:
333 case OSYNC_CHANGE_TYPE_MODIFIED:
337 case OSYNC_CHANGE_TYPE_DELETED:
342 osync_context_report_error(ctx, OSYNC_ERROR_NOT_SUPPORTED, "Unknown change type");
348 osync_context_report_success(ctx
);
350 osync_trace(TRACE_EXIT
, "%s", __func__
);
354 static void gc_sync_done(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
356 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
);
357 struct gc_plgdata
*plgdata
= NULL
;
361 result
= get_mili_timestamp(plgdata
->timestamp
, TIMESTAMP_MAX_SIZE
,
363 osync_anchor_update(plgdata
->anchor_path
, "gdata", plgdata
->timestamp
);
366 static void gc_disconnect(void *data
, OSyncPluginInfo
*info
, OSyncContext
*ctx
)
368 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, ctx
);
369 struct gc_plgdata
*plgdata
= data
;
371 osync_context_report_success(ctx
);
372 osync_trace(TRACE_EXIT
, "%s", __func__
);
375 static void gc_finalize(void *data
)
377 osync_trace(TRACE_ENTRY
, "%s(%p)", __func__
, data
);
378 struct gc_plgdata
*plgdata
= data
;
381 osync_trace(TRACE_EXIT
, "%s", __func__
);
384 static void *gc_initialize(OSyncPlugin
*plugin
,
385 OSyncPluginInfo
*info
,
388 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, plugin
, info
, error
);
389 struct gc_plgdata
*plgdata
;
390 OSyncPluginConfig
*config
;
391 OSyncPluginAuthentication
*auth
;
392 OSyncPluginAdvancedOption
*advanced
;
393 OSyncList
*resources
;
395 const char *objtype
, *tmp
;
398 plgdata
= osync_try_malloc0(sizeof(struct gc_plgdata
), error
);
399 config
= osync_plugin_info_get_config(info
);
400 if ((!plgdata
) || (!config
)) {
401 osync_error_set(error
, OSYNC_ERROR_GENERIC
,
402 "Unable to get config data.");
406 advanced
= osync_plugin_config_get_advancedoption_value_by_name(config
, "xslt");
408 fprintf(stderr
, "Cannot locate xslt config!\n");
412 if (!(plgdata
->xslt_path
= strdup(osync_plugin_advancedoption_get_value(advanced
))))
415 /* TODO: add timezone in configuration */
416 if (!(plgdata
->timestamp
= malloc(TIMESTAMP_MAX_SIZE
)))
418 plgdata
->timezone
= NULL
;
420 resources
= osync_plugin_config_get_resources(config
);
421 numobjs
= osync_plugin_info_num_objtypes(info
);
423 for (i
= 1, r
= resources
; r
; r
= r
->next
, i
++) {
424 fprintf(stderr
, "field: %s\n", osync_plugin_resource_get_objtype(r
->data
));
425 if (!(strcmp(osync_plugin_resource_get_objtype(r
->data
), "event")))
426 if (!(plgdata
->calendar
= gcal_new(GCALENDAR
)))
429 /* fprintf(stderr, "\tcreated calendar obj!\n"); */
430 gcal_set_store_xml(plgdata
->calendar
, 1);
433 if (!(strcmp(osync_plugin_resource_get_objtype(r
->data
), "contact")))
434 if (!(plgdata
->contacts
= gcal_new(GCONTACT
)))
437 /* fprintf(stderr, "\tcreated contact obj!\n"); */
438 gcal_set_store_xml(plgdata
->contacts
, 1);
444 /* TODO: how works resource policy? For while, copy everything... */
445 for (i
= 0; i
< numobjs
; i
++) {
447 if (!plgdata
->username
) {
448 auth
= osync_plugin_config_get_authentication(config
);
449 tmp
= osync_plugin_authentication_get_username(auth
);
453 if (!(plgdata
->username
= strdup(tmp
)))
458 if (!plgdata
->password
) {
459 tmp
= osync_plugin_authentication_get_password(auth
);
463 if (!(plgdata
->password
= strdup(tmp
)))
467 /* TODO: get proxy/calendar title/resources/etc */
471 OSyncObjTypeSinkFunctions functions
;
472 memset(&functions
, 0, sizeof(functions
));
473 functions
.connect
= gc_connect
;
474 functions
.get_changes
= gc_get_changes
;
475 functions
.commit
= gc_commit_change
;
476 functions
.disconnect
= gc_disconnect
;
477 functions
.sync_done
= gc_sync_done
;
480 if (plgdata
->calendar
) {
481 fprintf(stderr
, "\n\n\tcreating calendar sink...\n");
482 OSyncFormatEnv
*formatenv1
= osync_plugin_info_get_format_env(info
);
483 plgdata
->gcal_format
= osync_format_env_find_objformat(formatenv1
, "xmlformat-event");
484 if (!plgdata
->gcal_format
)
486 osync_objformat_ref(plgdata
->gcal_format
);
488 plgdata
->gcal_sink
= osync_objtype_sink_new("event", error
);
489 if (!plgdata
->gcal_sink
)
492 osync_objtype_sink_set_functions(plgdata
->gcal_sink
, functions
, plgdata
);
493 osync_plugin_info_add_objtype(info
, plgdata
->gcal_sink
);
497 if (plgdata
->contacts
) {
498 fprintf(stderr
, "\n\n\tcreating contact sink...\n");
499 OSyncFormatEnv
*formatenv2
= osync_plugin_info_get_format_env(info
);
500 plgdata
->gcont_format
= osync_format_env_find_objformat(formatenv2
, "xmlformat-contact");
501 if (!plgdata
->gcont_format
)
503 osync_objformat_ref(plgdata
->gcont_format
);
505 plgdata
->gcont_sink
= osync_objtype_sink_new("contact", error
);
506 if (!plgdata
->gcont_sink
)
509 osync_objtype_sink_set_functions(plgdata
->gcont_sink
, functions
, plgdata
);
510 osync_plugin_info_add_objtype(info
, plgdata
->gcont_sink
);
515 plgdata
->anchor_path
= g_strdup_printf("%s/anchor.db",
516 osync_plugin_info_get_configdir(info
));
517 if (!(plgdata
->anchor_path
))
520 fprintf(stderr
, "\tanchor: %s\n", plgdata
->anchor_path
);
522 if (plgdata
->calendar
)
523 if (!(plgdata
->xslt_ctx_gcal
= xslt_new()))
526 fprintf(stderr
, "\tsucceed creating xslt_gcal!\n");
528 if (plgdata
->contacts
)
529 if (!(plgdata
->xslt_ctx_gcont
= xslt_new()))
532 fprintf(stderr
, "\tsucceed creating xslt_gcont!\n");
534 osync_trace(TRACE_EXIT
, "%s", __func__
);
542 osync_trace(TRACE_EXIT_ERROR
, "%s: %s", __func__
, osync_error_print(error
));
546 static osync_bool
gc_discover(void *data
, OSyncPluginInfo
*info
, OSyncError
**error
)
548 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, data
, info
, error
);
550 struct gc_plgdata
*plgdata
= data
;
552 if (plgdata
->calendar
)
553 osync_objtype_sink_set_available(plgdata
->gcal_sink
, TRUE
);
554 if (plgdata
->contacts
)
555 osync_objtype_sink_set_available(plgdata
->gcont_sink
, TRUE
);
557 OSyncVersion
*version
= osync_version_new(error
);
558 osync_version_set_plugin(version
, "google-data");
559 osync_plugin_info_set_version(info
, version
);
560 osync_version_unref(version
);
562 osync_trace(TRACE_EXIT
, "%s", __func__
);
566 osync_bool
get_sync_info(OSyncPluginEnv
*env
, OSyncError
**error
)
568 osync_trace(TRACE_ENTRY
, "%s(%p, %p)", __func__
, env
, error
);
569 OSyncPlugin
*plugin
= osync_plugin_new(error
);
573 osync_plugin_set_name(plugin
, "google-data");
574 osync_plugin_set_longname(plugin
, "Google calendar/plugin");
575 osync_plugin_set_description(plugin
, "Google calendar and contacts plugin");
577 osync_plugin_set_initialize(plugin
, gc_initialize
);
578 osync_plugin_set_finalize(plugin
, gc_finalize
);
579 osync_plugin_set_discover(plugin
, gc_discover
);
581 osync_plugin_env_register_plugin(env
, plugin
);
582 osync_plugin_unref(plugin
);
584 osync_trace(TRACE_EXIT
, "%s", __func__
);
588 osync_trace(TRACE_EXIT_ERROR
, "Unable to register: %s", osync_error_print(error
));
589 osync_error_unref(error
);
593 int get_version(void)