Index: daemon/gvfsbackendftp.c
===================================================================
--- daemon/gvfsbackendftp.c	(revision 1791)
+++ daemon/gvfsbackendftp.c	(working copy)
@@ -134,6 +134,14 @@
 
 #define STATUS_GROUP(status) ((status) / 100)
 
+/* Return TRUE if error has been handled and set to conn->error */
+typedef gboolean (* Error550Handler) (FtpConnection *conn);
+
+
+/* forward declaration */
+static GFileInfo *
+create_file_info (GVfsBackendFtp *ftp, FtpConnection *conn, const char *filename, char **symlink);
+
 /*** FTP CONNECTION ***/
 
 struct _FtpConnection
@@ -151,6 +159,10 @@
   gsize			read_bytes;
 
   SoupSocket *		data;
+  
+  /* used in Error550Handlers */
+  FtpFile *		current_file;
+  GVfsBackendFtp *	ftp;
 };
 
 static void
@@ -218,7 +230,7 @@
  * Sets an error based on an FTP response code.
  **/
 static void
-ftp_connection_set_error_from_response (FtpConnection *conn, guint response)
+ftp_connection_set_error_from_response (FtpConnection *conn, guint response, Error550Handler *error_handlers)
 {
   const char *msg;
   int code;
@@ -249,6 +261,15 @@
       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. */
+	if (error_handlers)
+	  {
+	    while (*error_handlers)
+	      {
+		if (((Error550Handler)*error_handlers) (conn))
+		  return;
+	        error_handlers++;
+	      }
+	  } 
 	code = G_IO_ERROR_NOT_FOUND;
 	msg = _("File unavailable");
 	break;
@@ -323,7 +344,8 @@
  **/
 static guint
 ftp_connection_receive (FtpConnection *conn,
-			ResponseFlags  flags)
+			ResponseFlags  flags,
+			Error550Handler *error_handlers)
 {
   SoupSocketIOStatus status;
   gsize n_bytes;
@@ -438,30 +460,30 @@
       case 1:
 	if (flags & RESPONSE_PASS_100)
 	  break;
-	ftp_connection_set_error_from_response (conn, response);
+	ftp_connection_set_error_from_response (conn, response, NULL);
 	return 0;
       case 2:
 	if (flags & RESPONSE_FAIL_200)
 	  {
-	    ftp_connection_set_error_from_response (conn, response);
+	    ftp_connection_set_error_from_response (conn, response, NULL);
 	    return 0;
 	  }
 	break;
       case 3:
 	if (flags & RESPONSE_PASS_300)
 	  break;
-	ftp_connection_set_error_from_response (conn, response);
+	ftp_connection_set_error_from_response (conn, response, NULL);
 	return 0;
       case 4:
 	if (flags & RESPONSE_PASS_400)
 	  break;
-	ftp_connection_set_error_from_response (conn, response);
+	ftp_connection_set_error_from_response (conn, response, NULL);
 	return 0;
 	break;
       case 5:
 	if (flags & RESPONSE_PASS_500)
 	  break;
-	ftp_connection_set_error_from_response (conn, response);
+	ftp_connection_set_error_from_response (conn, response, error_handlers);
 	return 0;
       default:
 	g_assert_not_reached ();
@@ -490,6 +512,7 @@
 static guint
 ftp_connection_sendv (FtpConnection *conn,
 		      ResponseFlags  flags,
+		      Error550Handler *error_handlers,
 		      const char *   format,
 		      va_list	     varargs)
 {
@@ -536,18 +559,20 @@
     }
   g_string_free (command, TRUE);
 
-  response = ftp_connection_receive (conn, flags);
+  response = ftp_connection_receive (conn, flags, error_handlers);
   return response;
 }
 
 static guint
 ftp_connection_send (FtpConnection *conn,
 		     ResponseFlags  flags,
+		     Error550Handler *error_handlers,
 		     const char *   format,
-		     ...) G_GNUC_PRINTF (3, 4);
+		     ...) G_GNUC_PRINTF (4, 5);
 static guint
 ftp_connection_send (FtpConnection *conn,
 		     ResponseFlags  flags,
+		     Error550Handler *error_handlers,
 		     const char *   format,
 		     ...)
 {
@@ -557,6 +582,7 @@
   va_start (varargs, format);
   response = ftp_connection_sendv (conn,
 				   flags,
+				   error_handlers,
 				   format,
 				   varargs);
   va_end (varargs);
@@ -622,7 +648,7 @@
 		   _("Could not connect to host"));
     }
 
-  ftp_connection_receive (conn, 0);
+  ftp_connection_receive (conn, 0, NULL);
   return conn;
 }
 
@@ -636,7 +662,7 @@
   if (ftp_connection_in_error (conn))
     return 0;
 
