Index: configure.ac
===================================================================
--- configure.ac	(revision 1170)
+++ configure.ac	(working copy)
@@ -202,6 +202,41 @@
 
 AM_CONDITIONAL(USE_HAL, [test "$msg_hal" = "yes"])
 
+dnl *****************************************************
+dnl *** Check if we should build with obexftp backend ***
+dnl *****************************************************
+AC_ARG_ENABLE(obexftp, [  --disable-obexftp           build without ObexFTP backend])
+msg_obexftp=no
+OBEXFTP_LIBS=
+OBEXFTP_CFLAGS=
+
+if test "x$enable_obexftp" != "xno"; then
+  PKG_CHECK_EXISTS(dbus-glib-1 bluez >= 3.12, msg_obexftp=yes)
+
+  dnl Make sure we have expat
+  AC_CHECK_LIB(expat, XML_ParserCreate_MM,
+               [ AC_CHECK_HEADERS(expat.h, have_expat=true, have_expat=false) ],
+               have_expat=false)
+
+  if test "x$msg_obexftp" == "xyes" -a "x$have_expat" == "xtrue"; then
+    PKG_CHECK_MODULES(OBEXFTP, dbus-glib-1 bluez >= 3.12)
+    AC_SUBST(OBEXFTP_LIBS)
+    AC_SUBST(OBEXFTP_CFLAGS)
+
+    msg_obexftp=yes
+    AC_DEFINE(HAVE_OBEXFTP, 1, [Define to 1 if ObexFTP is going to be built])
+    XML_CFLAGS=""
+    XML_LIBS="-lexpat"
+  else
+    msg_obexftp=no
+  fi
+fi
+
+AC_SUBST(XML_LIBS)
+AC_SUBST(XML_CFLAGS)
+
+AM_CONDITIONAL(USE_OBEXFTP, [test "$msg_obexftp" = "yes"])
+
 dnl ==========================================================================
 dnl Samba 3.0
 
@@ -340,6 +375,7 @@
 echo "gvfs configuration summary:"
 echo "
         HTTP/WebDAV support           $msg_http
+        ObexFTP support               $msg_obexftp
 	Samba support:	              $msg_samba
 	FUSE support:                 $msg_fuse
         CDDA support:                 $msg_cdda
