/* 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@redhat.com>
 */

#include <config.h>

#include <gmountsource.h>
#include <gio/gio.h>
#include <gvfsdbus.h>
#include "gvfsdaemonprotocol.h"

#include <string.h>

struct _GMountSource
{
  GObject parent_instance;

  char *dbus_id;
  char *obj_path;
};

G_DEFINE_TYPE (GMountSource, g_mount_source, G_TYPE_OBJECT)

static void
g_mount_source_finalize (GObject *object)
{
  GMountSource *source;

  source = G_MOUNT_SOURCE (object);

  g_free (source->dbus_id);
  g_free (source->obj_path);
  
  if (G_OBJECT_CLASS (g_mount_source_parent_class)->finalize)
    (*G_OBJECT_CLASS (g_mount_source_parent_class)->finalize) (object);
}

static void
g_mount_source_class_init (GMountSourceClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  
  gobject_class->finalize = g_mount_source_finalize;
}

static void
g_mount_source_init (GMountSource *mount_source)
{
}

GMountSource *
g_mount_source_new (const char *dbus_id,
		    const char *obj_path)
{
  GMountSource *source;

  source = g_object_new (G_TYPE_MOUNT_SOURCE, NULL);

  source->dbus_id = g_strdup (dbus_id);
  source->obj_path = g_strdup (obj_path);
  
  return source;
}

GMountSource *
g_mount_source_new_dummy (void)
{
  GMountSource *source;

  source = g_object_new (G_TYPE_MOUNT_SOURCE, NULL);
  
  source->dbus_id = g_strdup ("");
  source->obj_path = g_strdup ("/");
  
  return source;
}

GVariant *
g_mount_source_to_dbus (GMountSource *source)
{
  g_assert (source->dbus_id != NULL);
  g_assert (source->obj_path != NULL);

  return g_variant_new ("(so)",
                        source->dbus_id,
                        source->obj_path);
}

GMountSource *
g_mount_source_from_dbus (GVariant *value)
{
  const gchar *obj_path, *dbus_id;

  g_variant_get (value, "(&s&o)",
                 &dbus_id,
                 &obj_path);
  
  return g_mount_source_new (dbus_id, obj_path);
}

const char *
g_mount_source_get_dbus_id (GMountSource *mount_source)
{
  return mount_source->dbus_id;
}

const char *
g_mount_source_get_obj_path (GMountSource *mount_source)
{
  return mount_source->obj_path;
}

typedef struct AskPasswordData AskPasswordData;

struct AskPasswordData {

  /* results: */
  gboolean       aborted;
  char          *password;
  char          *username;
  char          *domain;
  GPasswordSave  password_save;
  gboolean	 anonymous;
};

typedef struct AskSyncData AskSyncData;

struct AskSyncData {

  /* For sync calls */
  GMutex mutex;
  GCond cond;

  /* results: */
  GAsyncResult *result;
};

static void
ask_password_data_free (gpointer _data)
{
  AskPasswordData *data = (AskPasswordData *) _data;
  g_free (data->password);
  g_free (data->username);
  g_free (data->domain);
  g_free (data);
}

static GVfsDBusMountOperation *
create_mount_operation_proxy_sync (GMountSource        *source,
                                   GError             **error)
{
  GVfsDBusMountOperation *proxy;
  GError *local_error;

  /* If no dbus id specified, reply that we weren't handled */
  if (source->dbus_id[0] == 0)
    { 
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Internal Error");
      return NULL;
    }

  local_error = NULL;
  /* Synchronously creating a proxy using unique/private d-bus name and not loading properties or connecting signals should not block */
  proxy = gvfs_dbus_mount_operation_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                                            G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
                                                            source->dbus_id,
                                                            source->obj_path,
                                                            NULL,
                                                            &local_error);
  if (proxy == NULL)
    {
      g_dbus_error_strip_remote_error (local_error);
      g_propagate_error (error, local_error);
    }

  return proxy;
}