-  status = ftp_connection_send (conn, RESPONSE_PASS_300,
+  status = ftp_connection_send (conn, RESPONSE_PASS_300, NULL,
                                 "USER %s", username);
   
   if (STATUS_GROUP (status) == 3)
@@ -651,7 +677,7 @@
        */
       if (password == NULL)
 	password = "gvfsd-ftp-" VERSION "@example.com";
-      status = ftp_connection_send (conn, 0,
+      status = ftp_connection_send (conn, 0, NULL,
 				    "PASS %s", password);
     }
 
@@ -689,22 +715,22 @@
 ftp_connection_use (FtpConnection *conn)
 {
   /* only binary transfers please */
-  ftp_connection_send (conn, 0, "TYPE I");
+  ftp_connection_send (conn, 0, NULL, "TYPE I");
   if (ftp_connection_in_error (conn))
     return FALSE;
 
   /* check supported features */
-  if (ftp_connection_send (conn, 0, "FEAT") != 0)
+  if (ftp_connection_send (conn, 0, NULL, "FEAT") != 0)
     ftp_connection_parse_features (conn);
   else
     conn->features = FTP_FEATURES_DEFAULT;
 
   /* RFC 2428 suggests to send this to make NAT routers happy */
   if (conn->features & FTP_FEATURE_EPSV)
-    ftp_connection_send (conn, 0, "EPSV ALL");
+    ftp_connection_send (conn, 0, NULL, "EPSV ALL");
   g_clear_error (&conn->error);
 
-  if (ftp_connection_send (conn, 0, "SYST"))
+  if (ftp_connection_send (conn, 0, NULL, "SYST"))
     ftp_connection_parse_system (conn);
   g_clear_error (&conn->error);
 
@@ -722,7 +748,7 @@
 
   if (conn->features & FTP_FEATURE_EPSV)
     {
-      status = ftp_connection_send (conn, RESPONSE_PASS_500, "EPSV");
+      status = ftp_connection_send (conn, RESPONSE_PASS_500, NULL, "EPSV");
       if (STATUS_GROUP (status) == 2)
 	{
 	  s = strrchr (conn->read_buffer, '(');
@@ -742,7 +768,7 @@
 	}
     }
   /* only binary transfers please */
-  status = ftp_connection_send (conn, 0, "PASV");
+  status = ftp_connection_send (conn, 0, NULL, "PASV");
   if (status == 0)
     return FALSE;
 
@@ -848,7 +874,7 @@
 ftp_connection_cd (FtpConnection *conn, const FtpFile *file)
 {
   guint response = ftp_connection_send (conn,
-					RESPONSE_PASS_500,
+					RESPONSE_PASS_500, NULL,
 					"CWD %s", file);
   if (response == 550)
     {
@@ -859,7 +885,7 @@
     }
   else if (STATUS_GROUP (response) == 5)
     {
-      ftp_connection_set_error_from_response (conn, response);
+      ftp_connection_set_error_from_response (conn, response, NULL);
       response = 0;
     }
 
@@ -890,8 +916,8 @@
   ftp_connection_ensure_data_connection (conn);
 
   ftp_connection_send (conn,
-		       RESPONSE_PASS_100 | RESPONSE_FAIL_200,
-		       "LIST");
+		       RESPONSE_PASS_100 | RESPONSE_FAIL_200, NULL, 
+		       "LIST -a");
 }
 
 static GFileInfo *
@@ -1108,7 +1134,7 @@
 	/* Figure out if this connection had a timeout sent. If so, skip it. */
 	g_mutex_unlock (ftp->mutex);
 	ftp_connection_push_job (conn, job);
-	if (ftp_connection_send (conn, 0, "NOOP"))
+	if (ftp_connection_send (conn, 0, NULL, "NOOP"))
 	  break;
 	    
 	g_clear_error (&conn->error);
@@ -1460,6 +1486,31 @@
   g_vfs_job_succeeded (G_VFS_JOB (job));
 }
 
+static gboolean
+error_550_check_directory (FtpConnection *conn)
+{
+  GFileInfo *real;
+
+  real = create_file_info (conn->ftp,
+                           conn,
+                           (char*) (conn->current_file),
+                           NULL);
+  if (real)
+    {
+      if (g_file_info_get_file_type (real) == G_FILE_TYPE_DIRECTORY)
+        {
+          g_set_error (&conn->error, G_IO_ERROR, 
+                       G_IO_ERROR_IS_DIRECTORY,
+                      _("Can't open directory"));
+          DEBUG ("error: %s\n", conn->error->message);
+        }  
+      g_object_unref (real);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 static void
 do_open_for_read (GVfsBackend *backend,
 		  GVfsJobOpenForRead *job,
@@ -1468,6 +1519,7 @@
   GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
   FtpConnection *conn;
   FtpFile *file;
+  static const Error550Handler open_read_handlers[] = { error_550_check_directory, NULL };
 
   conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job));
   if (!conn)
@@ -1476,9 +1528,13 @@
   ftp_connection_ensure_data_connection (conn);
 
   file = ftp_filename_from_gvfs_path (conn, filename);
+  conn->current_file = file;
+  conn->ftp = ftp;
   ftp_connection_send (conn,
-		       RESPONSE_PASS_100 | RESPONSE_FAIL_200,
+		       RESPONSE_PASS_100 | RESPONSE_FAIL_200, (Error550Handler*)&open_read_handlers[0],
 		       "RETR %s", file);
+  conn->current_file = NULL;
+  conn->ftp = NULL;
   g_free (file);
 
   if (ftp_connection_in_error (conn))
@@ -1502,7 +1558,7 @@
 
   ftp_connection_push_job (conn, G_VFS_JOB (job));
   ftp_connection_close_data_connection (conn);
-  ftp_connection_receive (conn, 0); 
+  ftp_connection_receive (conn, 0, NULL); 
   g_vfs_backend_ftp_push_connection (ftp, conn);
 }
 
