Diff between fad29b14b3e82fd222116073948cd8fc6e26be56 and a4d3df2ea53a2d9c5669a03708071a6fcbbeec9c

Changed Files

File Additions Deletions Status
obexd/plugins/filesystem.c +394 -0 added
obexd/src/ftp.c +9 -193 modified
obexd/src/mimetype.c +88 -0 added
obexd/src/mimetype.h +39 -0 added
obexd/src/obex.c +70 -49 modified
obexd/src/obex.h +9 -4 modified
obexd/src/opp.c +1 -1 modified
obexd/src/plugin.c +1 -0 modified

Full Patch

diff --git a/obexd/plugins/filesystem.c b/obexd/plugins/filesystem.c
new file mode 100644
index 0000000..e275bf2
--- /dev/null
+++ b/obexd/plugins/filesystem.c
@@ -0,0 +1,394 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2009  Intel Corporation
+ *  Copyright (C) 2007-2009  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 <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#include <wait.h>
+
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "plugin.h"
+#include "logging.h"
+#include "mimetype.h"
+#include "obex.h"
+
+#define EOL_CHARS "\n"
+
+#define FL_VERSION "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" EOL_CHARS
+
+#define FL_TYPE "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">" EOL_CHARS
+
+#define FL_TYPE_PCSUITE "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\"" EOL_CHARS \
+                        "  [ <!ATTLIST folder mem-type CDATA #IMPLIED> ]>" EOL_CHARS
+
+#define FL_BODY_BEGIN "<folder-listing version=\"1.0\">" EOL_CHARS
+
+#define FL_BODY_END "</folder-listing>" EOL_CHARS
+
+#define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>" EOL_CHARS
+
+#define FL_FILE_ELEMENT "<file name=\"%s\" size=\"%lu\"" \
+			" %s accessed=\"%s\" " \
+			"modified=\"%s\" created=\"%s\"/>" EOL_CHARS
+
+#define FL_FOLDER_ELEMENT "<folder name=\"%s\" %s accessed=\"%s\" " \
+			"modified=\"%s\" created=\"%s\"/>" EOL_CHARS
+
+#define FL_FOLDER_ELEMENT_PCSUITE "<folder name=\"%s\" %s accessed=\"%s\"" \
+			" modified=\"%s\" mem-type=\"DEV\"" \
+			" created=\"%s\"/>" EOL_CHARS
+
+static const guint8 FTP_TARGET[TARGET_SIZE] = {
+			0xF9, 0xEC, 0x7B, 0xC4,  0x95, 0x3C, 0x11, 0xD2,
+			0x98, 0x4E, 0x52, 0x54,  0x00, 0xDC, 0x9E, 0x09  };
+
+static gchar *file_stat_line(gchar *filename, struct stat *fstat,
+				struct stat *dstat, gboolean root,
+				gboolean pcsuite)
+{
+	gchar perm[51], atime[18], ctime[18], mtime[18];
+	gchar *escaped, *ret = NULL;
+
+	snprintf(perm, 50, "user-perm=\"%s%s%s\" group-perm=\"%s%s%s\" "
+			"other-perm=\"%s%s%s\"",
+			(fstat->st_mode & S_IRUSR ? "R" : ""),
+			(fstat->st_mode & S_IWUSR ? "W" : ""),
+			(dstat->st_mode & S_IWUSR ? "D" : ""),
+			(fstat->st_mode & S_IRGRP ? "R" : ""),
+			(fstat->st_mode & S_IWGRP ? "W" : ""),
+			(dstat->st_mode & S_IWGRP ? "D" : ""),
+			(fstat->st_mode & S_IROTH ? "R" : ""),
+			(fstat->st_mode & S_IWOTH ? "W" : ""),
+			(dstat->st_mode & S_IWOTH ? "D" : ""));
+
+	strftime(atime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_atime));
+	strftime(ctime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_ctime));
+	strftime(mtime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_mtime));
+
+	escaped = g_markup_escape_text(filename, -1);
+
+	if (S_ISDIR(fstat->st_mode)) {
+		if (pcsuite && root && g_str_equal(filename, "Data"))
+			ret = g_strdup_printf(FL_FOLDER_ELEMENT_PCSUITE,
+						escaped, perm, atime,
+						mtime, ctime);
+		else
+			ret = g_strdup_printf(FL_FOLDER_ELEMENT, escaped, perm,
+						atime, mtime, ctime);
+	} else if (S_ISREG(fstat->st_mode))
+		ret = g_strdup_printf(FL_FILE_ELEMENT, escaped, fstat->st_size,
+					perm, atime, mtime, ctime);
+
+	g_free(escaped);
+
+	return ret;
+}
+
+static gpointer filesystem_open(const char *name, int oflag, mode_t mode,
+				size_t *size)
+{
+	struct stat stats;
+	struct statvfs buf;
+	int fd = open(name, oflag, mode);
+
+	if (fd < 0)
+		return NULL;
+
+	if (fstat(fd, &stats) < 0) {
+		error("fstat(fd=%d): %s (%d)", fd, strerror(errno), errno);
+		goto failed;
+	}
+
+	if (oflag == O_RDONLY) {
+		*size =  stats.st_size;
+		return GINT_TO_POINTER(fd);
+	}
+
+	if (fstatvfs(fd, &buf) < 0)
+		goto failed;
+
+	if (buf.f_bsize * buf.f_bavail < *size) {
+		debug("Not enough free space on disk");
+		errno = -ENOSPC;
+		goto failed;
+	}
+
+	return GINT_TO_POINTER(fd);
+
+failed:
+	close(fd);
+	return NULL;
+}
+
+static int filesystem_close(gpointer object)
+{
+	return close(GPOINTER_TO_INT(object));
+}
+
+static ssize_t filesystem_read(gpointer object, void *buf, size_t count)
+{
+	return read(GPOINTER_TO_INT(object), buf, count);
+}
+
+static ssize_t filesystem_write(gpointer object, const void *buf, size_t count)
+{
+	return write(GPOINTER_TO_INT(object), buf, count);
+}
+
+static gpointer capability_open(const char *name, int oflag, mode_t mode,
+				size_t *size)
+{
+	GError *gerr = NULL;
+	gchar *buf;
+	gint exit;
+	gboolean ret;
+
+	if (oflag != O_RDONLY)
+		goto fail;
+
+	if (name[0] != '!') {
+		ret = g_file_get_contents(name, &buf, NULL, &gerr);
+		if (ret == FALSE) {
+			error("%s", gerr->message);
+			goto fail;
+		}
+
+		goto done;
+	}
+
+	ret = g_spawn_command_line_sync(name + 1, &buf, NULL, &exit, &gerr);
+	if (ret == FALSE) {
+		error("%s", gerr->message);
+		goto fail;
+	}
+
+	if (WEXITSTATUS(exit) != EXIT_SUCCESS) {
+		error("%s failed", name + 1);
+		g_free(buf);
+		goto fail;
+	}
+
+done:
+	if (size)
+		*size = strlen(buf);
+
+	return buf;
+
+fail:
+	if (gerr)
+		g_error_free(gerr);
+
+	errno = EPERM;
+	return NULL;
+}
+
+static int capability_close(gpointer object)
+{
+	g_free(object);
+	return 0;
+}
+
+static ssize_t capability_read(gpointer object, void *buf, size_t count)
+{
+	strncpy(buf, object, count);
+	return strlen(buf);
+}
+
+static gpointer folder_open(const char *name, int oflag, mode_t mode,
+				size_t *size)
+{
+	DIR *dir = opendir(name);
+
+	if (dir == NULL)
+		return NULL;
+
+	if (size)
+		*size = 1;
+
+	return dir;
+}
+
+static int folder_close(gpointer object)
+{
+	DIR *dir = (DIR *) object;
+
+	return closedir(dir);
+}
+
+static ssize_t folder_read(gpointer object, void *buf, size_t count)
+{
+	struct obex_session *os;
+	struct stat fstat, dstat;
+	struct dirent *ep;
+	DIR *dp = (DIR *) object;
+	GString *listing;
+	gboolean root, pcsuite;
+	gint err, len;
+
+	os = obex_get_session(object);
+	if (os->finished)
+		return 0;
+
+	pcsuite = os->server->services & OBEX_PCSUITE ? TRUE : FALSE;
+
+	listing = g_string_new(FL_VERSION);
+	listing = g_string_append(listing, pcsuite ? FL_TYPE_PCSUITE : FL_TYPE);
+
+	listing = g_string_append(listing, FL_BODY_BEGIN);
+
+	root = g_str_equal(os->current_folder, os->server->folder);
+
+	if (root && os->server->symlinks)
+		err = stat(os->current_folder, &dstat);
+	else {
+		listing = g_string_append(listing, FL_PARENT_FOLDER_ELEMENT);
+		err = lstat(os->current_folder, &dstat);
+	}
+
+	if (err < 0) {
+		err = -errno;
+		error("%s: %s(%d)", root ? "stat" : "lstat",
+				strerror(errno), errno);
+		goto failed;
+	}
+
+	while ((ep = readdir(dp))) {
+		gchar *name;
+		gchar *fullname;
+		gchar *line;
+
+		if (ep->d_name[0] == '.')
+			continue;
+
+		name = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL);
+		if (name == NULL) {
+			error("g_filename_to_utf8: invalid filename");
+			continue;
+		}
+
+		fullname = g_build_filename(os->current_folder, ep->d_name, NULL);
+
+		if (root && os->server->symlinks)
+			err = stat(fullname, &fstat);
+		else
+			err = lstat(fullname, &fstat);
+
+		if (err < 0) {
+			debug("%s: %s(%d)", root ? "stat" : "lstat",
+					strerror(errno), errno);
+			g_free(name);
+			g_free(fullname);
+			continue;
+		}
+
+		g_free(fullname);
+
+		line = file_stat_line(name, &fstat, &dstat, root, pcsuite);
+		if (line == NULL) {
+			g_free(name);
+			continue;
+		}
+
+		g_free(name);
+
+		listing = g_string_append(listing, line);
+		g_free(line);
+	}
+
+	listing = g_string_append(listing, FL_BODY_END);
+	len = listing->len;
+	memcpy(buf, listing->str, len);
+	g_string_free(listing, TRUE);
+	os->finished = TRUE;
+
+	return len;
+
+failed:
+	g_string_free(listing, TRUE);
+	return err;
+}
+
+struct obex_mime_type_driver file = {
+	.open = filesystem_open,
+	.close = filesystem_close,
+	.read = filesystem_read,
+	.write = filesystem_write,
+	.remove = remove,
+};
+
+struct obex_mime_type_driver capability = {
+	.target = FTP_TARGET,
+	.mimetype = "x-obex/capability",
+	.open = capability_open,
+	.close = capability_close,
+	.read = capability_read,
+};
+
+struct obex_mime_type_driver folder = {
+	.target = FTP_TARGET,
+	.mimetype = "x-obex/folder-listing",
+	.open = folder_open,
+	.close = folder_close,
+	.read = folder_read,
+};
+
+static int filesystem_init(void)
+{
+	int err;
+
+	err = obex_mime_type_driver_register(&folder);
+	if (err < 0)
+		return err;
+
+	err = obex_mime_type_driver_register(&capability);
+	if (err < 0)
+		return err;
+
+	return obex_mime_type_driver_register(&file);
+}
+
+static void filesystem_exit(void)
+{
+	obex_mime_type_driver_unregister(&folder);
+	obex_mime_type_driver_unregister(&capability);
+	obex_mime_type_driver_unregister(&file);
+}
+
+OBEX_PLUGIN_DEFINE("filesystem", filesystem_init, filesystem_exit)
diff --git a/obexd/src/ftp.c b/obexd/src/ftp.c
index a53e43f..ffbcf73 100644
--- a/obexd/src/ftp.c
+++ b/obexd/src/ftp.c
@@ -48,206 +48,22 @@
 #include "logging.h"
 #include "obex.h"
 #include "dbus.h"
