r44170 - in /desktop/experimental/gvfs/debian: changelog control control.in patches/revert-0001-Remove-obsolte-obexftp-code.patch patches/series
laney at users.alioth.debian.org
laney at users.alioth.debian.org
Wed Mar 18 12:35:40 UTC 2015
Author: laney
Date: Wed Mar 18 12:35:40 2015
New Revision: 44170
URL: http://svn.debian.org/wsvn/pkg-gnome/?sc=1&rev=44170
Log:
New upstream release 1.23.92
Added:
desktop/experimental/gvfs/debian/patches/revert-0001-Remove-obsolte-obexftp-code.patch
Modified:
desktop/experimental/gvfs/debian/changelog
desktop/experimental/gvfs/debian/control
desktop/experimental/gvfs/debian/control.in
desktop/experimental/gvfs/debian/patches/series
Modified: desktop/experimental/gvfs/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-gnome/desktop/experimental/gvfs/debian/changelog?rev=44170&op=diff
==============================================================================
--- desktop/experimental/gvfs/debian/changelog [utf-8] (original)
+++ desktop/experimental/gvfs/debian/changelog [utf-8] Wed Mar 18 12:35:40 2015
@@ -1,3 +1,24 @@
+gvfs (1.23.92-1) UNRELEASED; urgency=medium
+
+ * New upstream release 1.23.92
+ + metadata: Reliability improvements
+ + afc, gphoto2: Fix force unmount when device is removed
+ + ftp: Prevent segfault when unmounting
+ + ftp: Bug fixes for directory parsing
+ + dav: Fix crash on mount when using dns-sd
+ + common: Increase mount timeout to 30 minutes
+ + Several smaller bugfixes
+ + Update man pages
+ * debian/patches/revert-0001-Remove-obsolte-obexftp-code.patch: Temporarily
+ restore obexftp - it's still useful to be able to use this via Bluez 4,
+ for some usecases.
+ * debian/patches/metadata-dont-flush-null-tree.patch: Drop, this bug was
+ fixed upstream.
+ * debian/control{,.in}: Bump required version of glib to ≥ 2.43.2, as per
+ upstream.
+
+ -- Iain Lane <laney at debian.org> Tue, 17 Mar 2015 12:33:43 +0000
+
gvfs (1.23.90-1) experimental; urgency=medium
* New upstream release 1.23.90
Modified: desktop/experimental/gvfs/debian/control
URL: http://svn.debian.org/wsvn/pkg-gnome/desktop/experimental/gvfs/debian/control?rev=44170&op=diff
==============================================================================
--- desktop/experimental/gvfs/debian/control [utf-8] (original)
+++ desktop/experimental/gvfs/debian/control [utf-8] Wed Mar 18 12:35:40 2015
@@ -14,7 +14,7 @@
gnome-pkg-tools (>= 0.7),
pkg-config,
gtk-doc-tools,
- libglib2.0-dev (>= 2.37.0),
+ libglib2.0-dev (>= 2.43.2),
libdbus-1-dev,
intltool (>= 0.35.0),
openssh-client,
Modified: desktop/experimental/gvfs/debian/control.in
URL: http://svn.debian.org/wsvn/pkg-gnome/desktop/experimental/gvfs/debian/control.in?rev=44170&op=diff
==============================================================================
--- desktop/experimental/gvfs/debian/control.in [utf-8] (original)
+++ desktop/experimental/gvfs/debian/control.in [utf-8] Wed Mar 18 12:35:40 2015
@@ -10,7 +10,7 @@
gnome-pkg-tools (>= 0.7),
pkg-config,
gtk-doc-tools,
- libglib2.0-dev (>= 2.37.0),
+ libglib2.0-dev (>= 2.43.2),
libdbus-1-dev,
intltool (>= 0.35.0),
openssh-client,
Added: desktop/experimental/gvfs/debian/patches/revert-0001-Remove-obsolte-obexftp-code.patch
URL: http://svn.debian.org/wsvn/pkg-gnome/desktop/experimental/gvfs/debian/patches/revert-0001-Remove-obsolte-obexftp-code.patch?rev=44170&op=file
==============================================================================
--- desktop/experimental/gvfs/debian/patches/revert-0001-Remove-obsolte-obexftp-code.patch (added)
+++ desktop/experimental/gvfs/debian/patches/revert-0001-Remove-obsolte-obexftp-code.patch [utf-8] Wed Mar 18 12:35:40 2015
@@ -0,0 +1,3580 @@
+reverted:
+Index: b/INSTALL
+===================================================================
+--- /dev/null
++++ b/INSTALL
+@@ -0,0 +1,3 @@
++The ObexFTP backend requires the obex-data-server D-Bus service in
++addition to the dependencies listed in the configure. See:
++http://wiki.muiline.com/obex-data-server
+Index: b/client/test-uri-utils.c
+===================================================================
+--- a/client/test-uri-utils.c
++++ b/client/test-uri-utils.c
+@@ -14,6 +14,8 @@
+ { "https://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:443/", "[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]", 443 },
+ { "http://test:443/", "test", 443 },
+ { "http://test/", "test", -1 },
++ { "obex://[00:FF:FF:FF:FF:FF]/MMC/foo.jpg", "[00:FF:FF:FF:FF:FF]", -1 },
++ { "obex://[00:FF:FF:FF:FF:FF]/C:", "[00:FF:FF:FF:FF:FF]", -1 },
+ { "http://windows-host:8080/C:/", "windows-host", 8080 },
+ { "smb://user:password@192.192.192.192/foobar", "192.192.192.192", -1 },
+ { "https://d134w4tst3t.s3.amazonaws.com/a?Signature=6VJ9%2BAdPVZ4Z7NnPShRvtDsLofc%3D&Expires=1249330377&AWSAccessKeyId=0EYZF4DV8A7WM0H73602", "d134w4tst3t.s3.amazonaws.com", -1 },
+Index: b/configure.ac
+===================================================================
+--- a/configure.ac
++++ b/configure.ac
+@@ -407,6 +407,40 @@
+
+ AM_CONDITIONAL(USE_GOA, [test "$msg_goa" = "yes"])
+
++dnl *****************************************************
++dnl *** Check if we should build with obexftp backend ***
++dnl *****************************************************
++AC_ARG_ENABLE(obexftp, AS_HELP_STRING([--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 >= 4.0, 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 >= 4.0)
++ 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])
++ EXPAT_CFLAGS=""
++ EXPAT_LIBS="-lexpat"
++ else
++ msg_obexftp=no
++ fi
++fi
++
++AC_SUBST(EXPAT_CFLAGS)
++AC_SUBST(EXPAT_LIBS)
++AM_CONDITIONAL(USE_OBEXFTP, [test "$msg_obexftp" = "yes"])
++
+ dnl *************************
+ dnl *** Check for gphoto2 ***
+ dnl *************************
+@@ -927,6 +961,7 @@
+
+ Blu-ray metadata support: $msg_bluray
+ HTTP/WebDAV support: $msg_http
++ ObexFTP support $msg_obexftp
+ Samba support: $msg_samba
+ FUSE support: $msg_fuse
+ CDDA support: $msg_cdda
+Index: b/daemon/Makefile.am
+===================================================================
+--- a/daemon/Makefile.am
++++ b/daemon/Makefile.am
+@@ -11,6 +11,7 @@
+ -I$(top_builddir) \
+ -I$(top_builddir)/common \
+ $(GLIB_CFLAGS) \
++ $(OBEXFTP_CFLAGS) $(EXPAT_CFLAGS) \
+ $(KEYRING_CFLAGS) \
+ -DLIBEXEC_DIR=\"$(libexecdir)\" \
+ -DMOUNTABLE_DIR=\"$(mountdir)\" \
+@@ -88,6 +89,20 @@
+ libexec_PROGRAMS += gvfsd-mtp
+ endif
+
++mount_in_files += obexftp.mount.in
++if USE_OBEXFTP
++mount_DATA += obexftp.mount
++libexec_PROGRAMS += gvfsd-obexftp
++BUILT_SOURCES = obexftp-marshal.c obexftp-marshal.h
++
++obexftp-marshal.h: obexftp-marshal.list
++ $(AM_V_GEN) glib-genmarshal $< --prefix=obexftp_marshal --header > $@
++
++obexftp-marshal.c: obexftp-marshal.list
++ $(AM_V_GEN) echo "#include \"obexftp-marshal.h\"" > $@ && glib-genmarshal $< --prefix=obexftp_marshal --body >> $@
++
++endif
++
+ mount_in_files += dns-sd.mount.in
+ if HAVE_AVAHI
+ mount_DATA += dns-sd.mount
+@@ -125,6 +140,7 @@
+ EXTRA_DIST = \
+ gvfs-daemon.service.in \
+ $(mount_in_files) \
++ obexftp-marshal.list \
+ $(gvfs_gschemas) \
+ $(gvfs_gschemas_convert_DATA) \
+ $(gsettings_ENUM_FILES) \
+@@ -259,6 +275,29 @@
+
+ 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 \
++ gvfsbackendobexftp-cap-parser.c gvfsbackendobexftp-cap-parser.h \
++ daemon-main.c daemon-main.h \
++ daemon-main-generic.c
++
++gvfsd_obexftp_CPPFLAGS = \
++ $(flags) \
++ -DBACKEND_HEADER=gvfsbackendobexftp.h \
++ -DDEFAULT_BACKEND_TYPE=obex \
++ -DMAX_JOB_THREADS=1 \
++ -DBACKEND_TYPES='"obex", G_VFS_TYPE_BACKEND_OBEXFTP,'
++if USE_HAL
++gvfsd_obexftp_CPPFLAGS += $(HAL_CFLAGS)
++endif
++
++gvfsd_obexftp_LDADD = $(OBEXFTP_LIBS) $(EXPAT_LIBS) $(libraries)
++if USE_HAL
++gvfsd_obexftp_LDADD += $(HAL_LIBS)
++endif
++
+ gvfsd_ftp_SOURCES = \
+ gvfsftpconnection.c gvfsftpconnection.h \
+ gvfsftpdircache.c gvfsftpdircache.h \
+Index: b/daemon/gvfsbackendobexftp-cap-parser.c
+===================================================================
+--- /dev/null
++++ b/daemon/gvfsbackendobexftp-cap-parser.c
+@@ -0,0 +1,592 @@
++/*
++ * Copyright (C) 2004-2005 Nokia Corporation.
++ * Copyright (C) 2008 Bastien Nocera <hadess at hadess.net>
++ *
++ * 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., 51 Franklin Street - Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include <config.h>
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++#include <expat.h>
++
++#include "gvfsbackendobexftp-cap-parser.h"
++
++#define d(x)
++
++struct _OvuCaps {
++ GList *memory_entries;
++
++ /* FIXME: Add "Services" and "Inbox" data here later. */
++};
++
++struct _OvuCapsMemory {
++ gchar *type;
++ goffset free;
++ goffset used;
++ guint has_free : 1;
++ guint has_used : 1;
++ guint case_sensitive : 1;
++};
++
++typedef enum {
++ PARSER_STATE_INVALID,
++
++ PARSER_STATE_START,
++ PARSER_STATE_CAPABILITY,
++
++ PARSER_STATE_GENERAL,
++
++ PARSER_STATE_MEMORY,
++ PARSER_STATE_MEMORY_TYPE,
++ PARSER_STATE_MEMORY_LOCATION,
++ PARSER_STATE_MEMORY_FREE,
++ PARSER_STATE_MEMORY_USED,
++ PARSER_STATE_MEMORY_SHARED,
++ PARSER_STATE_MEMORY_FILESIZE,
++ PARSER_STATE_MEMORY_FOLDERSIZE,
++ PARSER_STATE_MEMORY_FILELEN,
++ PARSER_STATE_MEMORY_FOLDERLEN,
++ PARSER_STATE_MEMORY_CASE,
++ PARSER_STATE_MEMORY_EXT,
++
++ PARSER_STATE_INBOX,
++ PARSER_STATE_SERVICE,
++
++ PARSER_STATE_SKIP
++} ParserState;
++
++
++typedef struct {
++ GList *state;
++
++ GList *memory_entries;
++
++ gchar *memory_type;
++ goffset memory_free;
++ goffset memory_used;
++ gboolean memory_has_free;
++ gboolean memory_has_used;
++ gboolean memory_case_sensitive;
++
++ GError **error;
++} ParserData;
++
++static void cap_parser_start_node_cb (void *user_data,
++ const char *node_name,
++ const char **attr);
++
++static void cap_parser_end_node_cb (void *user_data,
++ const char *node_name);
++static void cap_parser_text_cb (void *user_data,
++ const XML_Char *s,
++ int len);
++static XML_Parser
++cap_parser_create_parser (ParserData *data);
++
++
++static void
++cap_parser_push_state (ParserData *data, ParserState state)
++{
++ data->state = g_list_prepend (data->state,
++ GINT_TO_POINTER (state));
++}
++
++static ParserState
++cap_parser_pop_state (ParserData *data)
++{
++ ParserState state;
++
++ if (!data->state) {
++ return PARSER_STATE_INVALID;
++ }
++
++ state = GPOINTER_TO_INT (data->state->data);
++ data->state = g_list_delete_link (data->state, data->state);
++
++ return state;
++}
++
++static ParserState
++cap_parser_peek_state (ParserData *data)
++{
++ if (!data->state) {
++ return PARSER_STATE_START;
++ }
++
++ return GPOINTER_TO_INT (data->state->data);
++}
++
++static const char *
++cap_parser_get_attribute_value (const char *name, const char **attr)
++{
++ gint i = 0;
++
++ while (attr[i]) {
++ if (strcmp (name, attr[i]) == 0) {
++ return attr[i + 1];
++ }
++ i += 2;
++ }
++
++ return "";
++}
++
++static void
++cap_parser_start_node_cb (void *user_data,
++ const char *node_name,
++ const char **attr)
++{
++ ParserData *data;
++ ParserState state;
++ const gchar *version;
++
++ data = (ParserData *) user_data;
++
++ state = cap_parser_peek_state (data);
++
++ switch (state) {
++ case PARSER_STATE_START:
++ if (strcmp (node_name, "Capability") != 0) {
++ g_set_error (data->error,
++ G_MARKUP_ERROR,
++ G_MARKUP_ERROR_INVALID_CONTENT,
++ "Outermost element must be a <Capability>, not <%s>",
++ node_name);
++ return;
++ }
++
++ version = cap_parser_get_attribute_value ("version", attr);
++ /* Assume an empty version is fine */
++ if (strcmp (version, "1.0") != 0 && version[0] != '\0') {
++ g_warning ("Version expected is '1.0', not '%s'\n", version);
++ }
++
++ cap_parser_push_state (data, PARSER_STATE_CAPABILITY);
++ break;
++
++ case PARSER_STATE_CAPABILITY:
++ if (strcmp (node_name, "General") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_GENERAL);
++ }
++ else if (strcmp (node_name, "Inbox") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_INBOX);
++ }
++ else if (strcmp (node_name, "Service") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_SERVICE);
++ } else {
++ g_set_error (data->error,
++ G_MARKUP_ERROR,
++ G_MARKUP_ERROR_INVALID_CONTENT,
++ "Don't expect node '%s' as child of 'Cap'",
++ node_name);
++ return;
++ }
++ break;
++
++ case PARSER_STATE_GENERAL:
++ if (strcmp (node_name, "Memory") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY);
++ }
++ else if (strcmp (node_name, "Manufacturer") == 0 ||
++ strcmp (node_name, "Model") == 0 ||
++ strcmp (node_name, "SN") == 0 ||
++ strcmp (node_name, "OEM") == 0 ||
++ strcmp (node_name, "SW") == 0 ||
++ strcmp (node_name, "FW") == 0 ||
++ strcmp (node_name, "HW") == 0 ||
++ strcmp (node_name, "Language") == 0 ||
++ strcmp (node_name, "Ext") == 0) {
++
++ /* Skip these for now. */
++ cap_parser_push_state (data, PARSER_STATE_SKIP);
++ } else {
++ g_set_error (data->error,
++ G_MARKUP_ERROR,
++ G_MARKUP_ERROR_INVALID_CONTENT,
++ "Don't expect node '%s' as child of 'General'",
++ node_name);
++ return;
++ }
++
++ break;
++
++ case PARSER_STATE_MEMORY:
++ if (strcmp (node_name, "MemType") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_TYPE);
++ }
++ else if (strcmp (node_name, "Location") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_LOCATION);
++ }
++ else if (strcmp (node_name, "Free") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_FREE);
++ }
++ else if (strcmp (node_name, "Used") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_USED);
++ }
++ else if (strcmp (node_name, "Shared") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_SHARED);
++ }
++ else if (strcmp (node_name, "FileSize") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_FILESIZE);
++ }
++ else if (strcmp (node_name, "FolderSize") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_FOLDERSIZE);
++ }
++ else if (strcmp (node_name, "FileNLen") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_FILELEN);
++ }
++ else if (strcmp (node_name, "FolderNLen") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_FOLDERLEN);
++ }
++ else if (strcmp (node_name, "CaseSenN") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_CASE);
++ data->memory_case_sensitive = TRUE;
++ }
++ else if (strcmp (node_name, "Ext") == 0) {
++ cap_parser_push_state (data, PARSER_STATE_MEMORY_EXT);
++ } else {
++ g_set_error (data->error,
++ G_MARKUP_ERROR,
++ G_MARKUP_ERROR_INVALID_CONTENT,
++ "Don't expect node '%s' as child of 'Memory'",
++ node_name);
++ return;
++ }
++ break;
++
++ case PARSER_STATE_INBOX:
++ case PARSER_STATE_SERVICE:
++ /* Skip these for now. */
++ cap_parser_push_state (data, PARSER_STATE_SKIP);
++ break;
++
++ case PARSER_STATE_SKIP:
++ cap_parser_push_state (data, PARSER_STATE_SKIP);
++ break;
++
++ default:
++ g_warning ("Node not handled: '%s'\n", node_name);
++ cap_parser_push_state (data, PARSER_STATE_SKIP);
++ break;
++ }
++}
++
++static void
++cap_parser_reset_memory (ParserData *data)
++{
++ g_free (data->memory_type);
++ data->memory_type = NULL;
++ data->memory_free = 0;
++ data->memory_used = 0;
++ data->memory_has_free = FALSE;
++ data->memory_has_used = FALSE;
++ data->memory_case_sensitive = FALSE;
++}
++
++static void
++cap_parser_end_node_cb (void *user_data, const char *node_name)
++{
++ ParserData *data;
++ ParserState state;
++ OvuCapsMemory *memory;
++
++ data = (ParserData *) user_data;
++
++ state = cap_parser_pop_state (data);
++
++ switch (state) {
++ case PARSER_STATE_INVALID:
++ return;
++
++ case PARSER_STATE_MEMORY:
++ memory = ovu_caps_memory_new (data->memory_type,
++ data->memory_free,
++ data->memory_used,
++ data->memory_has_free,
++ data->memory_has_used,
++ data->memory_case_sensitive);
++
++ data->memory_entries = g_list_prepend (data->memory_entries,
++ memory);
++ cap_parser_reset_memory (data);
++ break;
++
++ case PARSER_STATE_CAPABILITY:
++ data->memory_entries = g_list_reverse (data->memory_entries);
++ break;
++
++ default:
++ break;
++ }
++}
++
++/* Parse a long, return -1 if input is not strictly valid or null. */
++static goffset
++parse_long (const gchar *str, gboolean *success)
++{
++ gchar *endptr;
++ glong l;
++
++ *success = TRUE;
++
++ if (!str) {
++ *success = FALSE;
++ return 0;
++ }
++
++ l = strtol (str, &endptr, 10);
++ if (endptr[0] != '\0' || l < 0) {
++ *success = FALSE;
++ l = 0;
++ }
++
++ return l;
++}
++
++static void
++cap_parser_text_cb (void *user_data,
++ const XML_Char *s,
++ int len)
++{
++ ParserData *data;
++ ParserState state;
++ gchar *tmp;
++
++ data = (ParserData *) user_data;
++
++ /* text is not null terminated. */
++ tmp = g_strndup (s, len);
++
++ state = cap_parser_peek_state (data);
++
++ switch (state) {
++ case PARSER_STATE_MEMORY_TYPE:
++ data->memory_type = g_strdup (tmp);
++ break;
++ case PARSER_STATE_MEMORY_FREE:
++ data->memory_free = parse_long (tmp, &data->memory_has_free);
++ break;
++ case PARSER_STATE_MEMORY_USED:
++ data->memory_used = parse_long (tmp, &data->memory_has_used);
++ break;
++
++ default:
++ break;
++ }
++
++ g_free (tmp);
++}
++
++static XML_Parser
++cap_parser_create_parser (ParserData *data)
++{
++ XML_Parser parser;
++
++ parser = XML_ParserCreate (NULL);
++
++ XML_SetElementHandler (parser,
++ cap_parser_start_node_cb,
++ cap_parser_end_node_cb);
++
++ XML_SetCharacterDataHandler (parser, cap_parser_text_cb);
++
++ XML_SetUserData (parser, data);
++
++ return parser;
++}
++
++static void
++cap_parser_free (ParserData *data, gboolean free_data)
++{
++ cap_parser_reset_memory (data);
++
++ if (free_data) {
++ g_list_foreach (data->memory_entries,
++ (GFunc) ovu_caps_memory_free, NULL);
++ }
++
++ g_free (data);
++}
++
++OvuCaps *
++ovu_caps_parser_parse (const gchar *buf,
++ gint len,
++ GError **error)
++{
++ ParserData *data;
++ XML_Parser parser;
++ OvuCaps *caps;
++
++ data = g_new0 (ParserData, 1);
++
++ data->error = error;
++ parser = cap_parser_create_parser (data);
++
++ if (XML_Parse (parser, buf, len, TRUE) == 0) {
++ caps = NULL;
++
++ if (*error == NULL) {
++ g_set_error_literal (error,
++ G_MARKUP_ERROR,
++ G_MARKUP_ERROR_INVALID_CONTENT,
++ "Couldn't parse the incoming data");
++ }
++
++ cap_parser_free (data, TRUE);
++ } else {
++ caps = g_new0 (OvuCaps, 1);
++ caps->memory_entries = data->memory_entries;
++
++ cap_parser_free (data, FALSE);
++ }
++
++ XML_ParserFree (parser);
++
++ return caps;
++}
++
++OvuCapsMemory *
++ovu_caps_memory_new (const gchar *type,
++ goffset free,
++ goffset used,
++ gboolean has_free,
++ gboolean has_used,
++ gboolean case_sensitive)
++{
++ OvuCapsMemory *memory;
++
++ memory = g_new0 (OvuCapsMemory, 1);
++
++ memory->type = g_strdup (type);
++ memory->free = free;
++ memory->used = used;
++ memory->has_free = has_free;
++ memory->has_used = has_used;
++ memory->case_sensitive = case_sensitive;
++
++ return memory;
++}
++
++void
++ovu_caps_memory_free (OvuCapsMemory *memory)
++{
++ g_free (memory->type);
++ g_free (memory);
++}
++
++gboolean
++ovu_caps_memory_equal (OvuCapsMemory *m1, OvuCapsMemory *m2)
++{
++ if (strcmp (m1->type, m2->type) != 0) {
++ d(g_print ("type mismatch: %s %s\n",
++ m1->type, m2->type));
++ return FALSE;
++ }
++
++ if (m1->free != m2->free) {
++ d(g_print ("free mismatch: %d %d\n",
++ (int) m1->free, (int) m2->free));
++ return FALSE;
++ }
++
++ if (m1->used != m2->used) {
++ d(g_print ("used mismatch: %d %d\n",
++ (int) m1->used, (int) m2->used));
++ return FALSE;
++ }
++
++ if (m1->case_sensitive != m2->case_sensitive) {
++ d(g_print ("case mismatch: %d %d\n",
++ m1->case_sensitive,
++ m2->case_sensitive));
++ return FALSE;
++ }
++
++ return TRUE;
++}
++
++void
++ovu_caps_free (OvuCaps *caps)
++{
++ g_list_free_full (caps->memory_entries, (GDestroyNotify) ovu_caps_memory_free);
++
++ g_free (caps);
++}
++
++GList *
++ovu_caps_get_memory_entries (OvuCaps *caps)
++{
++ g_return_val_if_fail (caps != NULL, NULL);
++
++ return caps->memory_entries;
++}
++
++OvuCapsMemory *
++ovu_caps_get_memory_type (OvuCaps *caps,
++ const gchar *mem_type)
++{
++ GList *tmp;
++
++ g_return_val_if_fail (caps != NULL, NULL);
++
++ for (tmp = caps->memory_entries; tmp != NULL; tmp = tmp->next) {
++ OvuCapsMemory *memory = tmp->data;
++
++ /* treat a NULL memory type as matching anything */
++ if (mem_type == NULL || (memory->type != NULL &&
++ !strcmp(mem_type, memory->type)))
++ return memory;
++ }
++ return NULL;
++}
++
++const gchar *
++ovu_caps_memory_get_type (OvuCapsMemory *memory)
++{
++ return memory->type;
++}
++
++goffset
++ovu_caps_memory_get_used (OvuCapsMemory *memory)
++{
++ return memory->used;
++}
++
++goffset
++ovu_caps_memory_get_free (OvuCapsMemory *memory)
++{
++ return memory->free;
++}
++
++gboolean
++ovu_caps_memory_has_used (OvuCapsMemory *memory)
++{
++ return memory->has_used;
++}
++
++gboolean
++ovu_caps_memory_has_free (OvuCapsMemory *memory)
++{
++ return memory->has_free;
++}
++
++gboolean
++ovu_caps_memory_get_case_sensitive (OvuCapsMemory *memory)
++{
++ return memory->case_sensitive;
++}
+Index: b/daemon/gvfsbackendobexftp-cap-parser.h
+===================================================================
+--- /dev/null
++++ b/daemon/gvfsbackendobexftp-cap-parser.h
+@@ -0,0 +1,53 @@
++/*
++ * Copyright (C) 2004 Nokia Corporation.
++ *
++ * 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., 51 Franklin Street - Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#ifndef __OVU_CAP_PARSER_H__
++#define __OVU_CAP_PARSER_H__
++
++#include <glib.h>
++
++typedef struct _OvuCaps OvuCaps;
++typedef struct _OvuCapsMemory OvuCapsMemory;
++
++OvuCaps * ovu_caps_parser_parse (const gchar *buf,
++ gint len,
++ GError **error);
++
++GList * ovu_caps_get_memory_entries (OvuCaps *caps);
++OvuCapsMemory *ovu_caps_get_memory_type (OvuCaps *caps,
++ const gchar *mem_type);
++void ovu_caps_free (OvuCaps *caps);
++OvuCapsMemory * ovu_caps_memory_new (const gchar *type,
++ goffset free,
++ goffset used,
++ gboolean has_free,
++ gboolean has_used,
++ gboolean case_sensitive);
++void ovu_caps_memory_free (OvuCapsMemory *memory);
++gboolean ovu_caps_memory_equal (OvuCapsMemory *m1,
++ OvuCapsMemory *m2);
++const gchar * ovu_caps_memory_get_type (OvuCapsMemory *memory);
++goffset ovu_caps_memory_get_used (OvuCapsMemory *memory);
++goffset ovu_caps_memory_get_free (OvuCapsMemory *memory);
++gboolean ovu_caps_memory_has_used (OvuCapsMemory *memory);
++gboolean ovu_caps_memory_has_free (OvuCapsMemory *memory);
++gboolean ovu_caps_memory_get_case_sensitive (OvuCapsMemory *memory);
++
++#endif /* __OVU_CAP_PARSER_H__ */
++
+Index: b/daemon/gvfsbackendobexftp-fl-parser.c
+===================================================================
+--- /dev/null
++++ b/daemon/gvfsbackendobexftp-fl-parser.c
+@@ -0,0 +1,437 @@
++/*
++ * Copyright (C) 2004-2005 Nokia Corporation.
++ * Copyright (C) 2008 Bastien Nocera <hadess at 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., 51 Franklin Street - Fifth Floor,
++ * Boston, MA 02110-1301, 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) {
++ GIcon *icon;
++ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
++ g_file_info_set_content_type (info, "inode/directory");
++
++ icon = g_themed_icon_new ("folder");
++ g_file_info_set_icon (info, icon);
++ g_object_unref (icon);
++ icon = g_themed_icon_new ("folder-symbolic");
++ g_file_info_set_symbolic_icon (info, icon);
++ g_object_unref (icon);
++ } 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;
++ }
++
++ if (g_file_info_get_content_type (info) == NULL) {
++ char *mime_type;
++ mime_type = g_content_type_guess (g_file_info_get_name (info), NULL, 0, NULL);
++ g_file_info_set_content_type (info, mime_type);
++ g_free (mime_type);
++ }
++
++ if (g_file_info_get_content_type (info) == NULL) {
++ g_file_info_set_content_type (info, "application/octet-stream");
++ }
++
++ if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) {
++ GIcon *icon;
++ const char *content_type;
++
++ content_type = g_file_info_get_content_type (info);
++
++ icon = g_content_type_get_icon (content_type);
++ if (icon != NULL) {
++ if (G_IS_THEMED_ICON (icon))
++ g_themed_icon_append_name (G_THEMED_ICON (icon), "text-x-generic");
++ g_file_info_set_icon (info, icon);
++ g_object_unref (icon);
++ }
++
++ icon = g_content_type_get_symbolic_icon (content_type);
++ if (icon != NULL) {
++ if (G_IS_THEMED_ICON (icon))
++ g_themed_icon_append_name (G_THEMED_ICON (icon), "text-x-generic-symbolic");
++ g_file_info_set_symbolic_icon (info, icon);
++ g_object_unref (icon);
++ }
++ }
++
++ /* 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) {
++ char *display_name;
++ /* 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);
++ display_name = g_filename_display_name (value);
++ g_file_info_set_display_name (info, display_name);
++ d(g_print ("Name: '%s'\n", display_name));
++ g_free (display_name);
++ }
++ 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 (value == NULL || value[0] == '\0')
++ continue;
++
++ device = om_mem_type_id_from_string (value);
++ g_file_info_set_attribute_uint32 (info,
++ G_FILE_ATTRIBUTE_UNIX_RDEV,
++ device);
++ 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_free_full (data->elements, g_object_unref);
++ 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_literal (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: b/daemon/gvfsbackendobexftp-fl-parser.h
+===================================================================
+--- /dev/null
++++ b/daemon/gvfsbackendobexftp-fl-parser.h
+@@ -0,0 +1,37 @@
++/*
++ * Copyright (C) 2004 Nokia Corporation.
++ * Copyright (C) 2008 Bastien Nocera <hadess at 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., 51 Franklin Street - Fifth Floor,
++ * Boston, MA 02110-1301, 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: b/daemon/gvfsbackendobexftp.c
+===================================================================
+--- /dev/null
++++ b/daemon/gvfsbackendobexftp.c
+@@ -0,0 +1,2183 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2006-2008 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., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ * Authors: Bastien Nocera <hadess at hadess.net>
++ * Cosimo Cecchi <cosimoc at gnome.org>
++ */
++
++#include <config.h>
++
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <errno.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <string.h>
++#include <stdlib.h>
++
++#include <glib/gstdio.h>
++#include <glib/gi18n.h>
++#include <gio/gio.h>
++#if defined(HAVE_HAL)
++ #include <libhal.h>
++#endif
++#include <dbus/dbus-glib.h>
++#include <dbus/dbus-glib-bindings.h>
++#include <dbus/dbus-glib-lowlevel.h>
++#include <bluetooth/bluetooth.h>
++
++#include "gvfsbackendobexftp.h"
++#include "gvfsbackendobexftp-fl-parser.h"
++#include "gvfsbackendobexftp-cap-parser.h"
++#include "obexftp-marshal.h"
++
++#include "gvfsjobopenforread.h"
++#include "gvfsjobread.h"
++#include "gvfsjobseekread.h"
++#include "gvfsjobmount.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
++
++#define ASYNC_SUCCESS 2
++#define ASYNC_RUNNING 1
++#define ASYNC_PENDING 0
++#define ASYNC_ERROR -1
++
++#define CACHE_LIFESPAN 3
++
++struct _GVfsBackendObexftp
++{
++ GVfsBackend parent_instance;
++
++ char *display_name;
++ char *bdaddr;
++ char *icon_name;
++ char *symbolic_icon_name;
++ gint usbintfnum;
++
++ DBusGConnection *connection;
++ DBusGProxy *manager_proxy;
++ DBusGProxy *session_proxy;
++
++ /* Use for the async notifications and errors */
++ GCond cond;
++ GMutex mutex;
++ int status;
++ gboolean doing_io;
++ GError *error;
++
++ /* Folders listing cache */
++ char *files_listing;
++ char *directory;
++ time_t time_captured;
++};
++
++typedef struct {
++ char *source;
++ goffset size;
++ int fd;
++} ObexFTPOpenHandle;
++
++G_DEFINE_TYPE (GVfsBackendObexftp, g_vfs_backend_obexftp, G_VFS_TYPE_BACKEND);
++
++static void session_connect_error_cb (DBusGProxy *proxy,
++ const char *session_object,
++ const gchar *error_name,
++ const gchar *error_message,
++ gpointer user_data);
++static void session_connected_cb (DBusGProxy *proxy,
++ const char *session_object,
++ gpointer user_data);
++
++/* Parse USB paths from do_mount(), or obex-data-server */
++static gboolean
++_get_numbers_from_usb_path (const char *path, int *usb_bus_num, int *usb_device_num, int *usb_intf_num)
++{
++ char **tokens;
++ char *endp;
++ gboolean has_brackets = FALSE;
++
++ if (path == NULL)
++ return FALSE;
++ if (*path == '[')
++ {
++ path++;
++ has_brackets = TRUE;
++ }
++
++ tokens = g_strsplit (path + 4, ",", 0);
++ if (g_strv_length (tokens) != 3)
++ {
++ g_strfreev (tokens);
++ return FALSE;
++ }
++
++ *usb_bus_num = strtol (tokens[0], &endp, 10);
++ if (*endp != '\0')
++ {
++ g_strfreev (tokens);
++ return FALSE;
++ }
++
++ *usb_device_num = strtol (tokens[1], &endp, 10);
++ if (*endp != '\0')
++ {
++ g_strfreev (tokens);
++ return FALSE;
++ }
++
++ *usb_intf_num = strtol (tokens[2], &endp, 10);
++ if ((has_brackets && *endp != ']') || (!has_brackets && *endp != '\0'))
++ {
++ g_strfreev (tokens);
++ return FALSE;
++ }
++
++ g_strfreev (tokens);
++
++ return TRUE;
++}
++
++/* Used to detect broken listings from
++ * old Nokia 3650s */
++static gboolean
++_is_nokia_3650 (const char *bdaddr)
++{
++ if (!bdaddr)
++ return FALSE;
++
++ /* Don't ask, Nokia seem to use a Bluetooth
++ * HCI from Murata */
++ return g_str_has_prefix(bdaddr, "00:60:57");
++}
++
++static char *
++_get_bluetooth_name_and_icon (DBusGProxy *device,
++ char **icon_name,
++ char **symbolic_icon_name)
++{
++ GHashTable *hash;
++
++ if (dbus_g_proxy_call (device, "GetProperties", NULL,
++ G_TYPE_INVALID, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
++ &hash, G_TYPE_INVALID) != FALSE)
++ {
++ GValue *value;
++ char *name;
++
++ value = g_hash_table_lookup (hash, "Alias");
++ name = value ? g_value_dup_string(value) : NULL;
++
++ value = g_hash_table_lookup (hash, "Icon");
++ if (value)
++ {
++ *icon_name = g_value_dup_string (value);
++ }
++ else
++ {
++ *icon_name = g_strdup ("bluetooth");
++ }
++
++ *symbolic_icon_name = g_strdup ("bluetooth-symbolic");
++
++ g_hash_table_destroy (hash);
++ return name;
++ }
++
++ return NULL;
++}
++
++
++#define _DBUS_POINTER_SHIFT(p) ((void*) (((char*)p) + sizeof (void*)))
++#define DBUS_G_CONNECTION_FROM_CONNECTION(x) ((DBusGConnection*) _DBUS_POINTER_SHIFT(x))
++
++static gchar *
++_get_bluetooth_device_properties (const char *bdaddr,
++ char **icon_name,
++ char **symbolic_icon_name)
++{
++ DBusConnection *conn;
++ DBusGProxy *manager;
++ GPtrArray *adapters;
++ gchar *name;
++ guint i;
++
++ name = NULL;
++
++ conn = dbus_bus_get_private (DBUS_BUS_SYSTEM, NULL);
++ if (conn == NULL)
++ return name;
++
++ manager = dbus_g_proxy_new_for_name (DBUS_G_CONNECTION_FROM_CONNECTION(conn), "org.bluez",
++ "/", "org.bluez.Manager");
++ if (manager == NULL)
++ {
++ dbus_connection_close (conn);
++ dbus_connection_unref (conn);
++ return name;
++ }
++
++ if (dbus_g_proxy_call (manager, "ListAdapters", NULL, G_TYPE_INVALID, dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &adapters, G_TYPE_INVALID) == FALSE)
++ {
++ g_object_unref (manager);
++ dbus_connection_close (conn);
++ dbus_connection_unref (conn);
++ return name;
++ }
++
++ for (i = 0; i < adapters->len && name == NULL; i++)
++ {
++ DBusGProxy *adapter;
++ char *device_path;
++
++ adapter = dbus_g_proxy_new_for_name (DBUS_G_CONNECTION_FROM_CONNECTION(conn), "org.bluez",
++ g_ptr_array_index (adapters, i), "org.bluez.Adapter");
++ if (dbus_g_proxy_call (adapter, "FindDevice", NULL,
++ G_TYPE_STRING, bdaddr, G_TYPE_INVALID,
++ DBUS_TYPE_G_OBJECT_PATH, &device_path, G_TYPE_INVALID) != FALSE)
++ {
++ DBusGProxy *device;
++ device = dbus_g_proxy_new_for_name (DBUS_G_CONNECTION_FROM_CONNECTION(conn), "org.bluez", device_path, "org.bluez.Device");
++ name = _get_bluetooth_name_and_icon (device, icon_name, symbolic_icon_name);
++ g_object_unref (device);
++ }
++ g_object_unref (adapter);
++ }
++
++ g_ptr_array_free (adapters, TRUE);
++ g_object_unref (manager);
++ dbus_connection_close (conn);
++ dbus_connection_unref (conn);
++
++ return name;
++}
++
++#ifdef HAVE_HAL
++static gboolean
++_is_same_path (const char *path, int usb_bus_num, int usb_device_num, int usb_intf_num)
++{
++ int bus, dev, intf;
++
++ if (!_get_numbers_from_usb_path (path, &bus, &dev, &intf))
++ return FALSE;
++
++ if (bus == usb_bus_num &&
++ dev == usb_device_num &&
++ intf == usb_intf_num)
++ return TRUE;
++
++ return FALSE;
++}
++
++static int
++_find_ods_usb_intfnum (DBusGProxy *obex_manager, int device_usb_bus_num, int device_usb_device_num, int device_usb_interface_num)
++{
++ int i, n;
++ GHashTable *hash;
++
++ if (obex_manager == NULL)
++ return -1;
++
++ if (dbus_g_proxy_call (obex_manager, "GetUsbInterfacesNum", NULL, G_TYPE_INVALID, G_TYPE_UINT, &n, G_TYPE_INVALID) == FALSE)
++ return -1;
++
++ for (i = 0; i < n; i++)
++ {
++ if (dbus_g_proxy_call (obex_manager, "GetUsbInterfaceInfo", NULL,
++ G_TYPE_UINT, i, G_TYPE_INVALID,
++ DBUS_TYPE_G_STRING_STRING_HASHTABLE, &hash,
++ G_TYPE_INVALID) != FALSE)
++ {
++ const char* ods_path = g_hash_table_lookup (hash, "Path");
++
++ if (ods_path == NULL)
++ {
++ g_hash_table_destroy (hash);
++ return -2;
++ }
++
++ if (_is_same_path (ods_path, device_usb_bus_num, device_usb_device_num, device_usb_interface_num))
++ {
++ g_hash_table_destroy (hash);
++ return i;
++ }
++ g_hash_table_destroy (hash);
++ }
++ }
++ return -1;
++}
++#endif
++
++static gint
++_get_usb_intfnum_and_properties (DBusGProxy *obex_manager,
++ const char *device,
++ char **display_name,
++ char **icon_name,
++ char **symbolic_icon_name)
++{
++ int usb_bus_num;
++ int usb_device_num;
++ int usb_intf_num;
++#ifdef HAVE_HAL
++ char **obex_devices;
++ int num_obex_devices;
++ int n;
++ DBusError dbus_error;
++ DBusConnection *dbus_connection;
++ LibHalContext *hal_ctx;
++#endif
++ int ods_intf_num = 1;
++
++ /* Parse the [usb:1,41,3] string */
++ if (!g_str_has_prefix (device, "[usb:"))
++ {
++ return -1;
++ }
++
++ if (!_get_numbers_from_usb_path (device, &usb_bus_num, &usb_device_num, &usb_intf_num))
++ return -1;
++
++ g_message ("Parsed '%s' into bus=%d device=%d interface=%d", device, usb_bus_num, usb_device_num, usb_intf_num);
++
++#ifdef HAVE_HAL
++ dbus_error_init (&dbus_error);
++ dbus_connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbus_error);
++ if (dbus_error_is_set (&dbus_error))
++ {
++ dbus_error_free (&dbus_error);
++ return -1;
++ }
++ dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
++
++ hal_ctx = libhal_ctx_new ();
++ if (hal_ctx == NULL)
++ {
++ dbus_connection_close (dbus_connection);
++ dbus_connection_unref (dbus_connection);
++ dbus_error_free (&dbus_error);
++ return -1;
++ }
++
++ libhal_ctx_set_dbus_connection (hal_ctx, dbus_connection);
++ if (!libhal_ctx_init (hal_ctx, &dbus_error))
++ {
++ libhal_ctx_free (hal_ctx);
++ dbus_connection_close (dbus_connection);
++ dbus_connection_unref (dbus_connection);
++ dbus_error_free (&dbus_error);
++ return -1;
++ }
++ obex_devices = libhal_find_device_by_capability (hal_ctx, "obex", &num_obex_devices, NULL);
++
++ for (n = 0; n < num_obex_devices; n++)
++ {
++ char *udi = obex_devices[n];
++ LibHalPropertySet *ps;
++
++ ps = libhal_device_get_all_properties (hal_ctx, udi, NULL);
++ if (ps != NULL)
++ {
++ const char *subsystem;
++
++ subsystem = libhal_ps_get_string (ps, "info.subsystem");
++ if (subsystem != NULL && strcmp (subsystem, "usb") == 0)
++ {
++ int device_usb_bus_num;
++ int device_usb_device_num;
++ int device_usb_interface_num;
++ const char *icon_from_hal;
++ const char *name_from_hal;
++
++ device_usb_bus_num = libhal_ps_get_int32 (ps, "usb.bus_number");
++ device_usb_device_num = libhal_ps_get_int32 (ps, "usb.linux.device_number");
++ device_usb_interface_num = libhal_ps_get_int32 (ps, "usb.interface.number");
++ icon_from_hal = libhal_ps_get_string (ps, "info.desktop.icon");
++ name_from_hal = libhal_ps_get_string (ps, "info.desktop.name");
++
++ g_message ("looking at usb device '%s' with bus=%d, device=%d interface=%d",
++ udi, device_usb_bus_num, device_usb_device_num, device_usb_interface_num);
++
++ if (device_usb_bus_num == usb_bus_num &&
++ device_usb_device_num == usb_device_num &&
++ device_usb_interface_num == usb_intf_num)
++ {
++ const char *parent_udi;
++
++ g_message ("udi '%s' matches %s", udi, device);
++ parent_udi = libhal_ps_get_string (ps, "info.parent");
++
++ if (parent_udi != NULL)
++ {
++ LibHalPropertySet *ps2;
++
++ ps2 = libhal_device_get_all_properties (hal_ctx, parent_udi, NULL);
++ if (ps2 != NULL)
++ {
++ ods_intf_num = _find_ods_usb_intfnum (obex_manager,
++ device_usb_bus_num,
++ device_usb_device_num,
++ device_usb_interface_num);
++
++ if (ods_intf_num >= 0)
++ {
++ if (icon_from_hal != NULL)
++ *icon_name = g_strdup (icon_from_hal);
++ else
++ *icon_name = "drive-removable-media-usb";
++
++ *symbolic_icon_name = g_strdup ("drive-removable-media-symbolic");
++
++ if (name_from_hal != NULL)
++ *display_name = g_strdup (name_from_hal);
++ else
++ *display_name = g_strdup_printf ("%s - %s",
++ libhal_ps_get_string (ps2, "usb_device.vendor"),
++ libhal_ps_get_string (ps2, "usb_device.product"));
++
++ libhal_free_property_set (ps2);
++ libhal_free_property_set (ps);
++
++ goto end;
++ }
++ else if (ods_intf_num == -2)
++ {
++ libhal_free_property_set (ps2);
++ libhal_free_property_set (ps);
++
++ goto end;
++ }
++ libhal_free_property_set (ps2);
++ }
++ }
++ }
++ }
++ libhal_free_property_set (ps);
++ }
++ }
++
++end:
++ libhal_free_string_array (obex_devices);
++ libhal_ctx_free (hal_ctx);
++
++ dbus_connection_close (dbus_connection);
++ dbus_connection_unref (dbus_connection);
++ dbus_error_free (&dbus_error);
++#endif /* HAVE_HAL */
++
++ return ods_intf_num;
++}
++
++static void
++g_vfs_backend_obexftp_finalize (GObject *object)
++{
++ GVfsBackendObexftp *backend;
++
++ backend = G_VFS_BACKEND_OBEXFTP (object);
++
++ g_free (backend->display_name);
++ g_free (backend->bdaddr);
++ g_free (backend->icon_name);
++ g_free (backend->symbolic_icon_name);
++ g_free (backend->files_listing);
++ g_free (backend->directory);
++
++ if (backend->session_proxy != NULL)
++ g_object_unref (backend->session_proxy);
++ g_mutex_clear (&backend->mutex);
++ g_cond_clear (&backend->cond);
++
++ 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)
++{
++ DBusConnection *conn;
++ DBusGConnection *connection;
++ DBusError error;
++
++ /* Otherwise dbus-glib doesn't setup it value types */
++ connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
++
++ if (connection != NULL)
++ dbus_g_connection_unref (connection);
++
++ /* Connect to the session bus via a private connection */
++ dbus_error_init (&error);
++ conn = dbus_bus_get_private (DBUS_BUS_SESSION, &error);
++ if (conn == NULL) {
++ g_printerr ("Connecting to session bus failed: %s\n", error.message);
++ dbus_error_free (&error);
++ return;
++ }
++ dbus_connection_setup_with_g_main (conn, NULL);
++
++ backend->connection = dbus_connection_get_g_connection (conn);
++ if (backend->connection == NULL) {
++ g_printerr ("Connecting to session bus failed\n");
++ return;
++ }
++
++ g_mutex_init (&backend->mutex);
++ g_cond_init (&backend->cond);
++
++ backend->manager_proxy = dbus_g_proxy_new_for_name (backend->connection,
++ "org.openobex",
++ "/org/openobex",
++ "org.openobex.Manager");
++
++ dbus_g_proxy_add_signal(backend->manager_proxy, "SessionConnectError",
++ DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
++ dbus_g_proxy_connect_signal(backend->manager_proxy, "SessionConnectError",
++ G_CALLBACK(session_connect_error_cb), backend, NULL);
++ dbus_g_proxy_add_signal(backend->manager_proxy, "SessionConnected",
++ DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
++ dbus_g_proxy_connect_signal(backend->manager_proxy, "SessionConnected",
++ G_CALLBACK(session_connected_cb), backend, NULL);
++}
++
++static gboolean
++_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, ¤t_path, G_TYPE_INVALID) == FALSE)
++ {
++ g_message ("GetCurrentPath failed");
++ return FALSE;
++ }
++
++ if (strcmp (filename, current_path) == 0)
++ {
++ 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");
++ //FIXME change the retval from org.openobex.Error.NotAuthorized to
++ //no such file or directory
++ 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 gboolean
++_retrieve_folder_listing (GVfsBackend *backend,
++ const char *filename,
++ char **files,
++ GError **error)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ time_t current;
++
++ current = time (NULL);
++
++ if (op_backend->directory != NULL &&
++ strcmp (op_backend->directory, filename) == 0 &&
++ op_backend->time_captured > current - CACHE_LIFESPAN)
++ {
++ *files = g_strdup (op_backend->files_listing);
++ return TRUE;
++ }
++ else
++ {
++ g_free (op_backend->directory);
++ op_backend->directory = NULL;
++ g_free (op_backend->files_listing);
++ op_backend->files_listing = NULL;
++ }
++
++ if (dbus_g_proxy_call (op_backend->session_proxy, "RetrieveFolderListing", error,
++ G_TYPE_INVALID,
++ G_TYPE_STRING, files, G_TYPE_INVALID) == FALSE)
++ {
++ return FALSE;
++ }
++
++ op_backend->directory = g_strdup (filename);
++ op_backend->files_listing = g_strdup (*files);
++ op_backend->time_captured = time (NULL);
++
++ return TRUE;
++}
++
++static gboolean
++_query_file_info_helper (GVfsBackend *backend,
++ const char *filename,
++ GFileInfo *info,
++ GError **error)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ char *parent, *basename, *files;
++ GList *elements, *l;
++ gboolean found;
++
++ g_debug ("+ _query_file_info_helper, filename: %s\n", filename);
++
++ if (strcmp (filename, "/") == 0)
++ {
++ char *display;
++
++ /* That happens when you want '/'
++ * and we don't have any info about it :( */
++ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
++ g_file_info_set_content_type (info, "inode/directory");
++ g_file_info_set_name (info, "/");
++ if (op_backend->icon_name) {
++ GIcon *icon;
++
++ g_vfs_backend_set_icon_name (backend, op_backend->icon_name);
++ icon = g_themed_icon_new (op_backend->icon_name);
++ g_file_info_set_icon (info, icon);
++ g_object_unref (icon);
++ }
++ if (op_backend->symbolic_icon_name) {
++ GIcon *icon;
++
++ g_vfs_backend_set_icon_name (backend, op_backend->symbolic_icon_name);
++ icon = g_themed_icon_new (op_backend->symbolic_icon_name);
++ g_file_info_set_symbolic_icon (info, icon);
++ g_object_unref (icon);
++ }
++ display = g_strdup_printf (_("%s on %s"), "/", op_backend->display_name);
++ g_file_info_set_display_name (info, display);
++ g_free (display);
++ return TRUE;
++ }
++
++ parent = g_path_get_dirname (filename);
++ if (_change_directory (op_backend, parent, error) == FALSE)
++ {
++ g_free (parent);
++ return FALSE;
++ }
++
++ files = NULL;
++ if (_retrieve_folder_listing (backend, parent, &files, error) == FALSE)
++ {
++ g_free (parent);
++ return FALSE;
++ }
++
++ g_free (parent);
++
++ if (gvfsbackendobexftp_fl_parser_parse (files, strlen (files), &elements, error) == FALSE)
++ {
++ g_free (files);
++ return FALSE;
++ }
++ 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);
++ found = TRUE;
++ break;
++ }
++ }
++
++ if (found == FALSE)
++ {
++ g_set_error_literal (error, G_IO_ERROR,
++ G_IO_ERROR_NOT_FOUND,
++ g_strerror (ENOENT));
++ }
++
++ g_free (basename);
++ g_list_free_full (elements, g_object_unref);
++
++ g_debug ("- _query_file_info_helper\n");
++
++ return found;
++}
++
++static void
++_invalidate_cache_helper (GVfsBackendObexftp *op_backend)
++{
++ g_free (op_backend->directory);
++ op_backend->directory = NULL;
++}
++
++static void
++error_occurred_cb (DBusGProxy *proxy, const gchar *error_name, const gchar *error_message, gpointer user_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (user_data);
++
++ g_message("ErrorOccurred");
++ g_message("Error name: %s", error_name);
++ g_message("Error message: %s", error_message);
++
++ if (strcmp (error_name, "org.openobex.Error.LinkError") == 0)
++ {
++ g_message ("link lost to remote device");
++ g_vfs_backend_force_unmount ((GVfsBackend*)op_backend);
++ return;
++ }
++
++ /* Something is waiting on us */
++ g_mutex_lock (&op_backend->mutex);
++ if (op_backend->doing_io)
++ {
++ op_backend->status = ASYNC_ERROR;
++ op_backend->error = g_error_new_literal (DBUS_GERROR,
++ DBUS_GERROR_REMOTE_EXCEPTION,
++ error_message);
++ g_cond_signal (&op_backend->cond);
++ g_mutex_unlock (&op_backend->mutex);
++ return;
++ }
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_message ("Unhandled error, file a bug");
++ g_vfs_backend_force_unmount ((GVfsBackend*)op_backend);
++}
++
++static void
++session_connect_error_cb (DBusGProxy *proxy,
++ const char *session_object,
++ const gchar *error_name,
++ const gchar *error_message,
++ gpointer user_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (user_data);
++
++ g_mutex_lock (&op_backend->mutex);
++ op_backend->status = ASYNC_ERROR;
++ op_backend->error = g_error_new_literal (DBUS_GERROR,
++ DBUS_GERROR_REMOTE_EXCEPTION,
++ error_message);
++ g_cond_signal (&op_backend->cond);
++ g_mutex_unlock (&op_backend->mutex);
++}
++
++static void
++session_connected_cb (DBusGProxy *proxy,
++ const char *session_object,
++ gpointer user_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (user_data);
++
++ g_mutex_lock (&op_backend->mutex);
++ op_backend->status = ASYNC_SUCCESS;
++ g_cond_signal (&op_backend->cond);
++ g_mutex_unlock (&op_backend->mutex);
++}
++
++static void
++cancelled_cb (DBusGProxy *proxy, gpointer user_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (user_data);
++
++ g_message ("transfer got cancelled");
++
++ g_mutex_lock (&op_backend->mutex);
++ op_backend->status = ASYNC_ERROR;
++ g_cond_signal (&op_backend->cond);
++ g_mutex_unlock (&op_backend->mutex);
++}
++
++static void
++disconnected_cb (DBusGProxy *proxy, gpointer user_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (user_data);
++
++ g_message ("disconnected_cb");
++ g_vfs_backend_force_unmount ((GVfsBackend*)op_backend);
++}
++
++static void
++closed_cb (DBusGProxy *proxy, gpointer user_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (user_data);
++
++ g_message ("closed_cb");
++ g_vfs_backend_force_unmount ((GVfsBackend*)op_backend);
++}
++
++static void
++do_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;
++ GError *error = NULL;
++ const gchar *path = NULL;
++ GMountSpec *obexftp_mount_spec;
++ guint count;
++
++ g_debug ("+ do_mount\n");
++
++ device = g_mount_spec_get (mount_spec, "host");
++
++ if (device == NULL || (strlen (device) != BDADDR_LEN + 2 && !g_str_has_prefix(device, "[usb:")) )
++ {
++ g_vfs_job_failed (G_VFS_JOB (job),
++ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
++ _("Invalid mount spec"));
++ return;
++ }
++
++ op_backend->bdaddr = NULL;
++ op_backend->usbintfnum = -1;
++
++ if (!g_str_has_prefix(device, "[usb:"))
++ {
++ /* Strip the brackets */
++ op_backend->bdaddr = g_strndup (device + 1, BDADDR_LEN);
++ if (bachk (op_backend->bdaddr) < 0)
++ {
++ g_free (op_backend->bdaddr);
++ g_vfs_job_failed (G_VFS_JOB (job),
++ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
++ _("Invalid mount spec"));
++ return;
++ }
++ }
++ else
++ {
++ op_backend->usbintfnum = _get_usb_intfnum_and_properties (op_backend->manager_proxy, device, &op_backend->display_name, &op_backend->icon_name, &op_backend->symbolic_icon_name);
++ if (op_backend->usbintfnum < 0)
++ {
++ if (op_backend->usbintfnum == -2)
++ {
++ g_vfs_job_failed (G_VFS_JOB (job),
++ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
++ _("USB support missing. Please contact your software vendor"));
++ }
++ else
++ {
++ g_vfs_job_failed (G_VFS_JOB (job),
++ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
++ _("Invalid mount spec"));
++ }
++ return;
++ }
++ }
++
++ /* FIXME, Have a way for the mount to be cancelled, see:
++ * Use CancelSessionConnect */
++ op_backend->status = ASYNC_PENDING;
++
++ if (op_backend->bdaddr)
++ {
++ if (dbus_g_proxy_call (op_backend->manager_proxy, "CreateBluetoothSession", &error,
++ G_TYPE_STRING, op_backend->bdaddr, G_TYPE_STRING, "00:00:00:00:00:00", G_TYPE_STRING, "ftp", G_TYPE_INVALID,
++ DBUS_TYPE_G_OBJECT_PATH, &path, G_TYPE_INVALID) == FALSE)
++ {
++ g_free (op_backend->bdaddr);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++ op_backend->display_name = _get_bluetooth_device_properties (op_backend->bdaddr, &op_backend->icon_name, &op_backend->symbolic_icon_name);
++ if (!op_backend->display_name)
++ op_backend->display_name = g_strdelimit (g_strdup (op_backend->bdaddr), ":", '-');
++ if (!op_backend->icon_name)
++ op_backend->icon_name = g_strdup ("bluetooth");
++ if (!op_backend->symbolic_icon_name)
++ op_backend->symbolic_icon_name = g_strdup ("bluetooth-symbolic");
++ g_debug (" do_mount: %s (%s) mounted\n", op_backend->display_name, op_backend->bdaddr);
++ }
++ else
++ {
++ if (dbus_g_proxy_call (op_backend->manager_proxy, "CreateUsbSession", &error,
++ G_TYPE_UINT, op_backend->usbintfnum, G_TYPE_STRING, "ftp", G_TYPE_INVALID,
++ DBUS_TYPE_G_OBJECT_PATH, &path, G_TYPE_INVALID) == FALSE)
++ {
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++ g_debug (" do_mount: usb interface %d mounted\n", op_backend->usbintfnum);
++ }
++
++ g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);
++
++ op_backend->session_proxy = dbus_g_proxy_new_for_name (op_backend->connection,
++ "org.openobex",
++ path,
++ "org.openobex.Session");
++
++ g_vfs_backend_set_display_name (G_VFS_BACKEND (op_backend),
++ op_backend->display_name);
++ g_vfs_backend_set_icon_name (G_VFS_BACKEND (op_backend), op_backend->icon_name);
++ g_vfs_backend_set_symbolic_icon_name (G_VFS_BACKEND (op_backend), op_backend->symbolic_icon_name);
++
++ obexftp_mount_spec = g_mount_spec_new ("obex");
++ g_mount_spec_set (obexftp_mount_spec, "host", device);
++ g_vfs_backend_set_mount_spec (G_VFS_BACKEND (op_backend), obexftp_mount_spec);
++ g_mount_spec_unref (obexftp_mount_spec);
++
++ dbus_g_proxy_add_signal(op_backend->session_proxy, "ErrorOccurred",
++ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
++ dbus_g_proxy_connect_signal(op_backend->session_proxy, "ErrorOccurred",
++ G_CALLBACK(error_occurred_cb), op_backend, NULL);
++
++ dbus_g_proxy_add_signal(op_backend->session_proxy, "Cancelled",
++ G_TYPE_INVALID);
++ dbus_g_proxy_connect_signal(op_backend->session_proxy, "Cancelled",
++ G_CALLBACK(cancelled_cb), op_backend, NULL);
++
++ dbus_g_proxy_add_signal(op_backend->session_proxy, "Disconnected",
++ G_TYPE_INVALID);
++ dbus_g_proxy_connect_signal(op_backend->session_proxy, "Disconnected",
++ G_CALLBACK(disconnected_cb), op_backend, NULL);
++
++ dbus_g_proxy_add_signal(op_backend->session_proxy, "Closed",
++ G_TYPE_INVALID);
++ dbus_g_proxy_connect_signal(op_backend->session_proxy, "Closed",
++ G_CALLBACK(closed_cb), op_backend, NULL);
++
++ dbus_g_proxy_add_signal(op_backend->session_proxy, "TransferStarted",
++ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INVALID);
++ dbus_g_proxy_add_signal(op_backend->session_proxy, "TransferCompleted",
++ G_TYPE_INVALID);
++ dbus_g_proxy_add_signal(op_backend->session_proxy, "TransferProgress",
++ G_TYPE_UINT64, G_TYPE_INVALID);
++
++ /* Now wait until the device is connected */
++ count = 0;
++ g_mutex_lock (&op_backend->mutex);
++
++ while (op_backend->status == ASYNC_PENDING && count < 100) {
++ gint64 end_time;
++ end_time = g_get_monotonic_time () + 100 * G_TIME_SPAN_MILLISECOND;
++ count++;
++ if (g_cond_wait_until (&op_backend->cond, &op_backend->mutex, end_time) != FALSE)
++ break;
++ }
++ g_mutex_unlock (&op_backend->mutex);
++
++ if (op_backend->status == ASYNC_ERROR || op_backend->status == ASYNC_PENDING)
++ {
++ g_message ("mount failed, didn't connect");
++
++ g_free (op_backend->display_name);
++ op_backend->display_name = NULL;
++ g_free (op_backend->bdaddr);
++ op_backend->bdaddr = NULL;
++ g_object_unref (op_backend->session_proxy);
++ op_backend->session_proxy = NULL;
++
++ if (op_backend->status != ASYNC_PENDING)
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), op_backend->error);
++ else
++ g_vfs_job_failed (G_VFS_JOB (job),
++ G_IO_ERROR, G_IO_ERROR_BUSY,
++ _("Connection to the device lost"));
++ return;
++ }
++
++ op_backend->status = ASYNC_PENDING;
++
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ g_debug ("- do_mount\n");
++}
++
++static void
++transfer_started_cb (DBusGProxy *proxy, const gchar *filename,
++ const gchar *local_path, guint64 byte_count, gpointer user_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (user_data);
++
++ g_message ("transfer of %s to %s started", filename, local_path);
++
++ g_mutex_lock (&op_backend->mutex);
++ op_backend->status = ASYNC_SUCCESS;
++ g_cond_signal (&op_backend->cond);
++ g_mutex_unlock (&op_backend->mutex);
++}
++
++static void
++do_open_for_read (GVfsBackend *backend,
++ GVfsJobOpenForRead *job,
++ const char *filename)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ GError *error = NULL;
++ ObexFTPOpenHandle *handle;
++ char *target, *basename;
++ GFileInfo *info;
++ goffset size;
++ int fd, success;
++
++ g_debug ("+ do_open_for_read, filename: %s\n", filename);
++
++ g_mutex_lock (&op_backend->mutex);
++ op_backend->doing_io = TRUE;
++
++ /* Change into the directory and cache the file size */
++ info = g_file_info_new ();
++ if (_query_file_info_helper (backend, filename, info, &error) == FALSE)
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ g_object_unref (info);
++ return;
++ }
++ /* If we're trying to open a directory for reading, exit out */
++ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_IS_DIRECTORY,
++ _("Can't open directory"));
++ g_object_unref (info);
++ return;
++ }
++
++ size = g_file_info_get_size (info);
++ g_object_unref (info);
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return;
++ }
++
++ fd = g_file_open_tmp ("gvfsobexftp-tmp-XXXXXX",
++ &target, &error);
++ if (fd < 0)
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return;
++ }
++
++ op_backend->status = ASYNC_PENDING;
++
++ dbus_g_proxy_connect_signal(op_backend->session_proxy, "TransferStarted",
++ G_CALLBACK(transfer_started_cb), op_backend, NULL);
++
++ basename = g_path_get_basename (filename);
++ if (dbus_g_proxy_call (op_backend->session_proxy, "CopyRemoteFile", &error,
++ G_TYPE_STRING, basename,
++ G_TYPE_STRING, target,
++ G_TYPE_INVALID,
++ G_TYPE_INVALID) == FALSE)
++ {
++ g_message ("CopyRemoteFile failed");
++
++ g_free (basename);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++
++ dbus_g_proxy_disconnect_signal(op_backend->session_proxy, "TransferStarted",
++ G_CALLBACK(transfer_started_cb), op_backend);
++
++ /* Close the target */
++ g_unlink (target);
++ g_free (target);
++ close (fd);
++
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ return;
++ }
++
++ /* Wait for TransferStarted or ErrorOccurred to have happened */
++ while (op_backend->status == ASYNC_PENDING)
++ g_cond_wait (&op_backend->cond, &op_backend->mutex);
++ success = op_backend->status;
++ dbus_g_proxy_disconnect_signal(op_backend->session_proxy, "TransferStarted",
++ G_CALLBACK(transfer_started_cb), op_backend);
++
++ /* We either got success or an async error */
++ g_assert (success != ASYNC_PENDING);
++
++ g_message ("filename: %s (%s) copying to %s (retval %d)", filename, basename, target, success);
++ g_free (basename);
++
++ g_unlink (target);
++ g_free (target);
++ op_backend->status = ASYNC_PENDING;
++
++ if (success == ASYNC_ERROR)
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ close (fd);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job),
++ op_backend->error);
++ g_error_free (op_backend->error);
++ op_backend->error = NULL;
++ return;
++ }
++
++ handle = g_new0 (ObexFTPOpenHandle, 1);
++ handle->source = g_strdup (filename);
++ handle->fd = fd;
++ handle->size = size;
++ g_vfs_job_open_for_read_set_handle (job, handle);
++
++ g_debug ("- do_open_for_read, filename: %s\n", filename);
++
++ g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), FALSE);
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++}
++
++static int
++is_busy (DBusGProxy *session_proxy, GVfsJob *job)
++{
++ GError *error = NULL;
++ gboolean busy;
++
++ if (dbus_g_proxy_call (session_proxy, "IsBusy", &error,
++ G_TYPE_INVALID,
++ G_TYPE_BOOLEAN, &busy, G_TYPE_INVALID) == FALSE)
++ {
++ g_vfs_job_failed_from_error (job, error);
++ g_error_free (error);
++ return -1;
++ }
++
++ return busy;
++}
++
++static void
++do_read (GVfsBackend *backend,
++ GVfsJobRead *job,
++ GVfsBackendHandle handle,
++ char *buffer,
++ gsize bytes_requested)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ ObexFTPOpenHandle *backend_handle = (ObexFTPOpenHandle *) handle;
++ ssize_t bytes_read = 0;
++ gboolean busy = TRUE;
++
++ while (bytes_read == 0 && busy != FALSE)
++ {
++ bytes_read = read (backend_handle->fd, buffer, bytes_requested);
++ if (bytes_read != 0)
++ break;
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return;
++ }
++
++ busy = is_busy (op_backend->session_proxy, G_VFS_JOB (job));
++ if (busy < 0)
++ return;
++
++ g_usleep (G_USEC_PER_SEC / 100);
++ }
++
++ if (bytes_read < 0)
++ {
++ g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
++ }
++ else if (bytes_read == 0)
++ {
++ g_vfs_job_read_set_size (job, 0);
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++ }
++ else
++ {
++ g_vfs_job_read_set_size (job, bytes_read);
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++ }
++}
++
++static void
++do_close_read (GVfsBackend *backend,
++ GVfsJobCloseRead *job,
++ GVfsBackendHandle handle)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ ObexFTPOpenHandle *backend_handle = (ObexFTPOpenHandle *) handle;
++ int busy;
++
++ g_debug ("+ do_close_read\n");
++
++ busy = is_busy (op_backend->session_proxy, G_VFS_JOB (job));
++ if (busy < 0) {
++ g_message ("busy error");
++ return;
++ }
++
++ g_mutex_lock (&op_backend->mutex);
++
++ if (busy > 0)
++ {
++ op_backend->status = ASYNC_PENDING;
++
++ if (dbus_g_proxy_call (op_backend->session_proxy, "Cancel", NULL,
++ G_TYPE_INVALID, G_TYPE_INVALID) != FALSE)
++ {
++ while (op_backend->status == ASYNC_PENDING)
++ g_cond_wait (&op_backend->cond, &op_backend->mutex);
++ }
++ }
++
++ g_mutex_unlock (&op_backend->mutex);
++
++ close (backend_handle->fd);
++ g_free (backend_handle->source);
++ g_free (backend_handle);
++
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ g_debug ("- do_close_read\n");
++}
++
++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);
++ GError *error = NULL;
++
++ g_debug ("+ do_query_info, filename: %s\n", filename);
++
++ g_mutex_lock (&op_backend->mutex);
++
++ if (_query_file_info_helper (backend, filename, info, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ g_debug ("- do_query_info\n");
++
++ return;
++}
++
++static void
++do_query_fs_info (GVfsBackend *backend,
++ GVfsJobQueryFsInfo *job,
++ const char *filename,
++ GFileInfo *info,
++ GFileAttributeMatcher *attribute_matcher)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ OvuCaps *caps;
++ OvuCapsMemory *memory;
++ GError *error = NULL;
++ char *caps_str;
++ const char *mem_type;
++ GList *l;
++ gboolean has_free_memory;
++
++ g_debug ("+ do_query_fs_info, filename: %s\n", filename);
++
++ g_mutex_lock (&op_backend->mutex);
++
++ /* Get the capabilities */
++ if (dbus_g_proxy_call (op_backend->session_proxy, "GetCapability", &error,
++ G_TYPE_INVALID,
++ G_TYPE_STRING, &caps_str, G_TYPE_INVALID) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ g_free (caps_str);
++ return;
++ }
++
++ /* No caps from the server? */
++ if (caps_str == NULL)
++ {
++ /* Best effort, don't error out */
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++ return;
++ }
++
++ caps = ovu_caps_parser_parse (caps_str, strlen (caps_str), &error);
++ g_free (caps_str);
++ if (caps == NULL)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++
++ /* Check whether we have no free space available */
++ has_free_memory = FALSE;
++ for (l = ovu_caps_get_memory_entries (caps); l != NULL; l = l->next)
++ {
++ if (ovu_caps_memory_has_free (l->data) != FALSE)
++ {
++ has_free_memory = TRUE;
++ break;
++ }
++ }
++ if (has_free_memory == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ /* Best effort, don't error out */
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++ return;
++ }
++
++ /* Check whether we have only one memory type */
++ l = ovu_caps_get_memory_entries (caps);
++ if (g_list_length (l) == 1)
++ {
++ memory = l->data;
++ goto set_info_from_memory;
++ }
++
++ if (_query_file_info_helper (backend, filename, info, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ ovu_caps_free (caps);
++ return;
++ }
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ ovu_caps_free (caps);
++ return;
++ }
++
++ mem_type = NULL;
++ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_RDEV) != FALSE)
++ {
++ guint rdev;
++ rdev = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_RDEV);
++ mem_type = om_mem_type_id_to_string (rdev);
++ }
++ memory = ovu_caps_get_memory_type (caps, mem_type);
++
++set_info_from_memory:
++ if (memory != NULL && ovu_caps_memory_has_free (memory) != FALSE)
++ {
++ g_file_info_set_attribute_uint64 (info,
++ G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
++ ovu_caps_memory_get_free (memory));
++ if (ovu_caps_memory_has_used (memory) != FALSE)
++ {
++ g_file_info_set_attribute_uint64 (info,
++ G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,
++ ovu_caps_memory_get_free (memory)
++ + ovu_caps_memory_get_used (memory));
++ }
++ }
++ ovu_caps_free (caps);
++
++ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "obexftp");
++
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_debug ("- do_query_fs_info\n");
++}
++
++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_debug ("+ do_enumerate, filename: %s\n", filename);
++
++ g_mutex_lock (&op_backend->mutex);
++
++ if (_change_directory (op_backend, filename, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++
++ files = NULL;
++ if (_retrieve_folder_listing (backend, filename, &files, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ 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_mutex_unlock (&op_backend->mutex);
++ /* See http://web.archive.org/web/20070826221251/http://docs.kde.org/development/en/extragear-pim/kdebluetooth/components.kio_obex.html#devices
++ * for the reasoning */
++ if (strstr (files, "SYSTEM\"obex-folder-listing.dtd") != NULL && _is_nokia_3650 (op_backend->bdaddr))
++ {
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_NOT_SUPPORTED,
++ _("Device requires a software update"));
++ }
++ else
++ {
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ }
++ g_message ("gvfsbackendobexftp_fl_parser_parse failed");
++ g_free (files);
++ 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_free_full (elements, g_object_unref);
++ g_vfs_job_enumerate_done (job);
++
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_debug ("- do_enumerate\n");
++}
++
++typedef struct {
++ GVfsBackendObexftp *op_backend;
++ GFileProgressCallback progress_callback;
++ gpointer progress_callback_data;
++ goffset total_bytes;
++} PushData;
++
++static void
++push_transfer_started_cb (DBusGProxy *proxy,
++ const gchar *filename,
++ const gchar *local_path,
++ guint64 total_bytes,
++ gpointer user_data)
++{
++ PushData *job_data = (PushData *) user_data;
++ GVfsBackendObexftp *op_backend = job_data->op_backend;
++
++ g_message ("transfer of %s to %s started", filename, local_path);
++
++ g_mutex_lock (&op_backend->mutex);
++
++ op_backend->status = ASYNC_RUNNING;
++ job_data->total_bytes = (goffset) total_bytes;
++ if (job_data->progress_callback)
++ job_data->progress_callback (0, job_data->total_bytes,
++ job_data->progress_callback_data);
++
++ g_cond_signal (&op_backend->cond);
++ g_mutex_unlock (&op_backend->mutex);
++}
++
++static void
++push_transfer_completed_cb (DBusGProxy *proxy,
++ gpointer user_data)
++{
++ PushData *job_data = (PushData *) user_data;
++ GVfsBackendObexftp *op_backend = job_data->op_backend;
++
++ g_message ("transfer completed");
++
++ g_mutex_lock (&op_backend->mutex);
++
++ op_backend->status = ASYNC_SUCCESS;
++
++ g_cond_signal (&op_backend->cond);
++ g_mutex_unlock (&op_backend->mutex);
++}
++
++static void
++push_transfer_progress_cb (DBusGProxy *proxy,
++ guint64 bytes_transferred,
++ gpointer user_data)
++{
++ PushData *job_data = (PushData *) user_data;
++
++ g_message ("transfer progress");
++
++ if (job_data->progress_callback)
++ job_data->progress_callback ((goffset) bytes_transferred,
++ job_data->total_bytes,
++ job_data->progress_callback_data);
++}
++
++static void
++push_data_free (PushData *job_data)
++{
++ g_object_unref (job_data->op_backend);
++ g_slice_free (PushData, job_data);
++}
++
++static gboolean
++_push_single_file_helper (GVfsBackendObexftp *op_backend,
++ GVfsJobPush *job,
++ const char *local_path,
++ const char *destination,
++ GError **error,
++ PushData *job_data)
++{
++ char *dirname;
++ int success;
++
++ dirname = g_path_get_dirname (destination);
++
++ if (_change_directory (op_backend, dirname, error) == FALSE)
++ {
++ g_free (dirname);
++ return FALSE;
++ }
++
++ g_free (dirname);
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_set_error_literal (error, G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return FALSE;
++ }
++
++ op_backend->status = ASYNC_PENDING;
++
++ /* connect to the transfer signals */
++ dbus_g_proxy_connect_signal (op_backend->session_proxy, "TransferStarted",
++ G_CALLBACK (push_transfer_started_cb), job_data,
++ NULL);
++ dbus_g_proxy_connect_signal (op_backend->session_proxy, "TransferCompleted",
++ G_CALLBACK (push_transfer_completed_cb), job_data,
++ NULL);
++ dbus_g_proxy_connect_signal (op_backend->session_proxy, "TransferProgress",
++ G_CALLBACK (push_transfer_progress_cb), job_data,
++ NULL);
++
++ if (dbus_g_proxy_call (op_backend->session_proxy, "SendFile", error,
++ G_TYPE_STRING, local_path, G_TYPE_INVALID,
++ G_TYPE_INVALID) == FALSE)
++ {
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferStarted",
++ G_CALLBACK (push_transfer_started_cb), job_data);
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferCompleted",
++ G_CALLBACK (push_transfer_completed_cb), job_data);
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferProgress",
++ G_CALLBACK (push_transfer_progress_cb), job_data);
++ return FALSE;
++ }
++
++ /* wait for the TransferStarted or ErrorOccurred signal */
++ while (op_backend->status == ASYNC_PENDING)
++ g_cond_wait (&op_backend->cond, &op_backend->mutex);
++
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferStarted",
++ G_CALLBACK (push_transfer_started_cb), job_data);
++ success = op_backend->status;
++
++ /* we either got the operation running or an error */
++ if (success == ASYNC_ERROR)
++ {
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferCompleted",
++ G_CALLBACK (push_transfer_completed_cb), job_data);
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferProgress",
++ G_CALLBACK (push_transfer_progress_cb), job_data);
++
++ *error = g_error_copy (op_backend->error);
++ g_error_free (op_backend->error);
++ op_backend->error = NULL;
++ return FALSE;
++ }
++
++ while (op_backend->status == ASYNC_RUNNING)
++ g_cond_wait (&op_backend->cond, &op_backend->mutex);
++
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferCompleted",
++ G_CALLBACK (push_transfer_completed_cb), job_data);
++ dbus_g_proxy_disconnect_signal (op_backend->session_proxy, "TransferProgress",
++ G_CALLBACK (push_transfer_progress_cb), job_data);
++ success = op_backend->status;
++
++ /* same as before, either we have success or an error */
++ if (success == ASYNC_ERROR)
++ {
++ *error = g_error_copy (op_backend->error);
++ g_error_free (op_backend->error);
++ op_backend->error = NULL;
++ return FALSE;
++ }
++
++ return TRUE;
++}
++
++static void
++do_push (GVfsBackend *backend,
++ GVfsJobPush *job,
++ const char *destination,
++ const char *local_path,
++ GFileCopyFlags flags,
++ gboolean remove_source,
++ GFileProgressCallback progress_callback,
++ gpointer progress_callback_data)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ GError *error = NULL;
++ gboolean is_dir, overwrite, res;
++ GFileType target_type;
++ PushData *job_data;
++ GFileInfo *info;
++
++ g_debug ("+ do_push, destination: %s, local_path: %s\n", destination, local_path);
++
++ g_mutex_lock (&op_backend->mutex);
++ op_backend->doing_io = TRUE;
++
++ overwrite = (flags & G_FILE_COPY_OVERWRITE);
++ is_dir = g_file_test (local_path, G_FILE_TEST_IS_DIR);
++ info = g_file_info_new ();
++ target_type = 0;
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return;
++ }
++
++ res = _query_file_info_helper (backend, destination, info, &error);
++
++ if (error != NULL)
++ {
++ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++
++ return;
++ }
++ else
++ {
++ g_clear_error (&error);
++ res = TRUE;
++ }
++ }
++
++ if (res)
++ target_type = g_file_info_get_file_type (info);
++
++ g_object_unref (info);
++
++ /* error handling */
++ if (is_dir)
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++
++ if (target_type != 0)
++ {
++ if (overwrite)
++ {
++ if (target_type == G_FILE_TYPE_DIRECTORY)
++ {
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_WOULD_MERGE,
++ _("Can't copy directory over directory"));
++ return;
++ }
++ }
++ else
++ {
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_EXISTS,
++ _("Target file exists"));
++ return;
++ }
++ }
++
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_WOULD_RECURSE,
++ _("Can't recursively copy directory"));
++ return;
++ }
++ else
++ {
++ if (target_type != 0)
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++
++ if (overwrite)
++ {
++ if (target_type == G_FILE_TYPE_DIRECTORY)
++ {
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_IS_DIRECTORY,
++ _("Can't copy file over directory"));
++ return;
++ }
++ }
++ else
++ {
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_EXISTS,
++ _("Target file exists"));
++ return;
++ }
++ }
++ }
++
++ job_data = g_slice_new0 (PushData);
++ job_data->op_backend = g_object_ref (op_backend);
++ job_data->progress_callback = progress_callback;
++ job_data->progress_callback_data = progress_callback_data;
++
++ /* start the actual transfer operation */
++ if (_push_single_file_helper (op_backend, job, local_path, destination,
++ &error, job_data) == FALSE)
++ {
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++ push_data_free (job_data);
++
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ return;
++ }
++
++ push_data_free (job_data);
++
++ /* we called _query_file_info_helper (), so we need to invalidate the
++ * cache, as a query_info () will be called on us after we return.
++ */
++ _invalidate_cache_helper (op_backend);
++
++ if (remove_source && g_unlink (local_path) == -1)
++ {
++ int errsv = errno;
++
++ g_vfs_job_failed (G_VFS_JOB (job),
++ G_IO_ERROR,
++ g_io_error_from_errno (errsv),
++ _("Error deleting file: %s"),
++ g_strerror (errsv));
++ }
++ else
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ op_backend->doing_io = FALSE;
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_debug ("- do_push\n");
++}
++
++static void
++do_delete (GVfsBackend *backend,
++ GVfsJobDelete *job,
++ const char *filename)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ char *basename, *parent;
++ GError *error = NULL;
++ GFileInfo *info;
++
++ g_debug ("+ do_delete, filename: %s\n", filename);
++
++ g_mutex_lock (&op_backend->mutex);
++
++ /* Check whether we have a directory */
++ info = g_file_info_new ();
++ if (_query_file_info_helper (backend, filename, info, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ g_object_unref (info);
++ return;
++ }
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ g_object_unref (info);
++ return;
++ }
++
++ /* Get the listing of the directory, and abort if it's not empty */
++ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
++ {
++ GList *elements;
++ char *files;
++ guint len;
++
++ g_object_unref (info);
++
++ if (_change_directory (op_backend, filename, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return;
++ }
++
++ files = NULL;
++ if (_retrieve_folder_listing (backend, filename, &files, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ 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_mutex_unlock (&op_backend->mutex);
++ 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);
++
++ len = g_list_length (elements);
++ g_list_free_full (elements, g_object_unref);
++
++ if (len != 0)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_set_error_literal (&error, G_IO_ERROR,
++ G_IO_ERROR_NOT_EMPTY,
++ g_strerror (ENOTEMPTY));
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++ }
++ else
++ {
++ g_object_unref (info);
++ }
++
++ basename = g_path_get_basename (filename);
++ if (strcmp (basename, G_DIR_SEPARATOR_S) == 0
++ || strcmp (basename, ".") == 0)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_free (basename);
++ g_vfs_job_failed_from_errno (G_VFS_JOB (job), EPERM);
++ return;
++ }
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ g_free (basename);
++ return;
++ }
++
++ parent = g_path_get_dirname (filename);
++ if (_change_directory (op_backend, parent, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_free (basename);
++ g_free (parent);
++ g_error_free (error);
++ return;
++ }
++ g_free (parent);
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ g_free (basename);
++ return;
++ }
++
++ if (dbus_g_proxy_call (op_backend->session_proxy, "DeleteRemoteFile", &error,
++ G_TYPE_STRING, basename, G_TYPE_INVALID,
++ G_TYPE_INVALID) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++ g_free (basename);
++
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_debug ("- do_delete\n");
++}
++
++static void
++do_make_directory (GVfsBackend *backend,
++ GVfsJobMakeDirectory *job,
++ const char *filename)
++{
++ GVfsBackendObexftp *op_backend = G_VFS_BACKEND_OBEXFTP (backend);
++ char *basename, *parent;
++ GError *error = NULL;
++ GFileInfo *info;
++
++ g_debug ("+ do_make_directory, filename: %s\n", filename);
++
++ g_mutex_lock (&op_backend->mutex);
++
++ /* Check if the folder already exists */
++ info = g_file_info_new ();
++ if (_query_file_info_helper (backend, filename, info, &error) != FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_object_unref (info);
++ g_vfs_job_failed_from_errno (G_VFS_JOB (job), EEXIST);
++ return;
++ }
++ g_object_unref (info);
++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++
++ if (error)
++ g_clear_error (&error);
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return;
++ }
++
++ parent = g_path_get_dirname (filename);
++ if (_change_directory (op_backend, parent, &error) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++ g_free (parent);
++
++ if (g_vfs_job_is_cancelled (G_VFS_JOB (job)))
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ return;
++ }
++
++ basename = g_path_get_basename (filename);
++ if (dbus_g_proxy_call (op_backend->session_proxy, "CreateFolder", &error,
++ G_TYPE_STRING, basename, G_TYPE_INVALID,
++ G_TYPE_INVALID) == FALSE)
++ {
++ g_mutex_unlock (&op_backend->mutex);
++ g_free (basename);
++ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++ g_error_free (error);
++ return;
++ }
++ g_free (basename);
++
++ /* reset the current directory so that we won't cache when querying
++ * info after this has succeeded.
++ */
++ _invalidate_cache_helper (op_backend);
++
++ g_vfs_job_succeeded (G_VFS_JOB (job));
++
++ g_mutex_unlock (&op_backend->mutex);
++
++ g_debug ("- do_make_directory\n");
++}
++
++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);
++
++ dbus_threads_init_default ();
++
++ gobject_class->finalize = g_vfs_backend_obexftp_finalize;
++
++ backend_class->mount = do_mount;
++ backend_class->open_for_read = do_open_for_read;
++ backend_class->read = do_read;
++ backend_class->close_read = do_close_read;
++ backend_class->query_info = do_query_info;
++ backend_class->query_fs_info = do_query_fs_info;
++ backend_class->enumerate = do_enumerate;
++ backend_class->delete = do_delete;
++ backend_class->make_directory = do_make_directory;
++ backend_class->push = do_push;
++
++ /* ErrorOccurred */
++ dbus_g_object_register_marshaller (obexftp_marshal_VOID__STRING_STRING,
++ G_TYPE_NONE, G_TYPE_STRING,
++ G_TYPE_STRING, G_TYPE_INVALID);
++ /* TransferStarted */
++ dbus_g_object_register_marshaller(obexftp_marshal_VOID__STRING_STRING_UINT64,
++ G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INVALID);
++ /* TransferProgress */
++ dbus_g_object_register_marshaller(obexftp_marshal_VOID__UINT64,
++ G_TYPE_NONE, G_TYPE_UINT64, G_TYPE_INVALID);
++ /* SessionConnected */
++ dbus_g_object_register_marshaller(obexftp_marshal_VOID__STRING,
++ G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
++ /* SessionConnectError */
++ dbus_g_object_register_marshaller (obexftp_marshal_VOID__STRING_STRING_STRING,
++ G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
++}
++
++/*
++ * vim: sw=2 ts=8 cindent expandtab cinoptions=f0,>4,n2,{2,(0,^-2,t0 ai
++ */
+Index: b/daemon/gvfsbackendobexftp.h
+===================================================================
+--- /dev/null
++++ b/daemon/gvfsbackendobexftp.h
+@@ -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., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ * Author: Alexander Larsson <alexl at 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: b/daemon/obexftp-marshal.list
+===================================================================
+--- /dev/null
++++ b/daemon/obexftp-marshal.list
+@@ -0,0 +1,5 @@
++VOID:STRING
++VOID:UINT64
++VOID:STRING,STRING
++VOID:STRING,STRING,STRING
++VOID:STRING,STRING,UINT64
+Index: b/daemon/obexftp.mount.in
+===================================================================
+--- /dev/null
++++ b/daemon/obexftp.mount.in
+@@ -0,0 +1,4 @@
++[Mount]
++Type=obex
++Exec=@libexecdir@/gvfsd-obexftp
++AutoMount=false
+Index: b/man/gvfs.xml
+===================================================================
+--- a/man/gvfs.xml
++++ b/man/gvfs.xml
+@@ -37,7 +37,7 @@
+ includes a 'local' implementation using POSIX. gvfs
+ provides implementations that go beyond that and allow
+ to access files and storage using many protocols, such
+- as ftp, http, sftp, dav, nfs, etc. It also provides
++ as ftp, http, sftp, dav, nfs, obexftp, etc. It also provides
+ support for trash folders, for cd burning and for monitoring
+ interesting devices and volumes on the computer.</para>
+
+@@ -80,6 +80,7 @@
+ <listitem><para>gvfsd-mtp - mounts over MTP</para></listitem>
+ <listitem><para>gvfsd-network - provides network://</para></listitem>
+ <listitem><para>gvfsd-nfs - mounts over NFS</para></listitem>
++ <listitem><para>gvfsd-obexftp - mounts over obexftp</para></listitem>
+ <listitem><para>gvfsd-recent - provides recent://</para></listitem>
+ <listitem><para>gvfsd-sftp - mounts over sftp</para></listitem>
+ <listitem><para>gvfsd-smb - mounts Windows Shares Filesystem volumes</para></listitem>
+Index: b/po/POTFILES.in
+===================================================================
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -35,6 +35,7 @@
+ daemon/gvfsbackendmtp.c
+ daemon/gvfsbackendnetwork.c
+ daemon/gvfsbackendnfs.c
++daemon/gvfsbackendobexftp.c
+ daemon/gvfsbackendrecent.c
+ daemon/gvfsbackendsftp.c
+ daemon/gvfsbackendsmbbrowse.c
Modified: desktop/experimental/gvfs/debian/patches/series
URL: http://svn.debian.org/wsvn/pkg-gnome/desktop/experimental/gvfs/debian/patches/series?rev=44170&op=diff
==============================================================================
--- desktop/experimental/gvfs/debian/patches/series [utf-8] (original)
+++ desktop/experimental/gvfs/debian/patches/series [utf-8] Wed Mar 18 12:35:40 2015
@@ -1,6 +1,6 @@
+revert-0001-Remove-obsolte-obexftp-code.patch
01_modules_dir.patch
04_hurd_path_max.patch
-metadata-dont-flush-null-tree.patch
metadata-nuke-junk-data.patch
dont-crash-on-null-job.patch
handle-inactive-vfs.patch
More information about the pkg-gnome-commits
mailing list