All pastes #1092432 Raw Edit

download method

public diff v1 · immutable
#1092432 ·published 2008-08-04 12:39 UTC
rendered paste body
diff --git a/client/gdaemonfile.c b/client/gdaemonfile.cindex 9e055c2..8a1ae74 100644--- a/client/gdaemonfile.c+++ b/client/gdaemonfile.c@@ -1939,50 +1939,71 @@ progress_callback_message (DBusConnection  *connection,   return DBUS_HANDLER_RESULT_HANDLED; } + static gboolean g_daemon_file_copy (GFile                  *source,-		    GFile                  *destination,-		    GFileCopyFlags          flags,-		    GCancellable           *cancellable,-		    GFileProgressCallback   progress_callback,-		    gpointer                progress_callback_data,-		    GError                **error)+                    GFile                  *destination,+                    GFileCopyFlags          flags,+                    GCancellable           *cancellable,+                    GFileProgressCallback   progress_callback,+                    gpointer                progress_callback_data,+                    GError                **error) {-  GDaemonFile *daemon_source;   DBusMessage *reply;   char *local_path;   char *obj_path, *dbus_obj_path;   dbus_uint32_t flags_dbus;   struct ProgressCallbackData data;+  gboolean source_is_daemon;+  gboolean dest_is_daemon;+  gboolean can_copy;+  gboolean can_upload;+  gboolean can_download; -  if (!G_IS_DAEMON_FILE (destination))-    {-      /* Fall back to default move */-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Move not supported");-      return FALSE;-    }+  local_path = NULL;+  can_copy = can_upload = can_download = FALSE;+  source_is_daemon = G_IS_DAEMON_FILE (source);+  dest_is_daemon   = G_IS_DAEMON_FILE (destination); -  if (G_IS_DAEMON_FILE (source))+  if (source_is_daemon && dest_is_daemon)     {-      daemon_source = G_DAEMON_FILE (source);-      local_path = NULL;+      can_copy = TRUE;     }-  else+  else if (dest_is_daemon && !source_is_daemon)     {-      daemon_source = NULL;       local_path = g_file_get_path (source); -      if (local_path == NULL)-	{-	  /* This will cause the fallback code to be involved */-	  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,-			       _("Operation not supported, files on different mounts"));-	  return FALSE;-	}+      if (local_path)+        can_upload = TRUE;     }-  -  if (progress_callback)+  else if (source_is_daemon && !dest_is_daemon)+    {+      local_path = g_file_get_path (destination);++      if (local_path)+        can_download = TRUE;+    }+  else     {+      /* This will cause the fallback code to be involved */+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,+                           "Copy not supported");+      return FALSE;+    }+++   if (can_copy == FALSE && local_path == NULL)+     {+       /* This will cause the fallback code to be involved */+       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,+                            _("Operation not supported, files on different mounts"));+       return FALSE;++     }+++   if (progress_callback)+     {       obj_path = g_strdup_printf ("/org/gtk/vfs/callback/%p", &obj_path);       dbus_obj_path = obj_path;     }@@ -1998,29 +2019,40 @@ g_daemon_file_copy (GFile                  *source,    flags_dbus = flags;   -  if (daemon_source)+  if (can_copy)     {       reply = do_sync_2_path_call (source, destination, -				   G_VFS_DBUS_MOUNT_OP_COPY,-				   obj_path, progress_callback_message, &data,-				   NULL, cancellable, error,-				   DBUS_TYPE_UINT32, &flags_dbus,-				   DBUS_TYPE_OBJECT_PATH, &dbus_obj_path,-				   0);+                                   G_VFS_DBUS_MOUNT_OP_COPY,+                                   obj_path, progress_callback_message, &data,+                                   NULL, cancellable, error,+                                   DBUS_TYPE_UINT32, &flags_dbus,+                                   DBUS_TYPE_OBJECT_PATH, &dbus_obj_path,+                                   0);     }-  else+  else if (can_upload)     {       reply = do_sync_2_path_call (destination, NULL, -				   G_VFS_DBUS_MOUNT_OP_UPLOAD,-				   obj_path, progress_callback_message, &data,-				   NULL, cancellable, error,-				   G_DBUS_TYPE_CSTRING, &local_path,-				   DBUS_TYPE_UINT32, &flags_dbus,-				   DBUS_TYPE_OBJECT_PATH, &dbus_obj_path,-				   0);-      g_free (local_path);-    }-+                                   G_VFS_DBUS_MOUNT_OP_UPLOAD,+                                   obj_path, progress_callback_message, &data,+                                   NULL, cancellable, error,+                                   G_DBUS_TYPE_CSTRING, &local_path,+                                   DBUS_TYPE_UINT32, &flags_dbus,+                                   DBUS_TYPE_OBJECT_PATH, &dbus_obj_path,+                                   0);+    }+  else if (can_download)+    {+      reply = do_sync_2_path_call (source, NULL,+                                   G_VFS_DBUS_MOUNT_OP_DOWNLOAD,+                                   obj_path, progress_callback_message, &data,+                                   NULL, cancellable, error, +                                   G_DBUS_TYPE_CSTRING, &local_path,+                                   DBUS_TYPE_UINT32, &flags_dbus,+                                   DBUS_TYPE_OBJECT_PATH, &dbus_obj_path,+                                   0);+    }++  g_free (local_path);   g_free (obj_path);    if (reply == NULL)diff --git a/common/gvfsdaemonprotocol.h b/common/gvfsdaemonprotocol.hindex aa41f98..26d0622 100644--- a/common/gvfsdaemonprotocol.h+++ b/common/gvfsdaemonprotocol.h@@ -43,6 +43,7 @@ G_BEGIN_DECLS #define G_VFS_DBUS_MOUNT_OP_MAKE_SYMBOLIC_LINK "MakeSymbolicLink" #define G_VFS_DBUS_MOUNT_OP_COPY "Copy" #define G_VFS_DBUS_MOUNT_OP_UPLOAD "Upload"+#define G_VFS_DBUS_MOUNT_OP_DOWNLOAD "Download" #define G_VFS_DBUS_MOUNT_OP_MOVE "Move" #define G_VFS_DBUS_MOUNT_OP_SET_ATTRIBUTE "SetAttribute" #define G_VFS_DBUS_MOUNT_OP_QUERY_SETTABLE_ATTRIBUTES "QuerySettableAttributes"diff --git a/daemon/Makefile.am b/daemon/Makefile.amindex 9a5be5b..8681a69 100644--- a/daemon/Makefile.am+++ b/daemon/Makefile.am@@ -135,6 +135,7 @@ libdaemon_la_SOURCES = \ 	gvfsjobdelete.c gvfsjobdelete.h \ 	gvfsjobcopy.c gvfsjobcopy.h \ 	gvfsjobupload.c gvfsjobupload.h \+	gvfsjobdownload.c gvfsjobdownload.h \ 	gvfsjobmove.c gvfsjobmove.h \ 	gvfsjobmakedirectory.c gvfsjobmakedirectory.h \ 	gvfsjobmakesymlink.c gvfsjobmakesymlink.h \diff --git a/daemon/gvfsbackend.c b/daemon/gvfsbackend.cindex ddce08c..205661b 100644--- a/daemon/gvfsbackend.c+++ b/daemon/gvfsbackend.c@@ -49,6 +49,7 @@ #include <gvfsjobmakesymlink.h> #include <gvfsjobcreatemonitor.h> #include <gvfsjobupload.h>+#include <gvfsjobdownload.h> #include <gvfsjobcopy.h> #include <gvfsjobmove.h> #include <gvfsjobsetattribute.h>@@ -538,6 +539,10 @@ backend_dbus_handler (DBusConnection  *connection,     job = g_vfs_job_upload_new (connection, message, backend);   else if (dbus_message_is_method_call (message, 					G_VFS_DBUS_MOUNT_INTERFACE,+					G_VFS_DBUS_MOUNT_OP_DOWNLOAD))+    job = g_vfs_job_download_new (connection, message, backend);+  else if (dbus_message_is_method_call (message,+					G_VFS_DBUS_MOUNT_INTERFACE, 					G_VFS_DBUS_MOUNT_OP_MOVE))     job = g_vfs_job_move_new (connection, message, backend);   else if (dbus_message_is_method_call (message,diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.hindex d8c7867..599220e 100644--- a/daemon/gvfsbackend.h+++ b/daemon/gvfsbackend.h@@ -65,6 +65,7 @@ typedef struct _GVfsJobMakeDirectory   GVfsJobMakeDirectory; typedef struct _GVfsJobMakeSymlink     GVfsJobMakeSymlink; typedef struct _GVfsJobCopy            GVfsJobCopy; typedef struct _GVfsJobUpload          GVfsJobUpload;+typedef struct _GVfsJobDownload        GVfsJobDownload; typedef struct _GVfsJobMove            GVfsJobMove; typedef struct _GVfsJobSetAttribute    GVfsJobSetAttribute; typedef struct _GVfsJobQueryAttributes GVfsJobQueryAttributes;@@ -297,6 +298,20 @@ struct _GVfsBackendClass 				 GFileCopyFlags flags, 				 GFileProgressCallback progress_callback, 				 gpointer progress_callback_data);+  void     (*download)          (GVfsBackend *backend,+				 GVfsJobDownload *job,+				 const char *source,+				 const char *local_destination,+				 GFileCopyFlags flags,+				 GFileProgressCallback progress_callback,+				 gpointer progress_callback_data);+  gboolean (*try_download)      (GVfsBackend *backend,+				 GVfsJobDownload *job,+				 const char *source,+				 const char *local_destination,+				 GFileCopyFlags flags,+				 GFileProgressCallback progress_callback,+				 gpointer progress_callback_data);   void     (*upload)            (GVfsBackend *backend, 				 GVfsJobUpload *job, 				 const char *destination,diff --git a/daemon/gvfsbackendhttp.c b/daemon/gvfsbackendhttp.cindex 7e91a05..1d46924 100644--- a/daemon/gvfsbackendhttp.c+++ b/daemon/gvfsbackendhttp.c@@ -1,5 +1,5 @@ /* GIO - GLib Input, Output and Streaming Library- * + *  * Copyright (C) 2008 Red Hat, Inc.  *  * This library is free software; you can redistribute it and/or@@ -81,7 +81,7 @@ g_vfs_backend_http_init (GVfsBackendHttp *backend) {    const char         *debug; -  g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), FALSE);  +  g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), FALSE);    backend->session = soup_session_sync_new_with_options ("user-agent",                                                          "gvfs/" VERSION,@@ -214,7 +214,7 @@ http_error_code_from_status (guint status)    case SOUP_STATUS_NOT_FOUND:   case SOUP_STATUS_GONE:-    return G_IO_ERROR_NOT_FOUND;    +    return G_IO_ERROR_NOT_FOUND;    case SOUP_STATUS_GATEWAY_TIMEOUT:     return G_IO_ERROR_TIMED_OUT;@@ -229,6 +229,11 @@ g_vfs_job_failed_from_http_status (GVfsJob *job, guint status_code, const char * {   switch (status_code) { +  case SOUP_STATUS_CANCELLED:+    g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_CANCELLED,+                              _("Operation was cancelled"));+    break;+   case SOUP_STATUS_NOT_FOUND:     g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,                               message);@@ -263,7 +268,7 @@ http_backend_queue_message (GVfsBackend         *backend, {   GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend); -  soup_session_queue_message (op_backend->session_async, msg, +  soup_session_queue_message (op_backend->session_async, msg,                               callback, user_data); } /* ************************************************************************* */@@ -310,9 +315,9 @@ try_mount (GVfsBackend  *backend,       real_mount_spec->mount_prefix = g_mount_spec_canonicalize_path (path);       g_free (path);     }-  +   g_vfs_backend_set_mount_spec (backend, real_mount_spec);-  +   op_backend->mount_base = uri;    g_vfs_job_succeeded (G_VFS_JOB (job));@@ -331,7 +336,7 @@ open_for_read_ready (GObject      *source_object,   gboolean      can_seek;   GError       *error; -  stream = G_INPUT_STREAM (source_object); +  stream = G_INPUT_STREAM (source_object);   error  = NULL;   job    = G_VFS_JOB (user_data); @@ -356,7 +361,7 @@ open_for_read_ready (GObject      *source_object,   g_vfs_job_succeeded (job); } -static gboolean +static gboolean try_open_for_read (GVfsBackend        *backend,                    GVfsJobOpenForRead *job,                    const char         *filename)@@ -393,7 +398,7 @@ read_ready (GObject      *source_object,   GError       *error;   gssize        nread; -  stream = G_INPUT_STREAM (source_object); +  stream = G_INPUT_STREAM (source_object);   error  = NULL;   job    = G_VFS_JOB (user_data); @@ -498,7 +503,7 @@ close_read_ready (GObject      *source_object,   g_object_unref (stream); } -static gboolean +static gboolean try_close_read (GVfsBackend       *backend,                 GVfsJobCloseRead  *job,                 GVfsBackendHandle  handle)@@ -547,7 +552,7 @@ query_info_ready (SoupSession *session,   g_print ("basename:%s\n", basename);    /* read http/1.1 rfc, until then we copy the local files-   * behaviour */ +   * behaviour */   if (basename != NULL &&       g_file_attribute_matcher_matches (matcher,                                         G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))@@ -572,7 +577,7 @@ query_info_ready (SoupSession *session,       char *edit_name = g_filename_display_name (basename);       g_file_info_set_edit_name (info, edit_name);       g_free (edit_name);-    } +    }    g_free (basename); @@ -647,16 +652,217 @@ try_query_info (GVfsBackend           *backend,   return TRUE; } +typedef struct {++  GVfsJobDownload *job;+  GFileCopyFlags flags;+  const char *destination;++  SoupSession *session;++  GOutputStream *out;++  GFileProgressCallback progress_callback;+  gpointer progress_callback_data;++  goffset total_size;+  goffset data_read;++  GError *error;+  GCancellable *cancellable;++} DownloadData;++static void+download_got_headers (SoupMessage *msg,+                      gpointer     user_data)+{+  DownloadData *data = user_data;+  GFileOutputStream *out;+  GFile *file;+  const char *text;++  if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))+    return;++  file = g_file_new_for_path (data->destination);++  if (data->flags & G_FILE_COPY_OVERWRITE)+    {+      out = g_file_replace (file,+                            NULL,+                            data->flags & G_FILE_COPY_BACKUP,+                            0,+                            data->cancellable,+                            &data->error);+    }+  else+    {+      out = g_file_create (file,+                           0,+                           data->cancellable,+                           &data->error);+    }++  g_object_unref (file);++  if (out == NULL)+    {+      soup_session_cancel_message (data->session, msg, SOUP_STATUS_CANCELLED);+      return;+    }++  data->out = G_OUTPUT_STREAM (out);++  if (data->progress_callback == NULL)+    return;++  text = soup_message_headers_get (msg->response_headers,+                                   "Content-Length");+  if (text)+    {+      char *endptr = NULL;++      data->total_size = g_ascii_strtoull (text, &endptr, 10);++      if (endptr == text)+        data->total_size = -1;+    }++}++static void+download_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)+{+  DownloadData *data = user_data;+  gsize bytes_written = 0;+  gboolean res;++  res = g_output_stream_write_all (data->out,+                                   chunk->data,+                                   chunk->length,+                                   &bytes_written,+                                   data->cancellable,+                                   &data->error);++  if (res == FALSE)+    {+      soup_session_cancel_message (data->session, msg, SOUP_STATUS_CANCELLED);+      return;+    }++  if (data->progress_callback)+    {+      data->data_read += bytes_written;+      data->progress_callback (data->data_read, data->total_size,+                               data->progress_callback_data);+    }+}++static void+download_got_body (SoupMessage *msg,+                   gpointer     user_data)+{+  DownloadData *data = user_data;+  gboolean res;++  res = g_output_stream_close (data->out, data->cancellable, &data->error);++  if (res == FALSE)+    soup_session_cancel_message (data->session, msg, SOUP_STATUS_CANCELLED);++  g_object_unref (data->out);+  data->out = NULL;+}++static void+download_finished (SoupMessage *msg,+                   gpointer     user_data)+{+  DownloadData *data = user_data;++  /* In an error case the message will get cancelled+   * and the got_body function not be called so we+   * free the output stream here .. */++  if (data->out != NULL)+    {+      g_output_stream_close (data->out, data->cancellable, NULL);+      g_object_unref (data->out);+    }++}++static void+do_download (GVfsBackend *backend,+             GVfsJobDownload *job,+             const char *source,+             const char *local_destination,+             GFileCopyFlags flags,+             GFileProgressCallback progress_callback,+             gpointer progress_callback_data)+{+  DownloadData  data;+  SoupMessage  *msg;+  SoupURI      *uri;+  guint         status;++  uri = http_backend_uri_for_filename (backend, source, FALSE);+  msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);+  soup_uri_free (uri);++  soup_message_body_set_accumulate (msg->response_body, FALSE);++  g_signal_connect (msg, "got_headers",+                    G_CALLBACK (download_got_headers), &data);++  g_signal_connect (msg, "got_chunk",+                    G_CALLBACK (download_got_chunk), &data);++  g_signal_connect (msg, "got_body",+                    G_CALLBACK (download_got_body), &data);++  g_signal_connect (msg, "finished",+                    G_CALLBACK (download_finished), &data);++  memset (&data, 0, sizeof (data));++  data.job = job;+  data.flags = flags;+  data.cancellable = G_VFS_JOB (job)->cancellable;+  data.destination = local_destination;+  data.session = G_VFS_BACKEND_HTTP (backend)->session;+  data.progress_callback_data = progress_callback_data;+  data.progress_callback = progress_callback;+  data.total_size = -1;++  status = http_backend_send_message (backend, msg);++  if (! SOUP_STATUS_IS_SUCCESSFUL (status))+    {+      if (data.error != NULL)+        {+          g_vfs_job_failed_from_error (G_VFS_JOB (job), data.error);+          g_error_free (data.error);+        }+      else+        g_vfs_job_failed_from_http_status (G_VFS_JOB (job), status, msg->reason_phrase);+    }+  else+    g_vfs_job_succeeded (G_VFS_JOB (job));++  g_object_unref (msg);+}  static void g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass) {   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);   GVfsBackendClass *backend_class;-  +   gobject_class->finalize  = g_vfs_backend_http_finalize; -  backend_class = G_VFS_BACKEND_CLASS (klass); +  backend_class = G_VFS_BACKEND_CLASS (klass);    backend_class->try_mount         = try_mount;   backend_class->try_open_for_read = try_open_for_read;@@ -665,4 +871,5 @@ g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)   backend_class->try_close_read    = try_close_read;   backend_class->try_query_info    = try_query_info; +  backend_class->download          = do_download; }diff --git a/daemon/gvfsjobdownload.c b/daemon/gvfsjobdownload.cnew file mode 100644index 0000000..6828e5e--- /dev/null+++ b/daemon/gvfsjobdownload.c@@ -0,0 +1,211 @@+/* GIO - GLib Input, Output and Streaming Library+ *+ * Copyright (C) 2006-2007 Red Hat, Inc.+ *+ * This library is free software; you can redistribute it and/or+ * modify it under the terms of the GNU Lesser General Public+ * License as published by the Free Software Foundation; either+ * version 2 of the License, or (at your option) any later version.+ *+ * This library is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ * Lesser General Public License for more details.+ *+ * You should have received a copy of the GNU Lesser General+ * Public License along with this library; if not, write to the+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,+ * Boston, MA 02111-1307, USA.+ *+ * Author: Alexander Larsson <alexl@redhat.com>+ */++#include <config.h>++#include <strings.h>+#include <unistd.h>+#include <sys/types.h>+#include <sys/socket.h>+#include <sys/un.h>++#include <glib.h>+#include <dbus/dbus.h>+#include <glib/gi18n.h>+#include "gvfsjobdownload.h"+#include "gdbusutils.h"+#include "gvfsdaemonprotocol.h"++G_DEFINE_TYPE (GVfsJobDownload, g_vfs_job_download, G_VFS_TYPE_JOB_DBUS)++static void         run          (GVfsJob        *job);+static gboolean     try          (GVfsJob        *job);+static DBusMessage *create_reply (GVfsJob        *job,+                                  DBusConnection *connection,+                                  DBusMessage    *message);++static void+g_vfs_job_download_finalize (GObject *object)+{+  GVfsJobDownload *job;++  job = G_VFS_JOB_DOWNLOAD (object);++  g_free (job->local_destination);+  g_free (job->source);+  g_free (job->callback_obj_path);++  if (G_OBJECT_CLASS (g_vfs_job_download_parent_class)->finalize)+    (*G_OBJECT_CLASS (g_vfs_job_download_parent_class)->finalize) (object);+}++static void+g_vfs_job_download_class_init (GVfsJobDownloadClass *klass)+{+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);+  GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass);+  GVfsJobDBusClass *job_dbus_class = G_VFS_JOB_DBUS_CLASS (klass);++  gobject_class->finalize = g_vfs_job_download_finalize;+  job_class->run = run;+  job_class->try = try;+  job_dbus_class->create_reply = create_reply;+}++static void+g_vfs_job_download_init (GVfsJobDownload *job)+{+}++GVfsJob *+g_vfs_job_download_new (DBusConnection *connection,+                        DBusMessage *message,+                        GVfsBackend *backend)+{+  GVfsJobDownload *job;+  DBusMessage *reply;+  DBusError derror;+  int path1_len, path2_len;+  const char *path1_data, *path2_data, *callback_obj_path;+  dbus_uint32_t flags;++  dbus_error_init (&derror);+  if (!dbus_message_get_args (message, &derror,+                              DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,+                              &path1_data, &path1_len,+                              DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,+                              &path2_data, &path2_len,+                              DBUS_TYPE_UINT32, &flags,+                              DBUS_TYPE_OBJECT_PATH, &callback_obj_path,+                              0))+    {+      reply = dbus_message_new_error (message,+                                      derror.name,+                                      derror.message);+      dbus_error_free (&derror);++      dbus_connection_send (connection, reply, NULL);+      return NULL;+    }++  job = g_object_new (G_VFS_TYPE_JOB_DOWNLOAD,+                      "message", message,+                      "connection", connection,+                      NULL);++  job->source = g_strndup (path1_data, path1_len);+  job->local_destination = g_strndup (path2_data, path2_len);+  job->backend = backend;+  job->flags = flags;+  if (strcmp (callback_obj_path, "/org/gtk/vfs/void") != 0)+    job->callback_obj_path = g_strdup (callback_obj_path);++  return G_VFS_JOB (job);+}++static void+progress_callback (goffset current_num_bytes,+                   goffset total_num_bytes,+                   gpointer user_data)+{+  GVfsJob *job = G_VFS_JOB (user_data);+  GVfsJobDBus *dbus_job = G_VFS_JOB_DBUS (job);+  GVfsJobDownload *op_job = G_VFS_JOB_DOWNLOAD (job);+  dbus_uint64_t current_dbus, total_dbus;+  DBusMessage *message;++  g_print ("progress_callback %d/%d\n", (int)current_num_bytes, (int)total_num_bytes);++  if (op_job->callback_obj_path == NULL)+    return;++  message =+    dbus_message_new_method_call (dbus_message_get_sender (dbus_job->message),+                                  op_job->callback_obj_path,+                                  G_VFS_DBUS_PROGRESS_INTERFACE,+                                  G_VFS_DBUS_PROGRESS_OP_PROGRESS);+  dbus_message_set_no_reply (message, TRUE);++  current_dbus = current_num_bytes;+  total_dbus = total_num_bytes;+  dbus_message_append_args (message,+                            DBUS_TYPE_UINT64, &current_dbus,+                            DBUS_TYPE_UINT64, &total_dbus,+                            0);++  /* Queues reply (threadsafely), actually sends it in mainloop */+  dbus_connection_send (dbus_job->connection, message, NULL);+  dbus_message_unref (message);+}++static void+run (GVfsJob *job)+{+  GVfsJobDownload *op_job = G_VFS_JOB_DOWNLOAD (job);+  GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);++  if (class->download == NULL)+    {+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,+                        _("Operation not supported by backend"));+      return;+    }++  class->download (op_job->backend,+                   op_job,+                   op_job->source,+                   op_job->local_destination,+                   op_job->flags,+                   progress_callback,+                   job);+}++static gboolean+try (GVfsJob *job)+{+  GVfsJobDownload *op_job = G_VFS_JOB_DOWNLOAD (job);+  GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);++  if (class->try_download == NULL)+    return FALSE;++  return class->try_download (op_job->backend,+                              op_job,+                              op_job->source,+                              op_job->local_destination,+                              op_job->flags,+                              progress_callback,+                              job);+}++/* Might be called on an i/o thread */+static DBusMessage *+create_reply (GVfsJob *job,+              DBusConnection *connection,+              DBusMessage *message)+{+  DBusMessage *reply;++  reply = dbus_message_new_method_return (message);++  return reply;+}diff --git a/daemon/gvfsjobdownload.h b/daemon/gvfsjobdownload.hnew file mode 100644index 0000000..a92bb94--- /dev/null+++ b/daemon/gvfsjobdownload.h@@ -0,0 +1,66 @@+/* GIO - GLib Input, Output and Streaming Library+ *+ * Copyright (C) 2006-2007 Red Hat, Inc.+ *+ * This library is free software; you can redistribute it and/or+ * modify it under the terms of the GNU Lesser General Public+ * License as published by the Free Software Foundation; either+ * version 2 of the License, or (at your option) any later version.+ *+ * This library is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ * Lesser General Public License for more details.+ *+ * You should have received a copy of the GNU Lesser General+ * Public License along with this library; if not, write to the+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,+ * Boston, MA 02111-1307, USA.+ *+ * Author: Alexander Larsson <alexl@redhat.com>+ */++#ifndef __G_VFS_JOB_DOWNLOAD_H__+#define __G_VFS_JOB_DOWNLOAD_H__++#include <gio/gio.h>+#include <gvfsjob.h>+#include <gvfsjobdbus.h>+#include <gvfsbackend.h>++G_BEGIN_DECLS++#define G_VFS_TYPE_JOB_DOWNLOAD         (g_vfs_job_download_get_type ())+#define G_VFS_JOB_DOWNLOAD(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_DOWNLOAD, GVfsJobDownload))+#define G_VFS_JOB_DOWNLOAD_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_DOWNLOAD, GVfsJobDownloadClass))+#define G_VFS_IS_JOB_DOWNLOAD(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_DOWNLOAD))+#define G_VFS_IS_JOB_DOWNLOAD_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_DOWNLOAD))+#define G_VFS_JOB_DOWNLOAD_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_DOWNLOAD, GVfsJobDownloadClass))++typedef struct _GVfsJobDownloadClass   GVfsJobDownloadClass;++struct _GVfsJobDownload+{+  GVfsJobDBus parent_instance;++  GVfsBackend *backend;+  char *source;+  char *local_destination;+  GFileCopyFlags flags;+  char *callback_obj_path;+};++struct _GVfsJobDownloadClass+{+  GVfsJobDBusClass parent_class;+};++GType g_vfs_job_download_get_type (void) G_GNUC_CONST;++GVfsJob *g_vfs_job_download_new (DBusConnection *connection,+                                 DBusMessage    *message,+                                 GVfsBackend    *backend);++G_END_DECLS++#endif /* __G_VFS_JOB_DOWNLOAD_H__ */