gvfs r1414 - in trunk: . daemon
- From: otte svn gnome org
- To: svn-commits-list gnome org
- Subject: gvfs r1414 - in trunk: . daemon
- Date: Thu, 28 Feb 2008 10:54:49 +0000 (GMT)
Author: otte
Date: Thu Feb 28 10:54:49 2008
New Revision: 1414
URL: http://svn.gnome.org/viewvc/gvfs?rev=1414&view=rev
Log:
2008-02-28 Benjamin Otte <otte gnome org>
* daemon/Makefile.am:
* daemon/gvfsbackendftp.c:
* daemon/gvfsbackendftp.h:
drop my current FTP code and continue development in here. Should make
testing easier for adventurous people.
Modified:
trunk/ChangeLog
trunk/daemon/Makefile.am
trunk/daemon/gvfsbackendftp.c
trunk/daemon/gvfsbackendftp.h
Modified: trunk/daemon/Makefile.am
==============================================================================
--- trunk/daemon/Makefile.am (original)
+++ trunk/daemon/Makefile.am Thu Feb 28 10:54:49 2008
@@ -36,14 +36,13 @@
libexec_PROGRAMS=gvfsd gvfsd-ftp gvfsd-sftp gvfsd-trash gvfsd-computer gvfsd-burn gvfsd-localtest
-mount_in_files = ftp.mount.in sftp.mount.in trash.mount.in computer.mount.in burn.mount.in localtest.mount.in
+mount_in_files = sftp.mount.in trash.mount.in computer.mount.in burn.mount.in localtest.mount.in
mount_DATA = sftp.mount trash.mount computer.mount burn.mount localtest.mount
-# Disabled until the implementation is working: ftp.mount
-mount_in_files += http.mount.in dav.mount.in
+mount_in_files += http.mount.in dav.mount.in ftp.mount.in
if HAVE_HTTP
-mount_DATA += http.mount dav.mount
-libexec_PROGRAMS += gvfsd-http gvfsd-dav
+mount_DATA += http.mount dav.mount ftp.mount
+libexec_PROGRAMS += gvfsd-http gvfsd-dav gvfsd-ftp
endif
mount_in_files += smb.mount.in smb-browse.mount.in
@@ -223,9 +222,10 @@
-DBACKEND_HEADER=gvfsbackendftp.h \
-DDEFAULT_BACKEND_TYPE=ftp \
-DMAX_JOB_THREADS=1 \
+ $(HTTP_CFLAGS) \
-DBACKEND_TYPES='"ftp", G_VFS_TYPE_BACKEND_FTP,'
-gvfsd_ftp_LDADD = $(libraries)
+gvfsd_ftp_LDADD = $(libraries) $(HTTP_LIBS)
gvfsd_sftp_SOURCES = \
sftp.h \
Modified: trunk/daemon/gvfsbackendftp.c
==============================================================================
--- trunk/daemon/gvfsbackendftp.c (original)
+++ trunk/daemon/gvfsbackendftp.c Thu Feb 28 10:54:49 2008
@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* GIO - GLib Input, Output and Streaming Library
*
- * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2008 Benjamin Otte <otte gnome org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -18,22 +18,17 @@
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
- * Author: Alexander Larsson <alexl redhat com>
+ * Author: Benjmain Otte <otte gnome org>
*/
#include <config.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-
-#include <glib/gstdio.h>
#include <glib/gi18n.h>
-#include <gio/gio.h>
+#include <libsoup/soup.h>
#include "gvfsbackendftp.h"
#include "gvfsjobopenforread.h"
@@ -49,398 +44,1216 @@
#include "gvfsjobenumerate.h"
#include "gvfsdaemonprotocol.h"
+#if 1
+#define DEBUG g_print
+#else
+#define DEBUG(...)
+#endif
+
+typedef enum {
+ FTP_FEATURE_MDTM = (1 << 0),
+ FTP_FEATURE_SIZE = (1 << 1)
+} FtpFeatures;
+
struct _GVfsBackendFtp
{
- GVfsBackend parent_instance;
+ GVfsBackend backend;
- GMountSource *mount_source; /* Only used/set during mount */
- int mount_try;
- gboolean mount_try_again;
+ SoupAddress * addr;
+ char * user;
+ char * password;
+
+ /* connection collection */
+ GQueue * queue;
+ GMutex * mutex;
+ GCond * cond;
};
G_DEFINE_TYPE (GVfsBackendFtp, g_vfs_backend_ftp, G_VFS_TYPE_BACKEND)
-static void
-g_vfs_backend_ftp_finalize (GObject *object)
-{
- if (G_OBJECT_CLASS (g_vfs_backend_ftp_parent_class)->finalize)
- (*G_OBJECT_CLASS (g_vfs_backend_ftp_parent_class)->finalize) (object);
-}
+#define STATUS_GROUP(status) ((status) / 100)
-static void
-g_vfs_backend_ftp_init (GVfsBackendFtp *backend)
+/*** FTP CONNECTION ***/
+
+typedef struct _FtpConnection FtpConnection;
+
+struct _FtpConnection
{
-}
+ GCancellable * cancellable;
+
+ FtpFeatures features;
+
+ SoupSocket * commands;
+ gchar read_buffer[256];
+ gsize read_bytes;
+
+ SoupSocket * data;
+};
static void
-do_mount (GVfsBackend *backend,
- GVfsJobMount *job,
- GMountSpec *mount_spec,
- GMountSource *mount_source,
- gboolean is_automount)
+ftp_connection_free (FtpConnection *conn)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ if (conn->commands)
+ g_object_unref (conn->commands);
+ if (conn->data)
+ g_object_unref (conn->data);
- g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_slice_free (FtpConnection, conn);
}
-static gboolean
-try_mount (GVfsBackend *backend,
- GVfsJobMount *job,
- GMountSpec *mount_spec,
- GMountSource *mount_source,
- gboolean is_automount)
+static void
+ftp_error_set_from_response (GError **error, guint response)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- const char *server, *share; /* *domain, *user; */
+ /* FIXME: make error messages nicer */
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Operation failed"));
+}
- server = g_mount_spec_get (mount_spec, "server");
- share = g_mount_spec_get (mount_spec, "share");
+/**
+ * ResponseFlags:
+ * RESPONSE_PASS_100: Don't treat 1XX responses, but return them
+ * RESPONSE_PASS_300: Don't treat 3XX responses, but return them
+ * RESPONSE_PASS_400: Don't treat 4XX responses, but return them
+ * RESPONSE_PASS_500: Don't treat 5XX responses, but return them
+ * RESPONSE_FAIL_200: Fail on a 2XX response
+ */
- if (server == NULL || share == NULL)
+typedef enum {
+ RESPONSE_PASS_100 = (1 << 0),
+ RESPONSE_PASS_300 = (1 << 1),
+ RESPONSE_PASS_400 = (1 << 2),
+ RESPONSE_PASS_500 = (1 << 3),
+ RESPONSE_FAIL_200 = (1 << 4)
+} ResponseFlags;
+
+/**
+ * ftp_connection_receive:
+ * @conn: connection to receive from
+ * @flags: flags for handling the response
+ * @error: pointer to error message
+ *
+ * Reads a command and stores it in @conn->read_buffer. The read buffer will be
+ * null-terminated and contain @conn->read_bytes bytes. Afterwards, the response
+ * will be parsed and processed according to @flags. By default, all responses
+ * but 2xx will cause an error.
+ *
+ * Returns: 0 on error, the ftp code otherwise
+ **/
+static guint
+ftp_connection_receive (FtpConnection *conn,
+ ResponseFlags flags,
+ GError ** error)
+{
+ SoupSocketIOStatus status;
+ gsize n_bytes;
+ gboolean got_boundary;
+ char *last_line;
+ enum {
+ FIRST_LINE,
+ MULTILINE,
+ DONE
+ } reply_state = FIRST_LINE;
+ guint response = 0;
+ gsize bytes_left;
+
+ conn->read_bytes = 0;
+ bytes_left = sizeof (conn->read_buffer) - conn->read_bytes - 1;
+ while (reply_state != DONE && bytes_left >= 6)
{
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- _("No hostname specified"));
- return TRUE;
- }
-#if 0
- user = g_mount_spec_get (mount_spec, "user");
- domain = g_mount_spec_get (mount_spec, "domain");
-#endif
+ last_line = conn->read_buffer + conn->read_bytes;
+ status = soup_socket_read_until (conn->commands,
+ last_line,
+ bytes_left,
+ "\r\n",
+ 2,
+ &n_bytes,
+ &got_boundary,
+ conn->cancellable,
+ error);
+ switch (status)
+ {
+ case SOUP_SOCKET_OK:
+ case SOUP_SOCKET_EOF:
+ if (got_boundary)
+ break;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Invalid reply"));
+ /* fall through */
+ case SOUP_SOCKET_ERROR:
+ conn->read_buffer[conn->read_bytes] = 0;
+ return 0;
+ case SOUP_SOCKET_WOULD_BLOCK:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
- /* TODO */
+ bytes_left -= n_bytes;
+ conn->read_bytes += n_bytes;
+ conn->read_buffer[conn->read_bytes] = 0;
+ DEBUG ("<-- %s", last_line);
-#if 0
- op_backend->server = g_strdup (server);
- op_backend->share = g_strdup (share);
- op_backend->user = g_strdup (user);
- op_backend->domain = g_strdup (domain);
-#endif
+ if (reply_state == FIRST_LINE)
+ {
+ if (n_bytes < 4 ||
+ last_line[0] <= '0' || last_line[0] > '5' ||
+ last_line[1] < '0' || last_line[1] > '9' ||
+ last_line[2] < '0' || last_line[2] > '9')
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Invalid reply"));
+ return 0;
+ }
+ response = 100 * (last_line[0] - '0') +
+ 10 * (last_line[1] - '0') +
+ (last_line[2] - '0');
+ if (last_line[3] == ' ')
+ reply_state = DONE;
+ else if (last_line[3] == '-')
+ reply_state = MULTILINE;
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Invalid reply"));
+ return 0;
+ }
+ }
+ else
+ {
+ if (n_bytes >= 4 &&
+ memcmp (conn->read_buffer, last_line, 3) == 0 &&
+ last_line[3] == ' ')
+ reply_state = DONE;
+ }
+ }
- return FALSE;
-}
+ if (reply_state != DONE)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Invalid reply"));
+ return 0;
+ }
-static void
-do_open_for_read (GVfsBackend *backend,
- GVfsJobOpenForRead *job,
- const char *filename)
-{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
+ switch (STATUS_GROUP (response))
+ {
+ case 0:
+ return 0;
+ case 1:
+ if (flags & RESPONSE_PASS_100)
+ break;
+ ftp_error_set_from_response (error, response);
+ return 0;
+ case 2:
+ if (flags & RESPONSE_FAIL_200)
+ {
+ ftp_error_set_from_response (error, response);
+ return 0;
+ }
+ break;
+ case 3:
+ if (flags & RESPONSE_PASS_300)
+ break;
+ ftp_error_set_from_response (error, response);
+ return 0;
+ case 4:
+ if (flags & RESPONSE_PASS_400)
+ break;
+ ftp_error_set_from_response (error, response);
+ return 0;
+ break;
+ case 5:
+ if (flags & RESPONSE_PASS_500)
+ break;
+ ftp_error_set_from_response (error, response);
+ return 0;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
- /* TODO */
+ return response;
+}
- if (file == NULL)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
- else
+/**
+ * ftp_connection_send:
+ * @conn: the connection to send to
+ * @flags: #ResponseFlags to use
+ * @error: pointer to take an error
+ * @format: format string to construct command from
+ * (without trailing \r\n)
+ * @...: arguments to format string
+ *
+ * Takes a command, waits for an answer and parses it. Without any @flags, FTP
+ * codes other than 2xx cause an error. The last read ftp command will be put
+ * into @conn->read_buffer.
+ *
+ * Returns: 0 on error or the receied FTP code otherwise.
+ *
+ **/
+static guint
+ftp_connection_send (FtpConnection *conn,
+ ResponseFlags flags,
+ GError ** error,
+ const char * format,
+ ...) G_GNUC_PRINTF (4, 5);
+static guint
+ftp_connection_send (FtpConnection *conn,
+ ResponseFlags flags,
+ GError ** error,
+ const char * format,
+ ...)
+{
+ va_list varargs;
+ GString *command;
+ SoupSocketIOStatus status;
+ gsize n_bytes;
+ guint response;
+
+ command = g_string_new ("");
+ va_start (varargs, format);
+ g_string_append_vprintf (command, format, varargs);
+ va_end (varargs);
+ DEBUG ("--> %s\n", command->str);
+ g_string_append (command, "\r\n");
+ status = soup_socket_write (conn->commands,
+ command->str,
+ command->len,
+ &n_bytes,
+ conn->cancellable,
+ error);
+ switch (status)
{
- g_vfs_job_open_for_read_set_can_seek (job, TRUE);
- g_vfs_job_open_for_read_set_handle (job, file);
- g_vfs_job_succeeded (G_VFS_JOB (job));
+ case SOUP_SOCKET_OK:
+ case SOUP_SOCKET_EOF:
+ if (n_bytes == command->len)
+ break;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("broken transmission"));
+ /* fall through */
+ case SOUP_SOCKET_ERROR:
+ g_string_free (command, TRUE);
+ return 0;
+ case SOUP_SOCKET_WOULD_BLOCK:
+ default:
+ g_assert_not_reached ();
}
-#endif
+ g_string_free (command, TRUE);
+
+ response = ftp_connection_receive (conn, flags, error);
+ return response;
}
static void
-do_read (GVfsBackend *backend,
- GVfsJobRead *job,
- GVfsBackendHandle handle,
- char *buffer,
- gsize bytes_requested)
+ftp_connection_parse_features (FtpConnection *conn)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
+ struct {
+ const char * name; /* name of feature */
+ FtpFeatures enable; /* flags to enable with this feature */
+ } features[] = {
+ { "MDTM", FTP_FEATURE_MDTM },
+ { "SIZE", FTP_FEATURE_SIZE }
+ };
+ char **supported;
+ guint i, j;
- /* TODO */
+ supported = g_strsplit (conn->read_buffer, "\r\n", -1);
- if (res == -1)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
- else
+ for (i = 1; supported[i]; i++)
{
- g_vfs_job_read_set_size (job, res);
- g_vfs_job_succeeded (G_VFS_JOB (job));
-
+ const char *feature = supported[i];
+ if (feature[0] != ' ')
+ continue;
+ feature++;
+ for (j = 0; j < G_N_ELEMENTS (features); j++)
+ {
+ if (g_ascii_strcasecmp (feature, features[j].name) == 0)
+ {
+ DEBUG ("feature %s supported\n", features[j].name);
+ conn->features |= features[j].enable;
+ }
+ }
}
-#endif
}
-static void
-do_seek_on_read (GVfsBackend *backend,
- GVfsJobSeekRead *job,
- GVfsBackendHandle handle,
- goffset offset,
- GSeekType type)
-{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-
- switch (type)
- {
- case G_SEEK_SET:
- whence = SEEK_SET;
- break;
- case G_SEEK_CUR:
- whence = SEEK_CUR;
- break;
- case G_SEEK_END:
- whence = SEEK_END;
- break;
- default:
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Unsupported seek type"));
- return;
+static FtpConnection *
+ftp_connection_new (SoupAddress * addr,
+ GCancellable *cancellable,
+ const char * username,
+ const char * password,
+ GError ** error)
+{
+ FtpConnection *conn;
+ guint status;
+
+ conn = g_slice_new0 (FtpConnection);
+ conn->cancellable = cancellable;
+ conn->commands = soup_socket_new ("non-blocking", FALSE,
+ "remote-address", addr,
+ NULL);
+ status = soup_socket_connect_sync (conn->commands, cancellable);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ {
+ /* FIXME: better error messages depending on status please */
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_HOST_NOT_FOUND,
+ _("Could not connect to host"));
+ goto fail;
}
-#endif
- /* TODO */
-
-#if 0
- if (res == (off_t)-1)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
- else
+ status = ftp_connection_receive (conn, 0, error);
+ if (status == 0)
+ goto fail;
+
+ status = ftp_connection_send (conn, RESPONSE_PASS_300, NULL,
+ "USER %s", username);
+ if (status == 0)
{
- g_vfs_job_seek_read_set_offset (job, res);
- g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("Invalid username"));
+ goto fail;
}
-#endif
+ else if (STATUS_GROUP (status) == 3)
+ {
+ status = ftp_connection_send (conn, RESPONSE_PASS_300, NULL,
+ "PASS %s", password);
+ if (status == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("Invalid password"));
+ goto fail;
+ }
+ else if (STATUS_GROUP (status) == 3)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Accounts are not supported"));
+ goto fail;
+ }
+ }
+
+ /* only binary transfers please */
+ status = ftp_connection_send (conn, 0, error, "TYPE I");
+ if (status == 0)
+ goto fail;
+
+ /* check supported features */
+ status = ftp_connection_send (conn, 0, NULL, "FEAT");
+ if (status != 0)
+ ftp_connection_parse_features (conn);
+
+
+ return conn;
+
+fail:
+ ftp_connection_free (conn);
+ return NULL;
}
-static void
-do_close_read (GVfsBackend *backend,
- GVfsJobCloseRead *job,
- GVfsBackendHandle handle)
+static gboolean
+ftp_connection_ensure_data_connection (FtpConnection *conn,
+ GError ** error)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
+ guint ip1, ip2, ip3, ip4, port1, port2;
+ SoupAddress *addr;
+ const char *s;
+ char *ip;
+ guint status;
+
+ /* only binary transfers please */
+ status = ftp_connection_send (conn, 0, error, "PASV");
+ if (status == 0)
+ return FALSE;
+
+ /* parse response and try to find the address to connect to.
+ * This code does the sameas curl.
+ */
+ for (s = conn->read_buffer; *s; s++)
+ {
+ if (sscanf (s, "%u,%u,%u,%u,%u,%u",
+ &ip1, &ip2, &ip3, &ip4,
+ &port1, &port2) == 6)
+ break;
+ }
+ if (*s == 0)
+ {
+ return FALSE;
+ }
+ ip = g_strdup_printf ("%u.%u.%u.%u", ip1, ip2, ip3, ip4);
+ addr = soup_address_new (ip, port1 << 8 | port2);
+ g_free (ip);
+
+ conn->data = soup_socket_new ("non-blocking", FALSE,
+ "remote-address", addr,
+ NULL);
+ g_object_unref (addr);
+ status = soup_socket_connect_sync (conn->data , conn->cancellable);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ {
+ /* FIXME: better error messages depending on status please */
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_HOST_NOT_FOUND,
+ _("Could not connect to host"));
+ g_object_unref (conn->data);
+ conn->data = NULL;
+ return FALSE;
+ }
- /* TODO */
+ return TRUE;
+}
- if (res == -1)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
-#endif
+static void
+ftp_connection_close_data_connection (FtpConnection *conn)
+{
+ if (conn == NULL || conn->data == NULL)
+ return;
- g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_object_unref (conn->data);
+ conn->data = NULL;
}
+/*** BACKEND ***/
static void
-do_create (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
+g_vfs_backend_ftp_push_connection (GVfsBackendFtp *ftp, FtpConnection *conn)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
+ /* we allow conn == NULL to ease error cases */
+ if (conn == NULL)
+ return;
- /* TODO */
+ conn->cancellable = NULL;
- if (file == NULL)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
- else
+ g_mutex_lock (ftp->mutex);
+ if (ftp->queue)
{
- handle = g_new0 (FtpWriteHandle, 1);
- handle->file = file;
-
- g_vfs_job_open_for_write_set_can_seek (job, TRUE);
- g_vfs_job_open_for_write_set_handle (job, handle);
- g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_queue_push_tail (ftp->queue, conn);
+ g_cond_signal (ftp->cond);
}
-#endif
+ else
+ ftp_connection_free (conn);
+ g_mutex_unlock (ftp->mutex);
}
static void
-do_append_to (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- GFileCreateFlags flags)
+do_broadcast (GCancellable *cancellable, GCond *cond)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
+ g_cond_broadcast (cond);
+}
- /* TODO */
+static FtpConnection *
+g_vfs_backend_ftp_pop_connection (GVfsBackendFtp *ftp,
+ GCancellable * cancellable,
+ GError ** error)
+{
+ FtpConnection *conn;
- if (file == NULL)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
- else
+ g_mutex_lock (ftp->mutex);
+ conn = ftp->queue ? g_queue_pop_head (ftp->queue) : NULL;
+ if (conn == NULL && ftp->queue != NULL)
{
- handle = g_new0 (FtpWriteHandle, 1);
- handle->file = file;
-
- initial_offset = op_backend->ftp_context->lseek (op_backend->ftp_context, file,
- 0, SEEK_CUR);
- if (initial_offset == (off_t) -1)
- g_vfs_job_open_for_write_set_can_seek (job, FALSE);
- else
+ guint id = g_signal_connect (cancellable,
+ "cancelled",
+ G_CALLBACK (do_broadcast),
+ ftp->cond);
+ while (conn == NULL && ftp->queue == NULL && !g_cancellable_is_cancelled (cancellable))
{
- g_vfs_job_open_for_write_set_initial_offset (job, initial_offset);
- g_vfs_job_open_for_write_set_can_seek (job, TRUE);
+ g_cond_wait (ftp->cond, ftp->mutex);
+ conn = g_queue_pop_head (ftp->queue);
}
- g_vfs_job_open_for_write_set_handle (job, handle);
- g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_signal_handler_disconnect (cancellable, id);
}
-#endif
+ g_mutex_unlock (ftp->mutex);
+
+ if (conn == NULL)
+ {
+ /* FIXME: need different error on force-unmount? */
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ _("Operation cancelled"));
+ }
+ else
+ conn->cancellable = cancellable;
+
+ return conn;
}
static void
-do_replace (GVfsBackend *backend,
- GVfsJobOpenForWrite *job,
- const char *filename,
- const char *etag,
- gboolean make_backup,
- GFileCreateFlags flags)
+g_vfs_backend_ftp_finalize (GObject *object)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (object);
+
+ if (ftp->addr)
+ g_object_unref (ftp->addr);
+
+ /* has been cleared on unmount */
+ g_assert (ftp->queue == NULL);
+ g_cond_free (ftp->cond);
+ g_mutex_free (ftp->mutex);
+
+ g_free (ftp->user);
+ g_free (ftp->password);
+
+ if (G_OBJECT_CLASS (g_vfs_backend_ftp_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_vfs_backend_ftp_parent_class)->finalize) (object);
}
static void
-do_write (GVfsBackend *backend,
- GVfsJobWrite *job,
- GVfsBackendHandle _handle,
- char *buffer,
- gsize buffer_size)
+g_vfs_backend_ftp_init (GVfsBackendFtp *ftp)
+{
+ ftp->mutex = g_mutex_new ();
+ ftp->cond = g_cond_new ();
+}
+
+static void
+do_mount (GVfsBackend *backend,
+ GVfsJobMount *job,
+ GMountSpec *mount_spec,
+ GMountSource *mount_source,
+ gboolean is_automount)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ FtpConnection *conn;
+ char *prompt;
+ char *username;
+ char *password;
+ gboolean aborted, handled;
+ GError *error = NULL;
+
+ /* translators: %s here is the display name of the share */
+ prompt = g_strdup_printf (_("Enter passsword for %s"),
+ g_vfs_backend_get_display_name (backend));
+
+ handled = g_mount_source_ask_password (
+ mount_source,
+ prompt,
+ ftp->user ? ftp->user : "anonymous",
+ NULL,
+ G_ASK_PASSWORD_NEED_USERNAME |
+ G_ASK_PASSWORD_NEED_PASSWORD |
+ G_ASK_PASSWORD_ANONYMOUS_SUPPORTED,
+ &aborted,
+ &password,
+ &username,
+ NULL,
+ NULL);
+ if (handled && !aborted)
+ {
+ g_free (ftp->user);
+ g_free (ftp->password);
+ ftp->user = username;
+ ftp->password = password;
+ }
+ else if (handled)
+ {
+ g_free (username);
+ g_free (password);
+ }
+
+ if (ftp->user == NULL)
+ ftp->user = g_strdup ("anonymous");
- /* TODO */
+ g_free (prompt);
- if (res == -1)
- g_vfs_job_failed_from_errno (G_VFS_JOB (job), errno);
+ conn = ftp_connection_new (ftp->addr,
+ G_VFS_JOB (job)->cancellable,
+ ftp->user,
+ ftp->password,
+ &error);
+ if (conn == NULL)
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
else
{
- g_vfs_job_write_set_written_size (job, res);
+ ftp->queue = g_queue_new ();
+ g_vfs_backend_ftp_push_connection (ftp, conn);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
-#endif
}
-static void
-do_seek_on_write (GVfsBackend *backend,
- GVfsJobSeekWrite *job,
- GVfsBackendHandle _handle,
- goffset offset,
- GSeekType type)
+static gboolean
+try_mount (GVfsBackend *backend,
+ GVfsJobMount *job,
+ GMountSpec *mount_spec,
+ GMountSource *mount_source,
+ gboolean is_automount)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ const char *host, *port_str;
+ guint port;
+ char *name;
+
+ 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_str = g_mount_spec_get (mount_spec, "port");
+ if (port_str == NULL)
+ port = 21;
+ else
+ {
+ /* FIXME: error handling? */
+ port = strtoul (port_str, NULL, 10);
+ }
+
+ ftp->addr = soup_address_new (host, port);
+ ftp->user = g_strdup (g_mount_spec_get (mount_spec, "user"));
+
+ if (port_str == NULL)
+ /* translators: this is the default display name for FTP shares.
+ * %s is the hostname. */
+ name = g_strdup_printf (_("FTP on %s"), host);
+ else
+ /* translators: this is the display name for FTP shares on a non-default
+ * port. %s is hostname, %u the used port. */
+ name = g_strdup_printf (_("FTP on %s:%u"), host, port);
+
+ g_vfs_backend_set_display_name (backend, name);
+ g_free (name);
+ g_vfs_backend_set_mount_spec (backend, mount_spec);
+ g_vfs_backend_set_icon_name (backend, "folder-remote");
+
+ return FALSE;
}
static void
-do_close_write (GVfsBackend *backend,
- GVfsJobCloseWrite *job,
- GVfsBackendHandle _handle)
+do_unmount (GVfsBackend * backend,
+ GVfsJobUnmount *job)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ FtpConnection *conn;
+
+ g_mutex_lock (ftp->mutex);
+ while ((conn = g_queue_pop_head (ftp->queue)))
+ {
+ /* FIXME: properly quit */
+ ftp_connection_free (conn);
+ }
+ g_queue_free (ftp->queue);
+ ftp->queue = NULL;
+ g_cond_broadcast (ftp->cond);
+ g_mutex_unlock (ftp->mutex);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
-do_query_info (GVfsBackend *backend,
- GVfsJobQueryInfo *job,
- const char *filename,
- GFileQueryInfoFlags flags,
- GFileInfo *info,
- GFileAttributeMatcher *attribute_matcher)
+do_open_for_read (GVfsBackend *backend,
+ GVfsJobOpenForRead *job,
+ const char *filename)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ GError *error = NULL;
+ FtpConnection *conn;
+ guint status;
+
+ conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job)->cancellable, &error);
+ if (conn == NULL)
+ goto error;
+ if (!ftp_connection_ensure_data_connection (conn, &error))
+ goto error;
+
+ status = ftp_connection_send (conn,
+ RESPONSE_PASS_100 | RESPONSE_FAIL_200,
+ &error,
+ "RETR %s", filename);
+ if (status == 0)
+ goto error;
+
+ /* don't push the connection back, it's our handle now */
+ conn->cancellable = NULL;
+ g_vfs_job_open_for_read_set_handle (job, conn);
+ g_vfs_job_open_for_read_set_can_seek (job, FALSE);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return;
+
+error:
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
}
static void
-do_query_fs_info (GVfsBackend *backend,
- GVfsJobQueryFsInfo *job,
- const char *filename,
- GFileInfo *info,
- GFileAttributeMatcher *attribute_matcher)
+do_close_read (GVfsBackend * backend,
+ GVfsJobCloseRead *job,
+ GVfsBackendHandle handle)
{
- /* TODO */
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ GError *error = NULL;
+ FtpConnection *conn = handle;
+ guint response;
+
+ conn->cancellable = G_VFS_JOB (job)->cancellable;
+ ftp_connection_close_data_connection (conn);
+ response = ftp_connection_receive (conn, 0, &error);
+ if (response == 0)
+ {
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ if (response != 0)
+ ftp_error_set_from_response (&error, response);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
}
-static gboolean
-try_query_settable_attributes (GVfsBackend *backend,
- GVfsJobQueryAttributes *job,
- const char *filename)
+static void
+do_read (GVfsBackend * backend,
+ GVfsJobRead * job,
+ GVfsBackendHandle handle,
+ char * buffer,
+ gsize bytes_requested)
{
- /* TODO */
+ GError *error = NULL;
+ FtpConnection *conn = handle;
+ SoupSocketIOStatus status;
+ gsize n_bytes;
+
+ status = soup_socket_read (conn->data,
+ buffer,
+ bytes_requested,
+ &n_bytes,
+ G_VFS_JOB (job)->cancellable,
+ &error);
+ switch (status)
+ {
+ case SOUP_SOCKET_EOF:
+ case SOUP_SOCKET_OK:
+ g_vfs_job_read_set_size (job, n_bytes);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return;
+ case SOUP_SOCKET_ERROR:
+ break;
+ case SOUP_SOCKET_WOULD_BLOCK:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
- return TRUE;
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
}
static void
-do_enumerate (GVfsBackend *backend,
- GVfsJobEnumerate *job,
- const char *filename,
- GFileAttributeMatcher *attribute_matcher,
- GFileQueryInfoFlags flags)
-{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+do_start_write (GVfsBackendFtp *ftp,
+ FtpConnection *conn,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ GError *error = NULL;
+ guint status;
+
+ /* FIXME: can we honour the flags? */
+ if (!ftp_connection_ensure_data_connection (conn, &error))
+ goto error;
+
+ status = ftp_connection_send (conn,
+ RESPONSE_PASS_100 | RESPONSE_FAIL_200,
+ &error,
+ "STOR %s", filename);
+ if (status == 0)
+ goto error;
+
+ /* don't push the connection back, it's our handle now */
+ conn->cancellable = NULL;
+ g_vfs_job_open_for_write_set_handle (job, conn);
+ g_vfs_job_open_for_write_set_can_seek (job, FALSE);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return;
+
+error:
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
}
static void
-do_set_display_name (GVfsBackend *backend,
- GVfsJobSetDisplayName *job,
- const char *filename,
- const char *display_name)
-{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+do_create (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ FtpConnection *conn;
+ GError *error = NULL;
+
+ conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job)->cancellable, &error);
+ if (conn == NULL)
+ goto error;
+
+ do_start_write (ftp, conn, job, filename, flags);
+ return;
+
+error:
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
}
static void
-do_delete (GVfsBackend *backend,
- GVfsJobDelete *job,
- const char *filename)
+do_replace (GVfsBackend *backend,
+ GVfsJobOpenForWrite *job,
+ const char *filename,
+ const char *etag,
+ gboolean make_backup,
+ GFileCreateFlags flags)
+{
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ FtpConnection *conn;
+ GError *error = NULL;
+
+ if (make_backup)
+ {
+ /* FIXME: implement! */
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("backups not supported yet"));
+ return;
+ }
+
+ conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job)->cancellable, &error);
+ if (conn == NULL)
+ goto error;
+
+ do_start_write (ftp, conn, job, filename, flags);
+ return;
+
+error:
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+}
+
+static void
+do_close_write (GVfsBackend *backend,
+ GVfsJobCloseWrite *job,
+ GVfsBackendHandle handle)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ GError *error = NULL;
+ FtpConnection *conn = handle;
+ guint response;
+
+ conn->cancellable = G_VFS_JOB (job)->cancellable;
+ ftp_connection_close_data_connection (conn);
+ response = ftp_connection_receive (conn, 0, &error);
+ if (response == 0)
+ {
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ if (response != 0)
+ ftp_error_set_from_response (&error, response);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
}
static void
-do_make_directory (GVfsBackend *backend,
- GVfsJobMakeDirectory *job,
- const char *filename)
+do_write (GVfsBackend *backend,
+ GVfsJobWrite *job,
+ GVfsBackendHandle handle,
+ char *buffer,
+ gsize buffer_size)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ GError *error = NULL;
+ FtpConnection *conn = handle;
+ SoupSocketIOStatus status;
+ gsize n_bytes;
+
+ status = soup_socket_write (conn->data,
+ buffer,
+ buffer_size,
+ &n_bytes,
+ G_VFS_JOB (job)->cancellable,
+ &error);
+ switch (status)
+ {
+ case SOUP_SOCKET_EOF:
+ case SOUP_SOCKET_OK:
+ g_vfs_job_write_set_written_size (job, n_bytes);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return;
+ case SOUP_SOCKET_ERROR:
+ break;
+ case SOUP_SOCKET_WOULD_BLOCK:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+}
+
+typedef enum {
+ FILE_INFO_DISPLAY_NAME = (1 << 0),
+ FILE_INFO_SIZE = (1 << 1),
+ FILE_INFO_MTIME = (1 << 2),
+ FILE_INFO_TYPE = (1 << 3)
+} FileInfoFlags;
+
+static FileInfoFlags
+file_info_get_flags (FtpConnection * conn,
+ GFileAttributeMatcher *matcher)
+{
+ FileInfoFlags flags = 0;
+
+ if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
+ flags |= FILE_INFO_DISPLAY_NAME;
+
+ if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_SIZE) &&
+ (conn->features& FTP_FEATURE_SIZE))
+ flags |= FILE_INFO_SIZE;
+
+ if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_TIME_MODIFIED) &&
+ (conn->features & FTP_FEATURE_MDTM))
+ flags |= FILE_INFO_MTIME;
+
+ if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_STANDARD_TYPE))
+ flags |= FILE_INFO_TYPE;
+
+ return flags;
}
static void
-do_move (GVfsBackend *backend,
- GVfsJobMove *job,
- const char *source,
- const char *destination,
- GFileCopyFlags flags,
- GFileProgressCallback progress_callback,
- gpointer progress_callback_data)
+file_info_query (FtpConnection *conn,
+ const char *filename,
+ GFileInfo * info,
+ FileInfoFlags flags)
{
-#if 0
- GVfsBackendFtp *op_backend = G_VFS_BACKEND_FTP (backend);
-#endif
- /* TODO */
+ guint response;
+
+ DEBUG ("%p query %s\n", info, filename);
+ if (flags & FILE_INFO_DISPLAY_NAME)
+ {
+ char *display_name = g_filename_display_basename (filename);
+
+ 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);
+ }
+
+ if (flags & FILE_INFO_SIZE)
+ {
+ response = ftp_connection_send (conn, 0, NULL, "SIZE %s", filename);
+
+ if (response != 0)
+ {
+ guint64 size = g_ascii_strtoull (conn->read_buffer + 4, NULL, 10);
+ g_file_info_set_size (info, size);
+ }
+ }
+
+ if (flags & FILE_INFO_MTIME)
+ {
+ response = ftp_connection_send (conn, 0, NULL, "MDTM %s", filename);
+
+ if (response != 0)
+ {
+ GTimeVal tv;
+ char *date;
+
+ /* modify read buffer to get a valid iso time */
+ date = conn->read_buffer + 4;
+ memmove (date + 9, date + 8, 10);
+ date[8] = 'T';
+ date[19] = 0;
+ if (g_time_val_from_iso8601 (date, &tv))
+ g_file_info_set_modification_time (info, &tv);
+ else
+ DEBUG ("not a time: %s\n", date);
+ }
+ }
+
+ if (flags & FILE_INFO_TYPE)
+ {
+ /* kind of an evil trick here to determine the type.
+ * We cwd to the given filename.
+ * If it succeeds, it's a directroy, otherwise it's a file.
+ */
+ response = ftp_connection_send (conn, 0, NULL, "CWD %s", filename);
+ if (response == 0)
+ g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
+ else
+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+ }
+}
+
+static void
+do_query_info (GVfsBackend *backend,
+ GVfsJobQueryInfo *job,
+ const char *filename,
+ GFileQueryInfoFlags query_flags,
+ GFileInfo *info,
+ GFileAttributeMatcher *matcher)
+{
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ GError *error = NULL;
+ FtpConnection *conn;
+ FileInfoFlags flags;
+
+ conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job)->cancellable, &error);
+ if (conn == NULL)
+ goto error;
+
+ g_file_info_set_name (info, filename);
+ DEBUG ("%p query %s\n", info, filename);
+ flags = file_info_get_flags (conn, matcher);
+ file_info_query (conn, filename, info, flags);
+
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return;
+
+error:
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+}
+
+static void
+do_enumerate (GVfsBackend *backend,
+ GVfsJobEnumerate *job,
+ const char *filename,
+ GFileAttributeMatcher *matcher,
+ GFileQueryInfoFlags query_flags)
+{
+ GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
+ GError *error = NULL;
+ FtpConnection *conn;
+ char *name;
+ gsize size, n_bytes, bytes_read;
+ SoupSocketIOStatus status;
+ gboolean got_boundary;
+ GList *walk, *list = NULL;
+ guint response;
+ FileInfoFlags flags;
+
+ conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job)->cancellable, &error);
+ if (conn == NULL)
+ goto error;
+ if (!ftp_connection_ensure_data_connection (conn, &error))
+ goto error;
+
+ response = ftp_connection_send (conn,
+ RESPONSE_PASS_100 | RESPONSE_FAIL_200,
+ &error,
+ "NLST %s", filename);
+ if (response == 0)
+ goto error;
+
+ size = 128;
+ bytes_read = 0;
+ name = g_malloc (size);
+
+ do
+ {
+ if (bytes_read + 3 >= size)
+ {
+ if (size >= 16384)
+ {
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FILENAME_TOO_LONG,
+ _("filename too long"));
+ break;
+ }
+ size += 128;
+ name = g_realloc (name, size);
+ }
+ status = soup_socket_read_until (conn->data,
+ name + bytes_read,
+ size - bytes_read - 1,
+ "\r\n",
+ 2,
+ &n_bytes,
+ &got_boundary,
+ conn->cancellable,
+ &error);
+
+ bytes_read += n_bytes;
+ switch (status)
+ {
+ case SOUP_SOCKET_EOF:
+ case SOUP_SOCKET_OK:
+ if (n_bytes == 0)
+ {
+ status = SOUP_SOCKET_EOF;
+ break;
+ }
+ if (got_boundary)
+ {
+ name[bytes_read - 2] = 0;
+ DEBUG ("file: %s\n", name);
+ list = g_list_prepend (list, g_strdup (name));
+ bytes_read = 0;
+ }
+ break;
+ case SOUP_SOCKET_ERROR:
+ goto error2;
+ case SOUP_SOCKET_WOULD_BLOCK:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ while (status == SOUP_SOCKET_OK);
+
+ if (bytes_read)
+ {
+ name[bytes_read] = 0;
+ DEBUG ("file: %s\n", name);
+ list = g_list_prepend (list, name);
+ }
+ else
+ g_free (name);
+
+ response = ftp_connection_receive (conn, 0, &error);
+ if (response == 0)
+ goto error;
+ ftp_connection_close_data_connection (conn);
+
+ flags = file_info_get_flags (conn, matcher);
+ for (walk = list; walk; walk = walk->next)
+ {
+ GFileInfo *info = g_file_info_new ();
+ g_file_info_set_attribute_mask (info, matcher);
+ g_file_info_set_name (info, walk->data);
+ file_info_query (conn, walk->data, info, flags);
+ g_vfs_job_enumerate_add_info (job, info);
+ g_free (walk->data);
+ }
+ g_list_free (list);
+
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_enumerate_done (job);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return;
+
+error2:
+ ftp_connection_close_data_connection (conn);
+ ftp_connection_receive (conn, 0, NULL);
+error:
+ g_list_foreach (list, (GFunc) g_free, NULL);
+ g_list_free (list);
+ ftp_connection_close_data_connection (conn);
+ g_vfs_backend_ftp_push_connection (ftp, conn);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
}
static void
@@ -453,22 +1266,14 @@
backend_class->mount = do_mount;
backend_class->try_mount = try_mount;
+ backend_class->unmount = do_unmount;
backend_class->open_for_read = do_open_for_read;
- backend_class->read = do_read;
- backend_class->seek_on_read = do_seek_on_read;
backend_class->close_read = do_close_read;
+ backend_class->read = do_read;
backend_class->create = do_create;
- backend_class->append_to = do_append_to;
backend_class->replace = do_replace;
- backend_class->write = do_write;
- backend_class->seek_on_write = do_seek_on_write;
backend_class->close_write = do_close_write;
+ backend_class->write = do_write;
backend_class->query_info = do_query_info;
- backend_class->query_fs_info = do_query_fs_info;
backend_class->enumerate = do_enumerate;
- backend_class->set_display_name = do_set_display_name;
- backend_class->delete = do_delete;
- backend_class->make_directory = do_make_directory;
- backend_class->move = do_move;
- backend_class->try_query_settable_attributes = try_query_settable_attributes;
}
Modified: trunk/daemon/gvfsbackendftp.h
==============================================================================
--- trunk/daemon/gvfsbackendftp.h (original)
+++ trunk/daemon/gvfsbackendftp.h Thu Feb 28 10:54:49 2008
@@ -1,6 +1,6 @@
/* GIO - GLib Input, Output and Streaming Library
*
- * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2008 Benjamin Otte <otte gnome org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,7 +17,7 @@
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
- * Author: Alexander Larsson <alexl redhat com>
+ * Author: Benjamin Otte <otte gnome org>
*/
#ifndef __G_VFS_BACKEND_FTP_H__
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]