summaryrefslogtreecommitdiff
path: root/daemon/gvfsbackendftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gvfsbackendftp.c')
-rw-r--r--daemon/gvfsbackendftp.c87
1 files changed, 80 insertions, 7 deletions
diff --git a/daemon/gvfsbackendftp.c b/daemon/gvfsbackendftp.c
index ec55c73a..d2cd7777 100644
--- a/daemon/gvfsbackendftp.c
+++ b/daemon/gvfsbackendftp.c
@@ -135,6 +135,8 @@ G_DEFINE_TYPE (GVfsBackendFtp, g_vfs_backend_ftp, G_VFS_TYPE_BACKEND)
#define STATUS_GROUP(status) ((status) / 100)
+typedef void (* Ftp550Handler) (FtpConnection *conn, const FtpFile *file);
+
/*** FTP CONNECTION ***/
struct _FtpConnection
@@ -250,8 +252,8 @@ ftp_connection_set_error_from_response (FtpConnection *conn, guint response)
case 550: /* Requested action not taken. File unavailable (e.g., file not found, no access). */
/* FIXME: This is a lot of different errors. So we have to pretend to
* be smart here. */
- code = G_IO_ERROR_NOT_FOUND;
- msg = _("File unavailable");
+ code = G_IO_ERROR_FAILED;
+ msg = _("Operation failed");
break;
case 451: /* Requested action aborted: local error in processing. */
code = G_IO_ERROR_FAILED;
@@ -298,6 +300,7 @@ ftp_connection_set_error_from_response (FtpConnection *conn, guint response)
* 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_PASS_550: Don't treat 550 responses, but return them
* RESPONSE_FAIL_200: Fail on a 2XX response
*/
@@ -306,7 +309,8 @@ typedef enum {
RESPONSE_PASS_300 = (1 << 1),
RESPONSE_PASS_400 = (1 << 2),
RESPONSE_PASS_500 = (1 << 3),
- RESPONSE_FAIL_200 = (1 << 4)
+ RESPONSE_PASS_550 = (1 << 4),
+ RESPONSE_FAIL_200 = (1 << 5)
} ResponseFlags;
/**
@@ -460,7 +464,7 @@ ftp_connection_receive (FtpConnection *conn,
return 0;
break;
case 5:
- if (flags & RESPONSE_PASS_500)
+ if ((flags & RESPONSE_PASS_500) || (response == 550 && (flags & RESPONSE_PASS_550)))
break;
ftp_connection_set_error_from_response (conn, response);
return 0;
@@ -565,6 +569,57 @@ ftp_connection_send (FtpConnection *conn,
}
static void
+ftp_connection_check_file (FtpConnection *conn,
+ const Ftp550Handler *handlers,
+ const FtpFile *file)
+{
+ while (*handlers && !ftp_connection_in_error (conn))
+ {
+ (*handlers) (conn, file);
+ handlers++;
+ }
+}
+
+static guint
+ftp_connection_send_and_check (FtpConnection *conn,
+ ResponseFlags flags,
+ const Ftp550Handler *handlers,
+ const FtpFile *file,
+ const char *format,
+ ...) G_GNUC_PRINTF (5, 6);
+static guint
+ftp_connection_send_and_check (FtpConnection *conn,
+ ResponseFlags flags,
+ const Ftp550Handler *handlers,
+ const FtpFile *file,
+ const char *format,
+ ...)
+{
+ va_list varargs;
+ guint response;
+
+ /* check that there's no 550 handling used - don't allow bad use of API */
+ g_return_val_if_fail ((flags & RESPONSE_PASS_550) == 0, 0);
+ g_return_val_if_fail (handlers != NULL, 0);
+ g_return_val_if_fail (file != NULL, 0);
+
+ va_start (varargs, format);
+ response = ftp_connection_sendv (conn,
+ flags | RESPONSE_PASS_550,
+ format,
+ varargs);
+ va_end (varargs);
+ if (response == 550)
+ {
+ ftp_connection_check_file (conn, handlers, file);
+ if (!ftp_connection_in_error (conn))
+ ftp_connection_set_error_from_response (conn, response);
+ response = 0;
+ }
+ return response;
+}
+
+static void
ftp_connection_parse_features (FtpConnection *conn)
{
struct {
@@ -1462,6 +1517,21 @@ do_unmount (GVfsBackend * backend,
}
static void
+error_550_is_directory (FtpConnection *conn, const FtpFile *file)
+{
+ guint response = ftp_connection_send (conn,
+ RESPONSE_PASS_550,
+ "CWD %s", file);
+
+ if (STATUS_GROUP (response) == 2)
+ {
+ g_set_error (&conn->error, G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ _("File is directory"));
+ }
+}
+
+static void
do_open_for_read (GVfsBackend *backend,
GVfsJobOpenForRead *job,
const char *filename)
@@ -1469,6 +1539,7 @@ do_open_for_read (GVfsBackend *backend,
GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
FtpConnection *conn;
FtpFile *file;
+ static const Ftp550Handler open_read_handlers[] = { error_550_is_directory, NULL };
conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job));
if (!conn)
@@ -1477,9 +1548,11 @@ do_open_for_read (GVfsBackend *backend,
ftp_connection_ensure_data_connection (conn);
file = ftp_filename_from_gvfs_path (conn, filename);
- ftp_connection_send (conn,
- RESPONSE_PASS_100 | RESPONSE_FAIL_200,
- "RETR %s", file);
+ ftp_connection_send_and_check (conn,
+ RESPONSE_PASS_100 | RESPONSE_FAIL_200,
+ &open_read_handlers[0],
+ file,
+ "RETR %s", file);
g_free (file);
if (ftp_connection_in_error (conn))