upgrade obexd to 0.47
[profile/ivi/obexd.git] / plugins / syncevolution.c
1 /*
2  *
3  *  OBEX Server
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation
6  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <sys/types.h>
33
34 #include <glib.h>
35 #include <dbus/dbus.h>
36
37 #include <bluetooth/bluetooth.h>
38
39 #include "plugin.h"
40 #include "obex.h"
41 #include "service.h"
42 #include "mimetype.h"
43 #include "log.h"
44 #include "manager.h"
45 #include "btio.h"
46 #include "obexd.h"
47 #include "gdbus.h"
48 #include "filesystem.h"
49
50 #define SYNCML_TARGET_SIZE 11
51
52 static const uint8_t SYNCML_TARGET[SYNCML_TARGET_SIZE] = {
53                         0x53, 0x59, 0x4E, 0x43, 0x4D, 0x4C, 0x2D, 0x53,
54                         0x59, 0x4E, 0x43 };
55
56 #define SYNCEVOLUTION_CHANNEL  19
57
58 #define SYNCEVOLUTION_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\
59 <record>                                                                \
60  <attribute id=\"0x0001\">                                              \
61     <sequence>                                                          \
62       <uuid value=\"00000002-0000-1000-8000-0002ee000002\"/>            \
63     </sequence>                                                 \
64  </attribute>                                                           \
65                                                                         \
66  <attribute id=\"0x0004\">                                              \
67     <sequence>                                                          \
68       <sequence>                                                        \
69         <uuid value=\"0x0100\"/>                                        \
70       </sequence>                                                       \
71       <sequence>                                                        \
72         <uuid value=\"0x0003\"/>                                        \
73         <uint8 value=\"%u\" name=\"channel\"/>                          \
74       </sequence>                                                       \
75       <sequence>                                                        \
76         <uuid value=\"0x0008\"/>                                        \
77       </sequence>                                                       \
78     </sequence>                                                 \
79  </attribute>                                                           \
80                                                                         \
81  <attribute id=\"0x0100\">                                              \
82     <text value=\"%s\" name=\"name\"/>                                  \
83  </attribute>                                                           \
84 </record>"
85
86 #define SYNCE_BUS_NAME  "org.syncevolution"
87 #define SYNCE_PATH      "/org/syncevolution/Server"
88 #define SYNCE_SERVER_INTERFACE  "org.syncevolution.Server"
89 #define SYNCE_CONN_INTERFACE    "org.syncevolution.Connection"
90
91 struct synce_context {
92         struct obex_session *os;
93         DBusConnection *dbus_conn;
94         char *conn_obj;
95         unsigned int reply_watch;
96         unsigned int abort_watch;
97         GString *buffer;
98         int lasterr;
99         char *id;
100 };
101
102 static void append_dict_entry(DBusMessageIter *dict, const char *key,
103                                                         int type, void *val)
104 {
105         DBusMessageIter entry;
106
107         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
108                                                         NULL, &entry);
109         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
110         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
111         dbus_message_iter_close_container(dict, &entry);
112 }
113
114 static gboolean reply_signal(DBusConnection *conn, DBusMessage *msg,
115                                                                 void *data)
116 {
117         struct synce_context *context = data;
118         const char *path = dbus_message_get_path(msg);
119         DBusMessageIter iter, array_iter;
120         char *value;
121         int length;
122
123         if (strcmp(context->conn_obj, path) != 0) {
124                 obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
125                 context->lasterr = -EPERM;
126                 return FALSE;
127         }
128
129         dbus_message_iter_init(msg, &iter);
130
131         dbus_message_iter_recurse(&iter, &array_iter);
132         dbus_message_iter_get_fixed_array(&array_iter, &value, &length);
133
134         context->buffer = g_string_new_len(value, length);
135         obex_object_set_io_flags(context, G_IO_IN, 0);
136         context->lasterr = 0;
137
138         return TRUE;
139 }
140
141 static gboolean abort_signal(DBusConnection *conn, DBusMessage *msg,
142                                                                 void *data)
143 {
144         struct synce_context *context = data;
145
146         obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
147         context->lasterr = -EPERM;
148
149         return TRUE;
150 }
151
152 static void connect_cb(DBusPendingCall *call, void *user_data)
153 {
154         struct synce_context *context = user_data;
155         DBusConnection *conn;
156         DBusMessage *reply;
157         DBusError err;
158         char *path;
159
160         conn = context->dbus_conn;
161
162         reply = dbus_pending_call_steal_reply(call);
163
164         dbus_error_init(&err);
165         if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &path,
166                                                 DBUS_TYPE_INVALID) == FALSE) {
167                 error("%s", err.message);
168                 dbus_error_free(&err);
169                 goto failed;
170         }
171
172         DBG("Got conn object %s from syncevolution", path);
173         context->conn_obj = g_strdup(path);
174
175         context->reply_watch = g_dbus_add_signal_watch(conn, NULL, path,
176                                                 SYNCE_CONN_INTERFACE, "Reply",
177                                                 reply_signal, context, NULL);
178
179         context->abort_watch = g_dbus_add_signal_watch(conn, NULL, path,
180                                                 SYNCE_CONN_INTERFACE, "Abort",
181                                                 abort_signal, context, NULL);
182
183         dbus_message_unref(reply);
184
185         return;
186
187 failed:
188         obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
189         context->lasterr = -EPERM;
190 }
191
192 static void process_cb(DBusPendingCall *call, void *user_data)
193 {
194         struct synce_context *context = user_data;
195         DBusMessage *reply;
196         DBusError derr;
197
198         reply = dbus_pending_call_steal_reply(call);
199         dbus_error_init(&derr);
200         if (dbus_set_error_from_message(&derr, reply)) {
201                 error("process_cb(): syncevolution replied with an error:"
202                                         " %s, %s", derr.name, derr.message);
203                 dbus_error_free(&derr);
204
205                 obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
206                 context->lasterr = -EPERM;
207                 goto done;
208         }
209
210         obex_object_set_io_flags(context, G_IO_OUT, 0);
211         context->lasterr = 0;
212
213 done:
214         dbus_message_unref(reply);
215 }
216
217 static void *synce_connect(struct obex_session *os, int *err)
218 {
219         DBusConnection *conn;
220         struct synce_context *context;
221         char *address;
222
223         manager_register_session(os);
224
225         conn = manager_dbus_get_connection();
226         if (!conn)
227                 goto failed;
228
229         context = g_new0(struct synce_context, 1);
230         context->dbus_conn = conn;
231         context->lasterr = -EAGAIN;
232         context->os = os;
233
234         if (obex_getpeername(os, &address) == 0) {
235                 context->id = g_strdup_printf("%s+%d", address,
236                                                         SYNCEVOLUTION_CHANNEL);
237                 g_free(address);
238         }
239
240         if (err)
241                 *err = 0;
242
243         return context;
244
245 failed:
246         if (err)
247                 *err = -EPERM;
248
249         return NULL;
250 }
251
252 static int synce_put(struct obex_session *os, void *user_data)
253 {
254         return 0;
255 }
256
257 static int synce_get(struct obex_session *os, void *user_data)
258 {
259         return obex_get_stream_start(os, NULL);
260 }
261
262 static void close_cb(DBusPendingCall *call, void *user_data)
263 {
264         DBusMessage *reply;
265         DBusError derr;
266
267         reply = dbus_pending_call_steal_reply(call);
268         dbus_error_init(&derr);
269         if (dbus_set_error_from_message(&derr, reply)) {
270                 error("close_cb(): syncevolution replied with an error:"
271                                         " %s, %s", derr.name, derr.message);
272                 dbus_error_free(&derr);
273         }
274
275         dbus_message_unref(reply);
276 }
277
278 static void synce_disconnect(struct obex_session *os, void *user_data)
279 {
280         struct synce_context *context = user_data;
281
282         g_free(context);
283 }
284
285 static void *synce_open(const char *name, int oflag, mode_t mode,
286                                 void *user_data, size_t *size, int *err)
287 {
288         struct synce_context *context = user_data;
289
290         if (err)
291                 *err = context ? 0 : -EFAULT;
292
293         return user_data;
294 }
295
296 static int synce_close(void *object)
297 {
298         struct synce_context *context = object;
299         DBusMessage *msg;
300         const char *error;
301         gboolean normal;
302         DBusPendingCall *call;
303
304         if (!context->conn_obj)
305                 goto done;
306
307         msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
308                                                 SYNCE_CONN_INTERFACE, "Close");
309         if (!msg)
310                 goto failed;
311
312         normal = TRUE;
313         error = "none";
314         dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &normal,
315                                 DBUS_TYPE_STRING, &error, DBUS_TYPE_INVALID);
316
317         dbus_connection_send_with_reply(context->dbus_conn, msg, &call, -1);
318         dbus_pending_call_set_notify(call, close_cb, NULL, NULL);
319         dbus_message_unref(msg);
320         dbus_pending_call_unref(call);
321
322 failed:
323         g_dbus_remove_watch(context->dbus_conn, context->reply_watch);
324         context->reply_watch = 0;
325         g_dbus_remove_watch(context->dbus_conn, context->abort_watch);
326         context->abort_watch = 0;
327
328         g_free(context->conn_obj);
329         context->conn_obj = NULL;
330
331 done:
332         dbus_connection_unref(context->dbus_conn);
333         g_free(context);
334         return 0;
335 }
336
337 static ssize_t synce_read(void *object, void *buf, size_t count)
338 {
339         struct synce_context *context = object;
340         DBusConnection *conn;
341         char transport[36], transport_description[24];
342         const char *session;
343         DBusMessage *msg;
344         DBusMessageIter iter, dict;
345         gboolean authenticate;
346         DBusPendingCall *call;
347
348         if (context->buffer)
349                 return string_read(context->buffer, buf, count);
350
351         conn = manager_dbus_get_connection();
352         if (conn == NULL)
353                 return -EPERM;
354
355         msg = dbus_message_new_method_call(SYNCE_BUS_NAME, SYNCE_PATH,
356                                 SYNCE_SERVER_INTERFACE, "Connect");
357         if (!msg)
358                 return -EPERM;
359
360         dbus_message_iter_init_append(msg, &iter);
361         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
362                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
363                 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
364                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
365
366         append_dict_entry(&dict, "id", DBUS_TYPE_STRING, context->id);
367
368         snprintf(transport, sizeof(transport), "%s.obexd", OBEXD_SERVICE);
369         append_dict_entry(&dict, "transport", DBUS_TYPE_STRING, transport);
370
371         snprintf(transport_description, sizeof(transport_description),
372                                                 "version %s", VERSION);
373         append_dict_entry(&dict, "transport_description", DBUS_TYPE_STRING,
374                                                         transport_description);
375
376         dbus_message_iter_close_container(&iter, &dict);
377
378         authenticate = FALSE;
379         session = "";
380         dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &authenticate,
381                         DBUS_TYPE_STRING, &session, DBUS_TYPE_INVALID);
382
383         if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) {
384                 error("D-Bus call to %s failed.", SYNCE_SERVER_INTERFACE);
385                 dbus_message_unref(msg);
386                 return -EPERM;
387         }
388
389         dbus_pending_call_set_notify(call, connect_cb, context, NULL);
390
391         dbus_pending_call_unref(call);
392         dbus_message_unref(msg);
393
394         return -EAGAIN;
395 }
396
397 static ssize_t synce_write(void *object, const void *buf, size_t count)
398 {
399         struct synce_context *context = object;
400         DBusMessage *msg;
401         DBusMessageIter iter, array_iter;
402         DBusPendingCall *call;
403         const char *type = obex_get_type(context->os);
404
405         if (context->lasterr == 0)
406                 return count;
407
408         if (!context->conn_obj)
409                 return -EFAULT;
410
411         msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
412                                         SYNCE_CONN_INTERFACE, "Process");
413         if (!msg)
414                 return -EFAULT;
415
416         dbus_message_iter_init_append(msg, &iter);
417         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
418                                 DBUS_TYPE_BYTE_AS_STRING, &array_iter);
419
420         dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
421                                                 &buf, count);
422         dbus_message_iter_close_container(&iter, &array_iter);
423
424         dbus_message_append_args(msg, DBUS_TYPE_STRING, &type,
425                                                 DBUS_TYPE_INVALID);
426
427         if (!dbus_connection_send_with_reply(context->dbus_conn, msg,
428                                                                 &call, -1)) {
429                 error("D-Bus call to %s failed.", SYNCE_CONN_INTERFACE);
430                 dbus_message_unref(msg);
431                 return -EPERM;
432         }
433
434         dbus_pending_call_set_notify(call, process_cb, context, NULL);
435
436         dbus_message_unref(msg);
437         dbus_pending_call_unref(call);
438
439         return -EAGAIN;
440 }
441
442 static struct obex_mime_type_driver synce_driver = {
443         .target = SYNCML_TARGET,
444         .target_size = SYNCML_TARGET_SIZE,
445         .open = synce_open,
446         .close = synce_close,
447         .read = synce_read,
448         .write = synce_write,
449 };
450
451 static struct obex_service_driver synce = {
452         .name = "OBEX server for SyncML, using SyncEvolution",
453         .service = OBEX_SYNCEVOLUTION,
454         .channel = SYNCEVOLUTION_CHANNEL,
455         .secure = TRUE,
456         .record = SYNCEVOLUTION_RECORD,
457         .target = SYNCML_TARGET,
458         .target_size = SYNCML_TARGET_SIZE,
459         .get = synce_get,
460         .put = synce_put,
461         .connect = synce_connect,
462         .disconnect = synce_disconnect,
463 };
464
465 static int synce_init(void)
466 {
467         int err;
468
469         err = obex_mime_type_driver_register(&synce_driver);
470         if (err < 0)
471                 return err;
472
473         return obex_service_driver_register(&synce);
474 }
475
476 static void synce_exit(void)
477 {
478         obex_service_driver_unregister(&synce);
479         obex_mime_type_driver_unregister(&synce_driver);
480 }
481
482 OBEX_PLUGIN_DEFINE(syncevolution, synce_init, synce_exit)