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