/* the callback from dbus -> main thread */
static void
ask_password_reply (GVfsDBusMountOperation *proxy,
                    GAsyncResult *res,
                    gpointer user_data)
{
  GTask *task;
  AskPasswordData *data;
  gboolean handled, aborted, anonymous;
  guint32 password_save;
  gchar *password, *username, *domain;
  GError *error;

  task = G_TASK (user_data);
  handled = TRUE;

  error = NULL;
  if (!gvfs_dbus_mount_operation_call_ask_password_finish (proxy,
                                                           &handled,
                                                           &aborted,
                                                           &password,
                                                           &username,
                                                           &domain,
                                                           &anonymous,
                                                           &password_save,
                                                           res,
                                                           &error))
    {
      g_dbus_error_strip_remote_error (error);
      g_task_return_error (task, error);
    }
  else if (handled == FALSE)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Internal Error");
    }
  else
    {
      data = g_new0 (AskPasswordData, 1);
      data->aborted = aborted;

      if (!anonymous)
        {
          data->password = g_strdup (password);
          data->username = *username == 0 ? NULL : g_strdup (username);
          data->domain = *domain == 0 ? NULL : g_strdup (domain);
        }
      data->password_save = (GPasswordSave)password_save;
      data->anonymous = anonymous;

      g_task_return_pointer (task, data, ask_password_data_free);

      /* TODO: handle more args */
      g_free (password);
      g_free (username);
      g_free (domain);
    }

  g_object_unref (task);
}

void
g_mount_source_ask_password_async (GMountSource              *source,
                                   const char                *message_string,
                                   const char                *default_user,
                                   const char                *default_domain,
                                   GAskPasswordFlags          flags,
                                   GAsyncReadyCallback        callback,
                                   gpointer                   user_data)
{
  GTask *task;
  GVfsDBusMountOperation *proxy;
  GError *error = NULL;

  task = g_task_new (source, NULL, callback, user_data);
  g_task_set_source_tag (task, g_mount_source_ask_password_async);

  proxy = create_mount_operation_proxy_sync (source, &error);
  if (proxy == NULL)
    {
      g_task_return_error (task, error);
      g_object_unref (task);
      return;
    }

  /* 30 minute timeout */
  g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_MOUNT_TIMEOUT_MSECS);

  gvfs_dbus_mount_operation_call_ask_password (proxy,
                                               message_string ? message_string : "",
                                               default_user ? default_user : "",
                                               default_domain ? default_domain : "",
                                               flags,
                                               NULL,
                                               (GAsyncReadyCallback) ask_password_reply,
                                               task);
  g_object_unref (proxy);
}

/**
 * g_mount_source_ask_password_finish:
 * @source: the source to query
 * @result: the async result
 * @aborted: set to %TRUE if the password dialog was aborted by the user
 * @password_out: the to the password set by the user or to %NULL if none
 * @user_out: set to the username set by the user or to %NULL if none
 * @domain_out: set to the domain set by the user or to %NULL if none
 * @anonymous_out: set to %TRUE if the user selected anonymous login. This
 *                 should only happen if G_ASK_PASSWORD_ANONYMOUS_SUPPORTED
 *                 was supplied whe querying the password.
 * @password_save_out: set to the save flags to use when saving the password
 *                     in the keyring.
 *
 * Requests the reply parameters from a g_mount_source_ask_password_async() 
 * request. All out parameters can be set to %NULL to ignore them.
 * <note><para>Please be aware that out parameters other than the password
 * are set to %NULL if the user don't specify them so make sure to
 * check them.</para></note>
 *
 * Returns: %FALSE if the async reply contained an error.
 **/
gboolean
g_mount_source_ask_password_finish (GMountSource  *source,
                                    GAsyncResult  *result,
                                    gboolean      *aborted,
                                    char         **password_out,
                                    char         **user_out,
                                    char         **domain_out,
				    gboolean	  *anonymous_out,
				    GPasswordSave *password_save_out)
{
  AskPasswordData *data, def = { TRUE, };

  g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
  g_return_val_if_fail (g_async_result_is_tagged (result, g_mount_source_ask_password_async), FALSE);

  data = g_task_propagate_pointer (G_TASK (result), NULL);
  if (data == NULL)
    data = &def;

  if (aborted)
    *aborted = data->aborted;

  if (password_out)
    {
      *password_out = data->password;
      data->password = NULL;
    }

  if (user_out)
    {
      *user_out = data->username;
      data->username = NULL;
    }

  if (domain_out)
    {
      *domain_out = data->domain;
      data->domain = NULL;
    }

  if (anonymous_out)
    *anonymous_out = data->anonymous;

  if (password_save_out)
    *password_save_out = data->password_save;  
  
  return data != &def;
}


