484 lines
12 KiB
C
484 lines
12 KiB
C
/*
|
|
*
|
|
* OBEX Server
|
|
*
|
|
* Copyright (C) 2007-2010 Intel Corporation
|
|
* Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
|
|
*
|
|
*
|
|
* 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 <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <glib.h>
|
|
#include <dbus/dbus.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
|
|
#include <gdbus/gdbus.h>
|
|
|
|
#include "btio/btio.h"
|
|
#include "plugin.h"
|
|
#include "obex.h"
|
|
#include "service.h"
|
|
#include "mimetype.h"
|
|
#include "log.h"
|
|
#include "manager.h"
|
|
#include "obexd.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 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\
|
|
<record> \
|
|
<attribute id=\"0x0001\"> \
|
|
<sequence> \
|
|
<uuid value=\"00000002-0000-1000-8000-0002ee000002\"/> \
|
|
</sequence> \
|
|
</attribute> \
|
|
\
|
|
<attribute id=\"0x0004\"> \
|
|
<sequence> \
|
|
<sequence> \
|
|
<uuid value=\"0x0100\"/> \
|
|
</sequence> \
|
|
<sequence> \
|
|
<uuid value=\"0x0003\"/> \
|
|
<uint8 value=\"%u\" name=\"channel\"/> \
|
|
</sequence> \
|
|
<sequence> \
|
|
<uuid value=\"0x0008\"/> \
|
|
</sequence> \
|
|
</sequence> \
|
|
</attribute> \
|
|
\
|
|
<attribute id=\"0x0100\"> \
|
|
<text value=\"%s\" name=\"name\"/> \
|
|
</attribute> \
|
|
</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);
|
|
|
|
g_dbus_send_message_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 (!g_dbus_send_message_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 (!g_dbus_send_message_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)
|