Index: daemon/obexftp-marshal.list
===================================================================
--- daemon/obexftp-marshal.list	(revision 0)
+++ daemon/obexftp-marshal.list	(revision 0)
@@ -0,0 +1,2 @@
+VOID:STRING,STRING,INT
+VOID:STRING,STRING
Index: daemon/obexftp.mount.in
===================================================================
--- daemon/obexftp.mount.in	(revision 0)
+++ daemon/obexftp.mount.in	(revision 0)
@@ -0,0 +1,4 @@
+[Mount]
+Type=obex
+Exec=@libexecdir@/gvfsd-obexftp
+AutoMount=false
Index: daemon/gvfsbackendobexftp-fl-parser.c
===================================================================
--- daemon/gvfsbackendobexftp-fl-parser.c	(revision 0)
+++ daemon/gvfsbackendobexftp-fl-parser.c	(revision 0)
@@ -0,0 +1,399 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004-2005 Nokia Corporation.
+ * Copyright (C) 2008 Bastien Nocera <hadess@hadess.net> (gio port)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <gio/gio.h>
+#include <expat.h>
+
+#include "gvfsbackendobexftp-fl-parser.h"
+
+#define d(x)
+
+typedef struct {
+	GError **error;
+	GList   *elements;
+
+	gint     depth;
+} ParserData;
+
+/* Static functions declaration */
+static void       fl_parser_start_node_cb     (void                *data,
+					       const char          *el,
+					       const char         **attr);
+static void       fl_parser_end_node_cb       (void                *data,
+					       const char          *el);
+static XML_Parser fl_parser_create_context    (ParserData          *data);
+static gboolean   fl_parser_fill_file_info    (GFileInfo           *file_info,
+					       const char         **attr);
+static void       fl_parser_free_parser_data  (ParserData          *data,
+					       gboolean             free_list);
+
+
+/* Function implementations */
+static void 
+fl_parser_start_node_cb (void        *user_data,
+			 const char  *node_name,
+			 const char **attr)
+{
+	ParserData *data;
+	GFileInfo  *info;
+	
+	data = (ParserData *) user_data;
+	
+	data->depth++;
+	
+	d(g_print ("%d: %s\n", data->depth, node_name));
+
+	if (data->depth > 2) {
+		g_set_error (data->error,  
+			     G_MARKUP_ERROR,  
+			     G_MARKUP_ERROR_INVALID_CONTENT,  
+			     "Don't expect node '%s' as child of 'file', 'folder' or 'parent-folder'",  
+			     node_name); 
+		return;
+	}
+	else if (data->depth == 1) {
+		if (strcmp (node_name, "folder-listing") != 0) {
+			g_set_error (data->error,  
+				     G_MARKUP_ERROR,  
+				     G_MARKUP_ERROR_INVALID_CONTENT,  
+				     "Expected 'folder-listing', got '%s'",  
+				     node_name);  
+			return;
+		}
+
+		return;
+	}
+
+	if (strcmp (node_name, "parent-folder") == 0) {
+		/* Just ignore parent-folder items */
+		return;
+	}
+	
+	info = g_file_info_new ();
+
+	if (strcmp (node_name, "file") == 0) {
+		g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
+	}
+	else if (strcmp (node_name, "folder") == 0) {
+		g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+		g_file_info_set_content_type (info, "x-directory/normal");
+	} else {
+		g_set_error (data->error,
+			     G_MARKUP_ERROR,
+			     G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+			     "Unknown element '%s'",
+			     node_name);
+		return;
+	}
+
+	if (!fl_parser_fill_file_info (info, attr)) {
+		d(g_print ("Failed to fill GnomeVFSFileInfo from node '%s'\n",
+			   node_name));
+		g_object_unref (info);
+		return;
+	}
+
+	//FIXME
+#if 0
+	if (info->mime_type == NULL) {
+		info->mime_type = g_strdup (
+			gnome_vfs_mime_type_from_name (info->name));
+	}
+#endif
+	/* Permissions on folders in OBEX has different semantics than POSIX.
+	 * In POSIX, if a folder is not writable, it means that it's content
+	 * can't be removed, whereas in OBEX, it just means that the folder
+	 * itself can't be removed. Therefore we must set all folders to RWD and
+	 * handle the error when it happens. */
+	if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+		g_file_info_set_attribute_boolean (info,
+						   G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+						   TRUE);
+		g_file_info_set_attribute_boolean (info,
+						   G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+						   TRUE);
+	}
+	
+	data->elements = g_list_prepend (data->elements, info);
+}
+
+static void
+fl_parser_end_node_cb (void *user_data, const char *node_name)
+{
+	ParserData *data;
+
+	data = (ParserData *) user_data;
+
+	data->depth--;
+	
+	if (data->depth < 0) {  
+		g_set_error (data->error,  
+			     G_MARKUP_ERROR,  
+			     G_MARKUP_ERROR_INVALID_CONTENT,  
+			     "Closing non-open node '%s'",  
+			     node_name);  
+		return;  
+	} 
+
+	d(g_print ("%d: /%s\n", data->depth, node_name));
+}
+
+static XML_Parser
+fl_parser_create_context (ParserData *data)
+{
+	XML_Parser parser;
+	
+	parser = XML_ParserCreate (NULL);
+	
+	XML_SetElementHandler(parser, 
+			      (XML_StartElementHandler) fl_parser_start_node_cb,
+			      (XML_EndElementHandler) fl_parser_end_node_cb);
+	XML_SetUserData (parser, data);
+
+	return parser;
+}
+
+static gboolean
+fl_parser_fill_file_info (GFileInfo *info, const char **attr)
+{
+	gint i;
+	
+	for (i = 0; attr[i]; ++i) {
+		const gchar *name;
+		const gchar *value;
+
+		name  = attr[i];
+		value = attr[++i];
+		
+		if (strcmp (name, "name") == 0) {
+			/* Apparently someone decided it was a good idea
+			 * to send name="" mem-type="MMC" 
+			 */
+			if (!value || strcmp (value, "") == 0) {
+				return FALSE;
+			}
+
+			g_file_info_set_name (info, value);
+			d(g_print ("Name: '%s'\n", value));
+		}
+		else if (strcmp (name, "size") == 0) {
+			g_file_info_set_size (info, strtoll (value, NULL, 10));
+			d(g_print ("Size: '%"G_GINT64_FORMAT"'\n", g_file_info_get_size (info)));
+		}
+		else if (strcmp (name, "modified") == 0) {
+			GTimeVal time;
+
+			if (g_time_val_from_iso8601 (value, &time) == FALSE)
+				continue;
+			g_file_info_set_modification_time (info, &time);
+			d(g_print ("Modified: '%s' = '%d'\n", 
+				   value, (int)time.tv_sec));
+		}
+		else if (strcmp (name, "created") == 0) {
+			GTimeVal time;
+
+			if (g_time_val_from_iso8601 (value, &time) == FALSE)
+				continue;
+			g_file_info_set_attribute_uint64 (info,
+							  G_FILE_ATTRIBUTE_TIME_CREATED,
+							  time.tv_sec);
+			g_file_info_set_attribute_uint32 (info,
+							  G_FILE_ATTRIBUTE_TIME_CREATED_USEC,
+							  time.tv_usec);
+			d(g_print ("Created: '%s' = '%d'\n", 
+				   value, (int)time.tv_sec));
+		}
+		else if (strcmp (name, "accessed") == 0) {
+			GTimeVal time;
+
+			if (g_time_val_from_iso8601 (value, &time) == FALSE)
+				continue;
+			g_file_info_set_attribute_uint64 (info,
+							  G_FILE_ATTRIBUTE_TIME_ACCESS,
+							  time.tv_sec);
+			g_file_info_set_attribute_uint32 (info,
+							  G_FILE_ATTRIBUTE_TIME_ACCESS_USEC,
+							  time.tv_usec);
+			d(g_print ("Accessed: '%s' = '%d'\n", 
+				   value, (int)time.tv_sec));
+		}
+		else if (strcmp (name, "user-perm") == 0) {
+			/* The permissions don't map well to unix semantics,
+			 * since the user is most likely not the same on both
+			 * sides. We map the user permissions to "other" on the
+			 * local side. D is treated as write, otherwise files
+			 * can't be deleted through the module, even if it
+			 * should be possible.
+			 */
+			if (strstr (value, "R")) {
+				g_file_info_set_attribute_boolean (info,
+								   G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+								   TRUE);
+			}
+			if (strstr (value, "W") || strstr (value, "D")) {
+				g_file_info_set_attribute_boolean (info,
+								   G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+								   TRUE);
+			}
+		}
+		else if (strcmp (name, "group-perm") == 0) {
+			/* Ignore for now */
+			d(g_print ("Group permissions: '%s'\n", value));
+		}
+		else if (strcmp (name, "other-perm") == 0) {
+			/* Ignore for now */
+			d(g_print ("Other permissions: '%s'\n", value));
+		}
+		else if (strcmp (name, "owner") == 0) {
+			/* Ignore for now */
+			d(g_print ("Owner: '%s'\n", value));
+		}
+		else if (strcmp (name, "group") == 0) {
+			/* Ignore for now */
+			d(g_print ("Group: '%s'\n", value));
+		}
+		else if (strcmp (name, "type") == 0) {
+			g_file_info_set_content_type (info, value);
+			d(g_print ("Mime-Type: '%s'\n", value));
+		}
+		else if (strcmp (name, "xml:lang") == 0) {
+			d(g_print ("Lang: '%s'\n", value));
+		}
+		else if (strcmp (name, "mem-type") == 0) {
+			guint device;
+#if 0
+			if (value == NULL || value[0] == '\0')
+				continue;
+			info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_DEVICE;
+			info->device = om_mem_type_id_from_string (value);
+#endif
+			device = om_mem_type_id_from_string (value);
+			d(g_print ("Mem-Type: '%s' (%d)\n",
+				   value, device));
+		}
+		else {
+			d(g_print ("Unknown Attribute: %s = %s\n",
+				   name, value));
+		}
+	}
+
+	if (g_file_info_get_name (info) == NULL) { /* Required attribute */
+		/* Set error */
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+static void
+fl_parser_free_parser_data (ParserData *data, gboolean free_list)
+{
+	if (free_list) {
+		g_list_foreach (data->elements, (GFunc) g_object_unref, NULL);
+		g_list_free (data->elements);
+		data->elements = NULL;
+	}
+
+	g_free (data);
+}
+
+gboolean
+gvfsbackendobexftp_fl_parser_parse (const gchar *buf, gint len, GList **elements,
+				    GError **error)
+{
+	ParserData *data;
+	XML_Parser  parser;
+
+	data = g_new0 (ParserData, 1);
+	data->error = error;
+	data->elements = NULL;
+	data->depth = 0;
+
+	parser = fl_parser_create_context (data);
+	if (!parser) {
+		g_free (data);
+		return FALSE;
+	}
+
+	if (XML_Parse (parser, buf, len, TRUE) == 0) {
+		XML_ParserFree (parser);
+		fl_parser_free_parser_data (data, TRUE);
+
+		if (*error == NULL) {
+			g_set_error (error,
+				     G_MARKUP_ERROR,
+				     G_MARKUP_ERROR_INVALID_CONTENT,
+				     "Couldn't parse the incoming data");
+		}
+		return FALSE;
+	}
+
+	XML_ParserFree (parser);
+	
+	*elements = data->elements;
+
+	fl_parser_free_parser_data (data, FALSE);
+		
+	return TRUE;
+}
+
+static GPtrArray *mem_types = NULL;
+static GHashTable *mem_types_ht = NULL;
+
+guint
+om_mem_type_id_from_string (const gchar *memtype)
+{
+	guint mem_id;
+	gchar *value;
+
+	if (memtype == NULL || memtype[0] == '\0')
+		return 0;
+
+	if (mem_types_ht != NULL) {
+		mem_id = GPOINTER_TO_UINT (g_hash_table_lookup
+					   (mem_types_ht, memtype));
+		if (mem_id != 0)
+			return mem_id;
+	} else {
+		mem_types = g_ptr_array_new ();
+		/* Insert a dummy entry, so that we don't use 0 as a mem_id */
+		g_ptr_array_add (mem_types, NULL);
+		mem_types_ht = g_hash_table_new (g_str_hash, g_str_equal);
+	}
+	value = g_strdup (memtype);
+	mem_id = mem_types->len;
+	g_ptr_array_add (mem_types, value);
+	g_hash_table_insert (mem_types_ht, value, GUINT_TO_POINTER (mem_id));
+	return mem_id;
+}
+
+const gchar *
+om_mem_type_id_to_string (guint mem_id)
+{
+	if (mem_types == NULL || mem_id >= mem_types->len)
+		return NULL;
+	else
+		return g_ptr_array_index (mem_types, mem_id);
+}
Index: daemon/gvfsbackendobexftp-fl-parser.h
===================================================================
--- daemon/gvfsbackendobexftp-fl-parser.h	(revision 0)
+++ daemon/gvfsbackendobexftp-fl-parser.h	(revision 0)
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004 Nokia Corporation.
+ * Copyright (C) 2008 Bastien Nocera <hadess@hadess.net> (gio port)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GVFSBACKENDOBEXFTP_FL_PARSER_H__
+#define __GVFSBACKENDOBEXFTP_FL_PARSER_H__
+
+#include <glib.h>
+
+gboolean     gvfsbackendobexftp_fl_parser_parse             (const gchar *buf,
+							     gint         len,
+							     GList      **elements,
+							     GError     **error);
+void         gvfsbackendobexftp_fl_parser_free_element_list (GSList      *elements);
+
+
+
+guint        om_mem_type_id_from_string     (const gchar *memtype);
+const gchar *om_mem_type_id_to_string       (guint        mem_id);
+
+#endif /* __GVFSBACKENDOBEXFTP_FL_PARSER_H__ */
Index: daemon/gvfsbackendobexftp.c
===================================================================
--- daemon/gvfsbackendobexftp.c	(revision 0)
+++ daemon/gvfsbackendobexftp.c	(revision 0)
@@ -0,0 +1,1407 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <dbus/dbus-glib.h>
+#include <bluetooth/bluetooth.h>
+
+#include "gvfsbackendobexftp.h"
+#include "gvfsbackendobexftp-fl-parser.h"
+#include "obexftp-marshal.h"
+
+#include "gvfsjobopenforread.h"
+#include "gvfsjobread.h"
+#include "gvfsjobseekread.h"
+#include "gvfsjobopenforwrite.h"
+#include "gvfsjobwrite.h"
+#include "gvfsjobclosewrite.h"
+#include "gvfsjobseekwrite.h"
+#include "gvfsjobsetdisplayname.h"
+#include "gvfsjobqueryinfo.h"
+#include "gvfsjobqueryfsinfo.h"
+#include "gvfsjobqueryattributes.h"
+#include "gvfsjobenumerate.h"
+#include "gvfsdaemonprotocol.h"
+
+#define BDADDR_LEN 17
+
+struct _GVfsBackendObexftp
+{
+  GVfsBackend parent_instance;
+
+  /* The bdaddr is checked to be valid in try_mount */
+  char *bdaddr;
+
+  DBusGConnection *connection;
+  DBusGProxy *manager_proxy;
+  DBusGProxy *session_proxy;
+};
+
+G_DEFINE_TYPE (GVfsBackendObexftp, g_vfs_backend_obexftp, G_VFS_TYPE_BACKEND);
+
+static gchar *
+get_device_name (const char *bdaddr)
+{
+	DBusGConnection *connection;
+	DBusGProxy *manager;
+	gchar *name, **adapters;
+	guint i;
+
+	name = NULL;
+
+	connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
+	if (connection == NULL)
+		return NULL;
+
+	manager = dbus_g_proxy_new_for_name (connection, "org.bluez",
+					    "/org/bluez", "org.bluez.Manager");
+	if (manager == NULL) {
+		dbus_g_connection_unref (connection);
+		return NULL;
+	}
+
+	if (dbus_g_proxy_call (manager, "ListAdapters", NULL, G_TYPE_INVALID, G_TYPE_STRV, &adapters, G_TYPE_INVALID) == FALSE) {
+		g_object_unref (manager);
+		dbus_g_connection_unref (connection);
+		return NULL;
+	}
+
+	for (i = 0; adapters[i] != NULL; i++) {
+		DBusGProxy *adapter;
+
+		adapter = dbus_g_proxy_new_for_name (connection, "org.bluez",
+						    adapters[i], "org.bluez.Adapter");
+		if (dbus_g_proxy_call (adapter, "GetRemoteName", NULL,
+				   G_TYPE_STRING, bdaddr, G_TYPE_INVALID,
+				   G_TYPE_STRING, &name, G_TYPE_INVALID) != FALSE) {
+			if (name != NULL && name[0] != '\0') {
+				g_object_unref (adapter);
+				break;
+			}
+		}
+		g_object_unref (adapter);
+	}
+
+	g_object_unref (manager);
+	dbus_g_connection_unref (connection);
+
+	return name;
+}
+
+static void
+g_vfs_backend_obexftp_finalize (GObject *object)
+{
+  GVfsBackendObexftp *backend;
+
+  backend = G_VFS_BACKEND_OBEXFTP (object);
+
+  if (backend->session_proxy != NULL)
+	  g_object_unref (backend->session_proxy);
+  g_free (backend->bdaddr);
+
+  if (G_OBJECT_CLASS (g_vfs_backend_obexftp_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_vfs_backend_obexftp_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_backend_obexftp_init (GVfsBackendObexftp *backend)
+{
+	GError *err = NULL;
+
+	backend->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &err);
+	if (backend->connection == NULL) {
+		g_printerr ("Connecting to session bus failed: %s\n", err->message);
+		g_error_free (err);
+		return;
+	}
+
+	backend->manager_proxy = dbus_g_proxy_new_for_name (backend->connection,
+							    "org.openobex",
+							    "/org/openobex",
+							    "org.openobex.Manager");
+}
+
+#if 0
+#define SUB_DELIM_CHARS  "!$&'()*+,;="
+
+static gboolean
+is_valid (char c, const char *reserved_chars_allowed)
+{
+  if (g_ascii_isalnum (c) ||
+      c == '-' ||
+      c == '.' ||
+      c == '_' ||
+      c == '~')
+    return TRUE;
+
+  if (reserved_chars_allowed &&
+      strchr (reserved_chars_allowed, c) != NULL)
+    return TRUE;
+  
+  return FALSE;
+}
+
+static void
+g_string_append_encoded (GString *string,
+			 const char *encoded,
+			 const char *reserved_chars_allowed)
+{
+  char c;
+  static const gchar hex[16] = "0123456789ABCDEF";
+  
+  while ((c = *encoded++) != 0)
+    {
+      if (is_valid (c, reserved_chars_allowed))
+	g_string_append_c (string, c);
+      else
+	{
+	  g_string_append_c (string, '%');
+	  g_string_append_c (string, hex[((guchar)c) >> 4]);
+	  g_string_append_c (string, hex[((guchar)c) & 0xf]);
+	}
+    }
+}
+#endif
+static GString *
+create_obexftp_uri_string (const char *bdaddr,
+			   const char *path)
+{
+  GString *uri;
+
+  uri = g_string_new ("obex://");
+  g_string_append_c (uri, '[');
+  g_string_append (uri, bdaddr);
+  g_string_append_c (uri, ']');
+  g_string_append (uri, path);
+
+  return uri;
+}
+
+static char *
+create_obexftp_uri (const char *bdaddr,
+		    const char *path)
+{
+  GString *uri;
+  uri = create_obexftp_uri_string (bdaddr, path);
+  return g_string_free (uri, FALSE);
+}
+
+static gboolean
+same_paths (const char *filename, const char *current_path)
+{
+	GFile *req, *cur;
+	gboolean retval;
+
+	req = g_file_new_for_path (filename);
+	cur = g_file_new_for_path (current_path);
+	retval = g_file_equal (req, cur);
+	g_object_unref (req);
+	g_object_unref (cur);
+
+	return retval;
+}
+
+static gboolean
+do_change_directory (GVfsBackendObexftp *op_backend,
+		     const char *filename,
+		     GError **error)
+{
+	char *current_path, **req_components;
+	guint i;
+
+	if (dbus_g_proxy_call (op_backend->session_proxy, "GetCurrentPath", error,
+			       G_TYPE_INVALID,
+			       G_TYPE_STRING, &current_path, G_TYPE_INVALID) == FALSE) {
+		g_message ("GetCurrentPath failed");
+		return FALSE;
+	}
+
+	if (same_paths (filename, current_path) == TRUE) {
+		g_free (current_path);
+		return TRUE;
+	}
+
+	/* Are we already at the root? */
+	if (strcmp (current_path, "/") != 0) {
+		if (dbus_g_proxy_call (op_backend->session_proxy, "ChangeCurrentFolderToRoot", error,
+				       G_TYPE_INVALID,
+				       G_TYPE_INVALID) == FALSE) {
+			g_message ("ChangeCurrentFolderToRoot failed");
+			return FALSE;
+		}
+	}
+	g_free (current_path);
+
+	/* If we asked for the root, we're done */
+	if (strcmp (filename, "/") == 0)
+		return TRUE;
+
+	req_components = g_strsplit (filename, "/", -1);
+	for (i = 0; req_components[i] != NULL; i++) {
+		if (*req_components[i] == '\0')
+			continue;
+		if (dbus_g_proxy_call (op_backend->session_proxy, "ChangeCurrentFolder", error,
+				       G_TYPE_STRING, req_components[i], G_TYPE_INVALID,
+				       G_TYPE_INVALID) == FALSE) {
+			g_message ("ChangeCurrentFolder failed");
+			g_strfreev (req_components);
+			return FALSE;
+		}
+	}
+
+	g_strfreev (req_components);
+
+	return TRUE;
+}
+
+static void session_created_cb (DBusGProxy *proxy,
+				DBusGProxyCall *call,
+				GVfsJobMount *job)
+{
+	GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (G_VFS_JOB (job)->backend_data);
+	GError *error = NULL;
+	const gchar *path = NULL;
+	char *display_name, *server;
+	GMountSpec *obexftp_mount_spec;
+
+	if (dbus_g_proxy_end_call (op_backend->manager_proxy, call, &error,
+				   G_TYPE_STRING, &path, G_TYPE_INVALID) == FALSE) {
+		g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+		g_error_free (error);
+		return;
+	}
+
+	op_backend->session_proxy = dbus_g_proxy_new_for_name (op_backend->connection,
+							       "org.openobex",
+							       path,
+							       "org.openobex.Session");
+
+	display_name = get_device_name (op_backend->bdaddr);
+	g_vfs_backend_set_display_name (G_VFS_BACKEND  (op_backend),
+					display_name ? display_name : op_backend->bdaddr);
+	g_free (display_name);
+	g_vfs_backend_set_icon_name (G_VFS_BACKEND (op_backend), "folder-remote");
+
+	obexftp_mount_spec = g_mount_spec_new ("obex");
+	server = g_strdup_printf ("[%s]", op_backend->bdaddr);
+	g_mount_spec_set (obexftp_mount_spec, "host", server);
+	g_free (server);
+	g_vfs_backend_set_mount_spec (G_VFS_BACKEND (op_backend), obexftp_mount_spec);
+	g_mount_spec_unref (obexftp_mount_spec);
+
+	g_vfs_job_succeeded (G_VFS_JOB (job));
+
+	g_message ("session_created_cb");
+
+	//FIXME do shit with the session
+}
+
+static gboolean
+try_mount (GVfsBackend *backend,
+	   GVfsJobMount *job,
+	   GMountSpec *mount_spec,
+	   GMountSource *mount_source,
+	   gboolean is_automount)
+{
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  const char *device;
+  char *bdaddr;
+
+  g_message ("try_mount");
+
+  device = g_mount_spec_get (mount_spec, "host");
+
+  if (device == NULL || strlen (device) != BDADDR_LEN + 2)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+			G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+			_("Invalid mount spec"));
+      return TRUE;
+    }
+
+  /* Strip the brackets */
+  bdaddr = g_strndup (device + 1, 17);
+  if (bachk (bdaddr) < 0)
+    {
+       g_free (bdaddr);
+       g_vfs_job_failed (G_VFS_JOB (job),
+			G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+			_("Invalid mount spec"));
+      return TRUE;
+    }
+
+  op_backend->bdaddr = bdaddr;
+
+  dbus_g_proxy_begin_call (op_backend->manager_proxy, "CreateBluetoothSession",
+  			   (DBusGProxyCallNotify) session_created_cb, job, NULL,
+  			   G_TYPE_STRING, bdaddr, G_TYPE_STRING, "ftp",
+  			   G_TYPE_INVALID);
+
+  g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);
+
+  return TRUE;
+}
+
+static void 
+do_open_for_read (GVfsBackend *backend,
+		  GVfsJobOpenForRead *job,
+		  const char *filename)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  char *uri;
+  OBEXFTPCFILE *file;
+
+  uri = create_obexftp_uri (op_backend->server, op_backend->share, filename);
+  file = op_backend->obexftp_context->open (op_backend->obexftp_context, uri, O_RDONLY, 0);
+  g_free (uri);
+
+  if (file == NULL)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    {
+      g_vfs_job_open_for_read_set_can_seek (job, TRUE);
+      g_vfs_job_open_for_read_set_handle (job, file);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+#endif
+}
+
+static void
+do_read (GVfsBackend *backend,
+	 GVfsJobRead *job,
+	 GVfsBackendHandle handle,
+	 char *buffer,
+	 gsize bytes_requested)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  ssize_t res;
+
+  res = op_backend->obexftp_context->read (op_backend->obexftp_context, (OBEXFTPCFILE *)handle, buffer, bytes_requested);
+
+  if (res == -1)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    {
+      g_vfs_job_read_set_size (job, res);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+	    
+    }
+#endif
+}
+
+static void
+do_seek_on_read (GVfsBackend *backend,
+		 GVfsJobSeekRead *job,
+		 GVfsBackendHandle handle,
+		 goffset    offset,
+		 GSeekType  type)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  int whence;
+  off_t res;
+
+  switch (type)
+    {
+    case G_SEEK_SET:
+      whence = SEEK_SET;
+      break;
+    case G_SEEK_CUR:
+      whence = SEEK_CUR;
+      break;
+    case G_SEEK_END:
+      whence = SEEK_END;
+      break;
+    default:
+      g_vfs_job_failed (G_VFS_JOB (job),
+			G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Unsupported seek type"));
+      return;
+    }
+
+  res = op_backend->obexftp_context->lseek (op_backend->obexftp_context, (OBEXFTPCFILE *)handle, offset, whence);
+
+  if (res == (off_t)-1)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    {
+      g_vfs_job_seek_read_set_offset (job, res);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+
+  return;
+#endif
+}
+
+static void
+do_close_read (GVfsBackend *backend,
+	       GVfsJobCloseRead *job,
+	       GVfsBackendHandle handle)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  ssize_t res;
+
+  res = op_backend->obexftp_context->close_fn (op_backend->obexftp_context, (OBEXFTPCFILE *)handle);
+  if (res == -1)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+#endif
+}
+
+#if 0
+typedef struct {
+  OBEXFTPCFILE *file;
+  char *uri;
+  char *tmp_uri;
+  char *backup_uri;
+} ObexftpWriteHandle;
+
+static void
+obexftp_write_handle_free (ObexftpWriteHandle *handle)
+{
+  g_free (handle->uri);
+  g_free (handle->tmp_uri);
+  g_free (handle->backup_uri);
+  g_free (handle);
+}
+#endif
+static void
+do_create (GVfsBackend *backend,
+	   GVfsJobOpenForWrite *job,
+	   const char *filename,
+	   GFileCreateFlags flags)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  char *uri;
+  OBEXFTPCFILE *file;
+  ObexftpWriteHandle *handle;
+
+  uri = create_obexftp_uri (op_backend->server, op_backend->share, filename);
+  file = op_backend->obexftp_context->open (op_backend->obexftp_context, uri,
+					O_CREAT|O_WRONLY|O_EXCL, 0666);
+  g_free (uri);
+
+  if (file == NULL)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    {
+      handle = g_new0 (ObexftpWriteHandle, 1);
+      handle->file = file;
+
+      g_vfs_job_open_for_write_set_can_seek (job, TRUE);
+      g_vfs_job_open_for_write_set_handle (job, handle);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+#endif
+}
+
+static void
+do_append_to (GVfsBackend *backend,
+	      GVfsJobOpenForWrite *job,
+	      const char *filename,
+	      GFileCreateFlags flags)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  char *uri;
+  OBEXFTPCFILE *file;
+  ObexftpWriteHandle *handle;
+  off_t initial_offset;
+
+  uri = create_obexftp_uri (op_backend->server, op_backend->share, filename);
+  file = op_backend->obexftp_context->open (op_backend->obexftp_context, uri,
+					O_CREAT|O_WRONLY|O_APPEND, 0666);
+  g_free (uri);
+
+  if (file == NULL)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    {
+      handle = g_new0 (ObexftpWriteHandle, 1);
+      handle->file = file;
+
+      initial_offset = op_backend->obexftp_context->lseek (op_backend->obexftp_context, file,
+						       0, SEEK_CUR);
+      if (initial_offset == (off_t) -1)
+	g_vfs_job_open_for_write_set_can_seek (job, FALSE);
+      else
+	{
+	  g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
+	  g_vfs_job_open_for_write_set_can_seek (job, TRUE);
+	}
+      g_vfs_job_open_for_write_set_handle (job, handle);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+#endif
+}
+
+
+#if 0
+static void
+random_chars (char *str, int len)
+{
+  int i;
+  const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+  for (i = 0; i < len; i++)
+    str[i] = chars[g_random_int_range (0, strlen(chars))];
+}
+
+static char *
+get_dir_from_uri (const char *uri)
+{
+  const char *prefix_end;
+
+  prefix_end = uri + strlen (uri);
+
+  /* Skip slashes at end */
+  while (prefix_end > uri &&
+	 *(prefix_end - 1) == '/')
+    prefix_end--;
+
+  /* Skip to next slash */
+  while (prefix_end > uri &&
+	 *(prefix_end - 1) != '/')
+    prefix_end--;
+
+  return g_strndup (uri, prefix_end - uri);
+}
+
+static OBEXFTPCFILE *
+open_tmpfile (GVfsBackendObexftp *backend,
+	      const char *uri,
+	      char **tmp_uri_out)
+{
+  char *dir_uri, *tmp_uri;
+  char filename[] = "~gvfXXXX.tmp";
+  OBEXFTPCFILE *file;
+
+  dir_uri = get_dir_from_uri (uri);
+ 
+  do {
+    random_chars (filename + 4, 4);
+    tmp_uri = g_strconcat (dir_uri, filename, NULL);
+
+    file = backend->obexftp_context->open (backend->obexftp_context, tmp_uri,
+				       O_CREAT|O_WRONLY|O_EXCL, 0666);
+  } while (file == NULL && errno == EEXIST);
+
+  g_free (dir_uri);
+  
+  if (file)
+    {
+      *tmp_uri_out = tmp_uri;
+      return file;
+    }
+  else
+    {
+      g_free (tmp_uri);
+      return NULL;
+    }
+}
+#endif
+
+static gboolean
+copy_file (GVfsBackendObexftp *backend,
+	   GVfsJob *job,
+	   const char *from_uri,
+	   const char *to_uri)
+{
+#if 0
+  OBEXFTPCFILE *from_file, *to_file;
+  char buffer[4096];
+  size_t buffer_size;
+  ssize_t res;
+  char *p;
+  gboolean succeeded;
+  
+
+  from_file = NULL;
+  to_file = NULL;
+
+  succeeded = FALSE;
+  
+  from_file = backend->obexftp_context->open (backend->obexftp_context, from_uri,
+					  O_RDONLY, 0666);
+  if (from_file == NULL || g_vfs_job_is_cancelled (job))
+    goto out;
+  
+  to_file = backend->obexftp_context->open (backend->obexftp_context, to_uri,
+					O_CREAT|O_WRONLY|O_TRUNC, 0666);
+
+  if (from_file == NULL || g_vfs_job_is_cancelled (job))
+    goto out;
+
+  while (1)
+    {
+      
+      res = backend->obexftp_context->read (backend->obexftp_context, from_file,
+					buffer, sizeof(buffer));
+      if (res < 0 || g_vfs_job_is_cancelled (job))
+	goto out;
+      if (res == 0)
+	break; /* Succeeded */
+
+      buffer_size = res;
+      p = buffer;
+      while (buffer_size > 0)
+	{
+	  res = backend->obexftp_context->write (backend->obexftp_context, to_file,
+					     p, buffer_size);
+	  if (res < 0 || g_vfs_job_is_cancelled (job))
+	    goto out;
+	  buffer_size -= res;
+	  p += res;
+	}
+    }
+  succeeded = TRUE;
+ 
+ out: 
+  if (to_file)
+    backend->obexftp_context->close_fn (backend->obexftp_context, to_file);
+  if (from_file)
+    backend->obexftp_context->close_fn (backend->obexftp_context, from_file);
+  return succeeded;
+#endif
+}
+
+#if 0
+static char *
+create_etag (struct stat *statbuf)
+{
+  return g_strdup_printf ("%lu", (long unsigned int)statbuf->st_mtime);
+}
+#endif
+static void
+do_replace (GVfsBackend *backend,
+	    GVfsJobOpenForWrite *job,
+	    const char *filename,
+	    const char *etag,
+	    gboolean make_backup,
+	    GFileCreateFlags flags)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  struct stat original_stat;
+  int res;
+  char *uri, *tmp_uri, *backup_uri, *current_etag;
+  OBEXFTPCFILE *file;
+  GError *error = NULL;
+  ObexftpWriteHandle *handle;
+
+  uri = create_obexftp_uri (op_backend->server, op_backend->share, filename);
+  tmp_uri = NULL;
+  if (make_backup)
+    backup_uri = g_strconcat (uri, "~", NULL);
+  else
+    backup_uri = NULL;
+  
+  file = op_backend->obexftp_context->open (op_backend->obexftp_context, uri,
+					O_CREAT|O_WRONLY|O_EXCL, 0);
+  if (file == NULL && errno != EEXIST)
+    {
+      g_set_error (&error, G_IO_ERROR,
+		   g_io_error_from_errno (errno),
+		   g_strerror (errno));
+      goto error;
+    }
+  else if (file == NULL && errno == EEXIST)
+    {
+      if (etag != NULL)
+	{
+	  res = op_backend->obexftp_context->stat (op_backend->obexftp_context, uri, &original_stat);
+	  
+	  if (res == 0)
+	    {
+	      current_etag = create_etag (&original_stat);
+	      if (strcmp (etag, current_etag) != 0)
+		{
+		  g_free (current_etag);
+		  g_set_error (&error,
+			       G_IO_ERROR,
+			       G_IO_ERROR_WRONG_ETAG,
+			       _("The file was externally modified"));
+		  goto error;
+		}
+	      g_free (current_etag);
+	    }
+	}
+      
+      /* Backup strategy:
+       *
+       * By default we:
+       *  1) save to a tmp file (that doesn't exist already)
+       *  2) rename orig file to backup file
+       *     (or delete it if no backup)
+       *  3) rename tmp file to orig file
+       *
+       * However, this can fail if we can't write to the directory.
+       * In that case we just truncate the file, after having 
+       * copied directly to the backup filename.
+       */
+
+      file = open_tmpfile (op_backend, uri, &tmp_uri);
+      if (file == NULL)
+	{
+	  if (make_backup)
+	    {
+	      if (!copy_file (op_backend, G_VFS_JOB (job), uri, backup_uri))
+		{
+		  if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
+		    g_set_error (&error,
+				 G_IO_ERROR,
+				 G_IO_ERROR_CANCELLED,
+				 _("Operation was cancelled"));
+		  else
+		    g_set_error (&error,
+				 G_IO_ERROR,
+				 G_IO_ERROR_CANT_CREATE_BACKUP,
+				 _("Backup file creation failed"));
+		  goto error;
+		}
+	      g_free (backup_uri);
+	      backup_uri = NULL;
+	    }
+	  
+	  file = op_backend->obexftp_context->open (op_backend->obexftp_context, uri,
+						O_CREAT|O_WRONLY|O_TRUNC, 0);
+	  if (file == NULL)
+	    {
+	      g_set_error (&error, G_IO_ERROR,
+			   g_io_error_from_errno (errno),
+			   g_strerror (errno));
+	      goto error;
+	    }
+	}
+    }
+  else
+    {
+      /* Doesn't exist. Just write away */
+      g_free (backup_uri);
+      backup_uri = NULL;
+    }
+
+  handle = g_new (ObexftpWriteHandle, 1);
+  handle->file = file;
+  handle->uri = uri;
+  handle->tmp_uri = tmp_uri;
+  handle->backup_uri = backup_uri;
+  
+  g_vfs_job_open_for_write_set_can_seek (job, TRUE);
+  g_vfs_job_open_for_write_set_handle (job, handle);
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+  
+  return;
+  
+ error:
+  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+  g_error_free (error);
+  g_free (backup_uri);
+  g_free (tmp_uri);
+  g_free (uri);
+#endif
+}
+
+
+static void
+do_write (GVfsBackend *backend,
+	  GVfsJobWrite *job,
+	  GVfsBackendHandle _handle,
+	  char *buffer,
+	  gsize buffer_size)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  ObexftpWriteHandle *handle = _handle;
+  ssize_t res;
+
+  res = op_backend->obexftp_context->write (op_backend->obexftp_context, handle->file,
+					buffer, buffer_size);
+  if (res == -1)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    {
+      g_vfs_job_write_set_written_size (job, res);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+#endif
+}
+
+static void
+do_seek_on_write (GVfsBackend *backend,
+		  GVfsJobSeekWrite *job,
+		  GVfsBackendHandle _handle,
+		  goffset    offset,
+		  GSeekType  type)
+{
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  ObexftpWriteHandle *handle = _handle;
+  int whence;
+  off_t res;
+
+  switch (type)
+    {
+    case G_SEEK_SET:
+      whence = SEEK_SET;
+      break;
+    case G_SEEK_CUR:
+      whence = SEEK_CUR;
+      break;
+    case G_SEEK_END:
+      whence = SEEK_END;
+      break;
+    default:
+      g_vfs_job_failed (G_VFS_JOB (job),
+			G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Unsupported seek type"));
+      return;
+    }
+
+  res = op_backend->obexftp_context->lseek (op_backend->obexftp_context, handle->file, offset, whence);
+
+  if (res == (off_t)-1)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+  else
+    {
+      g_vfs_job_seek_write_set_offset (job, res);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+
+  return;
+}
+
+static void
+do_close_write (GVfsBackend *backend,
+		GVfsJobCloseWrite *job,
+		GVfsBackendHandle _handle)
+{
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  ObexftpWriteHandle *handle = _handle;
+  struct stat stat_at_close;
+  int stat_res;
+  ssize_t res;
+
+  stat_res = op_backend->obexftp_context->fstat (op_backend->obexftp_context, handle->file, &stat_at_close);
+  
+  res = op_backend->obexftp_context->close_fn (op_backend->obexftp_context, handle->file);
+
+  if (res == -1)
+    {
+      g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+      
+      if (handle->tmp_uri)
+	op_backend->obexftp_context->unlink (op_backend->obexftp_context, handle->tmp_uri);
+      goto out;
+    }
+
+  if (handle->tmp_uri)
+    {
+      if (handle->backup_uri)
+	{
+	  res = op_backend->obexftp_context->rename (op_backend->obexftp_context, handle->uri,
+						 op_backend->obexftp_context, handle->backup_uri);
+	  if (res ==  -1)
+	    {
+	      op_backend->obexftp_context->unlink (op_backend->obexftp_context, handle->tmp_uri);
+	      g_vfs_job_failed (G_VFS_JOB (job),
+				G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
+				_("Backup file creation failed: %d"), errno);
+	      goto out;
+	    }
+	}
+      else
+	op_backend->obexftp_context->unlink (op_backend->obexftp_context, handle->uri);
+      
+      res = op_backend->obexftp_context->rename (op_backend->obexftp_context, handle->tmp_uri,
+					     op_backend->obexftp_context, handle->uri);
+      if (res ==  -1)
+	{
+	  op_backend->obexftp_context->unlink (op_backend->obexftp_context, handle->tmp_uri);
+	  g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+	  goto out;
+	}
+    }
+  
+  if (stat_res == 0)
+    {
+      char *etag;
+      etag = create_etag (&stat_at_close);
+      g_vfs_job_close_write_set_etag (job, etag);
+      g_free (etag);
+    }
+  
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ out:
+  obexftp_write_handle_free (handle);  
+#endif
+}
+
+static void
+do_query_info (GVfsBackend *backend,
+	       GVfsJobQueryInfo *job,
+	       const char *filename,
+	       GFileQueryInfoFlags flags,
+	       GFileInfo *info,
+	       GFileAttributeMatcher *matcher)
+{
+	GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+	char *parent, *basename, *files;
+	GList *elements, *l;
+	GError *error = NULL;
+	gboolean found;
+
+	parent = g_path_get_dirname (filename);
+	if (strcmp (parent, ".") == 0) {
+		/* FIXME that happens when you want '/'
+		 * and we don't have any info about it :( */
+		g_free (parent);
+		parent = g_strdup ("/");
+	}
+
+	if (do_change_directory (op_backend, parent, &error) == FALSE) {
+		g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+		g_free (parent);
+		g_error_free (error);
+		return;
+	}
+
+	g_free (parent);
+	files = NULL;
+	if (dbus_g_proxy_call (op_backend->session_proxy, "RetrieveFolderListing", &error,
+			       G_TYPE_INVALID,
+			       G_TYPE_STRING, &files, G_TYPE_INVALID) == FALSE) {
+		g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+		g_error_free (error);
+		return;
+	}
+
+	if (gvfsbackendobexftp_fl_parser_parse (files, strlen (files), &elements, &error) == FALSE) {
+		g_message ("gvfsbackendobexftp_fl_parser_parse failed");
+		g_free (files);
+		g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+		g_error_free (error);
+		return;
+	}
+	g_free (files);
+
+	basename = g_path_get_basename (filename);
+	found = FALSE;
+
+	for (l = elements; l != NULL; l = l->next) {
+		if (strcmp (basename, g_file_info_get_name (l->data)) == 0) {
+			g_file_info_copy_into (l->data, info);
+			g_vfs_job_succeeded (G_VFS_JOB (job));
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (found == FALSE)
+		g_vfs_job_failed_from_errno (G_VFS_JOB (job), ENOENT);
+
+	g_free (basename);
+	g_list_foreach (elements, (GFunc)g_object_unref, NULL);
+	g_list_free (elements);
+
+	return;
+}
+
+static void
+do_query_fs_info (GVfsBackend *backend,
+		  GVfsJobQueryFsInfo *job,
+		  const char *filename,
+		  GFileInfo *info,
+		  GFileAttributeMatcher *attribute_matcher)
+{
+	g_message ("do_query_fs_info: %s", filename);
+#if 0
+  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
+  
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+#endif
+}
+
+
+static gboolean
+try_query_settable_attributes (GVfsBackend *backend,
+			       GVfsJobQueryAttributes *job,
+			       const char *filename)
+{
+	g_message ("try_query_settable_attributes");
+#if 0
+  GFileAttributeInfoList *list;
+
+  list = g_file_attribute_info_list_new ();
+
+  /* TODO: Add all settable attributes here */
+  /*
+  g_file_attribute_info_list_add (list,
+				  "obexftp:test",
+				  G_FILE_ATTRIBUTE_TYPE_UINT32);
+  */
+
+  g_vfs_job_query_attributes_set_list (job, list);
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+  
+  g_file_attribute_info_list_unref (list);
+  return TRUE;
+#endif
+}
+
+static void
+do_enumerate (GVfsBackend *backend,
+	      GVfsJobEnumerate *job,
+	      const char *filename,
+	      GFileAttributeMatcher *matcher,
+	      GFileQueryInfoFlags flags)
+{
+	GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+	GError *error = NULL;
+	char *files;
+	GList *elements = NULL;
+
+	g_print ("+ do_enumerate, filename: %s\n", filename);
+
+	if (do_change_directory (op_backend, filename, &error) == FALSE) {
+		g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+		g_error_free (error);
+		return;
+	}
+
+	files = NULL;
+	if (dbus_g_proxy_call (op_backend->session_proxy, "RetrieveFolderListing", &error,
+			       G_TYPE_INVALID,
+			       G_TYPE_STRING, &files, G_TYPE_INVALID) == FALSE) {
+		g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+		g_error_free (error);
+		return;
+	}
+
+	g_message ("files: %s", files);
+
+	if (gvfsbackendobexftp_fl_parser_parse (files, strlen (files), &elements, &error) == FALSE) {
+		g_message ("gvfsbackendobexftp_fl_parser_parse failed");
+		g_free (files);
+		g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+		g_error_free (error);
+		return;
+	}
+	g_free (files);
+
+	g_vfs_job_succeeded (G_VFS_JOB (job));
+
+	g_vfs_job_enumerate_add_infos (job, elements);
+
+	g_list_foreach (elements, (GFunc)g_object_unref, NULL);
+	g_list_free (elements);
+	g_vfs_job_enumerate_done (job);
+
+	g_print ("- do_enumerate\n");
+}
+
+static void
+do_set_display_name (GVfsBackend *backend,
+		     GVfsJobSetDisplayName *job,
+		     const char *filename,
+		     const char *display_name)
+{
+	g_print ("+ do_set_display_name, filename: %s, display_name: %s\n",
+		 filename, display_name);
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  char *from_uri, *to_uri;
+  char *dirname, *new_path;
+  int res, errsv;
+
+  dirname = g_path_get_basename (filename);
+
+  /* TODO: display name is in utf8, atm we assume libobexftp uris
+     are in utf8, but this might not be true if the user changed
+     the obexftp.conf file. Can we check this and convert? */
+
+  new_path = g_build_filename (dirname, display_name, NULL);
+  g_free (dirname);
+  
+  from_uri = create_obexftp_uri (op_backend->server, op_backend->share, filename);
+  to_uri = create_obexftp_uri (op_backend->server, op_backend->share, new_path);
+  
+  res = op_backend->obexftp_context->rename (op_backend->obexftp_context, from_uri,
+					 op_backend->obexftp_context, to_uri);
+  errsv = errno;
+  g_free (from_uri);
+  g_free (to_uri);
+
+  if (res != 0)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
+  else
+    {
+      g_vfs_job_set_display_name_set_new_path (job, new_path);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+  g_free (new_path);
+#endif
+  g_print ("- do_set_display_name\n");
+}
+
+static void
+do_delete (GVfsBackend *backend,
+	   GVfsJobDelete *job,
+	   const char *filename)
+{
+	g_message ("do_delete");
+#if 0
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  struct stat statbuf;
+  char *uri;
+  int errsv, res;
+
+  uri = create_obexftp_uri (op_backend->server, op_backend->share, filename);
+
+  res = op_backend->obexftp_context->stat (op_backend->obexftp_context, uri, &statbuf);
+  if (res == -1)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+			G_IO_ERROR,
+			g_io_error_from_errno (errno),
+			_("Error deleting file: %s"),
+			g_strerror (errno));
+      g_free (uri);
+      return;
+    }
+
+  if (S_ISDIR (statbuf.st_mode))
+    res = op_backend->obexftp_context->rmdir (op_backend->obexftp_context, uri);
+  else
+    res = op_backend->obexftp_context->unlink (op_backend->obexftp_context, uri);
+  errsv = errno;
+  g_free (uri);
+
+  if (res != 0)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
+  else
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+}
+
+static void
+do_make_directory (GVfsBackend *backend,
+		   GVfsJobMakeDirectory *job,
+		   const char *filename)
+{
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  char *uri;
+  int errsv, res;
+
+  uri = create_obexftp_uri (op_backend->server, op_backend->share, filename);
+  res = op_backend->obexftp_context->mkdir (op_backend->obexftp_context, uri, 0666);
+  errsv = errno;
+  g_free (uri);
+
+  if (res != 0)
+    g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
+  else
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+#endif
+}
+
+#if 0
+static void
+do_move (GVfsBackend *backend,
+	 GVfsJobMove *job,
+	 const char *source,
+	 const char *destination,
+	 GFileCopyFlags flags,
+	 GFileProgressCallback progress_callback,
+	 gpointer progress_callback_data)
+{
+  GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
+  char *source_uri, *dest_uri, *backup_uri;
+  gboolean destination_exist, source_is_dir;
+  struct stat statbuf;
+  int res, errsv;
+  
+  source_uri = create_obexftp_uri (op_backend->server, op_backend->share, source);
+
+  res = op_backend->obexftp_context->stat (op_backend->obexftp_context, source_uri, &statbuf);
+  if (res == -1)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+			G_IO_ERROR,
+			g_io_error_from_errno (errno),
+			_("Error moving file: %s"),
+			g_strerror (errno));
+      g_free (source_uri);
+      return;
+    }
+  else
+    source_is_dir = S_ISDIR (statbuf.st_mode);
+
+  dest_uri = create_obexftp_uri (op_backend->server, op_backend->share, destination);
+  
+  destination_exist = FALSE;
+  res = op_backend->obexftp_context->stat (op_backend->obexftp_context, dest_uri, &statbuf);
+  if (res == 0)
+    {
+      destination_exist = TRUE; /* Target file exists */
+
+      if (flags & G_FILE_COPY_OVERWRITE)
+	{
+	  /* Always fail on dirs, even with overwrite */
+	  if (S_ISDIR (statbuf.st_mode))
+	    {
+	      g_vfs_job_failed (G_VFS_JOB (job),
+				G_IO_ERROR,
+				G_IO_ERROR_WOULD_MERGE,
+				_("Can't move directory over directory"));
+	      g_free (source_uri);
+	      g_free (dest_uri);
+	      return;
+	    }
+	}
+      else
+	{
+	  g_vfs_job_failed (G_VFS_JOB (job),
+			    G_IO_ERROR,
+			    G_IO_ERROR_EXISTS,
+			    _("Target file already exists"));
+	  g_free (source_uri);
+	  g_free (dest_uri);
+	  return;
+	}
+    }
+
+  if (flags & G_FILE_COPY_BACKUP && destination_exist)
+    {
+      backup_uri = g_strconcat (dest_uri, "~", NULL);
+      res = op_backend->obexftp_context->rename (op_backend->obexftp_context, dest_uri,
+					     op_backend->obexftp_context, backup_uri);
+      if (res == -1)
+	{
+	  g_vfs_job_failed (G_VFS_JOB (job),
+			    G_IO_ERROR,
+			    G_IO_ERROR_CANT_CREATE_BACKUP,
+			    _("Backup file creation failed"));
+	  g_free (source_uri);
+	  g_free (dest_uri);
+	  g_free (backup_uri);
+	  return;
+	}
+      g_free (backup_uri);
+      destination_exist = FALSE; /* It did, but no more */
+    }
+
+  if (source_is_dir && destination_exist && (flags & G_FILE_COPY_OVERWRITE))
+    {
+      /* Source is a dir, destination exists (and is not a dir, because that would have failed
+	 earlier), and we're overwriting. Manually remove the target so we can do the rename. */
+      res = op_backend->obexftp_context->unlink (op_backend->obexftp_context, dest_uri);
+      errsv = errno;
+      if (res == -1)
+	{
+	  g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+			    g_io_error_from_errno (errsv),
+			    _("Error removing target file: %s"),
+			    g_strerror (errsv));
+	  g_free (source_uri);
+	  g_free (dest_uri);
+	  return;
+	}
+    }
+
+  
+  res = op_backend->obexftp_context->rename (op_backend->obexftp_context, source_uri,
+					 op_backend->obexftp_context, dest_uri);
+  errsv = errno;
+  g_free (source_uri);
+  g_free (dest_uri);
+
+  /* Catch moves across device boundaries */
+  if (res != 0)
+    {
+      if (errsv == EXDEV ||
+	  /* Unfortunately libobexftpclient doesn't correctly return EXDEV, but falls back
+	     to EINVAL, so we try to guess when this happens: */
+	  (errsv == EINVAL && source_is_dir))
+	g_vfs_job_failed (G_VFS_JOB (job), 
+			  G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
+			  _("Can't recursively move directory"));
+      else
+	g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
+    }
+  else
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+}
+#endif
+
+static void
+g_vfs_backend_obexftp_class_init (GVfsBackendObexftpClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
+
+  gobject_class->finalize = g_vfs_backend_obexftp_finalize;
+
+  backend_class->try_mount = try_mount;
+  backend_class->open_for_read = do_open_for_read;
+  backend_class->read = do_read;
+  backend_class->seek_on_read = do_seek_on_read;
+  backend_class->close_read = do_close_read;
+  backend_class->create = do_create;
+  backend_class->append_to = do_append_to;
+  backend_class->replace = do_replace;
+  backend_class->write = do_write;
+  backend_class->seek_on_write = do_seek_on_write;
+//  backend_class->close_write = do_close_write;
+  backend_class->query_info = do_query_info;
+  backend_class->query_fs_info = do_query_fs_info;
+  backend_class->enumerate = do_enumerate;
+  backend_class->set_display_name = do_set_display_name;
+  backend_class->delete = do_delete;
+//  backend_class->make_directory = do_make_directory;
+//  backend_class->move = do_move;
+  backend_class->try_query_settable_attributes = try_query_settable_attributes;
+
+  dbus_g_object_register_marshaller (obexftp_marshal_VOID__STRING_STRING_INT,
+				     G_TYPE_NONE, G_TYPE_STRING,
+				     G_TYPE_STRING, G_TYPE_INT, G_TYPE_INVALID);
+  dbus_g_object_register_marshaller (obexftp_marshal_VOID__STRING_STRING,
+				     G_TYPE_NONE, G_TYPE_STRING,
+				     G_TYPE_STRING, G_TYPE_INVALID);
+}
+
Index: daemon/gvfsbackendobexftp.h
===================================================================
--- daemon/gvfsbackendobexftp.h	(revision 0)
+++ daemon/gvfsbackendobexftp.h	(revision 0)
@@ -0,0 +1,50 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VFS_BACKEND_OBEXFTP_H__
+#define __G_VFS_BACKEND_OBEXFTP_H__
+
+#include <gvfsbackend.h>
+#include <gmountspec.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_BACKEND_OBEXFTP         (g_vfs_backend_obexftp_get_type ())
+#define G_VFS_BACKEND_OBEXFTP(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_OBEXFTP, GVfsBackendObexftp))
+#define G_VFS_BACKEND_OBEXFTP_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_OBEXFTP, GVfsBackendObexftpClass))
+#define G_VFS_IS_BACKEND_OBEXFTP(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_OBEXFTP))
+#define G_VFS_IS_BACKEND_OBEXFTP_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_OBEXFTP))
+#define G_VFS_BACKEND_OBEXFTP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_OBEXFTP, GVfsBackendObexftpClass))
+
+typedef struct _GVfsBackendObexftp        GVfsBackendObexftp;
+typedef struct _GVfsBackendObexftpClass   GVfsBackendObexftpClass;
+
+struct _GVfsBackendObexftpClass
+{
+  GVfsBackendClass parent_class;
+};
+
+GType g_vfs_backend_obexftp_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_VFS_BACKEND_OBEXFTP_H__ */
Index: daemon/Makefile.am
===================================================================
--- daemon/Makefile.am	(revision 1170)
+++ daemon/Makefile.am	(working copy)
@@ -8,6 +8,7 @@
 	-I$(top_srcdir)/common			\
 	-I$(top_builddir)			\
 	$(GLIB_CFLAGS) $(DBUS_CFLAGS) 		\