static void
ask_reply_sync  (GObject *source_object,
		 GAsyncResult *res,
		 gpointer user_data)
{
  AskSyncData *data;

  data = (AskSyncData *) user_data;

  data->result = g_object_ref (res);

  /* Wake up sync call thread */
  g_mutex_lock (&data->mutex);
  g_cond_signal (&data->cond);
  g_mutex_unlock (&data->mutex);
}

gboolean
g_mount_source_ask_password (GMountSource *source,
			     const char *message_string,
			     const char *default_user,
			     const char *default_domain,
			     GAskPasswordFlags flags,
			     gboolean *aborted_out,
			     char **password_out,
			     char **user_out,
			     char **domain_out,
			     gboolean *anonymous_out,
			     GPasswordSave *password_save_out)
{
  gboolean handled;
  AskSyncData data;

  memset (&data, 0, sizeof (data));
  g_mutex_init (&data.mutex);
  g_cond_init (&data.cond);
  g_mutex_lock (&data.mutex);


  g_mount_source_ask_password_async (source,
                                     message_string,
                                     default_user,
                                     default_domain,
                                     flags,
                                     ask_reply_sync,
                                     &data);
  
  g_cond_wait (&data.cond, &data.mutex);
  g_mutex_unlock (&data.mutex);

  g_cond_clear (&data.cond);
  g_mutex_clear (&data.mutex);


  handled = g_mount_source_ask_password_finish (source,
                                                data.result,
                                                aborted_out,
                                                password_out,
                                                user_out,
                                                domain_out,
						anonymous_out,
						password_save_out);
  g_object_unref (data.result);

  return handled;
}

static void
op_ask_password_reply (GObject *source_object,
                       GAsyncResult *res,
                       gpointer user_data)
{
  GMountOperationResult result;
  GMountOperation *op;
  GMountSource *source;
  gboolean handled, aborted;
  char *username;
  char *password;
  char *domain;
  GPasswordSave password_save;

  source = G_MOUNT_SOURCE (source_object);
  op = G_MOUNT_OPERATION (user_data);
  username = NULL;
  password = NULL;
  domain = NULL;

  handled = g_mount_source_ask_password_finish (source,
                                                res,
                                                &aborted,
                                                &password,
                                                &username,
                                                &domain,
						NULL,
						&password_save);

  if (!handled)
    result = G_MOUNT_OPERATION_UNHANDLED;
  else if (aborted)
    result = G_MOUNT_OPERATION_ABORTED;
  else
    {
      result = G_MOUNT_OPERATION_HANDLED;

      if (password)
	g_mount_operation_set_password (op, password);
      if (username)
	g_mount_operation_set_username (op, username);
      if (domain)
	g_mount_operation_set_domain (op, domain);
      g_mount_operation_set_password_save (op, password_save);
    }
  
  g_mount_operation_reply (op, result);
  g_object_unref (op);
}

static gboolean
op_ask_password (GMountOperation *op,
		 const char      *message,
		 const char      *default_user,
		 const char      *default_domain,
		 GAskPasswordFlags flags,
		 GMountSource *mount_source)
{
  g_mount_source_ask_password_async (mount_source,
				     message,
				     default_user,
				     default_domain,
                                     flags,
				     op_ask_password_reply,
				     g_object_ref (op));
  g_signal_stop_emission_by_name (op, "ask_password");
  return TRUE;
}

typedef struct AskQuestionData AskQuestionData;

struct AskQuestionData {

  /* results: */
  gboolean aborted;
  guint32  choice;
};

/* the callback from dbus -> main thread */
static void
ask_question_reply (GVfsDBusMountOperation *proxy,
                    GAsyncResult *res,
                    gpointer user_data)
{
  GTask *task;
  AskQuestionData *data;
  gboolean handled, aborted;
  guint32 choice;
  GError *error;

  task = G_TASK (user_data);
  handled = TRUE;

  error = NULL;
  if (!gvfs_dbus_mount_operation_call_ask_question_finish (proxy,
                                                           &handled,
                                                           &aborted,
                                                           &choice,
                                                           res,
                                                           &error))
    {
      g_dbus_error_strip_remote_error (error);
      g_task_return_error (task, error);
    }
  else if (handled == FALSE)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Internal Error");
    }
  else
    {
      data = g_new0 (AskQuestionData, 1);
      data->aborted = aborted;
      data->choice = choice;

      g_task_return_pointer (task, data, g_free);
    }

  g_object_unref (task);
}

