/* * * OBEX Server * * Copyright (C) 2007-2010 Intel Corporation * Copyright (C) 2007-2010 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "plugin.h" #include "obex.h" #include "service.h" #include "mimetype.h" #include "log.h" #include "manager.h" #include "btio.h" #include "obexd.h" #include "gdbus.h" #include "filesystem.h" #define SYNCML_TARGET_SIZE 11 static const uint8_t SYNCML_TARGET[SYNCML_TARGET_SIZE] = { 0x53, 0x59, 0x4E, 0x43, 0x4D, 0x4C, 0x2D, 0x53, 0x59, 0x4E, 0x43 }; #define SYNCEVOLUTION_CHANNEL 19 #define SYNCEVOLUTION_RECORD "\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ " #define SYNCE_BUS_NAME "org.syncevolution" #define SYNCE_PATH "/org/syncevolution/Server" #define SYNCE_SERVER_INTERFACE "org.syncevolution.Server" #define SYNCE_CONN_INTERFACE "org.syncevolution.Connection" struct synce_context { struct obex_session *os; DBusConnection *dbus_conn; char *conn_obj; unsigned int reply_watch; unsigned int abort_watch; GString *buffer; int lasterr; char *id; }; static void append_dict_entry(DBusMessageIter *dict, const char *key, int type, void *val) { DBusMessageIter entry; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val); dbus_message_iter_close_container(dict, &entry); } static gboolean reply_signal(DBusConnection *conn, DBusMessage *msg, void *data) { struct synce_context *context = data; const char *path = dbus_message_get_path(msg); DBusMessageIter iter, array_iter; char *value; int length; if (strcmp(context->conn_obj, path) != 0) { obex_object_set_io_flags(context, G_IO_ERR, -EPERM); context->lasterr = -EPERM; return FALSE; } dbus_message_iter_init(msg, &iter); dbus_message_iter_recurse(&iter, &array_iter); dbus_message_iter_get_fixed_array(&array_iter, &value, &length); context->buffer = g_string_new_len(value, length); obex_object_set_io_flags(context, G_IO_IN, 0); context->lasterr = 0; return TRUE; } static gboolean abort_signal(DBusConnection *conn, DBusMessage *msg, void *data) { struct synce_context *context = data; obex_object_set_io_flags(context, G_IO_ERR, -EPERM); context->lasterr = -EPERM; return TRUE; } static void connect_cb(DBusPendingCall *call, void *user_data) { struct synce_context *context = user_data; DBusConnection *conn; DBusMessage *reply; DBusError err; char *path; conn = context->dbus_conn; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&err); if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) { error("%s", err.message); dbus_error_free(&err); goto failed; } DBG("Got conn object %s from syncevolution", path); context->conn_obj = g_strdup(path); context->reply_watch = g_dbus_add_signal_watch(conn, NULL, path, SYNCE_CONN_INTERFACE, "Reply", reply_signal, context, NULL); context->abort_watch = g_dbus_add_signal_watch(conn, NULL, path, SYNCE_CONN_INTERFACE, "Abort", abort_signal, context, NULL); dbus_message_unref(reply); return; failed: obex_object_set_io_flags(context, G_IO_ERR, -EPERM); context->lasterr = -EPERM; } static void process_cb(DBusPendingCall *call, void *user_data) { struct synce_context *context = user_data; DBusMessage *reply; DBusError derr; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("process_cb(): syncevolution replied with an error:" " %s, %s", derr.name, derr.message); dbus_error_free(&derr); obex_object_set_io_flags(context, G_IO_ERR, -EPERM); context->lasterr = -EPERM; goto done; } obex_object_set_io_flags(context, G_IO_OUT, 0); context->lasterr = 0; done: dbus_message_unref(reply); } static void *synce_connect(struct obex_session *os, int *err) { DBusConnection *conn; struct synce_context *context; char *address; manager_register_session(os); conn = manager_dbus_get_connection(); if (!conn) goto failed; context = g_new0(struct synce_context, 1); context->dbus_conn = conn; context->lasterr = -EAGAIN; context->os = os; if (obex_getpeername(os, &address) == 0) { context->id = g_strdup_printf("%s+%d", address, SYNCEVOLUTION_CHANNEL); g_free(address); } if (err) *err = 0; return context; failed: if (err) *err = -EPERM; return NULL; } static int synce_put(struct obex_session *os, void *user_data) { return 0; } static int synce_get(struct obex_session *os, void *user_data) { return obex_get_stream_start(os, NULL); } static void close_cb(DBusPendingCall *call, void *user_data) { DBusMessage *reply; DBusError derr; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("close_cb(): syncevolution replied with an error:" " %s, %s", derr.name, derr.message); dbus_error_free(&derr); } dbus_message_unref(reply); } static void synce_disconnect(struct obex_session *os, void *user_data) { struct synce_context *context = user_data; g_free(context); } static void *synce_open(const char *name, int oflag, mode_t mode, void *user_data, size_t *size, int *err) { struct synce_context *context = user_data; if (err) *err = context ? 0 : -EFAULT; return user_data; } static int synce_close(void *object) { struct synce_context *context = object; DBusMessage *msg; const char *error; gboolean normal; DBusPendingCall *call; if (!context->conn_obj) goto done; msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj, SYNCE_CONN_INTERFACE, "Close"); if (!msg) goto failed; normal = TRUE; error = "none"; dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &normal, DBUS_TYPE_STRING, &error, DBUS_TYPE_INVALID); dbus_connection_send_with_reply(context->dbus_conn, msg, &call, -1); dbus_pending_call_set_notify(call, close_cb, NULL, NULL); dbus_message_unref(msg); dbus_pending_call_unref(call); failed: g_dbus_remove_watch(context->dbus_conn, context->reply_watch); context->reply_watch = 0; g_dbus_remove_watch(context->dbus_conn, context->abort_watch); context->abort_watch = 0; g_free(context->conn_obj); context->conn_obj = NULL; done: dbus_connection_unref(context->dbus_conn); g_free(context); return 0; } static ssize_t synce_read(void *object, void *buf, size_t count) { struct synce_context *context = object; DBusConnection *conn; char transport[36], transport_description[24]; const char *session; DBusMessage *msg; DBusMessageIter iter, dict; gboolean authenticate; DBusPendingCall *call; if (context->buffer) return string_read(context->buffer, buf, count); conn = manager_dbus_get_connection(); if (conn == NULL) return -EPERM; msg = dbus_message_new_method_call(SYNCE_BUS_NAME, SYNCE_PATH, SYNCE_SERVER_INTERFACE, "Connect"); if (!msg) return -EPERM; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); append_dict_entry(&dict, "id", DBUS_TYPE_STRING, context->id); snprintf(transport, sizeof(transport), "%s.obexd", OBEXD_SERVICE); append_dict_entry(&dict, "transport", DBUS_TYPE_STRING, transport); snprintf(transport_description, sizeof(transport_description), "version %s", VERSION); append_dict_entry(&dict, "transport_description", DBUS_TYPE_STRING, transport_description); dbus_message_iter_close_container(&iter, &dict); authenticate = FALSE; session = ""; dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &authenticate, DBUS_TYPE_STRING, &session, DBUS_TYPE_INVALID); if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) { error("D-Bus call to %s failed.", SYNCE_SERVER_INTERFACE); dbus_message_unref(msg); return -EPERM; } dbus_pending_call_set_notify(call, connect_cb, context, NULL); dbus_pending_call_unref(call); dbus_message_unref(msg); return -EAGAIN; } static ssize_t synce_write(void *object, const void *buf, size_t count) { struct synce_context *context = object; DBusMessage *msg; DBusMessageIter iter, array_iter; DBusPendingCall *call; const char *type = obex_get_type(context->os); if (context->lasterr == 0) return count; if (!context->conn_obj) return -EFAULT; msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj, SYNCE_CONN_INTERFACE, "Process"); if (!msg) return -EFAULT; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array_iter); dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &buf, count); dbus_message_iter_close_container(&iter, &array_iter); dbus_message_append_args(msg, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID); if (!dbus_connection_send_with_reply(context->dbus_conn, msg, &call, -1)) { error("D-Bus call to %s failed.", SYNCE_CONN_INTERFACE); dbus_message_unref(msg); return -EPERM; } dbus_pending_call_set_notify(call, process_cb, context, NULL); dbus_message_unref(msg); dbus_pending_call_unref(call); return -EAGAIN; } static struct obex_mime_type_driver synce_driver = { .target = SYNCML_TARGET, .target_size = SYNCML_TARGET_SIZE, .open = synce_open, .close = synce_close, .read = synce_read, .write = synce_write, }; static struct obex_service_driver synce = { .name = "OBEX server for SyncML, using SyncEvolution", .service = OBEX_SYNCEVOLUTION, .channel = SYNCEVOLUTION_CHANNEL, .secure = TRUE, .record = SYNCEVOLUTION_RECORD, .target = SYNCML_TARGET, .target_size = SYNCML_TARGET_SIZE, .get = synce_get, .put = synce_put, .connect = synce_connect, .disconnect = synce_disconnect, }; static int synce_init(void) { int err; err = obex_mime_type_driver_register(&synce_driver); if (err < 0) return err; return obex_service_driver_register(&synce); } static void synce_exit(void) { obex_service_driver_unregister(&synce); obex_mime_type_driver_unregister(&synce_driver); } OBEX_PLUGIN_DEFINE(syncevolution, synce_init, synce_exit)