+#include "mimetype.h"
 
 #define LST_TYPE "x-obex/folder-listing"
 #define CAP_TYPE "x-obex/capability"
 
-#define EOL_CHARS "\n"
-
-#define FL_VERSION "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" EOL_CHARS
-
-#define FL_TYPE "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">" EOL_CHARS
-
-#define FL_TYPE_PCSUITE "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\"" EOL_CHARS \
-                        "  [ <!ATTLIST folder mem-type CDATA #IMPLIED> ]>" EOL_CHARS
-
-#define FL_BODY_BEGIN "<folder-listing version=\"1.0\">" EOL_CHARS
-
-#define FL_BODY_END "</folder-listing>" EOL_CHARS
-
-#define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>" EOL_CHARS
-
-#define FL_FILE_ELEMENT "<file name=\"%s\" size=\"%lu\"" \
-			" %s accessed=\"%s\" " \
-			"modified=\"%s\" created=\"%s\"/>" EOL_CHARS
-
-#define FL_FOLDER_ELEMENT "<folder name=\"%s\" %s accessed=\"%s\" " \
-			"modified=\"%s\" created=\"%s\"/>" EOL_CHARS
-
-#define FL_FOLDER_ELEMENT_PCSUITE "<folder name=\"%s\" %s accessed=\"%s\"" \
-			" modified=\"%s\" mem-type=\"DEV\"" \
-			" created=\"%s\"/>" EOL_CHARS
-
-static gchar *file_stat_line(gchar *filename, struct stat *fstat,
-				struct stat *dstat, gboolean root,
-				gboolean pcsuite)
-{
-	gchar perm[51], atime[18], ctime[18], mtime[18];
-	gchar *escaped, *ret = NULL;
-
-	snprintf(perm, 50, "user-perm=\"%s%s%s\" group-perm=\"%s%s%s\" "
-			"other-perm=\"%s%s%s\"",
-			(fstat->st_mode & S_IRUSR ? "R" : ""),
-			(fstat->st_mode & S_IWUSR ? "W" : ""),
-			(dstat->st_mode & S_IWUSR ? "D" : ""),
-			(fstat->st_mode & S_IRGRP ? "R" : ""),
-			(fstat->st_mode & S_IWGRP ? "W" : ""),
-			(dstat->st_mode & S_IWGRP ? "D" : ""),
-			(fstat->st_mode & S_IROTH ? "R" : ""),
-			(fstat->st_mode & S_IWOTH ? "W" : ""),
-			(dstat->st_mode & S_IWOTH ? "D" : ""));
-
-	strftime(atime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_atime));
-	strftime(ctime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_ctime));
-	strftime(mtime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_mtime));
-
-	escaped = g_markup_escape_text(filename, -1);
-
-	if (S_ISDIR(fstat->st_mode)) {
-		if (pcsuite && root && g_str_equal(filename, "Data"))
-			ret = g_strdup_printf(FL_FOLDER_ELEMENT_PCSUITE,
-						escaped, perm, atime,
-						mtime, ctime);
-		else
-			ret = g_strdup_printf(FL_FOLDER_ELEMENT, escaped, perm,
-						atime, mtime, ctime);
-	} else if (S_ISREG(fstat->st_mode))
-		ret = g_strdup_printf(FL_FILE_ELEMENT, escaped, fstat->st_size,
-					perm, atime, mtime, ctime);
-
-	g_free(escaped);
-
-	return ret;
-}
-
-static gint folder_listing(struct obex_session *os, guint32 *size)
+static gint folder_listing(struct obex_session *os, size_t *size)
 {
-	struct stat fstat, dstat;
-	struct dirent *ep;
-	DIR *dp;
-	GString *listing;
-	gboolean root, pcsuite;
-	gint err;
-
-	pcsuite = os->server->services & OBEX_PCSUITE ? TRUE : FALSE;
-
-	listing = g_string_new(FL_VERSION);
-	listing = g_string_append(listing, pcsuite ? FL_TYPE_PCSUITE : FL_TYPE);
-
-	listing = g_string_append(listing, FL_BODY_BEGIN);
-
-	root = g_str_equal(os->current_folder, os->server->folder);
-
-	if (root && os->server->symlinks)
-		err = stat(os->current_folder, &dstat);
-	else {
-		listing = g_string_append(listing, FL_PARENT_FOLDER_ELEMENT);
-		err = lstat(os->current_folder, &dstat);
-	}
-
-	if (err < 0) {
-		err = -errno;
-		error("%s: %s(%d)", root ? "stat" : "lstat",
-				strerror(errno), errno);
-		goto failed;
-	}
-
-	dp = opendir(os->current_folder);
-	if (dp == NULL) {
-		err = -errno;
-		error("opendir: failed to access %s", os->current_folder);
-		goto failed;
-	}
-
-	while ((ep = readdir(dp))) {
-		gchar *name;
-		gchar *fullname;
-		gchar *line;
-
-		if (ep->d_name[0] == '.')
-			continue;
-
-		name = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL);
-		if (name == NULL) {
-			error("g_filename_to_utf8: invalid filename");
-			continue;
-		}
-
-		fullname = g_build_filename(os->current_folder, ep->d_name, NULL);
-
-		if (root && os->server->symlinks)
-			err = stat(fullname, &fstat);
-		else
-			err = lstat(fullname, &fstat);
-
-		if (err < 0) {
-			debug("%s: %s(%d)", root ? "stat" : "lstat",
-					strerror(errno), errno);
-			g_free(name);
-			g_free(fullname);
-			continue;
-		}
-
-		g_free(fullname);
-
-		line = file_stat_line(name, &fstat, &dstat, root, pcsuite);
-		if (line == NULL) {
-			g_free(name);
-			continue;
-		}
-
-		g_free(name);
-
-		listing = g_string_append(listing, line);
-		g_free(line);
-	}
-
-	closedir(dp);
-
-	listing = g_string_append(listing, FL_BODY_END);
-	*size = listing->len + 1;
-	os->buf = (guint8*) g_string_free(listing, FALSE);
-
-	return 0;
-
-failed:
-	g_string_free(listing, TRUE);
-	return err;
+	return os_prepare_get(os, os->current_folder, size);
 }
 