gboolean
g_mount_source_ask_question (GMountSource *source,
			     const char   *message,
			     const char  **choices,
			     gboolean     *aborted_out,
			     gint         *choice_out)
{
  gint choice;
  gboolean handled, aborted;
  AskSyncData data;
  
  memset (&data, 0, sizeof (data));
  g_mutex_init (&data.mutex);
  g_cond_init (&data.cond);
  g_mutex_lock (&data.mutex);

  g_mount_source_ask_question_async (source,
                                     message,
				     choices,
                                     ask_reply_sync,
                                     &data);
  
  g_cond_wait (&data.cond, &data.mutex);
  g_mutex_unlock (&data.mutex);

  g_cond_clear (&data.cond);
  g_mutex_clear (&data.mutex);

  handled = g_mount_source_ask_question_finish (source,
                                                data.result,
                                                &aborted,
                                                &choice);
  
  g_object_unref (data.result);

  if (aborted_out)
    *aborted_out = aborted;

  if (choice_out)
    *choice_out = choice;
  
  return handled;	
}

void
g_mount_source_ask_question_async (GMountSource       *source,
				   const char         *message_string,
				   const char        **choices,
				   GAsyncReadyCallback callback,
				   gpointer            user_data)
{
  GTask *task;
  GVfsDBusMountOperation *proxy;
  GError *error = NULL;

  task = g_task_new (source, NULL, callback, user_data);
  g_task_set_source_tag (task, g_mount_source_ask_question_async);

  proxy = create_mount_operation_proxy_sync (source, &error);
  if (proxy == NULL)
    {
      g_task_return_error (task, error);
      g_object_unref (task);
      return;
    }

  /* 30 minute timeout */
  g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_MOUNT_TIMEOUT_MSECS);

  gvfs_dbus_mount_operation_call_ask_question (proxy,
                                               message_string ? message_string : "",
                                               choices,   
                                               NULL,
                                               (GAsyncReadyCallback) ask_question_reply,
                                               task);
  g_object_unref (proxy);
}

gboolean
g_mount_source_ask_question_finish (GMountSource *source,
				    GAsyncResult *result,
				    gboolean     *aborted,
				    gint         *choice_out)
{
  AskQuestionData *data, def = { TRUE, };

  g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
  g_return_val_if_fail (g_async_result_is_tagged (result, g_mount_source_ask_question_async), FALSE);

  data = g_task_propagate_pointer (G_TASK (result), NULL);
  if (data == NULL)
    data = &def;

  if (aborted)
    *aborted = data->aborted;

  if (choice_out)
    *choice_out = data->choice;

  return data != &def;	
}

static void
op_ask_question_reply (GObject      *source_object,
                       GAsyncResult *res,
                       gpointer      user_data)
{
  GMountOperationResult result;
  GMountOperation *op;
  GMountSource *source;
  gboolean handled, aborted;
  gint choice;

  source = G_MOUNT_SOURCE (source_object);
  op = G_MOUNT_OPERATION (user_data);

  handled = g_mount_source_ask_question_finish (source,
                                                res,
                                                &aborted,
						&choice);

  if (!handled)
    result = G_MOUNT_OPERATION_UNHANDLED;
  else if (aborted)
    result = G_MOUNT_OPERATION_ABORTED;
  else
    {
      result = G_MOUNT_OPERATION_HANDLED;
      g_mount_operation_set_choice (op, choice);
    }
  
  g_mount_operation_reply (op, result);
  g_object_unref (op);
}

static gboolean
op_ask_question (GMountOperation *op,
		 const char      *message,
		 const char     **choices,
		 GMountSource    *mount_source)
{
  g_mount_source_ask_question_async (mount_source,
				     message,
				     choices,
				     op_ask_question_reply,
				     g_object_ref (op));
  g_signal_stop_emission_by_name (op, "ask_question");
  return TRUE;
}

typedef struct ShowProcessesData ShowProcessesData;

struct ShowProcessesData {

  /* results: */
  gboolean aborted;
  guint32  choice;
};