+	$(OBEXFTP_CFLAGS) $(XML_CFLAGS)		\
 	-DDBUS_API_SUBJECT_TO_CHANGE		\
 	-DLIBEXEC_DIR=\"$(libexecdir)/\" 	\
 	-DMOUNTABLE_DIR=\"$(mountdir)/\" 	\
@@ -54,8 +55,22 @@
 libexec_PROGRAMS += gvfsd-cdda
 endif
 
-EXTRA_DIST = gvfs-daemon.service.in $(mount_in_files)
+if USE_OBEXFTP
+mount_in_files += obexftp.mount.in
+mount_DATA += obexftp.mount
+libexec_PROGRAMS += gvfsd-obexftp
+BUILT_SOURCES = obexftp-marshal.c obexftp-marshal.h
 
+obexftp-marshal.h: obexftp-marshal.list
+	glib-genmarshal $< --prefix=obexftp_marshal --header > $@
+
+obexftp-marshal.c: obexftp-marshal.list
+	echo "#include \"obexftp-marshal.h\"" > $@ && glib-genmarshal $< --prefix=obexftp_marshal --body >> $@
+
+endif
+
+EXTRA_DIST = gvfs-daemon.service.in $(mount_in_files) obexftp-marshal.list
+
 DISTCLEANFILES = gvfs-daemon.service $(mount_DATA)
 
 
@@ -159,6 +174,21 @@
 
 gvfsd_smb_browse_LDADD = $(SAMBA_LIBS) $(libraries)
 
+gvfsd_obexftp_SOURCES = \
+	gvfsbackendobexftp.c gvfsbackendobexftp.h \
+	obexftp-marshal.c obexftp-marshal.h \
+	gvfsbackendobexftp-fl-parser.c gvfsbackendobexftp-fl-parser.h \
+	daemon-main.c daemon-main.h \
+	daemon-main-generic.c
+
+gvfsd_obexftp_CPPFLAGS = \
+	-DBACKEND_HEADER=gvfsbackendobexftp.h \
+	-DDEFAULT_BACKEND_TYPE=obex \
+	-DMAX_JOB_THREADS=1 \
+	-DBACKEND_TYPES='"obex", G_VFS_TYPE_BACKEND_OBEXFTP,'
+
+gvfsd_obexftp_LDADD = $(OBEXFTP_LIBS) $(XML_LIBS) $(libraries)
+
 gvfsd_ftp_SOURCES = \
 	gvfsbackendftp.c gvfsbackendftp.h \
 	daemon-main.c daemon-main.h \
