summaryrefslogtreecommitdiff
path: root/daemon/gvfsbackendsftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gvfsbackendsftp.c')
-rw-r--r--daemon/gvfsbackendsftp.c3913
1 files changed, 0 insertions, 3913 deletions
diff --git a/daemon/gvfsbackendsftp.c b/daemon/gvfsbackendsftp.c
deleted file mode 100644
index 3527ebe7..00000000
--- a/daemon/gvfsbackendsftp.c
+++ /dev/null
@@ -1,3913 +0,0 @@
-/* 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 <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include <glib/gstdio.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#include <gio/gunixinputstream.h>
-#include <gio/gunixoutputstream.h>
-
-#include "gvfsbackendsftp.h"
-#include "gvfsjobopenforread.h"
-#include "gvfsjobread.h"
-#include "gvfsjobseekread.h"
-#include "gvfsjobopenforwrite.h"
-#include "gvfsjobwrite.h"
-#include "gvfsjobclosewrite.h"
-#include "gvfsjobseekwrite.h"
-#include "gvfsjobsetdisplayname.h"
-#include "gvfsjobqueryinfo.h"
-#include "gvfsjobmove.h"
-#include "gvfsjobdelete.h"
-#include "gvfsjobqueryfsinfo.h"
-#include "gvfsjobqueryattributes.h"
-#include "gvfsjobenumerate.h"
-#include "gvfsdaemonprotocol.h"
-#include "gvfskeyring.h"
-#include "sftp.h"
-#include "pty_open.h"
-
-/* TODO for sftp:
- * Implement can_delete & can_rename
- * fstat
- */
-
-#ifdef HAVE_GRANTPT
-/* We only use this on systems with unix98 ptys */
-#define USE_PTY 1
-#endif
-
-static GQuark id_q;
-
-typedef enum {
- SFTP_VENDOR_INVALID = 0,
- SFTP_VENDOR_OPENSSH,
- SFTP_VENDOR_SSH
-} SFTPClientVendor;
-
-typedef struct _MultiReply MultiReply;
-
-typedef void (*ReplyCallback) (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data);
-
-typedef void (*MultiReplyCallback) (GVfsBackendSftp *backend,
- MultiReply *replies,
- int n_replies,
- GVfsJob *job,
- gpointer user_data);
-
-
-typedef struct {
- MultiReply *replies;
- int n_replies;
- int n_outstanding;
- gpointer user_data;
- MultiReplyCallback callback;
-} MultiRequest;
-
-struct _MultiReply {
- int type;
- GDataInputStream *data;
- guint32 data_len;
-
- MultiRequest *request;
-};
-
-
-
-typedef struct {
- guchar *data;
- gsize size;
-} DataBuffer;
-
-typedef struct {
- DataBuffer *raw_handle;
- goffset offset;
- char *filename;
- char *tempname;
- guint32 permissions;
- gboolean make_backup;
-} SftpHandle;
-
-
-typedef struct {
- ReplyCallback callback;
- GVfsJob *job;
- gpointer user_data;
-} ExpectedReply;
-
-struct _GVfsBackendSftp
-{
- GVfsBackend parent_instance;
-
- SFTPClientVendor client_vendor;
- char *host;
- int port;
- gboolean user_specified;
- char *user;
- char *tmp_password;
-
- guint32 my_uid;
- guint32 my_gid;
-
- int protocol_version;
-
- GOutputStream *command_stream;
- GInputStream *reply_stream;
- GDataInputStream *error_stream;
-
- guint32 current_id;
-
- /* Output Queue */
-
- gsize command_bytes_written;
- GList *command_queue;
-
- /* Reply reading: */
- GHashTable *expected_replies;
- guint32 reply_size;
- guint32 reply_size_read;
- guint8 *reply;
-
- GMountSource *mount_source; /* Only used/set during mount */
- int mount_try;
- gboolean mount_try_again;
-};
-
-static void parse_attributes (GVfsBackendSftp *backend,
- GFileInfo *info,
- const char *basename,
- GDataInputStream *reply,
- GFileAttributeMatcher *attribute_matcher);
-
-
-G_DEFINE_TYPE (GVfsBackendSftp, g_vfs_backend_sftp, G_VFS_TYPE_BACKEND)
-
-static void
-data_buffer_free (DataBuffer *buffer)
-{
- if (buffer)
- {
- g_free (buffer->data);
- g_slice_free (DataBuffer, buffer);
- }
-}
-
-static void
-make_fd_nonblocking (int fd)
-{
- fcntl (fd, F_SETFL, O_NONBLOCK | fcntl (fd, F_GETFL));
-}
-
-static SFTPClientVendor
-get_sftp_client_vendor (void)
-{
- char *ssh_stderr;
- char *args[3];
- gint ssh_exitcode;
- SFTPClientVendor res = SFTP_VENDOR_INVALID;
-
- args[0] = g_strdup (SSH_PROGRAM);
- args[1] = g_strdup ("-V");
- args[2] = NULL;
- if (g_spawn_sync (NULL, args, NULL,
- G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL,
- NULL, NULL,
- NULL, &ssh_stderr,
- &ssh_exitcode, NULL))
- {
- if (ssh_stderr == NULL)
- res = SFTP_VENDOR_INVALID;
- else if ((strstr (ssh_stderr, "OpenSSH") != NULL) ||
- (strstr (ssh_stderr, "Sun_SSH") != NULL))
- res = SFTP_VENDOR_OPENSSH;
- else if (strstr (ssh_stderr, "SSH Secure Shell") != NULL)
- res = SFTP_VENDOR_SSH;
- else
- res = SFTP_VENDOR_INVALID;
- }
-
- g_free (ssh_stderr);
- g_free (args[0]);
- g_free (args[1]);
-
- return res;
-}
-
-static void
-g_vfs_backend_sftp_finalize (GObject *object)
-{
- GVfsBackendSftp *backend;
-
- backend = G_VFS_BACKEND_SFTP (object);
-
- g_hash_table_destroy (backend->expected_replies);
-
- if (backend->command_stream)
- g_object_unref (backend->command_stream);
-
- if (backend->reply_stream)
- g_object_unref (backend->reply_stream);
-
- if (backend->error_stream)
- g_object_unref (backend->error_stream);
-
- if (G_OBJECT_CLASS (g_vfs_backend_sftp_parent_class)->finalize)
- (*G_OBJECT_CLASS (g_vfs_backend_sftp_parent_class)->finalize) (object);
-}
-
-static void
-expected_reply_free (ExpectedReply *reply)
-{
- g_object_unref (reply->job);
- g_slice_free (ExpectedReply, reply);
-}
-
-static void
-g_vfs_backend_sftp_init (GVfsBackendSftp *backend)
-{
- backend->expected_replies = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)expected_reply_free);
-}
-
-static void
-look_for_stderr_errors (GVfsBackend *backend, GError **error)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- char *line;
-
- while (1)
- {
- line = g_data_input_stream_read_line (op_backend->error_stream, NULL, NULL, NULL);
-
- if (line == NULL)
- {
- /* Error (real or WOULDBLOCK) or EOF */
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("ssh program unexpectedly exited"));
- return;
- }
-
- if (strstr (line, "Permission denied") != NULL)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
- _("Permission denied"));
- return;
- }
- else if (strstr (line, "Name or service not known") != NULL)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND,
- _("Hostname not known"));
- return;
- }
- else if (strstr (line, "No route to host") != NULL)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND,
- _("No route to host"));
- return;
- }
- else if (strstr (line, "Connection refused") != NULL)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
- _("Connection refused by server"));
- return;
- }
- else if (strstr (line, "Host key verification failed") != NULL)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Host key verification failed"));
- return;
- }
-
- g_free (line);
- }
-}
-
-static char **
-setup_ssh_commandline (GVfsBackend *backend)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- guint last_arg;
- gchar **args;
-
- args = g_new0 (gchar *, 20); /* 20 is enought for now, bump size if code below changes */
-
- /* Fill in the first few args */
- last_arg = 0;
- args[last_arg++] = g_strdup (SSH_PROGRAM);
-
- if (op_backend->client_vendor == SFTP_VENDOR_OPENSSH)
- {
- args[last_arg++] = g_strdup ("-oForwardX11 no");
- args[last_arg++] = g_strdup ("-oForwardAgent no");
- args[last_arg++] = g_strdup ("-oClearAllForwardings yes");
- args[last_arg++] = g_strdup ("-oProtocol 2");
- args[last_arg++] = g_strdup ("-oNoHostAuthenticationForLocalhost yes");
-#ifndef USE_PTY
- args[last_arg++] = g_strdup ("-oBatchMode yes");
-#endif
-
- }
- else if (op_backend->client_vendor == SFTP_VENDOR_SSH)
- args[last_arg++] = g_strdup ("-x");
-
- if (op_backend->port != -1)
- {
- args[last_arg++] = g_strdup ("-p");
- args[last_arg++] = g_strdup_printf ("%d", op_backend->port);
- }
-
-
- args[last_arg++] = g_strdup ("-l");
- args[last_arg++] = g_strdup (op_backend->user);
-
- args[last_arg++] = g_strdup ("-s");
-
- if (op_backend->client_vendor == SFTP_VENDOR_SSH)
- {
- args[last_arg++] = g_strdup ("sftp");
- args[last_arg++] = g_strdup (op_backend->host);
- }
- else
- {
- args[last_arg++] = g_strdup (op_backend->host);
- args[last_arg++] = g_strdup ("sftp");
- }
-
- args[last_arg++] = NULL;
-
- return args;
-}
-
-static gboolean
-spawn_ssh (GVfsBackend *backend,
- char *args[],
- pid_t *pid,
- int *tty_fd,
- int *stdin_fd,
- int *stdout_fd,
- int *stderr_fd,
- GError **error)
-{
-#ifdef USE_PTY
- *tty_fd = pty_open(pid, PTY_REAP_CHILD, NULL,
- args[0], args, NULL,
- 300, 300,
- stdin_fd, stdout_fd, stderr_fd);
- if (*tty_fd == -1)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unable to spawn ssh program"));
- return FALSE;
- }
-#else
- GError *my_error;
- GPid gpid;
-
- *tty_fd = -1;
-
- my_error = NULL;
- if (!g_spawn_async_with_pipes (NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
- &gpid,
- stdin_fd, stdout_fd, stderr_fd, &my_error))
- {
- g_set_error (error,
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unable to spawn ssh program: %s"), my_error->message);
- g_error_free (my_error);
- return FALSE;
- }
- *pid = gpid;
-#endif
-
- return TRUE;
-}
-
-static guint32
-get_new_id (GVfsBackendSftp *backend)
-{
- return backend->current_id++;
-}
-
-static GDataOutputStream *
-new_command_stream (GVfsBackendSftp *backend, int type)
-{
- GOutputStream *mem_stream;
- GDataOutputStream *data_stream;
- guint32 id;
-
- mem_stream = g_memory_output_stream_new (NULL, 0, (GReallocFunc)g_realloc, NULL);
- data_stream = g_data_output_stream_new (mem_stream);
- g_object_unref (mem_stream);
-
- g_data_output_stream_put_int32 (data_stream, 0, NULL, NULL); /* LEN */
- g_data_output_stream_put_byte (data_stream, type, NULL, NULL);
- if (type != SSH_FXP_INIT)
- {
- id = get_new_id (backend);
- g_data_output_stream_put_uint32 (data_stream, id, NULL, NULL);
- g_object_set_qdata (G_OBJECT (data_stream), id_q, GUINT_TO_POINTER (id));
- }
-
- return data_stream;
-}
-
-static gsize
-get_data_size (GMemoryOutputStream *stream)
-{
- g_seekable_seek (G_SEEKABLE (stream),
- 0,
- G_SEEK_END,
- NULL, NULL);
- return g_seekable_tell (G_SEEKABLE (stream));
-}
-
-static gpointer
-get_data_from_command_stream (GDataOutputStream *command_stream, gsize *len)
-{
- GOutputStream *mem_stream;
- gpointer data;
- guint32 *len_ptr;
-
- mem_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (command_stream));
- data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_stream));
- *len = get_data_size (G_MEMORY_OUTPUT_STREAM (mem_stream));
-
- len_ptr = (guint32 *)data;
- *len_ptr = GUINT32_TO_BE (*len - 4);
-
- return data;
-}
-
-static gboolean
-send_command_sync_and_unref_command (GVfsBackendSftp *backend,
- GDataOutputStream *command_stream,
- GCancellable *cancellable,
- GError **error)
-{
- gpointer data;
- gsize len;
- gsize bytes_written;
- gboolean res;
-
- data = get_data_from_command_stream (command_stream, &len);
-
- res = g_output_stream_write_all (backend->command_stream,
- data, len,
- &bytes_written,
- cancellable, error);
-
- if (error == NULL && !res)
- g_warning ("Ignored send_command error\n");
-
- g_free (data);
- g_object_unref (command_stream);
-
- return res;
-}
-
-static gboolean
-wait_for_reply (GVfsBackend *backend, int stdout_fd, GError **error)
-{
- fd_set ifds;
- struct timeval tv;
- int ret;
-
- FD_ZERO (&ifds);
- FD_SET (stdout_fd, &ifds);
-
- tv.tv_sec = 20;
- tv.tv_usec = 0;
-
- ret = select (stdout_fd+1, &ifds, NULL, NULL, &tv);
-
- if (ret <= 0)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
- _("Timed out when logging in"));
- return FALSE;
- }
- return TRUE;
-}
-
-static GDataInputStream *
-make_reply_stream (guint8 *data, gsize len)
-{
- GInputStream *mem_stream;
- GDataInputStream *data_stream;
-
- mem_stream = g_memory_input_stream_new_from_data (data, len, g_free);
- data_stream = g_data_input_stream_new (mem_stream);
- g_object_unref (mem_stream);
-
- return data_stream;
-}
-
-static GDataInputStream *
-read_reply_sync (GVfsBackendSftp *backend, gsize *len_out, GError **error)
-{
- guint32 len;
- gsize bytes_read;
- GByteArray *array;
- guint8 *data;
-
- if (!g_input_stream_read_all (backend->reply_stream,
- &len, 4,
- &bytes_read, NULL, error))
- return NULL;
-
-
- len = GUINT32_FROM_BE (len);
-
- array = g_byte_array_sized_new (len);
-
- if (!g_input_stream_read_all (backend->reply_stream,
- array->data, len,
- &bytes_read, NULL, error))
- {
- g_byte_array_free (array, TRUE);
- return NULL;
- }
-
- if (len_out)
- *len_out = len;
-
- data = array->data;
- g_byte_array_free (array, FALSE);
-
- return make_reply_stream (data, len);
-}
-
-static void
-put_string (GDataOutputStream *stream, const char *str)
-{
- g_data_output_stream_put_uint32 (stream, strlen (str), NULL, NULL);
- g_data_output_stream_put_string (stream, str, NULL, NULL);
-}
-
-static void
-put_data_buffer (GDataOutputStream *stream, DataBuffer *buffer)
-{
- g_data_output_stream_put_uint32 (stream, buffer->size, NULL, NULL);
- g_output_stream_write_all (G_OUTPUT_STREAM (stream),
- buffer->data, buffer->size,
- NULL,
- NULL, NULL);
-}
-
-static char *
-read_string (GDataInputStream *stream, gsize *len_out)
-{
- guint32 len;
- char *data;
- GError *error;
-
- error = NULL;
- len = g_data_input_stream_read_uint32 (stream, NULL, &error);
- if (error)
- {
- g_error_free (error);
- return NULL;
- }
-
- data = g_malloc (len + 1);
-
- if (!g_input_stream_read_all (G_INPUT_STREAM (stream), data, len, NULL, NULL, NULL))
- {
- g_free (data);
- return NULL;
- }
-
- data[len] = 0;
-
- if (len_out)
- *len_out = len;
-
- return data;
-}
-
-static DataBuffer *
-read_data_buffer (GDataInputStream *stream)
-{
- DataBuffer *buffer;
-
- buffer = g_slice_new (DataBuffer);
- buffer->data = (guchar *)read_string (stream, &buffer->size);
-
- return buffer;
-}
-
-static gboolean
-get_hostname_and_fingerprint_from_line (const gchar *buffer,
- gchar **hostname_out,
- gchar **fingerprint_out)
-{
- gchar *pos;
- gchar *startpos;
- gchar *endpos;
- gchar *hostname = NULL;
- gchar *fingerprint = NULL;
-
- if (g_str_has_prefix (buffer, "The authenticity of host '"))
- {
- /* OpenSSH */
- pos = strchr (&buffer[26], '\'');
- if (pos == NULL)
- return FALSE;
-
- hostname = g_strndup (&buffer[26], pos - (&buffer[26]));
-
- startpos = strstr (pos, " key fingerprint is ");
- if (startpos == NULL)
- {
- g_free (hostname);
- return FALSE;
- }
-
- startpos = startpos + 20;
- endpos = strchr (startpos, '.');
- if (endpos == NULL)
- {
- g_free (hostname);
- return FALSE;
- }
-
- fingerprint = g_strndup (startpos, endpos - startpos);
- }
- else if (strstr (buffer, "Key fingerprint:") != NULL)
- {
- /* SSH.com*/
- startpos = strstr (buffer, "Key fingerprint:");
- if (startpos == NULL)
- {
- g_free (hostname);
- return FALSE;
- }
-
- startpos = startpos + 18;
- endpos = strchr (startpos, '\r');
- fingerprint = g_strndup (startpos, endpos - startpos);
- }
-
- *hostname_out = hostname;
- *fingerprint_out = fingerprint;
-
- return TRUE;
-}
-
-static const gchar *
-get_authtype_from_password_line (const char *password_line)
-{
- return g_str_has_prefix (password_line, "Enter passphrase for key") ?
- "publickey" : "password";
-}
-
-static char *
-get_object_from_password_line (const char *password_line)
-{
- char *chr, *ptr, *object = NULL;
-
- if (g_str_has_prefix (password_line, "Enter passphrase for key"))
- {
- ptr = strchr (password_line, '\'');
- if (ptr != NULL)
- {
- ptr += 1;
- chr = strchr (ptr, '\'');
- if (chr != NULL)
- {
- object = g_strndup (ptr, chr - ptr);
- }
- else
- {
- object = g_strdup (ptr);
- }
- }
- }
- return object;
-}
-
-static gboolean
-handle_login (GVfsBackend *backend,
- GMountSource *mount_source,
- int tty_fd, int stdout_fd, int stderr_fd,
- GError **error)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GInputStream *prompt_stream;
- GOutputStream *reply_stream;
- fd_set ifds;
- struct timeval tv;
- int ret;
- int prompt_fd;
- char buffer[1024];
- gsize len;
- gboolean aborted = FALSE;
- gboolean ret_val;
- char *new_password = NULL;
- char *new_user = NULL;
- GPasswordSave password_save = G_PASSWORD_SAVE_NEVER;
- gsize bytes_written;
- gboolean password_in_keyring = FALSE;
- const gchar *authtype = NULL;
- gchar *object = NULL;
-
- if (op_backend->client_vendor == SFTP_VENDOR_SSH)
- prompt_fd = stderr_fd;
- else
- prompt_fd = tty_fd;
-
- prompt_stream = g_unix_input_stream_new (prompt_fd, FALSE);
- reply_stream = g_unix_output_stream_new (tty_fd, FALSE);
-
- ret_val = TRUE;
- while (1)
- {
- FD_ZERO (&ifds);
- FD_SET (stdout_fd, &ifds);
- FD_SET (prompt_fd, &ifds);
-
- tv.tv_sec = 20;
- tv.tv_usec = 0;
-
- ret = select (MAX (stdout_fd, prompt_fd)+1, &ifds, NULL, NULL, &tv);
-
- if (ret <= 0)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
- _("Timed out when logging in"));
- ret_val = FALSE;
- break;
- }
-
- if (FD_ISSET (stdout_fd, &ifds))
- break; /* Got reply to initial INIT request */
-
- g_assert (FD_ISSET (prompt_fd, &ifds));
-
-
- len = g_input_stream_read (prompt_stream,
- buffer, sizeof (buffer) - 1,
- NULL, error);
-
- if (len == -1)
- {
- ret_val = FALSE;
- break;
- }
-
- buffer[len] = 0;
-
- /*
- * If the input URI contains a username
- * if the input URI contains a password, we attempt one login and return GNOME_VFS_ERROR_ACCESS_DENIED on failure.
- * if the input URI contains no password, we query the user until he provides a correct one, or he cancels.
- *
- * If the input URI contains no username
- * (a) the user is queried for a user name and a password, with the default login being his
- * local login name.
- *
- * (b) if the user decides to change his remote login name, we go to tty_retry because we need a
- * new SSH session, attempting one login with his provided credentials, and if that fails proceed
- * with (a), but use his desired remote login name as default.
- *
- * The "password" variable is only used for the very first login attempt,
- * or for the first re-login attempt when the user decided to change his name.
- * Otherwise, we "new_password" and "new_user_name" is used, as output variable
- * for user and keyring input.
- */
- if (g_str_has_suffix (buffer, "password: ") ||
- g_str_has_suffix (buffer, "Password: ") ||
- g_str_has_suffix (buffer, "Password:") ||
- g_str_has_prefix (buffer, "Password for ") ||
- g_str_has_prefix (buffer, "Enter Kerberos password") ||
- g_str_has_prefix (buffer, "Enter passphrase for key"))
- {
- authtype = get_authtype_from_password_line (buffer);
- object = get_object_from_password_line (buffer);
-
- /* If password is in keyring at this point is because it failed */
- if (!op_backend->tmp_password && (password_in_keyring ||
- !g_vfs_keyring_lookup_password (op_backend->user,
- op_backend->host,
- NULL,
- "sftp",
- object,
- authtype,
- op_backend->port != -1 ?
- op_backend->port
- :
- 0,
- NULL,
- NULL,
- &new_password)))
- {
- GAskPasswordFlags flags = G_ASK_PASSWORD_NEED_PASSWORD;
-
- if (g_vfs_keyring_is_available ())
- flags |= G_ASK_PASSWORD_SAVING_SUPPORTED;
- if (!op_backend->user_specified)
- flags |= G_ASK_PASSWORD_NEED_USERNAME;
-
- g_free (new_password);
-
- if (!g_mount_source_ask_password (mount_source,
- g_str_has_prefix (buffer, "Enter passphrase for key") ?
- _("Enter passphrase for key")
- :
- _("Enter password"),
- op_backend->user,
- NULL,
- flags,
- &aborted,
- &new_password,
- &new_user,
- NULL,
- NULL,
- &password_save) ||
- aborted)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
- _("Password dialog cancelled"));
- ret_val = FALSE;
- break;
- }
- }
- else if (op_backend->tmp_password)
- {
- /* I already a have a password of a previous login attempt
- * that failed because the user provided a new user name
- */
- new_password = op_backend->tmp_password;
- op_backend->tmp_password = NULL;
- }
- else
- password_in_keyring = TRUE;
-
- if (new_user && strcmp (new_user, op_backend->user) != 0)
- {
- g_free (op_backend->user);
- op_backend->user = new_user;
-
- g_free (op_backend->tmp_password);
- op_backend->tmp_password = new_password;
- new_password = NULL;
-
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- "Invalid user name");
- ret_val = FALSE;
- break;
- }
- else if (new_user)
- {
- g_free (new_user);
- }
-
- if (!g_output_stream_write_all (reply_stream,
- new_password, strlen (new_password),
- &bytes_written,
- NULL, NULL) ||
- !g_output_stream_write_all (reply_stream,
- "\n", 1,
- &bytes_written,
- NULL, NULL))
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
- _("Can't send password"));
- ret_val = FALSE;
- break;
- }
- }
- else if (g_str_has_prefix (buffer, "The authenticity of host '") ||
- strstr (buffer, "Key fingerprint:") != NULL)
- {
- const gchar *choices[] = {_("Log In Anyway"), _("Cancel Login")};
- const gchar *v_choices = (const gchar *)choices;
- const gchar *choice_string;
- gchar *hostname = NULL;
- gchar *fingerprint = NULL;
- gint choice;
- gchar *message;
-
- get_hostname_and_fingerprint_from_line (buffer, &hostname, &fingerprint);
-
- message = g_strdup_printf (_("The identity of the remote computer (%s) is unknown.\n"
- "This happens when you log in to a computer the first time.\n\n"
- "The identity sent by the remote computer is %s. "
- "If you want to be absolutely sure it is safe to continue, "
- "contact the system administrator."),
- hostname ? hostname : op_backend->host, fingerprint);
-
- g_free (hostname);
- g_free (fingerprint);
-
- if (!g_mount_source_ask_question (mount_source,
- message,
- &v_choices,
- 2,
- &aborted,
- &choice) ||
- aborted)
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
- _("Login dialog cancelled"));
- g_free (message);
- ret_val = FALSE;
- break;
- }
- g_free (message);
-
- choice_string = (choice == 0) ? "yes" : "no";
- if (!g_output_stream_write_all (reply_stream,
- choice_string,
- strlen (choice_string),
- &bytes_written,
- NULL, NULL) ||
- !g_output_stream_write_all (reply_stream,
- "\n", 1,
- &bytes_written,
- NULL, NULL))
- {
- g_set_error_literal (error,
- G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
- _("Can't send host identity confirmation"));
- ret_val = FALSE;
- break;
- }
- }
- }
-
- if (ret_val)
- {
- /* Login succeed, save password in keyring */
- g_vfs_keyring_save_password (op_backend->user,
- op_backend->host,
- NULL,
- "sftp",
- object,
- authtype,
- op_backend->port != -1 ?
- op_backend->port
- :
- 0,
- new_password,
- password_save);
- }
-
- g_free (object);
- g_free (new_password);
- g_object_unref (prompt_stream);
- g_object_unref (reply_stream);
- return ret_val;
-}
-
-static void
-fail_jobs_and_die (GVfsBackendSftp *backend, GError *error)
-{
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&iter, backend->expected_replies);
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- ExpectedReply *expected_reply = (ExpectedReply *) value;
- g_vfs_job_failed_from_error (expected_reply->job, error);
- }
-
- g_error_free (error);
-
- _exit (1);
-}
-
-static void
-check_input_stream_read_result (GVfsBackendSftp *backend, gssize res, GError *error)
-{
- if (G_UNLIKELY (res <= 0))
- {
- if (res == 0 || error == NULL)
- {
- g_clear_error (&error);
- g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Internal error: %s"),
- res == 0 ? "The underlying ssh process died" : "Unkown Error");
- }
-
- fail_jobs_and_die (backend, error);
- }
-}
-
-static void read_reply_async (GVfsBackendSftp *backend);
-
-static void
-read_reply_async_got_data (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- GVfsBackendSftp *backend = user_data;
- gssize res;
- GDataInputStream *reply;
- ExpectedReply *expected_reply;
- guint32 id;
- int type;
- GError *error;
-
- error = NULL;
- res = g_input_stream_read_finish (G_INPUT_STREAM (source_object), result, &error);
-
- check_input_stream_read_result (backend, res, error);
-
- backend->reply_size_read += res;
-
- if (backend->reply_size_read < backend->reply_size)
- {
- g_input_stream_read_async (backend->reply_stream,
- backend->reply + backend->reply_size_read, backend->reply_size - backend->reply_size_read,
- 0, NULL, read_reply_async_got_data, backend);
- return;
- }
-
- reply = make_reply_stream (backend->reply, backend->reply_size);
- backend->reply = NULL;
-
- type = g_data_input_stream_read_byte (reply, NULL, NULL);
- id = g_data_input_stream_read_uint32 (reply, NULL, NULL);
-
- expected_reply = g_hash_table_lookup (backend->expected_replies, GINT_TO_POINTER (id));
- if (expected_reply)
- {
- if (expected_reply->callback != NULL)
- (expected_reply->callback) (backend, type, reply, backend->reply_size,
- expected_reply->job, expected_reply->user_data);
- g_hash_table_remove (backend->expected_replies, GINT_TO_POINTER (id));
- }
- else
- g_warning ("Got unhandled reply of size %"G_GUINT32_FORMAT" for id %"G_GUINT32_FORMAT"\n", backend->reply_size, id);
-
- g_object_unref (reply);
-
- read_reply_async (backend);
-
-}
-
-static void
-read_reply_async_got_len (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- GVfsBackendSftp *backend = user_data;
- gssize res;
- GError *error;
-
- error = NULL;
- res = g_input_stream_read_finish (G_INPUT_STREAM (source_object), result, &error);
-
- check_input_stream_read_result (backend, res, error);
-
- backend->reply_size_read += res;
-
- if (backend->reply_size_read < 4)
- {
- g_input_stream_read_async (backend->reply_stream,
- &backend->reply_size + backend->reply_size_read, 4 - backend->reply_size_read,
- 0, NULL, read_reply_async_got_len, backend);
- return;
- }
- backend->reply_size = GUINT32_FROM_BE (backend->reply_size);
-
- backend->reply_size_read = 0;
- backend->reply = g_malloc (backend->reply_size);
- g_input_stream_read_async (backend->reply_stream,
- backend->reply, backend->reply_size,
- 0, NULL, read_reply_async_got_data, backend);
-}
-
-static void
-read_reply_async (GVfsBackendSftp *backend)
-{
- backend->reply_size_read = 0;
- g_input_stream_read_async (backend->reply_stream,
- &backend->reply_size, 4,
- 0, NULL, read_reply_async_got_len, backend);
-}
-
-static void send_command (GVfsBackendSftp *backend);
-
-static void
-send_command_data (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- GVfsBackendSftp *backend = user_data;
- gssize res;
- DataBuffer *buffer;
-
- res = g_output_stream_write_finish (G_OUTPUT_STREAM (source_object), result, NULL);
-
- if (res <= 0)
- {
- /* TODO: unmount, etc */
- g_warning ("Error sending command");
- return;
- }
-
- buffer = backend->command_queue->data;
-
- backend->command_bytes_written += res;
-
- if (backend->command_bytes_written < buffer->size)
- {
- g_output_stream_write_async (backend->command_stream,
- buffer->data + backend->command_bytes_written,
- buffer->size - backend->command_bytes_written,
- 0,
- NULL,
- send_command_data,
- backend);
- return;
- }
-
- data_buffer_free (buffer);
-
- backend->command_queue = g_list_delete_link (backend->command_queue, backend->command_queue);
-
- if (backend->command_queue != NULL)
- send_command (backend);
-}
-
-static void
-send_command (GVfsBackendSftp *backend)
-{
- DataBuffer *buffer;
-
- buffer = backend->command_queue->data;
-
- backend->command_bytes_written = 0;
- g_output_stream_write_async (backend->command_stream,
- buffer->data,
- buffer->size,
- 0,
- NULL,
- send_command_data,
- backend);
-}
-
-static void
-expect_reply (GVfsBackendSftp *backend,
- guint32 id,
- ReplyCallback callback,
- GVfsJob *job,
- gpointer user_data)
-{
- ExpectedReply *expected;
-
- expected = g_slice_new (ExpectedReply);
- expected->callback = callback;
- expected->job = g_object_ref (job);
- expected->user_data = user_data;
-
- g_hash_table_replace (backend->expected_replies, GINT_TO_POINTER (id), expected);
-}
-
-static DataBuffer *
-data_buffer_new (guchar *data, gsize len)
-{
- DataBuffer *buffer;
-
- buffer = g_slice_new (DataBuffer);
- buffer->data = data;
- buffer->size = len;
-
- return buffer;
-}
-
-static void
-queue_command_buffer (GVfsBackendSftp *backend,
- DataBuffer *buffer)
-{
- gboolean first;
-
- first = backend->command_queue == NULL;
-
- backend->command_queue = g_list_append (backend->command_queue, buffer);
-
- if (first)
- send_command (backend);
-}
-
-static void
-queue_command_stream_and_free (GVfsBackendSftp *backend,
- GDataOutputStream *command_stream,
- ReplyCallback callback,
- GVfsJob *job,
- gpointer user_data)
-{
- gpointer data;
- gsize len;
- DataBuffer *buffer;
- guint32 id;
-
- id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (command_stream), id_q));
- data = get_data_from_command_stream (command_stream, &len);
-
- buffer = data_buffer_new (data, len);
- g_object_unref (command_stream);
-
- expect_reply (backend, id, callback, job, user_data);
- queue_command_buffer (backend, buffer);
-}
-
-
-static void
-multi_request_cb (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply_stream,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- MultiReply *reply;
- MultiRequest *request;
- int i;
-
- reply = user_data;
- request = reply->request;
-
- reply->type = reply_type;
- reply->data = g_object_ref (reply_stream);
- reply->data_len = len;
-
- if (--request->n_outstanding == 0)
- {
- /* Call callback */
- if (request->callback != NULL)
- (request->callback) (backend,
- request->replies,
- request->n_replies,
- job,
- request->user_data);
-
- /* Free request data */
-
- for (i = 0; i < request->n_replies; i++)
- {
- reply = &request->replies[i];
- if (reply->data)
- g_object_unref (reply->data);
- }
- g_free (request->replies);
-
- g_free (request);
-
- }
-}
-
-static void
-queue_command_streams_and_free (GVfsBackendSftp *backend,
- GDataOutputStream **commands,
- int n_commands,
- MultiReplyCallback callback,
- GVfsJob *job,
- gpointer user_data)
-{
- MultiRequest *data;
- MultiReply *reply;
-
- int i;
-
- data = g_new0 (MultiRequest, 1);
-
- data->user_data = user_data;
- data->n_replies = n_commands;
- data->n_outstanding = n_commands;
- data->replies = g_new0 (MultiReply, n_commands);
- data->callback = callback;
-
- for (i = 0; i < n_commands; i++)
- {
- reply = &data->replies[i];
- reply->request = data;
- queue_command_stream_and_free (backend,
- commands[i],
- multi_request_cb,
- job,
- reply);
- }
-}
-
-static gboolean
-get_uid_sync (GVfsBackendSftp *backend)
-{
- GDataOutputStream *command;
- GDataInputStream *reply;
- int type;
-
- command = new_command_stream (backend, SSH_FXP_STAT);
- put_string (command, ".");
- send_command_sync_and_unref_command (backend, command, NULL, NULL);
-
- reply = read_reply_sync (backend, NULL, NULL);
- if (reply == NULL)
- return FALSE;
-
- type = g_data_input_stream_read_byte (reply, NULL, NULL);
- /*id =*/ (void) g_data_input_stream_read_uint32 (reply, NULL, NULL);
-
- /* On error, set uid to -1 and ignore */
- backend->my_uid = (guint32)-1;
- backend->my_gid = (guint32)-1;
- if (type == SSH_FXP_ATTRS)
- {
- GFileInfo *info;
-
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL, reply, NULL);
- if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_UID))
- {
- /* Both are always set if set */
- backend->my_uid = g_file_info_get_attribute_uint32 (info,
- G_FILE_ATTRIBUTE_UNIX_UID);
- backend->my_gid = g_file_info_get_attribute_uint32 (info,
- G_FILE_ATTRIBUTE_UNIX_GID);
- }
-
- g_object_unref (info);
- }
-
- g_object_unref (reply);
-
- return TRUE;
-}
-
-static void
-do_mount (GVfsBackend *backend,
- GVfsJobMount *job,
- GMountSpec *mount_spec,
- GMountSource *mount_source,
- gboolean is_automount)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- gchar **args; /* Enough for now, extend if you add more args */
- pid_t pid;
- int tty_fd, stdout_fd, stdin_fd, stderr_fd;
- GError *error;
- GInputStream *is;
- GDataOutputStream *command;
- GDataInputStream *reply;
- gboolean res;
- GMountSpec *sftp_mount_spec;
- char *extension_name, *extension_data;
- char *display_name;
-
- args = setup_ssh_commandline (backend);
-
- error = NULL;
- if (!spawn_ssh (backend,
- args, &pid,
- &tty_fd, &stdin_fd, &stdout_fd, &stderr_fd,
- &error))
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_strfreev (args);
- return;
- }
-
- g_strfreev (args);
-
- op_backend->command_stream = g_unix_output_stream_new (stdin_fd, TRUE);
-
- command = new_command_stream (op_backend, SSH_FXP_INIT);
- g_data_output_stream_put_int32 (command,
- SSH_FILEXFER_VERSION, NULL, NULL);
- send_command_sync_and_unref_command (op_backend, command, NULL, NULL);
-
- if (tty_fd == -1)
- res = wait_for_reply (backend, stdout_fd, &error);
- else
- res = handle_login (backend, mount_source, tty_fd, stdout_fd, stderr_fd, &error);
-
- if (!res)
- {
- if (error->code == G_IO_ERROR_INVALID_ARGUMENT)
- {
- /* New username provided by the user,
- * we need to re-spawn the ssh command
- */
- g_error_free (error);
- do_mount (backend, job, mount_spec, mount_source, is_automount);
- }
- else
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- }
-
- return;
- }
-
- op_backend->reply_stream = g_unix_input_stream_new (stdout_fd, TRUE);
-
- make_fd_nonblocking (stderr_fd);
- is = g_unix_input_stream_new (stderr_fd, TRUE);
- op_backend->error_stream = g_data_input_stream_new (is);
- g_object_unref (is);
-
- reply = read_reply_sync (op_backend, NULL, NULL);
- if (reply == NULL)
- {
- look_for_stderr_errors (backend, &error);
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return;
- }
-
- if (g_data_input_stream_read_byte (reply, NULL, NULL) != SSH_FXP_VERSION)
- {
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Protocol error"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return;
- }
-
- op_backend->protocol_version = g_data_input_stream_read_uint32 (reply, NULL, NULL);
-
- while ((extension_name = read_string (reply, NULL)) != NULL)
- {
- extension_data = read_string (reply, NULL);
- if (extension_data)
- {
- /* TODO: Do something with this */
- }
- g_free (extension_name);
- g_free (extension_data);
- }
-
- g_object_unref (reply);
-
- if (!get_uid_sync (op_backend))
- {
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Protocol error"));
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- return;
- }
-
- read_reply_async (op_backend);
-
- sftp_mount_spec = g_mount_spec_new ("sftp");
- if (op_backend->user_specified)
- g_mount_spec_set (sftp_mount_spec, "user", op_backend->user);
- g_mount_spec_set (sftp_mount_spec, "host", op_backend->host);
- if (op_backend->port != -1)
- {
- char *v;
- v = g_strdup_printf ("%d", op_backend->port);
- g_mount_spec_set (sftp_mount_spec, "port", v);
- g_free (v);
- }
-
- g_vfs_backend_set_mount_spec (backend, sftp_mount_spec);
- g_mount_spec_unref (sftp_mount_spec);
-
- /* Translators: This is the name of an sftp share, like "sftp on <hostname>" */
- display_name = g_strdup_printf (_("sftp on %s"), op_backend->host);
- g_vfs_backend_set_display_name (backend, display_name);
- g_free (display_name);
- g_vfs_backend_set_icon_name (backend, "folder-remote");
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
-static gboolean
-try_mount (GVfsBackend *backend,
- GVfsJobMount *job,
- GMountSpec *mount_spec,
- GMountSource *mount_source,
- gboolean is_automount)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- const char *user, *host, *port;
-
- op_backend->client_vendor = get_sftp_client_vendor ();
-
- if (op_backend->client_vendor == SFTP_VENDOR_INVALID)
- {
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unable to find supported ssh command"));
- return TRUE;
- }
-
- host = g_mount_spec_get (mount_spec, "host");
-
- if (host == NULL)
- {
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- _("No hostname specified"));
- return TRUE;
- }
-
- port = g_mount_spec_get (mount_spec, "port");
- op_backend->port = -1;
- if (port != NULL)
- {
- int p = atoi (port);
- if (p != 22)
- op_backend->port = p;
- }
-
- user = g_mount_spec_get (mount_spec, "user");
-
- op_backend->host = g_strdup (host);
- op_backend->user = g_strdup (user);
- if (op_backend->user)
- op_backend->user_specified = TRUE;
- else
- op_backend->user = g_strdup (g_get_user_name ());
-
- return FALSE;
-}
-
-static int
-io_error_code_for_sftp_error (guint32 code, int failure_error)
-{
- int error_code;
-
- error_code = G_IO_ERROR_FAILED;
-
- switch (code)
- {
- default:
- case SSH_FX_EOF:
- case SSH_FX_BAD_MESSAGE:
- case SSH_FX_NO_CONNECTION:
- case SSH_FX_CONNECTION_LOST:
- break;
-
- case SSH_FX_FAILURE:
- error_code = failure_error;
- break;
-
- case SSH_FX_NO_SUCH_FILE:
- error_code = G_IO_ERROR_NOT_FOUND;
- break;
-
- case SSH_FX_PERMISSION_DENIED:
- error_code = G_IO_ERROR_PERMISSION_DENIED;
- break;
-
- case SSH_FX_OP_UNSUPPORTED:
- error_code = G_IO_ERROR_NOT_SUPPORTED;
- break;
- }
- return error_code;
-}
-
-static gboolean
-error_from_status (GVfsJob *job,
- GDataInputStream *reply,
- int failure_error,
- int allowed_sftp_error,
- GError **error)
-{
- guint32 code;
- gint error_code;
- char *message;
-
- if (failure_error == -1)
- failure_error = G_IO_ERROR_FAILED;
-
- code = g_data_input_stream_read_uint32 (reply, NULL, NULL);
-
- if (code == SSH_FX_OK ||
- (allowed_sftp_error != -1 &&
- code == allowed_sftp_error))
- return TRUE;
-
- if (error)
- {
- error_code = io_error_code_for_sftp_error (code, failure_error);
- message = read_string (reply, NULL);
- if (message == NULL)
- message = g_strdup ("Unknown reason");
-
- *error = g_error_new_literal (G_IO_ERROR, error_code, message);
- g_free (message);
- }
-
- return FALSE;
-}
-
-static gboolean
-failure_from_status (GVfsJob *job,
- GDataInputStream *reply,
- int failure_error,
- int allowed_sftp_error)
-{
- GError *error;
-
- error = NULL;
- if (error_from_status (job, reply, failure_error, allowed_sftp_error, &error))
- return TRUE;
- else
- {
- g_vfs_job_failed_from_error (job, error);
- g_error_free (error);
- }
- return FALSE;
-}
-
-
-static gboolean
-result_from_status (GVfsJob *job,
- GDataInputStream *reply,
- int failure_error,
- int allowed_sftp_error)
-{
- gboolean res;
-
- res = failure_from_status (job, reply,
- failure_error,
- allowed_sftp_error);
- if (res)
- g_vfs_job_succeeded (job);
-
- return res;
-}
-
-static void
-set_access_attributes (GFileInfo *info,
- guint32 perm)
-{
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
- perm & 0x4);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
- perm & 0x2);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
- perm & 0x1);
-}
-
-
-static void
-parse_attributes (GVfsBackendSftp *backend,
- GFileInfo *info,
- const char *basename,
- GDataInputStream *reply,
- GFileAttributeMatcher *matcher)
-{
- guint32 flags;
- GFileType type;
- guint32 uid, gid;
- guint32 mode;
- gboolean has_uid, free_mimetype;
- char *mimetype;
- GIcon *icon;
-
- flags = g_data_input_stream_read_uint32 (reply, NULL, NULL);
-
- if (basename != NULL && basename[0] == '.')
- g_file_info_set_is_hidden (info, TRUE);
-
- if (basename != NULL)
- g_file_info_set_name (info, basename);
- else
- g_file_info_set_name (info, "/");
-
- if (basename != NULL && basename[strlen (basename) -1] == '~')
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP, TRUE);
-
- if (flags & SSH_FILEXFER_ATTR_SIZE)
- {
- guint64 size = g_data_input_stream_read_uint64 (reply, NULL, NULL);
- g_file_info_set_size (info, size);
- }
-
- has_uid = FALSE;
- uid = gid = 0; /* Avoid warnings */
- if (flags & SSH_FILEXFER_ATTR_UIDGID)
- {
- has_uid = TRUE;
- uid = g_data_input_stream_read_uint32 (reply, NULL, NULL);
- g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, uid);
- gid = g_data_input_stream_read_uint32 (reply, NULL, NULL);
- g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, gid);
- }
-
- type = G_FILE_TYPE_UNKNOWN;
-
- if (flags & SSH_FILEXFER_ATTR_PERMISSIONS)
- {
- mode = g_data_input_stream_read_uint32 (reply, NULL, NULL);
- g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, mode);
-
- mimetype = NULL;
- if (S_ISREG (mode))
- type = G_FILE_TYPE_REGULAR;
- else if (S_ISDIR (mode))
- {
- type = G_FILE_TYPE_DIRECTORY;
- mimetype = "inode/directory";
- }
- else if (S_ISFIFO (mode))
- {
- type = G_FILE_TYPE_SPECIAL;
- mimetype = "inode/fifo";
- }
- else if (S_ISSOCK (mode))
- {
- type = G_FILE_TYPE_SPECIAL;
- mimetype = "inode/socket";
- }
- else if (S_ISCHR (mode))
- {
- type = G_FILE_TYPE_SPECIAL;
- mimetype = "inode/chardevice";
- }
- else if (S_ISBLK (mode))
- {
- type = G_FILE_TYPE_SPECIAL;
- mimetype = "inode/blockdevice";
- }
- else if (S_ISLNK (mode))
- {
- type = G_FILE_TYPE_SYMBOLIC_LINK;
- g_file_info_set_is_symlink (info, TRUE);
- mimetype = "inode/symlink";
- }
-
- free_mimetype = FALSE;
- if (mimetype == NULL)
- {
- if (basename)
- {
- mimetype = g_content_type_guess (basename, NULL, 0, NULL);
- free_mimetype = TRUE;
- }
- else
- mimetype = "application/octet-stream";
- }
-
- g_file_info_set_content_type (info, mimetype);
- g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, mimetype);
-
- if (g_file_attribute_matcher_matches (matcher,
- G_FILE_ATTRIBUTE_STANDARD_ICON))
- {
- icon = NULL;
- if (S_ISDIR(mode))
- icon = g_themed_icon_new ("folder");
- else if (mimetype)
- {
- icon = g_content_type_get_icon (mimetype);
- if (G_IS_THEMED_ICON (icon))
- g_themed_icon_append_name (G_THEMED_ICON (icon), "text-x-generic");
- }
-
- if (icon == NULL)
- icon = g_themed_icon_new ("text-x-generic");
-
- g_file_info_set_icon (info, icon);
- g_object_unref (icon);
- }
-
-
- if (free_mimetype)
- g_free (mimetype);
-
- if (has_uid && backend->my_uid != (guint32)-1)
- {
- if (uid == backend->my_uid)
- set_access_attributes (info, (mode >> 6) & 0x7);
- else if (gid == backend->my_gid)
- set_access_attributes (info, (mode >> 3) & 0x7);
- else
- set_access_attributes (info, (mode >> 0) & 0x7);
- }
-
- }
-
- g_file_info_set_file_type (info, type);
-
- if (flags & SSH_FILEXFER_ATTR_ACMODTIME)
- {
- guint32 v;
- char *etag;
-
- v = g_data_input_stream_read_uint32 (reply, NULL, NULL);
- g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS, v);
- v = g_data_input_stream_read_uint32 (reply, NULL, NULL);
- g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, v);
-
- etag = g_strdup_printf ("%lu", (long unsigned int)v);
- g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
- g_free (etag);
- }
-
- if (flags & SSH_FILEXFER_ATTR_EXTENDED)
- {
- guint32 count, i;
- char *name, *val;
- count = g_data_input_stream_read_uint32 (reply, NULL, NULL);
- for (i = 0; i < count; i++)
- {
- name = read_string (reply, NULL);
- val = read_string (reply, NULL);
-
- g_free (name);
- g_free (val);
- }
- }
-
- /* We use the same setting as for local files. Can't really
- * do better, since there is no way in this version of sftp to find out
- * the remote charset encoding
- */
- if (g_file_attribute_matcher_matches (matcher,
- G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
- {
- if (basename != NULL)
- {
- char *display_name = g_filename_display_name (basename);
-
- if (strstr (display_name, "\357\277\275") != NULL)
- {
- char *p = display_name;
- display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
- g_free (p);
- }
- g_file_info_set_display_name (info, display_name);
- g_free (display_name);
- }
- else
- {
- char *name;
-
-
- /* Translators: This is the name of the root of an sftp share, like "/ on <hostname>" */
- name = g_strdup_printf (_("/ on %s"), G_VFS_BACKEND_SFTP (backend)->host);
- g_file_info_set_display_name (info, name);
- g_free (name);
- }
- }
-
- if (basename != NULL &&
- g_file_attribute_matcher_matches (matcher,
- G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME))
- {
- char *edit_name = g_filename_display_name (basename);
- g_file_info_set_edit_name (info, edit_name);
- g_free (edit_name);
- }
-}
-
-static SftpHandle *
-sftp_handle_new (GDataInputStream *reply)
-{
- SftpHandle *handle;
-
- handle = g_slice_new0 (SftpHandle);
- handle->raw_handle = read_data_buffer (reply);
- handle->offset = 0;
-
- return handle;
-}
-
-static void
-sftp_handle_free (SftpHandle *handle)
-{
- data_buffer_free (handle->raw_handle);
- g_free (handle->filename);
- g_free (handle->tempname);
- g_slice_free (SftpHandle, handle);
-}
-
-static void
-open_stat_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (g_vfs_job_is_finished (job))
- {
- /* Handled in stat reply */
- return;
- }
-
- if (reply_type == SSH_FXP_ATTRS)
- {
- GFileType type;
- GFileInfo *info = g_file_info_new ();
-
- parse_attributes (backend, info, NULL,
- reply, NULL);
- type = g_file_info_get_file_type (info);
- g_object_unref (info);
-
- if (type == G_FILE_TYPE_DIRECTORY)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
- _("File is directory"));
- return;
- }
- }
-
- if (GPOINTER_TO_INT (G_VFS_JOB(job)->backend_data) == 1)
- {
- /* We ran the read_reply and it was a generic failure */
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Failure"));
- }
-}
-
-static void
-open_for_read_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
-
- if (g_vfs_job_is_finished (job))
- {
- /* Handled in stat reply */
-
- /* Normally this should not happen as we
- sent an is_dir error. But i guess we can
- race */
- if (reply_type == SSH_FXP_HANDLE)
- {
- GDataOutputStream *command;
- DataBuffer *bhandle;
-
- bhandle = read_data_buffer (reply);
-
- command = new_command_stream (backend, SSH_FXP_CLOSE);
- put_data_buffer (command, bhandle);
- queue_command_stream_and_free (backend, command, NULL, G_VFS_JOB (job), NULL);
-
- data_buffer_free (bhandle);
- }
-
- return;
- }
-
- if (reply_type == SSH_FXP_STATUS)
- {
- if (failure_from_status (job,
- reply,
- -1,
- SSH_FX_FAILURE))
- {
- /* Unknown failure type, mark that we got this and
- return result from stat result */
- G_VFS_JOB(job)->backend_data = GINT_TO_POINTER (1);
- }
-
- return;
- }
-
- if (reply_type != SSH_FXP_HANDLE)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- handle = sftp_handle_new (reply);
-
- g_vfs_job_open_for_read_set_handle (G_VFS_JOB_OPEN_FOR_READ (job), handle);
- g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), TRUE);
- g_vfs_job_succeeded (job);
-}
-
-static gboolean
-try_open_for_read (GVfsBackend *backend,
- GVfsJobOpenForRead *job,
- const char *filename)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- G_VFS_JOB(job)->backend_data = GINT_TO_POINTER (0);
-
- command = new_command_stream (op_backend,
- SSH_FXP_STAT);
- put_string (command, filename);
- queue_command_stream_and_free (op_backend, command, open_stat_reply, G_VFS_JOB (job), NULL);
-
- command = new_command_stream (op_backend,
- SSH_FXP_OPEN);
- put_string (command, filename);
- g_data_output_stream_put_uint32 (command, SSH_FXF_READ, NULL, NULL); /* open flags */
- g_data_output_stream_put_uint32 (command, 0, NULL, NULL); /* Attr flags */
-
- queue_command_stream_and_free (op_backend, command, open_for_read_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-read_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
- guint32 count;
-
- handle = user_data;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- result_from_status (job, reply, -1, SSH_FX_EOF);
- return;
- }
-
- if (reply_type != SSH_FXP_DATA)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- count = g_data_input_stream_read_uint32 (reply, NULL, NULL);
-
- if (!g_input_stream_read_all (G_INPUT_STREAM (reply),
- G_VFS_JOB_READ (job)->buffer, count,
- NULL, NULL, NULL))
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- handle->offset += count;
-
- g_vfs_job_read_set_size (G_VFS_JOB_READ (job), count);
- g_vfs_job_succeeded (job);
-}
-
-static gboolean
-try_read (GVfsBackend *backend,
- GVfsJobRead *job,
- GVfsBackendHandle _handle,
- char *buffer,
- gsize bytes_requested)
-{
- SftpHandle *handle = _handle;
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_READ);
- put_data_buffer (command, handle->raw_handle);
- g_data_output_stream_put_uint64 (command, handle->offset, NULL, NULL);
- g_data_output_stream_put_uint32 (command, bytes_requested, NULL, NULL);
-
- queue_command_stream_and_free (op_backend, command, read_reply, G_VFS_JOB (job), handle);
-
- return TRUE;
-}
-
-static void
-seek_read_fstat_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
- GFileInfo *info;
- goffset file_size;
- GVfsJobSeekRead *op_job;
-
- handle = user_data;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- result_from_status (job, reply, -1, -1);
- return;
- }
-
- if (reply_type != SSH_FXP_ATTRS)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL,
- reply, NULL);
- file_size = g_file_info_get_size (info);
- g_object_unref (info);
-
- op_job = G_VFS_JOB_SEEK_READ (job);
-
- switch (op_job->seek_type)
- {
- case G_SEEK_CUR:
- handle->offset += op_job->requested_offset;
- break;
- case G_SEEK_SET:
- handle->offset = op_job->requested_offset;
- break;
- case G_SEEK_END:
- handle->offset = file_size + op_job->requested_offset;
- break;
- }
-
- if (handle->offset < 0)
- handle->offset = 0;
- if (handle->offset > file_size)
- handle->offset = file_size;
-
- g_vfs_job_seek_read_set_offset (op_job, handle->offset);
- g_vfs_job_succeeded (job);
-}
-
-static gboolean
-try_seek_on_read (GVfsBackend *backend,
- GVfsJobSeekRead *job,
- GVfsBackendHandle _handle,
- goffset offset,
- GSeekType type)
-{
- SftpHandle *handle = _handle;
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_FSTAT);
- put_data_buffer (command, handle->raw_handle);
-
- queue_command_stream_and_free (op_backend, command, seek_read_fstat_reply, G_VFS_JOB (job), handle);
-
- return TRUE;
-}
-
-static void
-delete_temp_file (GVfsBackendSftp *backend,
- SftpHandle *handle,
- GVfsJob *job)
-{
- GDataOutputStream *command;
-
- if (handle->tempname)
- {
- command = new_command_stream (backend,
- SSH_FXP_REMOVE);
- put_string (command, handle->tempname);
- queue_command_stream_and_free (backend, command, NULL, job, NULL);
- }
-}
-
-static void
-close_moved_tempfile (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
-
- handle = user_data;
-
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-
- /* On failure, don't remove tempfile, since we removed the new original file */
- sftp_handle_free (handle);
-}
-
-static void
-close_restore_permissions (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GDataOutputStream *command;
- SftpHandle *handle;
-
- handle = user_data;
-
- /* Here we don't really care whether or not setting the permissions succeeded
- or not. We just take the last step and rename the temp file to the
- actual file */
- command = new_command_stream (backend,
- SSH_FXP_RENAME);
- put_string (command, handle->tempname);
- put_string (command, handle->filename);
- queue_command_stream_and_free (backend, command, close_moved_tempfile, G_VFS_JOB (job), handle);
-}
-
-static void
-close_deleted_file (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GDataOutputStream *command;
- GError *error;
- gboolean res;
- SftpHandle *handle;
-
- handle = user_data;
-
- error = NULL;
- res = FALSE;
- if (reply_type == SSH_FXP_STATUS)
- res = error_from_status (job, reply, -1, -1, &error);
- else
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-
- if (res)
- {
- /* Removed original file, now first try to restore permissions */
- command = new_command_stream (backend,
- SSH_FXP_SETSTAT);
- put_string (command, handle->tempname);
- g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL);
- g_data_output_stream_put_uint32 (command, handle->permissions, NULL, NULL);
- queue_command_stream_and_free (backend, command, close_restore_permissions, G_VFS_JOB (job), handle);
- }
- else
- {
- /* The delete failed, remove any temporary files */
- delete_temp_file (backend,
- handle,
- G_VFS_JOB (job));
-
- g_vfs_job_failed_from_error (job, error);
- g_error_free (error);
- sftp_handle_free (handle);
- }
-}
-
-static void
-close_moved_file (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GDataOutputStream *command;
- GError *error;
- gboolean res;
- SftpHandle *handle;
-
- handle = user_data;
-
- error = NULL;
- res = FALSE;
- if (reply_type == SSH_FXP_STATUS)
- res = error_from_status (job, reply, -1, -1, &error);
- else
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-
- if (res)
- {
- /* moved original file to backup, now move new file in place */
-
- command = new_command_stream (backend,
- SSH_FXP_RENAME);
- put_string (command, handle->tempname);
- put_string (command, handle->filename);
- queue_command_stream_and_free (backend, command, close_moved_tempfile, G_VFS_JOB (job), handle);
- }
- else
- {
- /* Move original file to backup name failed, remove any temporary files */
- delete_temp_file (backend,
- handle,
- G_VFS_JOB (job));
-
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
- _("Error creating backup file: %s"), error->message);
- g_error_free (error);
- sftp_handle_free (handle);
- }
-}
-
-static void
-close_deleted_backup (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
- GDataOutputStream *command;
- char *backup_name;
-
- /* Ignore result here, if it failed we'll just get a new error when moving over it
- * This is simpler than ignoring NOEXIST errors
- */
-
- handle = user_data;
-
- command = new_command_stream (backend,
- SSH_FXP_RENAME);
- backup_name = g_strconcat (handle->filename, "~", NULL);
- put_string (command, handle->filename);
- put_string (command, backup_name);
- g_free (backup_name);
- queue_command_stream_and_free (backend, command, close_moved_file, G_VFS_JOB (job), handle);
-}
-
-static void
-close_write_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GDataOutputStream *command;
- GError *error;
- gboolean res;
- char *backup_name;
- SftpHandle *handle;
-
- handle = user_data;
-
- error = NULL;
- res = FALSE;
- if (reply_type == SSH_FXP_STATUS)
- res = error_from_status (job, reply, -1, -1, &error);
- else
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-
- if (res)
- {
- if (handle->tempname)
- {
- if (handle->make_backup)
- {
- command = new_command_stream (backend,
- SSH_FXP_REMOVE);
- backup_name = g_strconcat (handle->filename, "~", NULL);
- put_string (command, backup_name);
- g_free (backup_name);
- queue_command_stream_and_free (backend, command, close_deleted_backup, G_VFS_JOB (job), handle);
- }
- else
- {
- command = new_command_stream (backend,
- SSH_FXP_REMOVE);
- put_string (command, handle->filename);
- queue_command_stream_and_free (backend, command, close_deleted_file, G_VFS_JOB (job), handle);
- }
- }
- else
- {
- g_vfs_job_succeeded (job);
- sftp_handle_free (handle);
- }
- }
- else
- {
- /* The close failed, remove any temporary files */
- delete_temp_file (backend,
- handle,
- G_VFS_JOB (job));
-
- g_vfs_job_failed_from_error (job, error);
- g_error_free (error);
-
- sftp_handle_free (handle);
- }
-}
-
-static void
-close_write_fstat_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle = user_data;
- GDataOutputStream *command;
- GFileInfo *info;
- const char *etag;
-
- if (reply_type == SSH_FXP_ATTRS)
- {
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL,
- reply, NULL);
- etag = g_file_info_get_etag (info);
- if (etag)
- g_vfs_job_close_write_set_etag (G_VFS_JOB_CLOSE_WRITE (job), etag);
- g_object_unref (info);
- }
-
- command = new_command_stream (backend, SSH_FXP_CLOSE);
- put_data_buffer (command, handle->raw_handle);
-
- queue_command_stream_and_free (backend, command, close_write_reply, G_VFS_JOB (job), handle);
-}
-
-static gboolean
-try_close_write (GVfsBackend *backend,
- GVfsJobCloseWrite *job,
- GVfsBackendHandle _handle)
-{
- SftpHandle *handle = _handle;
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend, SSH_FXP_FSTAT);
- put_data_buffer (command, handle->raw_handle);
-
- queue_command_stream_and_free (op_backend, command, close_write_fstat_reply, G_VFS_JOB (job), handle);
-
- return TRUE;
-}
-
-
-static void
-close_read_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
-
- handle = user_data;
-
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- sftp_handle_free (handle);
-}
-
-static gboolean
-try_close_read (GVfsBackend *backend,
- GVfsJobCloseRead *job,
- GVfsBackendHandle _handle)
-{
- SftpHandle *handle = _handle;
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend, SSH_FXP_CLOSE);
- put_data_buffer (command, handle->raw_handle);
-
- queue_command_stream_and_free (op_backend, command, close_read_reply, G_VFS_JOB (job), handle);
-
- return TRUE;
-}
-
-static void
-create_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- result_from_status (job, reply, G_IO_ERROR_EXISTS, -1);
- return;
- }
-
- if (reply_type != SSH_FXP_HANDLE)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- handle = sftp_handle_new (reply);
-
- g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), handle);
- g_vfs_job_open_for_write_set_can_seek (G_VFS_JOB_OPEN_FOR_WRITE (job), TRUE);
- g_vfs_job_succeeded (job);
-}
-
-static gboolean
-try_create (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_OPEN);
- put_string (command, filename);
- g_data_output_stream_put_uint32 (command, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_EXCL, NULL, NULL); /* open flags */
- g_data_output_stream_put_uint32 (command, 0, NULL, NULL); /* Attr flags */
-
- queue_command_stream_and_free (op_backend, command, create_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-append_to_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- result_from_status (job, reply, -1, -1);
- return;
- }
-
- if (reply_type != SSH_FXP_HANDLE)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- handle = sftp_handle_new (reply);
-
- g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), handle);
- g_vfs_job_open_for_write_set_can_seek (G_VFS_JOB_OPEN_FOR_WRITE (job), FALSE);
- g_vfs_job_succeeded (job);
-}
-
-static gboolean
-try_append_to (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_OPEN);
- put_string (command, filename);
- g_data_output_stream_put_uint32 (command, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_APPEND, NULL, NULL); /* open flags */
- g_data_output_stream_put_uint32 (command, 0, NULL, NULL); /* Attr flags */
-
- queue_command_stream_and_free (op_backend, command, append_to_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-typedef struct {
- guint32 permissions;
- char *tempname;
- int temp_count;
-} ReplaceData;
-
-static void
-replace_data_free (ReplaceData *data)
-{
- g_free (data->tempname);
- g_slice_free (ReplaceData, data);
-}
-
-static void replace_create_temp (GVfsBackendSftp *backend,
- GVfsJobOpenForWrite *job);
-
-static void
-replace_create_temp_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GVfsJobOpenForWrite *op_job;
- SftpHandle *handle;
- ReplaceData *data;
- GError *error;
-
- op_job = G_VFS_JOB_OPEN_FOR_WRITE (job);
- data = G_VFS_JOB (job)->backend_data;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- error = NULL;
- if (error_from_status (job, reply, G_IO_ERROR_EXISTS, -1, &error))
- /* Open should not return OK */
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- else if (error->code == G_IO_ERROR_EXISTS)
- {
- /* It was *probably* the EXCL flag failing. I wish we had
- an actual real error code for that, grumble */
- g_error_free (error);
-
- replace_create_temp (backend, op_job);
- }
- else
- {
- g_vfs_job_failed_from_error (job, error);
- g_error_free (error);
- }
- return;
- }
-
- if (reply_type != SSH_FXP_HANDLE)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- handle = sftp_handle_new (reply);
- handle->filename = g_strdup (op_job->filename);
- handle->tempname = g_strdup (data->tempname);
- handle->permissions = data->permissions;
- handle->make_backup = op_job->make_backup;
-
- g_vfs_job_open_for_write_set_handle (op_job, handle);
- g_vfs_job_open_for_write_set_can_seek (op_job, TRUE);
-
- g_vfs_job_succeeded (job);
-}
-
-static void
-random_text (char *s)
-{
- static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- static const int NLETTERS = sizeof (letters) - 1;
- static int counter = 0;
-
- GTimeVal tv;
- glong value;
-
- /* Get some more or less random data. */
- g_get_current_time (&tv);
- value = (tv.tv_usec ^ tv.tv_sec) + counter++;
-
- /* Fill in the random bits. */
- s[0] = letters[value % NLETTERS];
- value /= NLETTERS;
- s[1] = letters[value % NLETTERS];
- value /= NLETTERS;
- s[2] = letters[value % NLETTERS];
- value /= NLETTERS;
- s[3] = letters[value % NLETTERS];
- value /= NLETTERS;
- s[4] = letters[value % NLETTERS];
- value /= NLETTERS;
- s[5] = letters[value % NLETTERS];
-}
-
-static void
-replace_create_temp (GVfsBackendSftp *backend,
- GVfsJobOpenForWrite *job)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
- char *dirname;
- ReplaceData *data;
- char basename[] = ".giosaveXXXXXX";
-
- data = G_VFS_JOB (job)->backend_data;
-
- data->temp_count++;
-
- if (data->temp_count == 100)
- {
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unable to create temporary file"));
- return;
- }
-
- g_free (data->tempname);
-
- dirname = g_path_get_dirname (job->filename);
- random_text (basename + 8);
- data->tempname = g_build_filename (dirname, basename, NULL);
- g_free (dirname);
-
- command = new_command_stream (op_backend,
- SSH_FXP_OPEN);
- put_string (command, data->tempname);
- g_data_output_stream_put_uint32 (command, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_EXCL, NULL, NULL); /* open flags */
- g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL); /* Attr flags */
- g_data_output_stream_put_uint32 (command, data->permissions, NULL, NULL);
- queue_command_stream_and_free (op_backend, command, replace_create_temp_reply, G_VFS_JOB (job), NULL);
-}
-
-static void
-replace_stat_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GFileInfo *info;
- GVfsJobOpenForWrite *op_job;
- const char *current_etag;
- guint32 permissions;
- ReplaceData *data;
-
- op_job = G_VFS_JOB_OPEN_FOR_WRITE (job);
-
- permissions = 0644;
-
- if (reply_type == SSH_FXP_ATTRS)
- {
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL,
- reply, NULL);
-
- if (op_job->etag != NULL)
- {
- current_etag = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE);
-
- if (current_etag == NULL ||
- strcmp (op_job->etag, current_etag) != 0)
- {
- g_vfs_job_failed (job,
- G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
- _("The file was externally modified"));
- g_object_unref (info);
- return;
- }
- }
-
- if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE))
- permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777;
- }
-
- data = g_slice_new0 (ReplaceData);
- data->permissions = permissions;
- g_vfs_job_set_backend_data (job, data, (GDestroyNotify)replace_data_free);
-
- replace_create_temp (backend, op_job);
-}
-
-static void
-replace_exclusive_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GVfsJobOpenForWrite *op_job;
- GDataOutputStream *command;
- SftpHandle *handle;
- GError *error;
-
- op_job = G_VFS_JOB_OPEN_FOR_WRITE (job);
- if (reply_type == SSH_FXP_STATUS)
- {
- error = NULL;
- if (error_from_status (job, reply, G_IO_ERROR_EXISTS, -1, &error))
- /* Open should not return OK */
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- else if (error->code == G_IO_ERROR_EXISTS)
- {
- /* It was *probably* the EXCL flag failing. I wish we had
- an actual real error code for that, grumble */
- g_error_free (error);
-
- /* Replace existing file code: */
-
- command = new_command_stream (backend,
- SSH_FXP_STAT);
- put_string (command, op_job->filename);
- queue_command_stream_and_free (backend, command, replace_stat_reply, G_VFS_JOB (job), NULL);
- }
- else
- {
- g_vfs_job_failed_from_error (job, error);
- g_error_free (error);
- }
- return;
- }
-
- if (reply_type != SSH_FXP_HANDLE)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- handle = sftp_handle_new (reply);
-
- g_vfs_job_open_for_write_set_handle (op_job, handle);
- g_vfs_job_open_for_write_set_can_seek (op_job, TRUE);
-
- g_vfs_job_succeeded (job);
-}
-
-static gboolean
-try_replace (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- const char *etag,
- gboolean make_backup,
- GFileCreateFlags flags)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_OPEN);
- put_string (command, filename);
- g_data_output_stream_put_uint32 (command, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_EXCL, NULL, NULL); /* open flags */
- g_data_output_stream_put_uint32 (command, 0, NULL, NULL); /* Attr flags */
-
- queue_command_stream_and_free (op_backend, command, replace_exclusive_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-write_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
-
- handle = user_data;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- if (result_from_status (job, reply, -1, -1))
- {
- handle->offset += G_VFS_JOB_WRITE (job)->data_size;
- }
- }
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static gboolean
-try_write (GVfsBackend *backend,
- GVfsJobWrite *job,
- GVfsBackendHandle _handle,
- char *buffer,
- gsize buffer_size)
-{
- SftpHandle *handle = _handle;
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_WRITE);
- put_data_buffer (command, handle->raw_handle);
- g_data_output_stream_put_uint64 (command, handle->offset, NULL, NULL);
- g_data_output_stream_put_uint32 (command, buffer_size, NULL, NULL);
- /* Ideally we shouldn't do this copy, but doing the writes as multiple writes
- caused problems on the read side in openssh */
- g_output_stream_write_all (G_OUTPUT_STREAM (command),
- buffer, buffer_size,
- NULL, NULL, NULL);
-
- queue_command_stream_and_free (op_backend, command, write_reply, G_VFS_JOB (job), handle);
-
- /* We always write the full size (on success) */
- g_vfs_job_write_set_written_size (job, buffer_size);
-
- return TRUE;
-}
-
-static void
-seek_write_fstat_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- SftpHandle *handle;
- GFileInfo *info;
- goffset file_size;
- GVfsJobSeekWrite *op_job;
-
- handle = user_data;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- result_from_status (job, reply, -1, -1);
- return;
- }
-
- if (reply_type != SSH_FXP_ATTRS)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL,
- reply, NULL);
- file_size = g_file_info_get_size (info);
- g_object_unref (info);
-
- op_job = G_VFS_JOB_SEEK_WRITE (job);
-
- switch (op_job->seek_type)
- {
- case G_SEEK_CUR:
- handle->offset += op_job->requested_offset;
- break;
- case G_SEEK_SET:
- handle->offset = op_job->requested_offset;
- break;
- case G_SEEK_END:
- handle->offset = file_size + op_job->requested_offset;
- break;
- }
-
- if (handle->offset < 0)
- handle->offset = 0;
- if (handle->offset > file_size)
- handle->offset = file_size;
-
- g_vfs_job_seek_write_set_offset (op_job, handle->offset);
- g_vfs_job_succeeded (job);
-}
-
-static gboolean
-try_seek_on_write (GVfsBackend *backend,
- GVfsJobSeekWrite *job,
- GVfsBackendHandle _handle,
- goffset offset,
- GSeekType type)
-{
- SftpHandle *handle = _handle;
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_FSTAT);
- put_data_buffer (command, handle->raw_handle);
-
- queue_command_stream_and_free (op_backend, command, seek_write_fstat_reply, G_VFS_JOB (job), handle);
-
- return TRUE;
-}
-
-typedef struct {
- DataBuffer *handle;
- int outstanding_requests;
-} ReadDirData;
-
-static
-void
-read_dir_data_free (ReadDirData *data)
-{
- data_buffer_free (data->handle);
- g_slice_free (ReadDirData, data);
-}
-
-static void
-read_dir_readlink_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- ReadDirData *data;
- GFileInfo *info = user_data;
- char *target;
-
- data = job->backend_data;
-
- if (reply_type == SSH_FXP_NAME)
- {
- /* count = */ (void) g_data_input_stream_read_uint32 (reply, NULL, NULL);
-
- target = read_string (reply, NULL);
- if (target)
- {
- g_file_info_set_symlink_target (info, target);
- g_free (target);
- }
- }
-
- g_vfs_job_enumerate_add_info (G_VFS_JOB_ENUMERATE (job), info);
- g_object_unref (info);
-
- if (--data->outstanding_requests == 0)
- g_vfs_job_enumerate_done (G_VFS_JOB_ENUMERATE (job));
-}
-
-static void
-read_dir_got_stat_info (GVfsBackendSftp *backend,
- GVfsJob *job,
- GFileInfo *info)
-{
- GVfsJobEnumerate *enum_job;
- GDataOutputStream *command;
- ReadDirData *data;
- char *abs_name;
-
- data = job->backend_data;
-
- enum_job = G_VFS_JOB_ENUMERATE (job);
-
- if (g_file_attribute_matcher_matches (enum_job->attribute_matcher,
- G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET))
- {
- data->outstanding_requests++;
- command = new_command_stream (backend,
- SSH_FXP_READLINK);
- abs_name = g_build_filename (enum_job->filename, g_file_info_get_name (info), NULL);
- put_string (command, abs_name);
- g_free (abs_name);
- queue_command_stream_and_free (backend, command, read_dir_readlink_reply, G_VFS_JOB (job), g_object_ref (info));
- }
- else
- g_vfs_job_enumerate_add_info (enum_job, info);
-}
-
-
-static void
-read_dir_symlink_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- const char *name;
- GFileInfo *info;
- GFileInfo *lstat_info;
- ReadDirData *data;
-
- lstat_info = user_data;
- name = g_file_info_get_name (lstat_info);
- data = job->backend_data;
-
- if (reply_type == SSH_FXP_ATTRS)
- {
- info = g_file_info_new ();
- g_file_info_set_name (info, name);
- g_file_info_set_is_symlink (info, TRUE);
-
- parse_attributes (backend, info, name, reply, G_VFS_JOB_ENUMERATE (job)->attribute_matcher);
-
- read_dir_got_stat_info (backend, job, info);
-
- g_object_unref (info);
- }
- else
- read_dir_got_stat_info (backend, job, lstat_info);
-
- g_object_unref (lstat_info);
-
- if (--data->outstanding_requests == 0)
- g_vfs_job_enumerate_done (G_VFS_JOB_ENUMERATE (job));
-}
-
-static void
-read_dir_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GVfsJobEnumerate *enum_job;
- guint32 count;
- int i;
- GDataOutputStream *command;
- ReadDirData *data;
-
- data = job->backend_data;
- enum_job = G_VFS_JOB_ENUMERATE (job);
-
- if (reply_type != SSH_FXP_NAME)
- {
- /* Ignore all error, including the expected END OF FILE.
- * Real errors are expected in open_dir anyway */
-
- /* Close handle */
-
- command = new_command_stream (backend,
- SSH_FXP_CLOSE);
- put_data_buffer (command, data->handle);
- queue_command_stream_and_free (backend, command, NULL, G_VFS_JOB (job), NULL);
-
- if (--data->outstanding_requests == 0)
- g_vfs_job_enumerate_done (enum_job);
-
- return;
- }
-
- count = g_data_input_stream_read_uint32 (reply, NULL, NULL);
- for (i = 0; i < count; i++)
- {
- GFileInfo *info;
- char *name;
- char *longname;
- char *abs_name;
-
- info = g_file_info_new ();
- name = read_string (reply, NULL);
- g_file_info_set_name (info, name);
-
- longname = read_string (reply, NULL);
- g_free (longname);
-
- parse_attributes (backend, info, name, reply, enum_job->attribute_matcher);
-
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK &&
- ! (enum_job->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
- {
- /* Default (at least for openssh) is for readdir to not follow symlinks.
- This was a symlink, and follow links was requested, so we need to manually follow it */
- command = new_command_stream (backend,
- SSH_FXP_STAT);
- abs_name = g_build_filename (enum_job->filename, name, NULL);
- put_string (command, abs_name);
- g_free (abs_name);
-
- queue_command_stream_and_free (backend, command, read_dir_symlink_reply, G_VFS_JOB (job), g_object_ref (info));
- data->outstanding_requests ++;
- }
- else if (strcmp (".", name) != 0 &&
- strcmp ("..", name) != 0)
- read_dir_got_stat_info (backend, job, info);
-
- g_object_unref (info);
- g_free (name);
- }
-
- command = new_command_stream (backend,
- SSH_FXP_READDIR);
- put_data_buffer (command, data->handle);
- queue_command_stream_and_free (backend, command, read_dir_reply, G_VFS_JOB (job), NULL);
-}
-
-static void
-open_dir_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
- ReadDirData *data;
-
- data = job->backend_data;
-
- if (reply_type == SSH_FXP_STATUS)
- {
- result_from_status (job, reply, -1, -1);
- return;
- }
-
- if (reply_type != SSH_FXP_HANDLE)
- {
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- return;
- }
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
- data->handle = read_data_buffer (reply);
-
- command = new_command_stream (op_backend,
- SSH_FXP_READDIR);
- put_data_buffer (command, data->handle);
-
- data->outstanding_requests = 1;
-
- queue_command_stream_and_free (op_backend, command, read_dir_reply, G_VFS_JOB (job), NULL);
-}
-
-static gboolean
-try_enumerate (GVfsBackend *backend,
- GVfsJobEnumerate *job,
- const char *filename,
- GFileAttributeMatcher *attribute_matcher,
- GFileQueryInfoFlags flags)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
- ReadDirData *data;
-
- data = g_slice_new0 (ReadDirData);
-
- g_vfs_job_set_backend_data (G_VFS_JOB (job), data, (GDestroyNotify)read_dir_data_free);
- command = new_command_stream (op_backend,
- SSH_FXP_OPENDIR);
- put_string (command, filename);
-
- queue_command_stream_and_free (op_backend, command, open_dir_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-query_info_reply (GVfsBackendSftp *backend,
- MultiReply *replies,
- int n_replies,
- GVfsJob *job,
- gpointer user_data)
-{
- char *basename;
- int i;
- MultiReply *lstat_reply, *reply;
- GFileInfo *lstat_info;
- GVfsJobQueryInfo *op_job;
-
- op_job = G_VFS_JOB_QUERY_INFO (job);
-
- i = 0;
- lstat_reply = &replies[i++];
-
- if (lstat_reply->type == SSH_FXP_STATUS)
- {
- result_from_status (job, lstat_reply->data, -1, -1);
- return;
- }
- else if (lstat_reply->type != SSH_FXP_ATTRS)
- {
- g_vfs_job_failed (job,
- G_IO_ERROR, G_IO_ERROR_FAILED,
- "%s", _("Invalid reply received"));
- return;
- }
-
- basename = NULL;
- if (strcmp (op_job->filename, "/") != 0)
- basename = g_path_get_basename (op_job->filename);
-
- if (op_job->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
- {
- parse_attributes (backend, op_job->file_info, basename,
- lstat_reply->data, op_job->attribute_matcher);
- }
- else
- {
- /* Look at stat results */
- reply = &replies[i++];
-
- if (reply->type == SSH_FXP_ATTRS)
- {
- parse_attributes (backend, op_job->file_info, basename,
- reply->data, op_job->attribute_matcher);
-
-
- lstat_info = g_file_info_new ();
- parse_attributes (backend, lstat_info, basename,
- lstat_reply->data, op_job->attribute_matcher);
- if (g_file_info_get_is_symlink (lstat_info))
- g_file_info_set_is_symlink (op_job->file_info, TRUE);
- g_object_unref (lstat_info);
- }
- else
- {
- /* Broken symlink, use lstat data */
- parse_attributes (backend, op_job->file_info, basename,
- lstat_reply->data, op_job->attribute_matcher);
- }
-
- }
-
- g_free (basename);
-
- if (g_file_attribute_matcher_matches (op_job->attribute_matcher,
- G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET))
- {
- /* Look at readlink results */
- reply = &replies[i++];
-
- if (reply->type == SSH_FXP_NAME)
- {
- char *symlink_target;
- guint32 count;
-
- count = g_data_input_stream_read_uint32 (reply->data, NULL, NULL);
- symlink_target = read_string (reply->data, NULL);
- g_file_info_set_symlink_target (op_job->file_info, symlink_target);
- g_free (symlink_target);
- }
- }
-
- g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
-static gboolean
-try_query_info (GVfsBackend *backend,
- GVfsJobQueryInfo *job,
- const char *filename,
- GFileQueryInfoFlags flags,
- GFileInfo *info,
- GFileAttributeMatcher *matcher)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *commands[3];
- GDataOutputStream *command;
- int n_commands;
-
- n_commands = 0;
-
- command = commands[n_commands++] =
- new_command_stream (op_backend,
- SSH_FXP_LSTAT);
- put_string (command, filename);
-
- if (! (job->flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
- {
- command = commands[n_commands++] =
- new_command_stream (op_backend,
- SSH_FXP_STAT);
- put_string (command, filename);
- }
-
- if (g_file_attribute_matcher_matches (job->attribute_matcher,
- G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET))
- {
- command = commands[n_commands++] =
- new_command_stream (op_backend,
- SSH_FXP_READLINK);
- put_string (command, filename);
- }
-
- queue_command_streams_and_free (op_backend, commands, n_commands, query_info_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-move_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- goffset *file_size;
-
- /* on any unknown error, return NOT_SUPPORTED to get the fallback implementation */
- if (reply_type == SSH_FXP_STATUS)
- {
- if (failure_from_status (job, reply, G_IO_ERROR_NOT_SUPPORTED, -1))
- {
- /* Succeeded, report file size */
- file_size = job->backend_data;
- if (file_size != NULL)
- g_vfs_job_move_progress_callback (*file_size, *file_size, job);
- g_vfs_job_succeeded (job);
- }
- }
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static void
-move_do_rename (GVfsBackendSftp *backend,
- GVfsJob *job)
-{
- GVfsJobMove *op_job;
- GDataOutputStream *command;
-
- op_job = G_VFS_JOB_MOVE (job);
-
- command = new_command_stream (backend,
- SSH_FXP_RENAME);
- put_string (command, op_job->source);
- put_string (command, op_job->destination);
-
- queue_command_stream_and_free (backend, command, move_reply, G_VFS_JOB (job), NULL);
-}
-
-static void
-move_delete_target_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- {
- if (failure_from_status (job, reply, -1, -1))
- move_do_rename (backend, job);
- }
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-
-static void
-move_lstat_reply (GVfsBackendSftp *backend,
- MultiReply *replies,
- int n_replies,
- GVfsJob *job,
- gpointer user_data)
-{
- GVfsJobMove *op_job;
- gboolean destination_exist, source_is_dir, dest_is_dir;
- GDataOutputStream *command;
- GFileInfo *info;
- goffset *file_size;
-
- op_job = G_VFS_JOB_MOVE (job);
-
- if (replies[0].type == SSH_FXP_STATUS)
- {
- result_from_status (job, replies[0].data, -1, -1);
- return;
- }
- else if (replies[0].type != SSH_FXP_ATTRS)
- {
- g_vfs_job_failed (job,
- G_IO_ERROR, G_IO_ERROR_FAILED,
- "%s", _("Invalid reply received"));
- return;
- }
-
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL,
- replies[0].data, NULL);
- source_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
- file_size = g_new (goffset, 1);
- *file_size = g_file_info_get_size (info);
- g_vfs_job_set_backend_data (G_VFS_JOB (job), file_size, g_free);
- g_object_unref (info);
-
- destination_exist = FALSE;
- if (replies[1].type == SSH_FXP_ATTRS)
- {
- destination_exist = TRUE; /* Target file exists */
-
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL,
- replies[1].data, NULL);
- dest_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
- g_object_unref (info);
-
- if (op_job->flags & G_FILE_COPY_OVERWRITE)
- {
-
- /* Always fail on dirs, even with overwrite */
- if (dest_is_dir)
- {
- if (source_is_dir)
- g_vfs_job_failed (job,
- G_IO_ERROR,
- G_IO_ERROR_WOULD_MERGE,
- _("Can't move directory over directory"));
- else
- g_vfs_job_failed (job,
- G_IO_ERROR,
- G_IO_ERROR_IS_DIRECTORY,
- _("File is directory"));
- return;
- }
- }
- else
- {
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR,
- G_IO_ERROR_EXISTS,
- _("Target file already exists"));
- return;
- }
- }
-
- /* TODO: Check flags & G_FILE_COPY_BACKUP */
-
- if (destination_exist && (op_job->flags & G_FILE_COPY_OVERWRITE))
- {
- command = new_command_stream (backend,
- SSH_FXP_REMOVE);
- put_string (command, op_job->destination);
- queue_command_stream_and_free (backend, command, move_delete_target_reply, G_VFS_JOB (job), NULL);
- return;
- }
-
- move_do_rename (backend, job);
-}
-
-
-static gboolean
-try_move (GVfsBackend *backend,
- GVfsJobMove *job,
- const char *source,
- const char *destination,
- GFileCopyFlags flags,
- GFileProgressCallback progress_callback,
- gpointer progress_callback_data)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
- GDataOutputStream *commands[2];
-
- command = commands[0] =
- new_command_stream (op_backend,
- SSH_FXP_LSTAT);
- put_string (command, source);
-
- command = commands[1] =
- new_command_stream (op_backend,
- SSH_FXP_LSTAT);
- put_string (command, destination);
-
- queue_command_streams_and_free (op_backend, commands, 2, move_lstat_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-set_display_name_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static gboolean
-try_set_display_name (GVfsBackend *backend,
- GVfsJobSetDisplayName *job,
- const char *filename,
- const char *display_name)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
- char *dirname, *basename, *new_name;
-
- /* We use the same setting as for local files. Can't really
- * do better, since there is no way in this version of sftp to find out
- * the remote charset encoding
- */
-
- dirname = g_path_get_dirname (filename);
- basename = g_filename_from_utf8 (display_name, -1, NULL, NULL, NULL);
- if (basename == NULL)
- basename = g_strdup (display_name);
- new_name = g_build_filename (dirname, basename, NULL);
- g_free (dirname);
- g_free (basename);
-
- g_vfs_job_set_display_name_set_new_path (job,
- new_name);
-
- command = new_command_stream (op_backend,
- SSH_FXP_RENAME);
- put_string (command, filename);
- put_string (command, new_name);
-
- queue_command_stream_and_free (op_backend, command, set_display_name_reply, G_VFS_JOB (job), NULL);
-
- g_free (new_name);
-
- return TRUE;
-}
-
-
-static void
-make_symlink_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static gboolean
-try_make_symlink (GVfsBackend *backend,
- GVfsJobMakeSymlink *job,
- const char *filename,
- const char *symlink_value)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_SYMLINK);
- put_string (command, filename);
- put_string (command, symlink_value);
-
- queue_command_stream_and_free (op_backend, command, make_symlink_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-make_directory_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static gboolean
-try_make_directory (GVfsBackend *backend,
- GVfsJobMakeDirectory *job,
- const char *filename)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_MKDIR);
- put_string (command, filename);
- /* No file info - flag 0 */
- g_data_output_stream_put_uint32 (command, 0, NULL, NULL);
-
- queue_command_stream_and_free (op_backend, command, make_directory_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-delete_remove_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static void
-delete_rmdir_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, G_IO_ERROR_NOT_EMPTY, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static void
-delete_lstat_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else if (reply_type != SSH_FXP_ATTRS)
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
- else
- {
- GFileInfo *info;
- GDataOutputStream *command;
-
- info = g_file_info_new ();
- parse_attributes (backend, info, NULL, reply, NULL);
-
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
- {
- command = new_command_stream (backend,
- SSH_FXP_RMDIR);
- put_string (command, G_VFS_JOB_DELETE (job)->filename);
- queue_command_stream_and_free (backend, command, delete_rmdir_reply, G_VFS_JOB (job), NULL);
- }
- else
- {
- command = new_command_stream (backend,
- SSH_FXP_REMOVE);
- put_string (command, G_VFS_JOB_DELETE (job)->filename);
- queue_command_stream_and_free (backend, command, delete_remove_reply, G_VFS_JOB (job), NULL);
- }
-
- g_object_unref (info);
- }
-}
-
-static gboolean
-try_delete (GVfsBackend *backend,
- GVfsJobDelete *job,
- const char *filename)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- command = new_command_stream (op_backend,
- SSH_FXP_LSTAT);
- put_string (command, filename);
- queue_command_stream_and_free (op_backend, command, delete_lstat_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static gboolean
-try_query_settable_attributes (GVfsBackend *backend,
- GVfsJobQueryAttributes *job,
- const char *filename)
-{
- GFileAttributeInfoList *list;
-
- list = g_file_attribute_info_list_new ();
-
- g_file_attribute_info_list_add (list,
- G_FILE_ATTRIBUTE_UNIX_MODE,
- G_FILE_ATTRIBUTE_TYPE_UINT32,
- G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
- G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
-
- g_vfs_job_query_attributes_set_list (job, list);
- g_vfs_job_succeeded (G_VFS_JOB (job));
- g_file_attribute_info_list_unref (list);
-
- return TRUE;
-}
-
-static void
-set_attribute_reply (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
-{
- if (reply_type == SSH_FXP_STATUS)
- result_from_status (job, reply, -1, -1);
- else
- g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid reply received"));
-}
-
-static gboolean
-try_set_attribute (GVfsBackend *backend,
- GVfsJobSetAttribute *job,
- const char *filename,
- const char *attribute,
- GFileAttributeType type,
- gpointer value_p,
- GFileQueryInfoFlags flags)
-{
- GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
- GDataOutputStream *command;
-
- if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) != 0)
- {
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Operation unsupported"));
- return TRUE;
- }
-
- command = new_command_stream (op_backend,
- SSH_FXP_SETSTAT);
- put_string (command, filename);
- g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL);
- g_data_output_stream_put_uint32 (command, (*(guint32 *)value_p) & 0777, NULL, NULL);
- queue_command_stream_and_free (op_backend, command, set_attribute_reply, G_VFS_JOB (job), NULL);
-
- return TRUE;
-}
-
-static void
-g_vfs_backend_sftp_class_init (GVfsBackendSftpClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
-
- id_q = g_quark_from_static_string ("command-id");
-
- gobject_class->finalize = g_vfs_backend_sftp_finalize;
-
- backend_class->mount = do_mount;
- backend_class->try_mount = try_mount;
- backend_class->try_open_for_read = try_open_for_read;
- backend_class->try_read = try_read;
- backend_class->try_seek_on_read = try_seek_on_read;
- backend_class->try_close_read = try_close_read;
- backend_class->try_close_write = try_close_write;
- backend_class->try_query_info = try_query_info;
- backend_class->try_enumerate = try_enumerate;
- backend_class->try_create = try_create;
- backend_class->try_append_to = try_append_to;
- backend_class->try_replace = try_replace;
- backend_class->try_write = try_write;
- backend_class->try_seek_on_write = try_seek_on_write;
- backend_class->try_move = try_move;
- backend_class->try_make_symlink = try_make_symlink;
- backend_class->try_make_directory = try_make_directory;
- backend_class->try_delete = try_delete;
- backend_class->try_set_display_name = try_set_display_name;
- backend_class->try_query_settable_attributes = try_query_settable_attributes;
- backend_class->try_set_attribute = try_set_attribute;
-}