/* the callback from dbus -> main thread */
static void
show_processes_reply (GVfsDBusMountOperation *proxy,
                      GAsyncResult *res,
                      gpointer user_data)
{
  GTask *task;
  ShowProcessesData *data;
  gboolean handled, aborted;
  guint32 choice;
  GError *error;

  task = G_TASK (user_data);
  handled = TRUE;

  error = NULL;
  if (!gvfs_dbus_mount_operation_call_show_processes_finish (proxy,
                                                             &handled,
                                                             &aborted,
                                                             &choice,
                                                             res,
                                                             &error))
    {
      g_dbus_error_strip_remote_error (error);
      g_task_return_error (task, error);
    }
  else if (handled == FALSE)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Internal Error");
    }
  else
    {
      data = g_new0 (ShowProcessesData, 1);
      data->aborted = aborted;
      data->choice = choice;

      g_task_return_pointer (task, data, g_free);
    }

  g_object_unref (task);
}

void
g_mount_source_show_processes_async (GMountSource        *source,
                                     const char          *message_string,
                                     GArray              *processes,
                                     const char         **choices,
                                     GAsyncReadyCallback  callback,
                                     gpointer             user_data)
{
  GTask *task;
  GVfsDBusMountOperation *proxy;
  GVariantBuilder builder;
  guint i;
  GError *error = NULL;

  task = g_task_new (source, NULL, callback, user_data);
  g_task_set_source_tag (task, g_mount_source_show_processes_async);

  proxy = create_mount_operation_proxy_sync (source, &error);
  if (proxy == NULL)
    {
      g_task_return_error (task, error);
      g_object_unref (task);
      return;
    }

  /* 30 minute timeout */
  g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_MOUNT_TIMEOUT_MSECS);

  g_variant_builder_init (&builder, G_VARIANT_TYPE ("ai"));
  for (i = 0; i < processes->len; i++)
    g_variant_builder_add (&builder, "i", 
                           g_array_index (processes, gint32, i));
  
  gvfs_dbus_mount_operation_call_show_processes (proxy,
                                                 message_string ? message_string : "",
                                                 choices,
                                                 g_variant_builder_end (&builder),
                                                 NULL,
                                                 (GAsyncReadyCallback) show_processes_reply,
                                                 task);
  g_object_unref (proxy);
}

gboolean
g_mount_source_show_processes_finish (GMountSource *source,
                                      GAsyncResult *result,
                                      gboolean     *aborted,
                                      gint         *choice_out)
{
  ShowProcessesData *data, def = { TRUE, };

  g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
  g_return_val_if_fail (g_async_result_is_tagged (result, g_mount_source_show_processes_async), FALSE);

  data = g_task_propagate_pointer (G_TASK (result), NULL);
  if (data == NULL)
    data = &def;

  if (aborted)
    *aborted = data->aborted;

  if (choice_out)
    *choice_out = data->choice;

  return data != &def;
}

gboolean
g_mount_source_show_processes (GMountSource *source,
                               const char   *message,
                               GArray       *processes,
                               const char  **choices,
                               gboolean     *aborted_out,
                               gint         *choice_out)
{
  gint choice;
  gboolean handled, aborted;
  AskSyncData data;

  memset (&data, 0, sizeof (data));
  g_mutex_init (&data.mutex);
  g_cond_init (&data.cond);
  g_mutex_lock (&data.mutex);

  g_mount_source_show_processes_async (source,
                                       message,
                                       processes,
                                       choices,
                                       ask_reply_sync,
                                       &data);

  g_cond_wait (&data.cond, &data.mutex);
  g_mutex_unlock (&data.mutex);

  g_cond_clear (&data.cond);
  g_mutex_clear (&data.mutex);

  handled = g_mount_source_show_processes_finish (source,
                                                  data.result,
                                                  &aborted,
                                                  &choice);

  g_object_unref (data.result);

  if (aborted_out)
    *aborted_out = aborted;

  if (choice_out)
    *choice_out = choice;

  return handled;
}

static void
op_show_processes_reply (GObject      *source_object,
                         GAsyncResult *res,
                         gpointer      user_data)
{
  GMountOperationResult result;
  GMountOperation *op;
  GMountSource *source;
  gboolean handled, aborted;
  gint choice;

  source = G_MOUNT_SOURCE (source_object);
  op = G_MOUNT_OPERATION (user_data);

  handled = g_mount_source_show_processes_finish (source,
                                                  res,
                                                  &aborted,
                                                  &choice);

  if (!handled)
    result = G_MOUNT_OPERATION_UNHANDLED;
  else if (aborted)
    result = G_MOUNT_OPERATION_ABORTED;
  else
    {
      result = G_MOUNT_OPERATION_HANDLED;
      g_mount_operation_set_choice (op, choice);
    }

  g_mount_operation_reply (op, result);
  g_object_unref (op);
}