-static gint get_capability(struct obex_session *os, guint32 *size)
+static gint get_capability(struct obex_session *os, size_t *size)
 {
-	GError *gerr = NULL;
-	gchar *buf;
-	gint exit;
-	gboolean ret;
-
-	if (os->server->capability == NULL)
-		return -ENOENT;
-
-	if (os->server->capability[0] != '!')
-		return os_prepare_get(os, os->server->capability, size);
-
-	ret = g_spawn_command_line_sync(os->server->capability + 1,
-					&buf, NULL, &exit, &gerr);
-	if (ret == FALSE) {
-		error("g_spawn_command_line_sync: %s", gerr->message);
-		g_error_free(gerr);
-		return -EPERM;
-	}
-
-	if (WEXITSTATUS(exit) != EXIT_SUCCESS) {
-		g_free(buf);
-		return -EPERM;
-	}
-
-	os->buf = (guint8 *) buf;
-	*size = strlen(buf);
-
-	return 0;
+	return os_prepare_get(os, os->server->capability, size);
 }
 
-static gint get_by_type(struct obex_session *os, gchar *type, guint32 *size)
+static gint get_by_type(struct obex_session *os, gchar *type, size_t *size)
 {
 	if (type == NULL)
 		return -ENOENT;
@@ -262,7 +78,7 @@ static gint get_by_type(struct obex_session *os, gchar *type, guint32 *size)
 }
 
 static gint ftp_prepare_get(struct obex_session *os, gchar *file,
-				guint32 *size)
+				size_t *size)
 {
 	gboolean root;
 
@@ -289,7 +105,7 @@ void ftp_get(obex_t *obex, obex_object_t *obj)
 {
 	obex_headerdata_t hv;
 	struct obex_session *os;
-	guint32 size;
+	size_t size;
 	gint err;
 	gchar *path;
 
@@ -355,7 +171,7 @@ static gint ftp_delete(struct obex_session *os)
 
 	path = g_build_filename(os->current_folder, os->name, NULL);
 
-	if (remove(path) < 0)
+	if (os->driver->remove(path) < 0)
 		ret = -errno;
 
 	g_free(path);
diff --git a/obexd/src/mimetype.c b/obexd/src/mimetype.c
new file mode 100644
index 0000000..b8e68c7
--- /dev/null
+++ b/obexd/src/mimetype.c
@@ -0,0 +1,88 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "mimetype.h"
+#include "obex.h"
+
+static GSList *drivers = NULL;
+
+struct obex_mime_type_driver *obex_mime_type_driver_find(const guint8 *target, const char *mimetype)
+{
+	GSList *l;
+
+	for (l = drivers; l; l = l->next) {
+		struct obex_mime_type_driver *driver = l->data;
+
+		if (driver->target && target &&
+				memcmp(target, driver->target, TARGET_SIZE))
+			continue;
+
+		if (g_strcmp0(mimetype, driver->mimetype) == 0)
+			return driver;
+	}
+
+	return NULL;
+}
+
+int obex_mime_type_driver_register(struct obex_mime_type_driver *driver)
+{
+	if (!driver) {
+		error("Invalid driver");
+		return -EINVAL;
+	}
+
+	if (obex_mime_type_driver_find(driver->target, driver->mimetype)) {
+		error("Permission denied: %s could not be registered", driver->mimetype);
+		return -EPERM;
+	}
+
+	debug("driver %p mimetype %s registered", driver, driver->mimetype);
+
+	drivers = g_slist_append(drivers, driver);
+
+	return 0;
+}
+
+void obex_mime_type_driver_unregister(struct obex_mime_type_driver *driver)
+{
+	if (!g_slist_find(drivers, driver)) {
+		error("Unable to unregister: No such driver %p", driver);
+		return;
+	}
+
+	debug("driver %p mimetype %s unregistered", driver, driver->mimetype);
+
+	drivers = g_slist_remove(drivers, driver);
+}
diff --git a/obexd/src/mimetype.h b/obexd/src/mimetype.h
new file mode 100644
index 0000000..8e2990e
--- /dev/null
+++ b/obexd/src/mimetype.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#define TARGET_SIZE 16
+
+struct obex_mime_type_driver {
+	const guint8 *target;
+	const char *mimetype;
+	gpointer (*open) (const char *name, int oflag, mode_t mode, size_t *size);
+	int (*close) (gpointer object);
+	ssize_t (*read) (gpointer object, void *buf, size_t count);
+	ssize_t (*write) (gpointer object, const void *buf, size_t count);
+	int (*remove) (const char *name);
+};
+
+int obex_mime_type_driver_register(struct obex_mime_type_driver *driver);
+void obex_mime_type_driver_unregister(struct obex_mime_type_driver *driver);
+struct obex_mime_type_driver *obex_mime_type_driver_find(const guint8 *target,
+							const char *mimetype);
diff --git a/obexd/src/obex.c b/obexd/src/obex.c
index 9ec9c5b..212c369 100644
--- a/obexd/src/obex.c
+++ b/obexd/src/obex.c
@@ -47,6 +47,7 @@
 #include "logging.h"
 #include "obex.h"
 #include "dbus.h"
+#include "mimetype.h"
 
 /* Default MTU's */
 #define DEFAULT_RX_MTU 32767
@@ -93,16 +94,17 @@ struct obex_commands pbap = {
 
 static void os_reset_session(struct obex_session *os)
 {
-	if (os->fd > 0) {
-		close(os->fd);
-		os->fd = -1;
+	if (os->object) {
+		os->driver->close(os->object);
+		os->object = NULL;
 		if (os->aborted && os->cmd == OBEX_CMD_PUT && os->current_folder) {
 			gchar *path;
 			path = g_build_filename(os->current_folder, os->name, NULL);
-			unlink(path);
+			os->driver->remove(path);
 			g_free(path);
 		}
 	}
+
 	if (os->name) {
 		g_free(os->name);
 		os->name = NULL;
@@ -115,6 +117,7 @@ static void os_reset_session(struct obex_session *os)
 		g_free(os->buf);
 		os->buf = NULL;
 	}
+	os->driver = NULL;
 	os->aborted = FALSE;
 	os->offset = 0;
 	os->size = OBJECT_SIZE_DELETE;
@@ -234,6 +237,7 @@ static void cmd_connect(struct obex_session *os,
 				os->server->services &
 						(OBEX_FTP | OBEX_PCSUITE)) {
 			os->target = FTP_TARGET;
+			os->services = OBEX_FTP | OBEX_PCSUITE;
 			os->cmds = &ftp;
 			break;
 		}
@@ -241,6 +245,7 @@ static void cmd_connect(struct obex_session *os,
 		if (memcmp(hd.bs, PBAP_TARGET, TARGET_SIZE) == 0 &&
 				os->server->services & OBEX_PBAP) {
 			os->target = PBAP_TARGET;
+			os->services = OBEX_PBAP;
 			os->cmds = &pbap;
 			pbap_phonebook_context_create(os);
 			break;
@@ -379,10 +384,21 @@ static void cmd_get(struct obex_session *os, obex_t *obex, obex_object_t *obj)
 
 			os->type = g_strndup((const gchar *) hd.bs, hlen);
 			debug("OBEX_HDR_TYPE: %s", os->type);
+			os->driver = obex_mime_type_driver_find(os->target, os->type);
 			break;
 		}
 	}
 
+	if (!os->driver) {
+		os->driver = obex_mime_type_driver_find(os->target, NULL);
+		if (!os->driver) {
+			error("No driver found");
+			OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+					OBEX_RSP_NOT_IMPLEMENTED);
+			return;
+		}
+	}
+
 	os->cmds->get(obex, obj);
 }
 
@@ -435,38 +451,34 @@ static void cmd_setpath(struct obex_session *os,
 	os->cmds->setpath(obex, obj);
 }
 
-int os_prepare_get(struct obex_session *os, gchar *file, guint32 *size)
+int os_prepare_get(struct obex_session *os, gchar *filename, size_t *size)
 {
-	gint fd, err;
-	struct stat stats;
+	gint err;
+	gpointer object;
 
-	fd = open(file, O_RDONLY);
-	if (fd < 0) {
-		err = -errno;
-		error("open(%s): %s (%d)", file, strerror(errno), errno);
-		goto fail;
+	if (!os->driver) {
+		error("No driver to handle %s", os->type);
+		return -ENOENT;
 	}
 
-	if (fstat(fd, &stats) < 0) {
+	object = os->driver->open(filename, O_RDONLY, 0, size);
+	if (object == NULL) {
 		err = -errno;
-		error("fstat(fd=%d (%s)): %s (%d)", fd, file,
-						strerror(errno), errno);
+		error("open(%s): %s (%d)", filename, strerror(errno), errno);
 		goto fail;
 	}
 
-	os->fd = fd;
+	os->object = object;
 	os->offset = 0;
 
-	if (stats.st_size > 0)
+	if (*size > 0)
 		os->buf = g_malloc0(os->tx_mtu);
 
-	*size = stats.st_size;
-
 	return 0;
 
 fail:
-	if (fd >= 0)
-		close(fd);
+	if (object)
+		os->driver->close(object);
 	return err;
 }
 
@@ -477,14 +489,15 @@ static gint obex_write_stream(struct obex_session *os,
 	gint32 len;
 	guint8 *ptr;
 
-	debug("obex_write_stream: name=%s type=%s tx_mtu=%d fd=%d",
+	debug("obex_write_stream: name=%s type=%s tx_mtu=%d file=%p",
 		os->name ? os->name : "", os->type ? os->type : "",
-		os->tx_mtu, os->fd);
+		os->tx_mtu, os->object);
 
 	if (os->aborted)
 		return -EPERM;
 
-	if (os->fd < 0) {
+
+	if (os->object == NULL) {
 		if (os->buf == NULL && os->finished == FALSE)
 			return -EIO;
 
@@ -493,7 +506,7 @@ static gint obex_write_stream(struct obex_session *os,
 		goto add_header;
 	}
 
-	len = read(os->fd, os->buf, os->tx_mtu);
+	len = os->driver->read(os->object, os->buf, os->tx_mtu);
 	if (len < 0) {
 		gint err = errno;
 		error("read(): %s (%d)", strerror(err), err);
@@ -535,8 +548,9 @@ gint os_prepare_put(struct obex_session *os)
 
 	path = g_build_filename(os->current_folder, os->name, NULL);
 
-	os->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
-	if (os->fd < 0) {
+	os->object = os->driver->open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600,
+					(size_t *) &os->size);
+	if (os->object == NULL) {
 		error("open(%s): %s (%d)", path, strerror(errno), errno);
 		g_free(path);
 		return -EPERM;
@@ -556,7 +570,7 @@ gint os_prepare_put(struct obex_session *os)
 	while (len < os->offset) {
 		gint w;
 
-		w = write(os->fd, os->buf + len, os->offset - len);
+		w = os->driver->write(os->object, os->buf + len, os->offset - len);
 		if (w < 0) {
 			gint err = errno;
 			error("write(%s): %s (%d)", path, strerror(errno),
@@ -598,7 +612,7 @@ static gint obex_read_stream(struct obex_session *os, obex_t *obex,
 		return -EIO;
 	}
 
-	if (os->fd < 0 && size > 0) {
+	if (os->object == NULL && size > 0) {
 		if (os->buf) {
 			error("Got more data but there is still a pending buffer");
 			return -EIO;
@@ -616,7 +630,7 @@ static gint obex_read_stream(struct obex_session *os, obex_t *obex,
 	while (len < size) {
 		gint w;
 
-		w = write(os->fd, buffer + len, size - len);
+		w = os->driver->write(os->object, buffer + len, size - len);
 		if (w < 0) {
 			gint err = errno;
 			if (err == EINTR)
@@ -636,11 +650,9 @@ static gint obex_read_stream(struct obex_session *os, obex_t *obex,
 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;
-	long long unsigned int free;
 	int ret;
 
 	os = OBEX_GetUserData(obex);
@@ -691,6 +703,7 @@ static gboolean check_put(obex_t *obex, obex_object_t *obj)
 
 			os->type = g_strndup((const gchar *) hd.bs, hlen);
 			debug("OBEX_HDR_TYPE: %s", os->type);
+			os->driver = obex_mime_type_driver_find(os->target, os->type);
 			break;
 
 		case OBEX_HDR_BODY:
@@ -718,6 +731,16 @@ static gboolean check_put(obex_t *obex, obex_object_t *obj)
 		return FALSE;
 	}
 
+	if (!os->driver) {
+		os->driver = obex_mime_type_driver_find(os->target, NULL);
+		if (!os->driver) {
+			error("No driver found");
+			OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+					OBEX_RSP_NOT_IMPLEMENTED);
+			return FALSE;
+		}
+	}
+
 	if (!os->cmds || !os->cmds->chkput)
 		goto done;
 
@@ -744,21 +767,6 @@ static gboolean check_put(obex_t *obex, obex_object_t *obj)
 		goto done;
 	}
 
-	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 = (unsigned long long) buf.f_bsize * buf.f_bavail;
-	debug("Free space in disk: %llu", free);
-	if ((guint64) os->size > free) {
-		debug("Free disk space not available");
-		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
-		return FALSE;
-	}
-
 done:
 	os->checked = TRUE;
 
@@ -926,7 +934,7 @@ static void obex_handle_destroy(gpointer user_data)
 
 	if (os->target == NULL) {
 		/* Got an error during a transfer. */
-		if (os->fd >= 0)
+		if (os->object)
 			emit_transfer_completed(os->cid, os->offset == os->size);
 
 		unregister_transfer(os->cid);
@@ -1001,7 +1009,6 @@ gint obex_session_start(GIOChannel *io, struct server *server)
 	os->server = server;
 	os->rx_mtu = server->rx_mtu ? server->rx_mtu : DEFAULT_RX_MTU;
 	os->tx_mtu = server->tx_mtu ? server->tx_mtu : DEFAULT_TX_MTU;
-	os->fd = -1;
 	os->size = OBJECT_SIZE_DELETE;
 
 	obex = OBEX_Init(OBEX_TRANS_FD, obex_event, 0);
@@ -1047,3 +1054,17 @@ gint obex_tty_session_stop(void)
 
 	return 0;
 }
+
+struct obex_session *obex_get_session(gpointer object)
+{
+	GSList *l;
+
+	for (l = sessions; l; l = l->next) {
+		struct obex_session *os = l->data;
+
+		if (os->object == object)
+			return os;
+	}
+
+	return NULL;
+}
diff --git a/obexd/src/obex.h b/obexd/src/obex.h
index 94a273e..13ecf81 100644
--- a/obexd/src/obex.h
+++ b/obexd/src/obex.h
@@ -31,14 +31,16 @@
 
 #include "phonebook.h"
 
+#define OBJECT_SIZE_UNKNOWN -1
+#define OBJECT_SIZE_DELETE -2
+
 #define OBEX_OPP	(1 << 0)
 #define OBEX_FTP	(1 << 2)
 #define OBEX_BIP	(1 << 3)
 #define OBEX_PBAP	(1 << 4)
 #define OBEX_PCSUITE	(1 << 5)
 
-#define OBJECT_SIZE_UNKNOWN -1
-#define OBJECT_SIZE_DELETE -2
+struct obex_mime_type_driver;
 
 struct obex_commands {
 	void (*get) (obex_t *obex, obex_object_t *obj);
@@ -67,6 +69,7 @@ struct server {
 struct obex_session {
 	GIOChannel	*io;
 	guint32		cid;
+	guint16		services;
 	guint16		tx_mtu;
 	guint16		rx_mtu;
 	uint8_t		cmd;
@@ -77,7 +80,7 @@ struct obex_session {
 	guint8		*buf;
 	gint32		offset;
 	gint32		size;
-	gint		fd;
+	gpointer	object;
 	gboolean	aborted;
 	const guint8	*target;
 	struct obex_commands *cmds;
@@ -85,10 +88,12 @@ struct obex_session {
 	gboolean	checked;
 	obex_t		*obex;
 	struct phonebook_context *pbctx;
+	struct obex_mime_type_driver *driver;
 	gboolean	finished;
 };
 
 gint obex_session_start(GIOChannel *io, struct server *server);
+struct obex_session *obex_get_session(gpointer object);
 gint obex_tty_session_stop(void);
 
 void opp_get(obex_t *obex, obex_object_t *obj);
@@ -106,7 +111,7 @@ gboolean pbap_phonebook_context_create(struct obex_session *session);
 void pbap_phonebook_context_destroy(struct obex_session *session);
 struct obex_session *pbap_get_session(struct phonebook_context *context);
 
-gint os_prepare_get(struct obex_session *os, gchar *file, guint32 *size);
+gint os_prepare_get(struct obex_session *os, gchar *file, size_t *size);
 gint os_prepare_put(struct obex_session *os);
 
 void server_free(struct server *server);
diff --git a/obexd/src/opp.c b/obexd/src/opp.c
index 9a50d68..a8dce13 100644
--- a/obexd/src/opp.c
+++ b/obexd/src/opp.c
@@ -107,7 +107,7 @@ void opp_get(obex_t *obex, obex_object_t *obj)
 {
 	struct obex_session *os;
 	obex_headerdata_t hv;
-	guint32 size;
+	size_t size;
 
 	os = OBEX_GetUserData(obex);
 	if (os == NULL)
diff --git a/obexd/src/plugin.c b/obexd/src/plugin.c
index 6280085..b4c4a11 100644
--- a/obexd/src/plugin.c
+++ b/obexd/src/plugin.c
@@ -74,6 +74,7 @@ static gboolean add_plugin(void *handle, struct obex_plugin_desc *desc)
 	}
 
 	plugins = g_slist_append(plugins, plugin);
+	debug("Plugin %s loaded", desc->name);
 
 	return TRUE;
 }