summaryrefslogtreecommitdiff
path: root/trunk/daemon/gvfsbackendobexftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/daemon/gvfsbackendobexftp.c')
-rw-r--r--trunk/daemon/gvfsbackendobexftp.c2116
1 files changed, 2116 insertions, 0 deletions
diff --git a/trunk/daemon/gvfsbackendobexftp.c b/trunk/daemon/gvfsbackendobexftp.c
new file mode 100644
index 00000000..f3b1517f
--- /dev/null
+++ b/trunk/daemon/gvfsbackendobexftp.c
@@ -0,0 +1,2116 @@
+/* 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Bastien Nocera <hadess@hadess.net>
+ * Cosimo Cecchi <cosimoc@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>
+#include <libhal.h>
+#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;
+ 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)
+{
+ 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");
+ }
+ 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)
+{
+ 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);
+ 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;
+}
+
+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;
+}
+
+static gint
+_get_usb_intfnum_and_properties (DBusGProxy *obex_manager, const char *device, char **display_name, char **icon_name)
+{
+ char **obex_devices;
+ int num_obex_devices;
+ int usb_bus_num;
+ int usb_device_num;
+ int usb_intf_num;
+ DBusError dbus_error;
+ DBusConnection *dbus_connection;
+ LibHalContext *hal_ctx;
+ int ods_intf_num = 1;
+ int n;
+
+ /* 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);
+
+ 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";
+
+ 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);
+
+ 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->files_listing);
+ g_free (backend->directory);
+
+ if (backend->session_proxy != NULL)
+ g_object_unref (backend->session_proxy);
+ g_mutex_free (backend->mutex);
+ g_cond_free (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)
+{
+ GError *err = NULL;
+
+ backend->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &err);
+ if (backend->connection == NULL) {
+ g_printerr ("Connecting to session bus failed: %s\n", err->message);
+ g_error_free (err);
+ return;
+ }
+
+ backend->mutex = g_mutex_new ();
+ backend->cond = g_cond_new ();
+ 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, &current_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);
+ }
+ 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_foreach (elements, (GFunc)g_object_unref, NULL);
+ g_list_free (elements);
+
+ 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");
+ _exit (1);
+ }
+
+ /* 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");
+ _exit (1);
+}
+
+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)
+{
+ g_message ("disconnected_cb");
+
+ _exit (1);
+}
+
+static void
+closed_cb (DBusGProxy *proxy, gpointer user_data)
+{
+ g_message ("closed_cb");
+
+ _exit (1);
+}
+
+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);
+ 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);
+ 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");
+ 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);
+
+ 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) {
+ GTimeVal val;
+ g_get_current_time (&val);
+ g_time_val_add (&val, 100000);
+ count++;
+ if (g_cond_timed_wait (op_backend->cond, op_backend->mutex, &val) != 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_foreach (elements, (GFunc)g_object_unref, NULL);
+ g_list_free (elements);
+ 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;
+ 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");
+
+ 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);
+ }
+
+ 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);
+
+ 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_foreach (elements, (GFunc)g_object_unref, NULL);
+ g_list_free (elements);
+
+ 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);
+
+ 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
+ */