static gboolean
op_show_processes (GMountOperation *op,
                   const char      *message,
                   GArray          *processes,
                   const char     **choices,
                   GMountSource    *mount_source)
{
  g_mount_source_show_processes_async (mount_source,
                                       message,
                                       processes,
                                       choices,
                                       op_show_processes_reply,
                                       g_object_ref (op));
  g_signal_stop_emission_by_name (op, "show_processes");
  return TRUE;
}

static void
show_unmount_progress_reply (GVfsDBusMountOperation *proxy,
                             GAsyncResult *res,
                             gpointer user_data)
{
  GError *error;

  error = NULL;
  if (!gvfs_dbus_mount_operation_call_show_unmount_progress_finish (proxy, res, &error))
    {
      g_warning ("ShowUnmountProgress request failed: %s", error->message);
      g_error_free (error);
    }
}

void
g_mount_source_show_unmount_progress (GMountSource *source,
                                      const char   *message_string,
                                      gint64      time_left,
                                      gint64      bytes_left)
{
  GVfsDBusMountOperation *proxy;

  /* If no dbus id specified, warn and return */
  if (source->dbus_id[0] == 0)
    {
      g_warning ("No dbus id specified in the mount source, "
                 "ignoring show-unmount-progress request");
      return;
    }
  
  proxy = create_mount_operation_proxy_sync (source, NULL);
  if (proxy == NULL)
    return;

  /* 30 minute timeout */
  g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_MOUNT_TIMEOUT_MSECS);

  gvfs_dbus_mount_operation_call_show_unmount_progress (proxy,
                                                        message_string ? message_string : "",
                                                        time_left,
                                                        bytes_left,
                                                        NULL,
                                                        (GAsyncReadyCallback) show_unmount_progress_reply,
                                                        NULL);
  g_object_unref (proxy);
}

static void
op_show_unmount_progress (GMountOperation *op,
                          const char      *message,
                          gint64           time_left,
                          gint64           bytes_left,
                          GMountSource    *mount_source)
{
  g_mount_source_show_unmount_progress (mount_source,
                                        message,
                                        time_left,
                                        bytes_left);
  g_signal_stop_emission_by_name (op, "show_unmount_progress");
}

static void
abort_reply (GVfsDBusMountOperation *proxy,
             GAsyncResult *res,
             gpointer user_data)
{
  gvfs_dbus_mount_operation_call_aborted_finish (proxy, res, NULL);
}

gboolean
g_mount_source_abort (GMountSource *source)
{
  GVfsDBusMountOperation *proxy;

  proxy = create_mount_operation_proxy_sync (source, NULL);
  if (proxy == NULL)
    return FALSE;
  
  gvfs_dbus_mount_operation_call_aborted (proxy, NULL, 
                                          (GAsyncReadyCallback) abort_reply, NULL);
  
  g_object_unref (proxy);
  return TRUE;
}

static void
op_aborted (GMountOperation *op,
	    GMountSource    *source)
{
  g_mount_source_abort (source);
}

gboolean
g_mount_source_is_dummy (GMountSource *source)
{
  g_return_val_if_fail (G_IS_MOUNT_SOURCE (source), TRUE);
  return source->dbus_id[0] == 0;
}


GMountOperation *
g_mount_source_get_operation (GMountSource *mount_source)
{
  GMountOperation *op;

  op = g_mount_operation_new ();
  g_object_set_data_full (G_OBJECT (op), "source",
			  g_object_ref (mount_source),
			  g_object_unref);

  g_signal_connect (op, "ask_password", (GCallback)op_ask_password, mount_source);
  g_signal_connect (op, "ask_question", (GCallback)op_ask_question, mount_source);
  g_signal_connect (op, "show_processes", (GCallback)op_show_processes, mount_source);
  g_signal_connect (op, "show_unmount_progress", (GCallback)op_show_unmount_progress, mount_source);
  g_signal_connect (op, "aborted", (GCallback)op_aborted, mount_source);

  return op;
}
