diff --git a/obexd/src/bluetooth.c b/obexd/src/bluetooth.c
new file mode 100644
index 0000000..48e7936
--- /dev/null
+++ b/obexd/src/bluetooth.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Copyright (C) 2007-2008 Instituto Nokia de Tecnologia (INdT)
+ * Copyright (C) 2007-2008 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 <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "obex.h"
+
+static GSList *handles = NULL;
+static sdp_session_t *session = NULL;
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static uint32_t register_record(const gchar *name,
+ guint16 service, guint8 channel)
+{
+ uuid_t root_uuid, uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_list_t *root, *svclass_id, *apseq, *profiles, *aproto, *proto[3];
+ sdp_data_t *sdp_data;
+ sdp_profile_desc_t profile;
+ sdp_record_t record;
+ uint8_t formats = 0xFF;
+ int ret;
+
+ switch (service) {
+ case OBEX_OPUSH:
+ sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID);
+ sdp_uuid16_create(&profile.uuid, OBEX_OBJPUSH_PROFILE_ID);
+ break;
+ case OBEX_FTP:
+ sdp_uuid16_create(&uuid, OBEX_FILETRANS_SVCLASS_ID);
+ sdp_uuid16_create(&profile.uuid, OBEX_FILETRANS_PROFILE_ID);
+ break;
+ default:
+ return 0;
+ }
+
+ /* Browse Groups */
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = 0xffffffff;
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ /* Service Class */
+ svclass_id = sdp_list_append(NULL, &uuid);
+ sdp_set_service_classes(&record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ /* Profile Descriptor */
+ profile.version = 0x0100;
+ profiles = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(&record, profiles);
+ sdp_list_free(profiles, NULL);
+
+ /* Protocol Descriptor */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+ sdp_data = sdp_data_alloc(SDP_UINT8, &channel);
+ proto[1] = sdp_list_append(proto[1], sdp_data);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(NULL, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_data_free(sdp_data);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(proto[2], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ /* Suported Repositories */
+ if (service == OBEX_OPUSH)
+ sdp_attr_add_new(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST,
+ SDP_UINT8, &formats);
+
+ /* Service Name */
+ sdp_set_info_attr(&record, name, NULL, NULL);
+
+ add_lang_attr(&record);
+
+ ret = sdp_record_register(session, &record, SDP_RECORD_PERSIST);
+
+ sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free);
+ sdp_list_free(record.pattern, free);
+
+ return (ret < 0 ? 0 : record.handle);
+}
+
+static gboolean connect_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+ struct sockaddr_rc raddr;
+ socklen_t alen;
+ struct server *server = user_data;
+ gchar address[18];
+ gint err, sk, nsk;
+
+ sk = g_io_channel_unix_get_fd(io);
+ alen = sizeof(raddr);
+ nsk = accept(sk, (struct sockaddr *) &raddr, &alen);
+ if (nsk < 0)
+ return TRUE;
+
+ alen = sizeof(raddr);
+ if (getpeername(nsk, (struct sockaddr *)&raddr, &alen) < 0) {
+ err = errno;
+ error("getpeername(): %s(%d)", strerror(err), err);
+ close(nsk);
+ return TRUE;
+ }
+
+ ba2str(&raddr.rc_bdaddr, address);
+ info("New connection from: %s channel: %d", address, raddr.rc_channel);
+
+ if (obex_session_start(nsk, server) < 0)
+ close(nsk);
+
+ return TRUE;
+}
+
+static void server_destroyed(gpointer user_data)
+{
+ struct server *server = user_data;
+
+ error("Server destroyed");
+
+ g_free(server->folder);
+ g_free(server);
+}
+
+static gint server_register(guint16 service, const gchar *name,
+ guint8 channel, const gchar *folder, gboolean auto_accept)
+{
+ struct sockaddr_rc laddr;
+ GIOChannel *io;
+ gint err, sk, arg;
+ struct server *server;
+ uint32_t *handle;
+
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ err = errno;
+ error("socket(): %s(%d)", strerror(err), err);
+ return -err;
+ }
+
+ arg = fcntl(sk, F_GETFL);
+ if (arg < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ arg |= O_NONBLOCK;
+ if (fcntl(sk, F_SETFL, arg) < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ memset(&laddr, 0, sizeof(laddr));
+ laddr.rc_family = AF_BLUETOOTH;
+ bacpy(&laddr.rc_bdaddr, BDADDR_ANY);
+ laddr.rc_channel = channel;
+
+ if (bind(sk, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ if (listen(sk, 10) < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ handle = malloc(sizeof(uint32_t));
+ *handle = register_record(name, service, channel);
+ if (*handle == 0) {
+ g_free(handle);
+ err = EIO;
+ goto failed;
+ }
+
+ handles = g_slist_prepend(handles, handle);
+
+ server = g_malloc0(sizeof(struct server));
+ server->service = service;
+ server->folder = g_strdup(folder);
+ server->auto_accept = auto_accept;
+
+ io = g_io_channel_unix_new(sk);
+ g_io_channel_set_close_on_unref(io, TRUE);
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ connect_event, server, server_destroyed);
+ g_io_channel_unref(io);
+
+ debug("Registered: %s, record handle: 0x%x, folder: %s", name, *handle, folder);
+
+ return 0;
+
+failed:
+ error("Bluetooth server register failed: %s(%d)", strerror(err), err);
+ close(sk);
+
+ return -err;
+}
+
+gint bluetooth_init(guint service, const gchar *name, const gchar *folder,
+ guint8 channel, gboolean auto_accept)
+{
+ if (!session) {
+ session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+ if (!session) {
+ gint err = errno;
+ error("sdp_connect(): %s(%d)", strerror(err), err);
+ return -err;
+ }
+ }
+
+ return server_register(service, name, channel, folder, auto_accept);
+}
+
+static void unregister_record(gpointer rec_handle, gpointer user_data)
+{
+ uint32_t *handle = rec_handle;
+
+ sdp_device_record_unregister_binary(session, BDADDR_ANY, *handle);
+ g_free(handle);
+}
+
+void bluetooth_exit(void)
+{
+ g_slist_foreach(handles, unregister_record, NULL);
+ g_slist_free(handles);
+
+ sdp_close(session);
+}
diff --git a/obexd/src/bluetooth.h b/obexd/src/bluetooth.h
new file mode 100644
index 0000000..9e7973b
--- /dev/null
+++ b/obexd/src/bluetooth.h
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Copyright (C) 2007-2008 Instituto Nokia de Tecnologia (INdT)
+ * Copyright (C) 2007-2008 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
+
+gint bluetooth_init(guint service, const gchar *name, const gchar *folder,
+ guint8 channel, gboolean auto_accept);
+void bluetooth_exit(void);
diff --git a/obexd/src/dbus.h b/obexd/src/dbus.h
new file mode 100644
index 0000000..a4234c2
--- /dev/null
+++ b/obexd/src/dbus.h
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 Instituto Nokia de Tecnologia (INdT)
+ *
+ *
+ * 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
+ *
+ */
+
+void emit_transfer_started(guint32 id);
+
+void emit_transfer_completed(guint32 id, gboolean success);
+
+void emit_transfer_progress(guint32 id, guint32 total, guint32 transfered);
+
+int request_authorization(gint32 cid, int fd, const gchar *filename,
+ const gchar *type, gint32 length, gint32 time,
+ gchar **new_folder, gchar **new_name);
+
+void register_transfer(guint32 id);
+
+void unregister_transfer(guint32 id);
diff --git a/obexd/src/ftp.c b/obexd/src/ftp.c
new file mode 100644
index 0000000..04ab623
--- /dev/null
+++ b/obexd/src/ftp.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Copyright (C) 2007-2008 Instituto Nokia de Tecnologia (INdT)
+ * Copyright (C) 2007-2008 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 <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "obex.h"
+#include "logging.h"
+
+#define LST_TYPE "x-obex/folder-listing"
+#define CAP_TYPE "x-obex/capability"
+
+#define CAP_FILE CONFIGDIR "/capability.xml"
+
+static gboolean get_by_type(struct obex_session *os, gchar *type, guint32 *size)
+{
+ if (g_str_equal(type, CAP_TYPE))
+ return os_prepare_get(os, CAP_FILE, size);
+
+ return FALSE;
+}
+
+void ftp_get(obex_t *obex, obex_object_t *obj)
+{
+ obex_headerdata_t hv;
+ struct obex_session *os;
+ guint32 size;
+
+ os = OBEX_GetUserData(obex);
+ if (os == NULL)
+ return;
+
+ if (os->current_folder == NULL)
+ goto fail;
+
+ if (os->name) {
+ gboolean ret;
+ gchar *path = g_build_filename(os->current_folder,
+ os->name, NULL);
+ ret = os_prepare_get(os, path, &size);
+
+ g_free(path);
+
+ if (!ret)
+ goto fail;
+ } else if (os->type) {
+ if (!get_by_type(os, os->type, &size))
+ goto fail;
+ } else
+ goto fail;
+
+ hv.bq4 = size;
+ OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_LENGTH, hv, 4, 0);
+
+ /* Add body header */
+ hv.bs = NULL;
+ if (size == 0)
+ OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_FIT_ONE_PACKET);
+ else
+ OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_STREAM_START);
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
+ OBEX_RSP_SUCCESS);
+
+ return;
+
+fail:
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+
+ return;
+}
+
+void ftp_put(obex_t *obex, obex_object_t *obj)
+{
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+ OBEX_RSP_NOT_IMPLEMENTED);
+}
+
+void ftp_setpath(obex_t *obex, obex_object_t *obj)
+{
+ struct obex_session *os;
+ guint8 *nonhdr;
+ gchar *fullname;
+
+ os = OBEX_GetUserData(obex);
+
+ if (OBEX_ObjectGetNonHdrData(obj, &nonhdr) != 2) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
+ OBEX_RSP_PRECONDITION_FAILED);
+ error("Set path failed: flag and constants not found!");
+ return;
+ }
+
+ /* Check flag "Backup" */
+ if ((nonhdr[0] & 0x01) == 0x01) {
+
+ debug("Set to parent path");
+
+ if (strcmp(os->server->folder, os->current_folder) == 0) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ return;
+ }
+
+ fullname = g_path_get_dirname(os->current_folder);
+ g_free(os->current_folder);
+ os->current_folder = g_strdup(fullname);
+ g_free(fullname);
+
+ debug("Set to parent path: %s", os->current_folder);
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+ return;
+ }
+
+ if (!os->name) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_BAD_REQUEST);
+ debug("Set path failed: name missing!");
+ return;
+ }
+
+ if (strlen(os->name) == 0) {
+ debug("Set to root");
+ g_free(os->current_folder);
+ os->current_folder = g_strdup(os->server->folder);
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+ return;
+ }
+
+ /* Check and set to name path */
+ if (strstr(os->name, "/") || strcmp(os->name, "..") == 0) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ error("Set path failed: name incorrect!");
+ return;
+ }
+
+ fullname = g_build_filename(os->current_folder, os->name, NULL);
+
+ debug("Fullname: %s", fullname);
+
+ if (g_file_test(fullname, G_FILE_TEST_IS_DIR)) {
+ g_free(os->current_folder);
+ os->current_folder = g_strdup(fullname);
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+ goto done;
+ }
+
+ if (!g_file_test(fullname, G_FILE_TEST_EXISTS) && nonhdr[0] == 0 &&
+ mkdir(fullname, 0755) >= 0) {
+ g_free(os->current_folder);
+ os->current_folder = g_strdup(fullname);
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+ goto done;
+
+ }
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+done:
+ g_free(fullname);
+}
diff --git a/obexd/src/logging.c b/obexd/src/logging.c
new file mode 100644
index 0000000..c1e489c
--- /dev/null
+++ b/obexd/src/logging.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 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 <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+#include <openobex/obex.h>
+
+static volatile int debug_enabled = 0;
+
+static inline void vinfo(const char *format, va_list ap)
+{
+ vsyslog(LOG_INFO, format, ap);
+}
+
+void info(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vinfo(format, ap);
+
+ va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_ERR, format, ap);
+
+ va_end(ap);
+}
+
+void debug(const char *format, ...)
+{
+ va_list ap;
+
+ if (!debug_enabled)
+ return;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_DEBUG, format, ap);
+
+ va_end(ap);
+}
+
+void toggle_debug(void)
+{
+ debug_enabled = (debug_enabled + 1) % 2;
+}
+
+void enable_debug(void)
+{
+ debug_enabled = 1;
+}
+
+void disable_debug(void)
+{
+ debug_enabled = 0;
+}
+
+void start_logging(const char *ident, const char *message, ...)
+{
+ va_list ap;
+
+ openlog(ident, LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+
+ va_start(ap, message);
+
+ vinfo(message, ap);
+
+ va_end(ap);
+}
+
+void stop_logging(void)
+{
+ closelog();
+}
+
+static struct {
+ int evt;
+ const char *name;
+} obex_event[] = {
+ { OBEX_EV_PROGRESS, "PROGRESS" }, /* Progress has been made */
+ { OBEX_EV_REQHINT, "REQHINT" }, /* An incoming request is about to come */
+ { OBEX_EV_REQ, "REQ" }, /* An incoming request has arrived */
+ { OBEX_EV_REQDONE, "REQDONE" }, /* Request has finished */
+ { OBEX_EV_LINKERR, "LINKERR" }, /* Link has been disconnected */
+ { OBEX_EV_PARSEERR, "PARSEERR" }, /* Malformed data encountered */
+ { OBEX_EV_ACCEPTHINT, "ACCEPTHINT" }, /* Connection accepted */
+ { OBEX_EV_ABORT, "ABORT" }, /* Request was aborted */
+ { OBEX_EV_STREAMEMPTY, "STREAMEMPTY" }, /* Need to feed more data when sending a stream */
+ { OBEX_EV_STREAMAVAIL, "STREAMAVAIL" }, /* Time to pick up data when receiving a stream */
+ { OBEX_EV_UNEXPECTED, "UNEXPECTED" }, /* Unexpected data, not fatal */
+ { OBEX_EV_REQCHECK, "REQCHECK" }, /* First packet of an incoming request has been parsed */
+ { 0xFF, NULL },
+};
+
+/* Possible commands */
+static struct {
+ int cmd;
+ const char *name;
+} obex_command[] = {
+ { OBEX_CMD_CONNECT, "CONNECT" },
+ { OBEX_CMD_DISCONNECT, "DISCONNECT" },
+ { OBEX_CMD_PUT, "PUT" },
+ { OBEX_CMD_GET, "GET" },
+ { OBEX_CMD_SETPATH, "SETPATH" },
+ { OBEX_CMD_SESSION, "SESSION" },
+ { OBEX_CMD_ABORT, "ABORT" },
+ { OBEX_FINAL, "FINAL" },
+ { 0xFF, NULL },
+};
+
+/* Possible Response */
+static struct {
+ int rsp;
+ const char *name;
+} obex_response[] = {
+ { OBEX_RSP_CONTINUE, "CONTINUE" },
+ { OBEX_RSP_SWITCH_PRO, "SWITCH_PRO" },
+ { OBEX_RSP_SUCCESS, "SUCCESS" },
+ { OBEX_RSP_CREATED, "CREATED" },
+ { OBEX_RSP_ACCEPTED, "ACCEPTED" },
+ { OBEX_RSP_NON_AUTHORITATIVE, "NON_AUTHORITATIVE" },
+ { OBEX_RSP_NO_CONTENT, "NO_CONTENT" },
+ { OBEX_RSP_RESET_CONTENT, "RESET_CONTENT" },
+ { OBEX_RSP_PARTIAL_CONTENT, "PARTIAL_CONTENT" },
+ { OBEX_RSP_MULTIPLE_CHOICES, "MULTIPLE_CHOICES" },
+ { OBEX_RSP_MOVED_PERMANENTLY, "MOVED_PERMANENTLY" },
+ { OBEX_RSP_MOVED_TEMPORARILY, "MOVED_TEMPORARILY" },
+ { OBEX_RSP_SEE_OTHER, "SEE_OTHER" },
+ { OBEX_RSP_NOT_MODIFIED, "NOT_MODIFIED" },
+ { OBEX_RSP_USE_PROXY, "USE_PROXY" },
+ { OBEX_RSP_BAD_REQUEST, "BAD_REQUEST" },
+ { OBEX_RSP_UNAUTHORIZED, "UNAUTHORIZED" },
+ { OBEX_RSP_PAYMENT_REQUIRED, "PAYMENT_REQUIRED" },
+ { OBEX_RSP_FORBIDDEN, "FORBIDDEN" },
+ { OBEX_RSP_NOT_FOUND, "NOT_FOUND" },
+ { OBEX_RSP_METHOD_NOT_ALLOWED, "METHOD_NOT_ALLOWED" },
+ { OBEX_RSP_NOT_ACCEPTABLE, "NOT_ACCEPTABLE" },
+ { OBEX_RSP_PROXY_AUTH_REQUIRED, "PROXY_AUTH_REQUIRED" },
+ { OBEX_RSP_REQUEST_TIME_OUT, "REQUEST_TIME_OUT" },
+ { OBEX_RSP_CONFLICT, "CONFLICT" },
+ { OBEX_RSP_GONE, "GONE" },
+ { OBEX_RSP_LENGTH_REQUIRED, "LENGTH_REQUIRED" },
+ { OBEX_RSP_PRECONDITION_FAILED, "PRECONDITION_FAILED" },
+ { OBEX_RSP_REQ_ENTITY_TOO_LARGE, "REQ_ENTITY_TOO_LARGE" },
+ { OBEX_RSP_REQ_URL_TOO_LARGE, "REQ_URL_TOO_LARGE" },
+ { OBEX_RSP_UNSUPPORTED_MEDIA_TYPE, "UNSUPPORTED_MEDIA_TYPE"},
+ { OBEX_RSP_INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR" },
+ { OBEX_RSP_NOT_IMPLEMENTED, "NOT_IMPLEMENTED" },
+ { OBEX_RSP_BAD_GATEWAY, "BAD_GATEWAY" },
+ { OBEX_RSP_SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE" },
+ { OBEX_RSP_GATEWAY_TIMEOUT, "GATEWAY_TIMEOUT" },
+ { OBEX_RSP_VERSION_NOT_SUPPORTED, "VERSION_NOT_SUPPORTED" },
+ { OBEX_RSP_DATABASE_FULL, "DATABASE_FULL" },
+ { OBEX_RSP_DATABASE_LOCKED, "DATABASE_LOCKED" },
+ { 0xFF, NULL },
+};
+
+void obex_debug(int evt, int cmd, int rsp)
+{
+ const char *evtstr = NULL, *cmdstr = NULL, *rspstr = NULL;
+ int i;
+
+ if (!debug_enabled)
+ return;
+
+ for (i = 0; obex_event[i].evt != 0xFF; i++) {
+ if (obex_event[i].evt != evt)
+ continue;
+ evtstr = obex_event[i].name;
+ }
+
+ for (i = 0; obex_command[i].cmd != 0xFF; i++) {
+ if (obex_command[i].cmd != cmd)
+ continue;
+ cmdstr = obex_command[i].name;
+ }
+
+ for (i = 0; obex_response[i].rsp != 0xFF; i++) {
+ if (obex_response[i].rsp != rsp)
+ continue;
+ rspstr = obex_response[i].name;
+ }
+
+ debug("%s(0x%x), %s(0x%x), %s(0x%x)", evtstr, evt, cmdstr, cmd, rspstr, rsp);
+}
diff --git a/obexd/src/logging.h b/obexd/src/logging.h
new file mode 100644
index 0000000..f30628a
--- /dev/null
+++ b/obexd/src/logging.h
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 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
+ *
+ */
+
+void info(const char *format, ...);
+void error(const char *format, ...);
+void debug(const char *format, ...);
+void obex_debug(int evt, int cmd, int rsp);
+void toggle_debug(void);
+void enable_debug(void);
+void disable_debug(void);
+void start_logging(const char *ident, const char *message, ...);
+void stop_logging(void);
diff --git a/obexd/src/main.c b/obexd/src/main.c
new file mode 100644
index 0000000..1ff0a0c
--- /dev/null
+++ b/obexd/src/main.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 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 <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "bluetooth.h"
+#include "obexd.h"
+#include "obex.h"
+
+#define OPUSH_CHANNEL 9
+#define FTP_CHANNEL 10
+
+#define DEFAULT_ROOT_PATH "/tmp"
+
+static GMainLoop *main_loop = NULL;
+
+static int server_start(int service, const char *root_path,
+ gboolean auto_accept)
+{
+ /* FIXME: Necessary check enabled transports(Bluetooth/USB) */
+
+ switch (service) {
+ case OBEX_OPUSH:
+ bluetooth_init(OBEX_OPUSH, "OBEX OPUSH server",
+ root_path, OPUSH_CHANNEL, auto_accept);
+ break;
+ case OBEX_FTP:
+ bluetooth_init(OBEX_FTP, "OBEX FTP server",
+ root_path, FTP_CHANNEL, auto_accept);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void server_stop()
+{
+ /* FIXME: If Bluetooth enabled */
+ bluetooth_exit();
+}
+
+static void sig_term(int sig)
+{
+ g_main_loop_quit(main_loop);
+}
+
+static void usage(void)
+{
+ printf("OBEX Server version %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\tobexd [options] <server>\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-n, --nodaemon Don't fork daemon to background\n"
+ "\t-d, --debug Enable output of debug information\n"
+ "\t-r, --root <path> Specify root folder location\n"
+ "\t-a, --auto-accept Automatically accept push requests\n"
+ "\t-h, --help Display help\n");
+ printf("Servers:\n"
+ "\t-o, --opp Enable OPP server\n"
+ "\t-f, --ftp Enable FTP server\n"
+ "\n");
+}
+
+static struct option options[] = {
+ { "nodaemon", 0, 0, 'n' },
+ { "debug", 0, 0, 'd' },
+ { "ftp", 0, 0, 'f' },
+ { "opp", 0, 0, 'o' },
+ { "help", 0, 0, 'h' },
+ { "root", 1, 0, 'r' },
+ { "auto-accept", 0, 0, 'a' },
+ { }
+};
+
+int main(int argc, char *argv[])
+{
+ DBusConnection *conn;
+ DBusError err;
+ struct sigaction sa;
+ int log_option = LOG_NDELAY | LOG_PID;
+ int opt, detach = 1, debug = 0, opush = 0, ftp = 0, auto_accept = 0;
+ const char *root_path = DEFAULT_ROOT_PATH;
+
+ while ((opt = getopt_long(argc, argv, "+ndhofr:a", options, NULL)) != EOF) {
+ switch(opt) {
+ case 'n':
+ detach = 0;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'h':
+ case 'o':
+ opush = 1;
+ break;
+ case 'f':
+ ftp = 1;
+ break;
+ case 'r':
+ root_path = optarg;
+ break;
+ case 'a':
+ auto_accept = 1;
+ break;
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (!(opush || ftp)) {
+ fprintf(stderr, "No server selected (use either "
+ "--opp or --ftp or both)\n");
+ exit(1);
+ }
+
+ if (detach) {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+ } else
+ log_option |= LOG_PERROR;
+
+ openlog("obexd", log_option, LOG_DAEMON);
+
+ if (debug) {
+ info("Enabling debug information");
+ enable_debug();
+ }
+
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ dbus_error_init(&err);
+
+ conn = g_dbus_setup_bus(DBUS_BUS_SESSION, OPENOBEX_SERVICE, &err);
+ if (conn == NULL) {
+ if (dbus_error_is_set(&err) == TRUE) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ } else
+ fprintf(stderr, "Can't register with session bus\n");
+ exit(1);
+ }
+
+ if (opush)
+ server_start(OBEX_OPUSH, root_path, auto_accept);
+
+ if (ftp)
+ server_start(OBEX_FTP, root_path, auto_accept);
+
+ if (!manager_init(conn))
+ goto fail;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sig_term;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ g_main_loop_run(main_loop);
+
+ manager_cleanup();
+
+ server_stop();
+
+fail:
+ dbus_connection_unref(conn);
+
+ g_main_loop_unref(main_loop);
+
+ closelog();
+
+ return 0;
+}
diff --git a/obexd/src/manager.c b/obexd/src/manager.c
new file mode 100644
index 0000000..e165c80
--- /dev/null
+++ b/obexd/src/manager.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 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 <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+
+#include <string.h>
+#include <errno.h>
+#include <gdbus.h>
+#include <sys/socket.h>
+
+#include "obexd.h"
+#include "logging.h"
+
+#define TRANSFER_INTERFACE OPENOBEX_SERVICE ".Transfer"
+
+#define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
+
+struct agent {
+ gchar *bus_name;
+ gchar *path;
+ gboolean auth_pending;
+ gchar *new_name;
+ gchar *new_folder;
+};
+
+static struct agent *agent = NULL;
+
+static void agent_free(struct agent *agent)
+{
+ g_free(agent->new_folder);
+ g_free(agent->new_name);
+ g_free(agent->bus_name);
+ g_free(agent->path);
+ g_free(agent);
+}
+
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+}
+
+static inline DBusMessage *agent_already_exists(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".AlreadyExists",
+ "Agent already exists");
+}
+
+static inline DBusMessage *agent_does_not_exist(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".DoesNotExist",
+ "Agent does not exist");
+}
+
+static inline DBusMessage *not_authorized(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".NotAuthorized",
+ "Not authorized");
+}
+
+static void agent_disconnected(void *user_data)
+{
+ debug("Agent exited");
+ agent_free(agent);
+ agent = NULL;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const gchar *path, *sender;
+
+ if (agent)
+ return agent_already_exists(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ sender = dbus_message_get_sender(msg);
+ agent = g_new0(struct agent, 1);
+ agent->bus_name = g_strdup(sender);
+ agent->path = g_strdup(path);
+
+ g_dbus_add_disconnect_watch(conn, sender,
+ agent_disconnected, NULL, NULL);
+
+ debug("Agent registered");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const gchar *path, *sender;
+
+ if (!agent)
+ return agent_does_not_exist(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ if (strcmp(agent->path, path) != 0)
+ return agent_does_not_exist(msg);
+
+ sender = dbus_message_get_sender(msg);
+ if (strcmp(agent->bus_name, sender) != 0)
+ return not_authorized(msg);
+
+ agent_free(agent);
+ agent = NULL;
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable manager_methods[] = {
+ { "RegisterAgent", "o", "", register_agent },
+ { "UnregisterAgent", "o", "", unregister_agent },
+ { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+ { "TransferStarted", "o" },
+ { "TransferCompleted", "ob" },
+ { }
+};
+
+static GDBusMethodTable transfer_methods[] = {
+ { "Cancel", "" },
+ { }
+};
+
+static GDBusSignalTable transfer_signals[] = {
+ { "Progress", "ii" },
+ { }
+};
+
+static DBusConnection *connection = NULL;
+
+gboolean manager_init(DBusConnection *conn)
+{
+ DBG("conn %p", conn);
+
+ connection = dbus_connection_ref(conn);
+ if (connection == NULL)
+ return FALSE;
+
+ return g_dbus_register_interface(connection, OPENOBEX_MANAGER_PATH,
+ OPENOBEX_MANAGER_INTERFACE,
+ manager_methods, manager_signals, NULL,
+ NULL, NULL);
+}
+
+void manager_cleanup(void)
+{
+ DBG("conn %p", connection);
+
+ g_dbus_unregister_interface(connection, OPENOBEX_MANAGER_PATH,
+ OPENOBEX_MANAGER_INTERFACE);
+
+ /* FIXME: Release agent? */
+
+ if (agent)
+ agent_free(agent);
+
+ dbus_connection_unref(connection);
+}
+
+void emit_transfer_started(guint32 id)
+{
+ gchar *path = g_strdup_printf("/transfer%u", id);
+
+ g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
+ OPENOBEX_MANAGER_INTERFACE, "TransferStarted",
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ g_free(path);
+}
+
+void emit_transfer_completed(guint32 id, gboolean success)
+{
+ gchar *path = g_strdup_printf("/transfer%u", id);
+
+ g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
+ OPENOBEX_MANAGER_INTERFACE, "TransferCompleted",
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_BOOLEAN, &success,
+ DBUS_TYPE_INVALID);
+
+ g_free(path);
+}
+
+void emit_transfer_progress(guint32 id, guint32 total, guint32 transfered)
+{
+ gchar *path = g_strdup_printf("/transfer%u", id);
+
+ g_dbus_emit_signal(connection, path,
+ TRANSFER_INTERFACE, "Progress",
+ DBUS_TYPE_INT32, &total,
+ DBUS_TYPE_INT32, &transfered,
+ DBUS_TYPE_INVALID);
+
+ g_free(path);
+}
+
+void register_transfer(guint32 id)
+{
+ gchar *path = g_strdup_printf("/transfer%u", id);
+
+ if (!g_dbus_register_interface(connection, path,
+ TRANSFER_INTERFACE,
+ transfer_methods, transfer_signals,
+ NULL, NULL, NULL)) {
+ error("Cannot register Transfer interface.");
+ g_free(path);
+ return;
+ }
+
+ g_free(path);
+}
+
+void unregister_transfer(guint32 id)
+{
+ gchar *path = g_strdup_printf("/transfer%u", id);
+
+ g_dbus_unregister_interface(connection, path,
+ TRANSFER_INTERFACE);
+
+ g_free(path);
+}
+
+static void agent_reply(DBusPendingCall *call, gpointer user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ const gchar *name;
+ DBusError derr;
+
+ agent->auth_pending = FALSE;
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Agent replied with an error: %s, %s",
+ derr.name, derr.message);
+ dbus_error_free(&derr);
+ return;
+ }
+
+ if (dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID)) {
+ /* Splits folder and name */
+ const gchar *slash = strrchr(name, '/');
+ if (!slash) {
+ agent->new_name = g_strdup(name);
+ agent->new_folder = NULL;
+ } else {
+ agent->new_name = g_strdup(slash + 1);
+ agent->new_folder = g_strndup(name, slash - name);
+ }
+ }
+}
+
+int request_authorization(gint32 cid, int fd, const gchar *filename,
+ const gchar *type, gint32 length, gint32 time,
+ gchar **new_folder, gchar **new_name)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ struct sockaddr_rc addr;
+ socklen_t addrlen;
+ gchar address[18];
+ const gchar *bda = address;
+ gchar *path;
+
+ if (!agent)
+ return -1;
+
+ if (agent->auth_pending)
+ return -EPERM;
+
+ if (!new_folder || !new_name)
+ return -EINVAL;
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if (getpeername(fd, (struct sockaddr *) &addr, &addrlen) < 0)
+ return -1;
+
+ ba2str(&addr.rc_bdaddr, address);
+
+ path = g_strdup_printf("/transfer%d", cid);
+
+ msg = dbus_message_new_method_call(agent->bus_name, agent->path,
+ "org.openobex.Agent", "Authorize");
+
+ dbus_message_append_args(msg,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_STRING, &bda,
+ DBUS_TYPE_STRING, &filename,
+ DBUS_TYPE_STRING, &type,
+ DBUS_TYPE_INT32, &length,
+ DBUS_TYPE_INT32, &time,
+ DBUS_TYPE_INVALID);
+
+ g_free(path);
+
+ if (!dbus_connection_send_with_reply(connection,
+ msg, &call, TIMEOUT))
+ return -EPERM;
+
+ dbus_message_unref(msg);
+
+ agent->auth_pending = TRUE;
+
+ dbus_pending_call_set_notify(call, agent_reply, NULL, NULL);
+ dbus_pending_call_unref(call);
+
+ /* Workaround: process events while agent doesn't reply */
+ while (agent->auth_pending)
+ g_main_context_iteration(NULL, TRUE);
+
+ if (!agent->new_name) {
+ return -EPERM;
+ }
+
+ *new_folder = agent->new_folder;
+ *new_name = agent->new_name;
+ agent->new_folder = NULL;
+ agent->new_name = NULL;
+
+ return 0;
+}
diff --git a/obexd/src/obex.c b/obexd/src/obex.c
new file mode 100644
index 0000000..4089b9f
--- /dev/null
+++ b/obexd/src/obex.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Copyright (C) 2007-2008 Instituto Nokia de Tecnologia (INdT)
+ * Copyright (C) 2007-2008 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 <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "obex.h"
+#include "dbus.h"
+
+/* Default MTU's */
+#define RX_MTU 32767
+#define TX_MTU 32767
+
+#define TARGET_SIZE 16
+static const guint8 FTP_TARGET[TARGET_SIZE] = { 0xF9, 0xEC, 0x7B, 0xC4,
+ 0x95, 0x3C, 0x11, 0xD2,
+ 0x98, 0x4E, 0x52, 0x54,
+ 0x00, 0xDC, 0x9E, 0x09 };
+
+/* Connection ID */
+static guint32 cid = 0x0000;
+
+typedef struct {
+ guint8 version;
+ guint8 flags;
+ guint16 mtu;
+} __attribute__ ((packed)) obex_connect_hdr_t;
+
+struct obex_commands opp = {
+ .get = opp_get,
+ .chkput = opp_chkput,
+ .put = opp_put,
+ .setpath = NULL,
+};
+
+struct obex_commands ftp = {
+ .get = ftp_get,
+ .put = ftp_put,
+ .setpath = ftp_setpath,
+};
+
+static void obex_session_free(struct obex_session *os)
+{
+ if (os->name)
+ g_free(os->name);
+ if (os->type)
+ g_free(os->type);
+ if (os->current_folder)
+ g_free(os->current_folder);
+ if (os->buf)
+ g_free(os->buf);
+ if (os->fd > 0)
+ close(os->fd);
+ g_free(os);
+}
+
+static void cmd_connect(struct obex_session *os,
+ obex_t *obex, obex_object_t *obj)
+{
+ obex_connect_hdr_t *nonhdr;
+ obex_headerdata_t hd;
+ uint8_t *buffer;
+ guint hlen, newsize;
+ guint16 mtu;
+ guint8 hi;
+
+ if (OBEX_ObjectGetNonHdrData(obj, &buffer) != sizeof(*nonhdr)) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ debug("Invalid OBEX CONNECT packet");
+ return;
+ }
+
+ nonhdr = (obex_connect_hdr_t *) buffer;
+ mtu = g_ntohs(nonhdr->mtu);
+ debug("Version: 0x%02x. Flags: 0x%02x OBEX packet length: %d",
+ nonhdr->version, nonhdr->flags, mtu);
+ /* Leave space for headers */
+ newsize = mtu - 200;
+
+ os->tx_mtu = newsize;
+
+ debug("Resizing stream chunks to %d", newsize);
+
+ /* connection id will be used to track the sessions, even for OPP */
+ os->cid = ++cid;
+
+ register_transfer(os->cid);
+
+ if (os->target == NULL) {
+ /* OPP doesn't contains target or connection id. */
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+ return;
+ }
+
+ hi = hlen = 0;
+ OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen);
+
+ if (hi != OBEX_HDR_TARGET || hlen != TARGET_SIZE
+ || memcmp(os->target, hd.bs, TARGET_SIZE) != 0) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ return;
+ }
+
+ /* Append received UUID in WHO header */
+ OBEX_ObjectAddHeader(obex, obj,
+ OBEX_HDR_WHO, hd, TARGET_SIZE,
+ OBEX_FL_FIT_ONE_PACKET);
+ hd.bq4 = cid;
+ OBEX_ObjectAddHeader(obex, obj,
+ OBEX_HDR_CONNECTION, hd, 4,
+ OBEX_FL_FIT_ONE_PACKET);
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+
+}
+
+static gboolean chk_cid(obex_t *obex, obex_object_t *obj, guint32 cid)
+{
+ struct obex_session *os;
+ obex_headerdata_t hd;
+ guint hlen;
+ guint8 hi;
+ gboolean ret = FALSE;
+
+ os = OBEX_GetUserData(obex);
+
+ /* OPUSH doesn't provide a connection id. */
+ if (os->target == NULL)
+ return TRUE;
+
+ while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+ if (hi == OBEX_HDR_CONNECTION && hlen == 4) {
+ ret = (hd.bq4 == cid ? TRUE : FALSE);
+ break;
+ }
+ }
+
+ OBEX_ObjectReParseHeaders(obex, obj);
+
+ if (ret == FALSE)
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SERVICE_UNAVAILABLE,
+ OBEX_RSP_SERVICE_UNAVAILABLE);
+
+ return ret;
+}
+
+static void cmd_get(struct obex_session *os, obex_t *obex, obex_object_t *obj)
+{
+ obex_headerdata_t hd;
+ guint hlen;
+ guint8 hi;
+
+ if (!os->cmds->get) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+ OBEX_RSP_NOT_IMPLEMENTED);
+ return;
+ }
+
+ g_return_if_fail(chk_cid(obex, obj, os->cid));
+
+ if (os->type) {
+ g_free(os->type);
+ os->type = NULL;
+ }
+
+ if (os->name) {
+ g_free(os->name);
+ os->name = NULL;
+ }
+
+ if (os->buf) {
+ g_free(os->buf);
+ os->buf = NULL;
+ }
+
+ while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+ switch (hi) {
+ case OBEX_HDR_NAME:
+ if (os->name) {
+ debug("Ignoring multiple name headers");
+ break;
+ }
+
+ if (hlen == 0)
+ continue;
+
+ os->name = g_convert((const gchar *) hd.bs, hlen,
+ "UTF8", "UTF16BE", NULL, NULL, NULL);
+ debug("OBEX_HDR_NAME: %s", os->name);
+ break;
+ case OBEX_HDR_TYPE:
+ if (os->type) {
+ debug("Ignoring multiple type headers");
+ break;
+ }
+
+ if (hlen == 0)
+ continue;
+
+ /* Ensure null termination */
+ if (hd.bs[hlen - 1] != '\0')
+ break;
+
+ if (!g_utf8_validate((const gchar *) hd.bs, -1, NULL)) {
+ debug("Invalid type header: %s", hd.bs);
+ break;
+ }
+
+ /* FIXME: x-obex/folder-listing - type is mandatory */
+
+ os->type = g_strndup((const gchar *) hd.bs, hlen);
+ debug("OBEX_HDR_TYPE: %s", os->type);
+ break;
+ }
+ }
+
+ os->cmds->get(obex, obj);
+}
+
+static void cmd_setpath(struct obex_session *os,
+ obex_t *obex, obex_object_t *obj)
+{
+ obex_headerdata_t hd;
+ guint32 hlen;
+ guint8 hi;
+
+ if (!os->cmds->setpath) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+ OBEX_RSP_NOT_IMPLEMENTED);
+ return;
+ }
+
+ g_return_if_fail(chk_cid(obex, obj, os->cid));
+
+ if (os->name) {
+ g_free(os->name);
+ os->name = NULL;
+ }
+
+ while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+ if (hi == OBEX_HDR_NAME) {
+ if (os->name) {
+ debug("Ignoring multiple name headers");
+ break;
+ }
+
+ /*
+ * This is because OBEX_UnicodeToChar() accesses
+ * the string even if its size is zero
+ */
+ if (hlen == 0) {
+ os->name = g_strdup("");
+ break;
+ }
+
+ os->name = g_convert((const gchar *) hd.bs, hlen,
+ "UTF8", "UTF16BE", NULL, NULL, NULL);
+
+ debug("Set path name: %s", os->name);
+ break;
+ }
+ }
+
+ if (!os->name) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_BAD_REQUEST,
+ OBEX_RSP_BAD_REQUEST);
+ return;
+ }
+
+ os->cmds->setpath(obex, obj);
+}
+
+gboolean os_prepare_get(struct obex_session *os, gchar *file, guint32 *size)
+{
+ gint fd;
+ struct stat stats;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ goto fail;
+
+ if (fstat(fd, &stats))
+ goto fail;
+
+ os->fd = fd;
+ os->offset = 0;
+
+ if (stats.st_size > 0)
+ os->buf = g_new0(guint8, os->tx_mtu);
+
+ *size = stats.st_size;
+
+ return TRUE;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return FALSE;
+}
+
+static gint obex_write(struct obex_session *os,
+ obex_t *obex, obex_object_t *obj)
+{
+ obex_headerdata_t hd;
+ gint32 len;
+
+ debug("obex_write name: %s type: %s tx_mtu: %d fd: %d",
+ os->name, os->type, os->tx_mtu, os->fd);
+
+ if (os->fd < 0)
+ return -EIO;
+
+ len = read(os->fd, os->buf, os->tx_mtu);
+ if (len < 0) {
+ gint err = errno;
+ error("read(): %s (%d)", strerror(err), err);
+ g_free(os->buf);
+ return -err;
+ }
+
+ os->offset += len;
+
+ if (len == 0) {
+ OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hd, 0,
+ OBEX_FL_STREAM_DATAEND);
+ g_free(os->buf);
+ os->buf = NULL;
+ return len;
+ }
+
+ hd.bs = os->buf;
+ OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hd, len,
+ OBEX_FL_STREAM_DATA);
+
+ return len;
+}
+
+static gint obex_read(struct obex_session *os,
+ obex_t *obex, obex_object_t *obj)
+{
+ gint size;
+ gint32 len = 0;
+ const guint8 *buffer;
+
+ size = OBEX_ObjectReadStream(obex, obj, &buffer);
+ if (size < 0) {
+ error("Error on OBEX stream");
+ return -EIO;
+ }
+
+ if (size > os->rx_mtu) {
+ error("Received more data than RX_MAX");
+ return -EIO;
+ }
+
+ if (os->fd < 0 && size > 0) {
+ if (os->buf) {
+ error("Got more data but there is still a pending buffer");
+ return -EIO;
+ }
+
+ os->buf = g_malloc0(os->rx_mtu);
+ memcpy(os->buf, buffer, size);
+ os->offset = size;
+
+ debug("Stored %u bytes into temporary buffer", size);
+
+ return 0;
+ }
+
+ while (len < size) {
+ gint w;
+
+ w = write(os->fd, buffer + len, size - len);
+ if (w < 0) {
+ gint err = errno;
+ if (err == EINTR)
+ continue;
+ else
+ return -err;
+ }
+
+ len += w;
+ }
+
+ os->offset += len;
+
+ return 0;
+}
+
+static gboolean check_put(obex_t *obex, obex_object_t *obj)
+{
+ struct obex_session *os;
+ struct statvfs buf;
+ obex_headerdata_t hd;
+ guint hlen;
+ guint8 hi;
+ guint64 free;
+
+ os = OBEX_GetUserData(obex);
+
+ if (os->type) {
+ g_free(os->type);
+ os->type = NULL;
+ }
+
+ if (os->name) {
+ g_free(os->name);
+ os->name = NULL;
+ }
+
+ while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+ switch (hi) {
+ case OBEX_HDR_NAME:
+ if (os->name) {
+ debug("Ignoring multiple name headers");
+ break;
+ }
+
+ if (hlen == 0)
+ continue;
+
+ os->name = g_convert((const gchar *) hd.bs, hlen,
+ "UTF8", "UTF16BE", NULL, NULL, NULL);
+ debug("OBEX_HDR_NAME: %s", os->name);
+ break;
+
+ case OBEX_HDR_TYPE:
+ if (os->type) {
+ debug("Ignoring multiple type headers");
+ break;
+ }
+
+ if (hlen == 0)
+ continue;
+
+ /* Ensure null termination */
+ if (hd.bs[hlen - 1] != '\0')
+ break;
+
+ if (!g_utf8_validate((const gchar *) hd.bs, -1, NULL)) {
+ debug("Invalid type header: %s", hd.bs);
+ break;
+ }
+
+ os->type = g_strndup((const gchar *) hd.bs, hlen);
+ debug("OBEX_HDR_TYPE: %s", os->type);
+ break;
+
+ case OBEX_HDR_BODY:
+ os->size = -1;
+ break;
+
+ case OBEX_HDR_LENGTH:
+ os->size = hd.bq4;
+ debug("OBEX_HDR_LENGTH: %d", os->size);
+ break;
+ }
+ }
+
+ OBEX_ObjectReParseHeaders(obex, obj);
+
+ if (!os->name) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_BAD_REQUEST,
+ OBEX_RSP_BAD_REQUEST);
+ g_free(os->type);
+ os->type = NULL;
+ return FALSE;
+ }
+
+ if (!os->cmds->chkput)
+ goto done;
+
+ if (os->cmds->chkput(obex, obj) < 0) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ return FALSE;
+ }
+
+ if (fstatvfs(os->fd, &buf) < 0) {
+ int err = errno;
+ error("fstatvfs(): %s(%d)", strerror(err), err);
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ return FALSE;
+ }
+
+ free = buf.f_bsize * buf.f_bavail;
+ debug("Free space in disk: %lu", free);
+ if (os->size > free) {
+ debug("Free disk space not available");
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ return FALSE;
+ }
+
+done:
+ os->checked = TRUE;
+
+ return TRUE;
+}
+
+static void cmd_put(struct obex_session *os, obex_t *obex, obex_object_t *obj)
+{
+ if (!os->cmds->put) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+ OBEX_RSP_NOT_IMPLEMENTED);
+ return;
+ }
+
+ g_return_if_fail(chk_cid(obex, obj, os->cid));
+
+ if (!os->checked) {
+ if (!check_put(obex, obj))
+ return;
+ }
+
+ os->cmds->put(obex, obj);
+}
+
+static void obex_event(obex_t *obex, obex_object_t *obj, gint mode,
+ gint evt, gint cmd, gint rsp)
+{
+ struct obex_session *os;
+
+ obex_debug(evt, cmd, rsp);
+
+ os = OBEX_GetUserData(obex);
+
+ switch (evt) {
+ case OBEX_EV_PROGRESS:
+ emit_transfer_progress(os->cid, os->size, os->offset);
+ break;
+ case OBEX_EV_ABORT:
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+ break;
+ case OBEX_EV_REQDONE:
+ switch (cmd) {
+ case OBEX_CMD_DISCONNECT:
+ OBEX_TransportDisconnect(obex);
+ break;
+ case OBEX_CMD_PUT:
+ case OBEX_CMD_GET:
+ emit_transfer_completed(os->cid,
+ os->offset == os->size);
+ if (os->fd >= 0) {
+ close(os->fd);
+ os->fd = -1;
+ }
+ g_free(os->buf);
+ os->buf = NULL;
+ os->offset = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ case OBEX_EV_REQHINT:
+ switch (cmd) {
+ case OBEX_CMD_PUT:
+ os->checked = FALSE;
+ OBEX_ObjectReadStream(obex, obj, NULL);
+ case OBEX_CMD_GET:
+ case OBEX_CMD_SETPATH:
+ case OBEX_CMD_CONNECT:
+ case OBEX_CMD_DISCONNECT:
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
+ OBEX_RSP_SUCCESS);
+ break;
+ default:
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+ OBEX_RSP_NOT_IMPLEMENTED);
+ break;
+ }
+ break;
+ case OBEX_EV_REQCHECK:
+ switch (cmd) {
+ case OBEX_CMD_PUT:
+ if (os->cmds->put)
+ check_put(obex, obj);
+ break;
+ default:
+ break;
+ }
+ break;
+ case OBEX_EV_REQ:
+ switch (cmd) {
+ case OBEX_CMD_DISCONNECT:
+ break;
+ case OBEX_CMD_CONNECT:
+ cmd_connect(os, obex, obj);
+ break;
+ case OBEX_CMD_SETPATH:
+ cmd_setpath(os, obex, obj);
+ break;
+ case OBEX_CMD_GET:
+ cmd_get(os, obex, obj);
+ break;
+ case OBEX_CMD_PUT:
+ cmd_put(os, obex, obj);
+ break;
+ default:
+ debug("Unknown request: 0x%X", cmd);
+ OBEX_ObjectSetRsp(obj,
+ OBEX_RSP_NOT_IMPLEMENTED, OBEX_RSP_NOT_IMPLEMENTED);
+ break;
+ }
+ break;
+ case OBEX_EV_STREAMAVAIL:
+ if (obex_read(os, obex, obj) < 0) {
+ debug("error obex_read()");
+ OBEX_CancelRequest(obex, 1);
+ }
+
+ break;
+ case OBEX_EV_STREAMEMPTY:
+ obex_write(os, obex, obj);
+ break;
+ case OBEX_EV_LINKERR:
+ break;
+ case OBEX_EV_PARSEERR:
+ break;
+ case OBEX_EV_UNEXPECTED:
+ break;
+
+ default:
+ debug("Unknown evt %d", evt);
+ break;
+ }
+}
+
+static void obex_handle_destroy(gpointer user_data)
+{
+ struct obex_session *os;
+ obex_t *obex = user_data;
+
+ os = OBEX_GetUserData(obex);
+
+ /* Got an error during a transfer. */
+ if (os->fd >= 0)
+ emit_transfer_completed(os->cid, os->offset == os->size);
+
+ unregister_transfer(os->cid);
+
+ obex_session_free(os);
+
+ OBEX_Cleanup(obex);
+}
+
+static gboolean obex_handle_input(GIOChannel *io,
+ GIOCondition cond, gpointer user_data)
+{
+ obex_t *obex = user_data;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR))
+ return FALSE;
+
+ if (OBEX_HandleInput(obex, 1) < 0) {
+ error("Handle input error");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gint obex_session_start(gint fd, struct server *server)
+{
+ struct obex_session *os;
+ GIOChannel *io;
+ obex_t *obex;
+ gint ret;
+
+ os = g_new0(struct obex_session, 1);
+ switch (server->service) {
+ case OBEX_OPUSH:
+ os->target = NULL;
+ os->cmds = &opp;
+ break;
+ case OBEX_FTP:
+ os->target = FTP_TARGET;
+ os->cmds = &ftp;
+ break;
+ default:
+ g_free(os);
+ debug("Invalid OBEX server");
+ return -EINVAL;
+ }
+
+ os->current_folder = g_strdup(server->folder);
+ os->server = server;
+ os->rx_mtu = RX_MTU;
+ os->tx_mtu = TX_MTU;
+ os->fd = -1;
+
+ obex = OBEX_Init(OBEX_TRANS_FD, obex_event, 0);
+ if (!obex) {
+ obex_session_free(os);
+ return -EIO;
+ }
+
+ OBEX_SetUserData(obex, os);
+
+ OBEX_SetTransportMTU(obex, os->rx_mtu, os->tx_mtu);
+
+ ret = FdOBEX_TransportSetup(obex, fd, fd, 0);
+ if (ret < 0) {
+ obex_session_free(os);
+ OBEX_Cleanup(obex);
+ return ret;
+ }
+
+ io = g_io_channel_unix_new(fd);
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ obex_handle_input, obex, obex_handle_destroy);
+ g_io_channel_unref(io);
+
+ return 0;
+}
+
+gint obex_session_stop()
+{
+ return 0;
+}
diff --git a/obexd/src/obex.h b/obexd/src/obex.h
new file mode 100644
index 0000000..bd8868f
--- /dev/null
+++ b/obexd/src/obex.h
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Copyright (C) 2007-2008 Instituto Nokia de Tecnologia (INdT)
+ * Copyright (C) 2007-2008 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 <glib.h>
+
+#define OBEX_OPUSH 0x00
+#define OBEX_FTP 0x01
+
+struct obex_commands {
+ void (*get) (obex_t *obex, obex_object_t *obj);
+ gint (*chkput) (obex_t *obex, obex_object_t *obj);
+ void (*put) (obex_t *obex, obex_object_t *obj);
+ void (*setpath) (obex_t *obex, obex_object_t *obj);
+};
+
+struct server {
+ guint16 service;
+ gboolean auto_accept;
+ gchar *folder;
+};
+
+struct obex_session {
+ guint32 cid;
+ guint16 tx_mtu;
+ guint16 rx_mtu;
+ gchar *name;
+ gchar *type;
+ gchar *current_folder;
+ guint8 *buf;
+ gint32 offset;
+ gint32 size;
+ gint fd;
+ const guint8 *target;
+ struct obex_commands *cmds;
+ struct server *server;
+ gboolean checked;
+};
+
+gint obex_session_start(gint fd, struct server *server);
+gint obex_session_stop();
+
+gint opp_chkput(obex_t *obex, obex_object_t *obj);
+void opp_put(obex_t *obex, obex_object_t *obj);
+void opp_get(obex_t *obex, obex_object_t *obj);
+
+void ftp_get(obex_t *obex, obex_object_t *obj);
+void ftp_put(obex_t *obex, obex_object_t *obj);
+void ftp_setpath(obex_t *obex, obex_object_t *obj);
+
+gboolean os_prepare_get(struct obex_session *os, gchar *file, guint32 *size);
diff --git a/obexd/src/obexd.h b/obexd/src/obexd.h
new file mode 100644
index 0000000..7baac91
--- /dev/null
+++ b/obexd/src/obexd.h
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 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
+ *
+ */
+
+#include <stdio.h>
+
+#define DBG(fmt, arg...) printf("%s: " fmt "\n" , __FUNCTION__ , ## arg)
+//#define DBG(fmt, arg...)
+
+#include <dbus/dbus.h>
+
+#define OPENOBEX_SERVICE "org.openobex"
+
+#define OPENOBEX_MANAGER_PATH "/"
+#define OPENOBEX_MANAGER_INTERFACE OPENOBEX_SERVICE ".Manager"
+#define ERROR_INTERFACE OPENOBEX_SERVICE ".Error"
+
+gboolean manager_init(DBusConnection *conn);
+void manager_cleanup(void);
diff --git a/obexd/src/opp.c b/obexd/src/opp.c
new file mode 100644
index 0000000..70e6fe6
--- /dev/null
+++ b/obexd/src/opp.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Copyright (C) 2007-2008 Instituto Nokia de Tecnologia (INdT)
+ * Copyright (C) 2007-2008 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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include <glib.h>
+
+#include "logging.h"
+#include "dbus.h"
+#include "obex.h"
+
+#define VCARD_TYPE "text/x-vcard"
+#define VCARD_FILE CONFIGDIR "/vcard.vcf"
+
+gint opp_chkput(obex_t *obex, obex_object_t *obj)
+{
+ struct obex_session *os;
+ gchar *new_folder, *new_name, *path;
+ gint32 time, len = 0;
+ gint ret;
+
+ os = OBEX_GetUserData(obex);
+ if (os == NULL)
+ return -EINVAL;
+
+ if (!os->size)
+ return -EINVAL;
+
+ if (os->server->auto_accept)
+ goto skip_auth;
+
+ time = 0;
+ ret = request_authorization(os->cid, OBEX_GetFD(obex),
+ os->name ? os->name : "",
+ os->type ? os->type : "",
+ os->size, time, &new_folder,
+ &new_name);
+
+ if (ret < 0)
+ return -EPERM;
+
+ if (new_folder) {
+ g_free(os->current_folder);
+ os->current_folder = new_folder;
+ }
+
+ if (new_name) {
+ g_free(os->name);
+ os->name = new_name;
+ }
+
+skip_auth:
+ path = g_build_filename(os->current_folder, os->name, NULL);
+
+ os->fd = open(path, O_WRONLY | O_CREAT, 0600);
+ if (os->fd < 0) {
+ error("open(%s): %s (%d)", path, strerror(errno), errno);
+ g_free(path);
+ return -EPERM;
+ }
+
+ g_free(path);
+
+ emit_transfer_started(os->cid);
+
+ if (!os->buf) {
+ debug("PUT request authorized, no buffered data");
+ return 0;
+ }
+
+ while (len < os->offset) {
+ gint w;
+
+ w = write(os->fd, os->buf + len, os->offset - len);
+ if (w < 0) {
+ gint err = errno;
+ if (err == EINTR)
+ continue;
+ else
+ return -err;
+ }
+
+ len += w;
+ }
+
+ return 0;
+}
+
+void opp_put(obex_t *obex, obex_object_t *obj)
+{
+ struct obex_session *os;
+
+ os = OBEX_GetUserData(obex);
+ if (os == NULL)
+ return;
+
+ if (os->current_folder == NULL) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ return;
+ }
+
+ if (os->name == NULL) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_BAD_REQUEST, OBEX_RSP_BAD_REQUEST);
+ return;
+ }
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+}
+
+void opp_get(obex_t *obex, obex_object_t *obj)
+{
+ struct obex_session *os;
+ obex_headerdata_t hv;
+ guint32 size;
+
+ os = OBEX_GetUserData(obex);
+ if (os == NULL)
+ return;
+
+ if (os->name)
+ goto fail;
+
+ if (os->type == NULL)
+ goto fail;
+
+ if (g_str_equal(os->type, VCARD_TYPE)) {
+ if (!os_prepare_get(os, VCARD_FILE, &size))
+ goto fail;
+ } else
+ goto fail;
+
+
+ hv.bq4 = size;
+ OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_LENGTH, hv, 4, 0);
+
+ /* Add body header */
+ hv.bs = NULL;
+ if (size == 0)
+ OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_FIT_ONE_PACKET);
+ else
+ OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_STREAM_START);
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+
+ return;
+
+fail:
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+
+ return;
+}