@@ -1554,6 +1610,7 @@
   va_start (varargs, format);
   status = ftp_connection_sendv (conn,
 				RESPONSE_PASS_100 | RESPONSE_FAIL_200,
+				NULL,
 				format,
 				varargs);
   va_end (varargs);
@@ -1597,9 +1654,6 @@
   g_free (dirname);
 }
 
-/* forward declaration */
-static GFileInfo *
-create_file_info (GVfsBackendFtp *ftp, FtpConnection *conn, const char *filename, char **symlink);
 
 static void
 do_create (GVfsBackend *backend,
@@ -1701,7 +1755,7 @@
   ftp_connection_push_job (conn, G_VFS_JOB (job));
 
   ftp_connection_close_data_connection (conn);
-  ftp_connection_receive (conn, 0); 
+  ftp_connection_receive (conn, 0, NULL); 
 
   g_vfs_backend_ftp_push_connection (ftp, conn);
 }
@@ -1808,7 +1862,7 @@
     g_free (name);
 
   ftp_connection_close_data_connection (conn);
-  ftp_connection_receive (conn, 0);
+  ftp_connection_receive (conn, 0, NULL);
   if (ftp_connection_in_error (conn))
     goto error;
 
@@ -1816,7 +1870,7 @@
 
 error2:
   ftp_connection_close_data_connection (conn);
-  ftp_connection_receive (conn, 0);
+  ftp_connection_receive (conn, 0, NULL);
 error:
   ftp_connection_close_data_connection (conn);
   g_list_foreach (list, (GFunc) g_free, NULL);
@@ -2138,11 +2192,11 @@
 		   _("Invalid filename"));
     }
   ftp_connection_send (conn,
-		       RESPONSE_PASS_300 | RESPONSE_FAIL_200,
+		       RESPONSE_PASS_300 | RESPONSE_FAIL_200, NULL, 
 		       "RNFR %s", original);
   g_free (original);
   ftp_connection_send (conn,
-		       0,
+		       0, NULL, 
 		       "RNTO %s", now);
 
   name = ftp_filename_to_gvfs_path (conn, now);
@@ -2172,12 +2226,12 @@
    * The file-first-then-directory order has been decided by coin-toss. */
   file = ftp_filename_from_gvfs_path (conn, filename);
   response = ftp_connection_send (conn,
-				  RESPONSE_PASS_500,
+				  RESPONSE_PASS_500, NULL, 
 				  "DELE %s", file);
   if (STATUS_GROUP (response) == 5)
     {
       response = ftp_connection_send (conn,
-				      RESPONSE_PASS_500,
+				      RESPONSE_PASS_500, NULL, 
 				      "RMD %s", file);
       if (response == 550)
 	{
@@ -2191,11 +2245,11 @@
 			   "%s", g_strerror (ENOTEMPTY));
 	    }
 	  else
-	    ftp_connection_set_error_from_response (conn, response);
+	    ftp_connection_set_error_from_response (conn, response, NULL);
 	}
       else if (STATUS_GROUP (response) == 5)
 	{
-	  ftp_connection_set_error_from_response (conn, response);
+	  ftp_connection_set_error_from_response (conn, response, NULL);
 	}
     }
 
@@ -2219,7 +2273,7 @@
 
   file = ftp_filename_from_gvfs_path (conn, filename);
   ftp_connection_send (conn,
-		       0,
+		       0, NULL,
 		       "MKD %s", file);
   /* FIXME: Compare created file with name from server result to be sure 
    * it's correct and otherwise fail. */
@@ -2295,10 +2349,10 @@
     }
 
   ftp_connection_send (conn,
-		       RESPONSE_PASS_300 | RESPONSE_FAIL_200,
+		       RESPONSE_PASS_300 | RESPONSE_FAIL_200, NULL,
 		       "RNFR %s", srcfile);
   ftp_connection_send (conn,
-		       0,
+		       0, NULL,
 		       "RNTO %s", destfile);
 
   gvfs_backend_ftp_purge_cache_of_file (ftp, conn, srcfile);
