[gvfs] afp: try to enumerate volumes



commit e8025856327d583ad14799549a1a7b06c3070929
Author: Carl-Anton Ingmarsson <ca ingmarsson gmail com>
Date:   Fri Jun 3 01:56:16 2011 +0200

    afp: try to enumerate volumes

 daemon/gvfsafpconnection.c |  592 ++++++++++++++++++++++++++++++++++++--------
 daemon/gvfsafpconnection.h |   45 +++-
 daemon/gvfsbackendafp.c    |  418 +++++++++++++++++++++++++-------
 daemon/gvfsbackendafp.h    |   16 ++
 4 files changed, 875 insertions(+), 196 deletions(-)
---
diff --git a/daemon/gvfsafpconnection.c b/daemon/gvfsafpconnection.c
index be30b9a..db77eaf 100644
--- a/daemon/gvfsafpconnection.c
+++ b/daemon/gvfsafpconnection.c
@@ -58,9 +58,14 @@ g_vfs_afp_reply_new (AfpErrorCode error_code, const char *data, gsize len)
 {
   GVfsAfpReply *reply;
 
-  reply = g_object_new (G_VFS_TYPE_AFP_REPLY,
-                        "base-stream", g_memory_input_stream_new_from_data (data, len, g_free),
-                        NULL);
+  if (data)
+    reply = g_object_new (G_VFS_TYPE_AFP_REPLY, "base-stream",
+                          g_memory_input_stream_new_from_data (data, len, g_free), 
+                          NULL);
+  else
+    reply = g_object_new (G_VFS_TYPE_AFP_REPLY, "base-stream",
+                          g_memory_input_stream_new (), NULL);
+  
 
   reply->error_code = error_code;
   
@@ -152,19 +157,17 @@ g_vfs_afp_command_new (AfpCommandType type)
   GVfsAfpCommand *command;
 
   command = g_object_new (G_VFS_TYPE_AFP_COMMAND,
-                          "base-stream", g_memory_output_stream_new (NULL, 0, g_realloc, g_free));
+                          "base-stream", g_memory_output_stream_new (NULL, 0, g_realloc, g_free),
+                          NULL);
 
   command->type = type;
   g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (command), type, NULL, NULL);
 
-  /* pad byte */
-  g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (command), 0, NULL, NULL);
-
   return command;
 }
 
 void
-g_vfs_afp_command_put_pascal (GVfsAfpCommand *command, char *str)
+g_vfs_afp_command_put_pascal (GVfsAfpCommand *command, const char *str)
 {
   size_t len;
 
@@ -175,21 +178,75 @@ g_vfs_afp_command_put_pascal (GVfsAfpCommand *command, char *str)
   g_output_stream_write (G_OUTPUT_STREAM (command), str, len, NULL, NULL);
 }
 
+void
+g_vfs_afp_command_pad_to_even (GVfsAfpCommand *command)
+{ 
+  if (g_vfs_afp_command_get_size (command) % 2 == 1)
+    g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (command), 0, NULL, NULL);
+}
+
+gsize
+g_vfs_afp_command_get_size (GVfsAfpCommand *command)
+{
+  GMemoryOutputStream *mem_stream;
 
+  mem_stream =
+    G_MEMORY_OUTPUT_STREAM (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (command)));
+
+  return g_memory_output_stream_get_data_size (mem_stream);
+}
+
+char *
+g_vfs_afp_command_get_data (GVfsAfpCommand *command)
+{
+  GMemoryOutputStream *mem_stream;
+
+  mem_stream =
+    G_MEMORY_OUTPUT_STREAM (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (command)));
+
+  return g_memory_output_stream_get_data (mem_stream);
+}
 
 /*
  * GVfsAfpConnection
  */
 G_DEFINE_TYPE (GVfsAfpConnection, g_vfs_afp_connection, G_TYPE_OBJECT);
 
+typedef struct {
+  guint8 flags;
+  guint8 command;
+  guint16 requestID;
+  union {
+    guint32 errorCode;
+    guint32 writeOffset;
+  };
+  guint32 totalDataLength;
+  guint32 reserved;
+} DSIHeader;
+
 struct _GVfsAfpConnectionPrivate
 {
+  GSocketConnectable *addr;
   GIOStream *conn;
 
   guint16 request_id;
 
   guint32 kRequestQuanta;
   guint32 kServerReplayCacheSize;
+
+  GQueue     *request_queue;
+  GHashTable *request_hash;
+
+  /* send loop */
+  gboolean send_loop_running;
+  gsize bytes_written;
+  DSIHeader write_dsi_header;
+
+  /* read loop */
+  gboolean read_loop_running;
+  gsize bytes_read;
+  DSIHeader read_dsi_header;
+  char *data;
 };
 
 typedef enum
@@ -202,18 +259,6 @@ typedef enum
   DSI_WRITE         = 6
 } DsiCommand;
 
-typedef struct {
-  guint8 flags;
-  guint8 command;
-  guint16 requestID;
-  union {
-    guint32 errorCode;
-    guint32 writeOffset;
-  };
-  guint32 totalDataLength;
-  guint32 reserved;
-} DSIHeader;
-
 static guint16
 get_request_id (GVfsAfpConnection *afp_connection)
 {
@@ -222,73 +267,378 @@ get_request_id (GVfsAfpConnection *afp_connection)
   return priv->request_id++;
 }
 
-static char *
-read_reply_sync (GVfsAfpConnection *afp_connection,
-                 DSIHeader         *dsi_header,
-                 GCancellable      *cancellable,
-                 GError            **error)
+typedef struct
+{
+  GVfsAfpCommand *command;
+  GVfsAfpConnectionReplyCallback reply_cb;
+  GCancellable *cancellable;
+  gpointer user_data;
+  GVfsAfpConnection *conn;
+} RequestData;
+
+static void
+free_request_data (RequestData *request_data)
+{
+  g_object_unref (request_data->command);
+  g_object_unref (request_data->cancellable);
+
+  g_slice_free (RequestData, request_data);
+}
+
+static void
+dispatch_reply (GVfsAfpConnection *afp_connection)
+{
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+
+  RequestData *req_data;
+
+  g_debug ("DISPATCH!!!\n");
+  req_data = g_hash_table_lookup (priv->request_hash,
+                                  GUINT_TO_POINTER (priv->read_dsi_header.requestID));
+  if (req_data)
+  {
+    GVfsAfpReply *reply;
+    
+    reply = g_vfs_afp_reply_new (priv->read_dsi_header.errorCode, priv->data,
+                                 priv->read_dsi_header.totalDataLength);
+    req_data->reply_cb (afp_connection, reply, NULL, req_data->user_data);
+    g_hash_table_remove (priv->request_hash, GUINT_TO_POINTER (priv->read_dsi_header.requestID));
+    return;
+  }
+
+  g_free (priv->data);
+}
+
+static void read_reply (GVfsAfpConnection *afp_connection);
+
+static void
+read_data_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+  GInputStream *input = G_INPUT_STREAM (object);
+  GVfsAfpConnection *afp_connection = G_VFS_AFP_CONNECTION (user_data);
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+  
+  gssize bytes_read;
+  GError *err = NULL;
+  
+  bytes_read = g_input_stream_read_finish (input, res, &err);
+  if (bytes_read == -1)
+  {
+    g_warning ("FAIL!!! \"%s\"\n", err->message);
+    g_error_free (err);
+  }
+
+  priv->bytes_read += bytes_read;
+  if (priv->bytes_read < priv->read_dsi_header.totalDataLength)
+  {
+    g_input_stream_read_async (input, priv->data + priv->bytes_read,
+                               priv->read_dsi_header.totalDataLength - priv->bytes_read,
+                               0, NULL, read_data_cb, afp_connection);
+    return;
+  }
+
+  dispatch_reply (afp_connection);
+  read_reply (afp_connection);
+}
+
+static void
+read_dsi_header_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+  GInputStream *input = G_INPUT_STREAM (object);
+  GVfsAfpConnection *afp_connection = G_VFS_AFP_CONNECTION (user_data);
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+  
+  gssize bytes_read;
+  GError *err = NULL;
+  
+  bytes_read = g_input_stream_read_finish (input, res, &err);
+  if (bytes_read == -1)
+  {
+    g_warning ("FAIL!!! \"%s\"\n", err->message);
+    g_error_free (err);
+  }
+
+  priv->bytes_read += bytes_read;
+  if (priv->bytes_read < sizeof (DSIHeader))
+  {
+    g_input_stream_read_async (input, &priv->read_dsi_header + priv->bytes_read,
+                               sizeof (DSIHeader) - priv->bytes_read, 0, NULL,
+                               read_dsi_header_cb, afp_connection);
+    return;
+  }
+
+  if (priv->read_dsi_header.totalDataLength > 0)
+  {
+    priv->data = g_malloc (priv->read_dsi_header.totalDataLength);
+    priv->bytes_read = 0;
+    g_input_stream_read_async (input, priv->data, priv->read_dsi_header.totalDataLength,
+                               0, NULL, read_data_cb, afp_connection);
+    return;
+  }
+
+  dispatch_reply (afp_connection);
+  read_reply (afp_connection);
+}
+
+static void
+read_reply (GVfsAfpConnection *afp_connection)
 {
   GVfsAfpConnectionPrivate *priv = afp_connection->priv;
   
   GInputStream *input;
+
+  input = g_io_stream_get_input_stream (priv->conn);
+
+  priv->bytes_read = 0;
+  priv->data = NULL;
+  g_input_stream_read_async (input, &priv->read_dsi_header, sizeof (DSIHeader), 0, NULL,
+                             read_dsi_header_cb, afp_connection);
+}
+
+static void send_request (GVfsAfpConnection *afp_connection);
+
+static void
+write_command_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+  GOutputStream *output = G_OUTPUT_STREAM (object);
+  RequestData *request_data = (RequestData *)user_data;
+  GVfsAfpConnectionPrivate *priv = request_data->conn->priv;
+  
+  gssize bytes_written;
+  GError *err = NULL;
+
+  char *data;
+  gsize size;
+  
+  bytes_written = g_output_stream_write_finish (output, res, &err);
+  if (bytes_written == -1)
+  {
+    request_data->reply_cb (request_data->conn, NULL, err, request_data->user_data);
+    free_request_data (request_data);
+    g_error_free (err);
+  }
+
+  data = g_vfs_afp_command_get_data (request_data->command);
+  size = g_vfs_afp_command_get_size (request_data->command);
+  
+  priv->bytes_written += bytes_written;
+  if (priv->bytes_written < size)
+  {
+    g_output_stream_write_async (output, data + priv->bytes_written,
+                                 size - priv->bytes_written, 0, NULL,
+                                 write_command_cb, request_data);
+    return;
+  }
+
+  g_hash_table_insert (priv->request_hash,
+                       GUINT_TO_POINTER (priv->write_dsi_header.requestID),
+                       request_data);
+
+  send_request (request_data->conn);
+}
+
+static void
+write_dsi_header_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+  GOutputStream *output = G_OUTPUT_STREAM (object);
+  RequestData *request_data = (RequestData *)user_data;
+  GVfsAfpConnectionPrivate *priv = request_data->conn->priv;
+  
+  gssize bytes_written;
+  GError *err = NULL;
+
+  char *data;
+  gsize size;
+  
+  bytes_written = g_output_stream_write_finish (output, res, &err);
+  g_debug ("Bytes written:%d\n", bytes_written);
+  if (bytes_written == -1)
+  {
+    request_data->reply_cb (request_data->conn, NULL, err, request_data->user_data);
+    free_request_data (request_data);
+    g_error_free (err);
+  }
+
+  priv->bytes_written += bytes_written;
+  if (priv->bytes_written < sizeof (DSIHeader))
+  {
+    g_output_stream_write_async (output, &priv->write_dsi_header + priv->bytes_written,
+                                 sizeof (DSIHeader) - priv->bytes_written, 0,
+                                 NULL, write_dsi_header_cb, request_data);
+    return;
+  }
+
+  data = g_vfs_afp_command_get_data (request_data->command);
+  size = g_vfs_afp_command_get_size (request_data->command);
+  
+  priv->bytes_written = 0;
+  g_output_stream_write_async (output, data, size, 0,
+                               NULL, write_command_cb, request_data);
+}
+
+static void
+send_request (GVfsAfpConnection *afp_connection)
+{
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+
+  RequestData *request_data;
+  guint32 writeOffset;
+  guint8 dsi_command;
+
+  request_data = g_queue_pop_head (priv->request_queue);
+
+  if (!request_data) {
+    priv->send_loop_running = FALSE;
+    return;
+  }
+ 
+  switch (request_data->command->type)
+  {
+    case AFP_COMMAND_WRITE:
+      writeOffset = 8;
+      dsi_command = DSI_WRITE;
+      break;
+    case AFP_COMMAND_WRITE_EXT:
+      writeOffset = 20;
+      dsi_command = DSI_WRITE;
+      break;
+
+    default:
+      writeOffset = 0;
+      dsi_command = DSI_COMMAND;
+      break;
+  }
+  
+  priv->write_dsi_header.flags = 0x00;
+  priv->write_dsi_header.command = dsi_command;
+  priv->write_dsi_header.requestID = get_request_id (afp_connection);
+  priv->write_dsi_header.writeOffset = writeOffset;
+  priv->write_dsi_header.totalDataLength = g_vfs_afp_command_get_size (request_data->command);
+  priv->write_dsi_header.reserved = 0;
+
+  priv->bytes_written = 0;
+  g_output_stream_write_async (g_io_stream_get_output_stream (priv->conn),
+                               &priv->write_dsi_header, sizeof (DSIHeader), 0,
+                               NULL, write_dsi_header_cb, request_data); 
+}
+
+static void
+run_loop (GVfsAfpConnection *afp_connection)
+{
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+
+  if (!priv->send_loop_running)
+  {
+    send_request (afp_connection);
+    priv->send_loop_running = TRUE;
+  }
+  if (!priv->read_loop_running)
+  {
+    read_reply (afp_connection);
+    priv->read_loop_running = TRUE;
+  }
+}
+
+void
+g_vfs_afp_connection_queue_command (GVfsAfpConnection *afp_connection,
+                                    GVfsAfpCommand *command,
+                                    GVfsAfpConnectionReplyCallback reply_cb,
+                                    GCancellable *cancellable,
+                                    gpointer user_data)
+{
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+
+  RequestData *request_data;
+  
+  request_data = g_slice_new (RequestData);
+  request_data->command = g_object_ref (command);
+  request_data->reply_cb = reply_cb;
+  request_data->cancellable = g_object_ref (cancellable);
+  request_data->user_data = user_data;
+  request_data->conn = afp_connection;
+
+  g_queue_push_tail (priv->request_queue, request_data);
+
+  run_loop (afp_connection);
+}
+
+static gboolean
+read_reply_sync (GInputStream      *input,
+                 DSIHeader         *dsi_header,
+                 char              **data,
+                 GCancellable      *cancellable,
+                 GError            **error)
+{
   gboolean res;
   gsize read_count, bytes_read;
-  char *data;
 
   g_assert (dsi_header != NULL);
-  
-  input = g_io_stream_get_input_stream (priv->conn);
 
   read_count = sizeof (DSIHeader);
   res = g_input_stream_read_all (input, dsi_header, read_count, &bytes_read,
-                                 NULL, NULL);
+                                 cancellable, error);
   if (!res || bytes_read < read_count)
-    return NULL;
+    return FALSE;
 
   dsi_header->requestID = GUINT16_FROM_BE (dsi_header->requestID);
   dsi_header->errorCode = GUINT32_FROM_BE (dsi_header->errorCode);
   dsi_header->totalDataLength = GUINT32_FROM_BE (dsi_header->totalDataLength);
 
-  data = g_malloc (dsi_header->totalDataLength);
+  if (dsi_header->totalDataLength == 0)
+  {
+    *data = NULL;
+    return TRUE;    
+  }
+  
+  *data = g_malloc (dsi_header->totalDataLength);
   read_count = dsi_header->totalDataLength;
 
-  res = g_input_stream_read_all (input, data, read_count, &bytes_read, cancellable, error);
+  res = g_input_stream_read_all (input, *data, read_count, &bytes_read, cancellable, error);
   if (!res || bytes_read < read_count)
   {
-    g_free (data);
-    return NULL;
+    g_free (*data);
+    return FALSE;
   }
 
-  return data;
+  return TRUE;
+}
+
+GVfsAfpReply *
+g_vfs_afp_connection_read_reply_sync (GVfsAfpConnection *afp_connection,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+  
+  gboolean res;
+  char *data;
+  DSIHeader dsi_header;
+
+  res = read_reply_sync (g_io_stream_get_input_stream (priv->conn), &dsi_header,
+                         &data, cancellable, error);
+  if (!res)
+    return NULL;
+
+  return g_vfs_afp_reply_new (dsi_header.errorCode, data, dsi_header.totalDataLength);
 }
 
 static gboolean
-send_request_sync (GVfsAfpConnection *afp_connection,
+send_request_sync (GOutputStream     *output,
                    DsiCommand        command,
+                   guint16           request_id,
                    guint32           writeOffset,
                    gsize             len,
                    char              *data,
-                   guint16           *request_id,
                    GCancellable      *cancellable,
                    GError            **error)
 {
-  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
-
-  GOutputStream *output;
-  guint16 req_id;
   DSIHeader dsi_header;
   gboolean res;
   gsize write_count, bytes_written;
 
-  output = g_io_stream_get_output_stream (priv->conn);
-
-  req_id = get_request_id (afp_connection);
-  if (request_id)
-    *request_id = get_request_id (afp_connection);
-
   dsi_header.flags = 0x00;
   dsi_header.command = command;
-  dsi_header.requestID = GUINT16_TO_BE (req_id);
+  dsi_header.requestID = GUINT16_TO_BE (request_id);
   dsi_header.writeOffset = GUINT32_TO_BE (writeOffset);
   dsi_header.totalDataLength = GUINT32_TO_BE (len); 
   dsi_header.reserved = 0;
@@ -301,7 +651,7 @@ send_request_sync (GVfsAfpConnection *afp_connection,
 
   if (data == NULL)
     return TRUE;
-  
+
   write_count = len;
   res = g_output_stream_write_all (output, data, write_count, &bytes_written,
                                    cancellable, error);
@@ -311,39 +661,21 @@ send_request_sync (GVfsAfpConnection *afp_connection,
   return TRUE;
 }
 
-GVfsAfpReply *
-g_vfs_afp_connection_read_reply_sync (GVfsAfpConnection *afp_connection,
-                                      GCancellable *cancellable,
-                                      GError **error)
-{
-  char *data;
-  DSIHeader dsi_header;
-
-  data = read_reply_sync (afp_connection, &dsi_header, cancellable, error);
-  if (!data)
-    return NULL;
-
-  return g_vfs_afp_reply_new (dsi_header.errorCode, data, dsi_header.totalDataLength);
-}
-
 gboolean
 g_vfs_afp_connection_send_command_sync (GVfsAfpConnection *afp_connection,
                                         GVfsAfpCommand    *afp_command,
                                         GCancellable      *cancellable,
                                         GError            **error)
 {
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
+  
   DsiCommand dsi_command;
+  guint16 req_id;
   guint32 writeOffset;
-  GMemoryOutputStream *mem_stream;
 
   /* set dsi_command */
   switch (afp_command->type)
   {
-    case AFP_COMMAND_GET_SRVR_INFO:
-      writeOffset = 0;
-      dsi_command = DSI_GET_STATUS;
-      break;
-
     case AFP_COMMAND_WRITE:
       writeOffset = 8;
       dsi_command = DSI_WRITE;
@@ -356,48 +688,50 @@ g_vfs_afp_connection_send_command_sync (GVfsAfpConnection *afp_connection,
     default:
       writeOffset = 0;
       dsi_command = DSI_COMMAND;
+      break;
   }
 
-  mem_stream =
-    G_MEMORY_OUTPUT_STREAM(g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (afp_command)));
-  
-  return send_request_sync (afp_connection, dsi_command, writeOffset,
-                            g_memory_output_stream_get_size (mem_stream),
-                            g_memory_output_stream_get_data (mem_stream),
-                            NULL, cancellable, error);
+  req_id = get_request_id (afp_connection);
+  return send_request_sync (g_io_stream_get_output_stream (priv->conn),
+                            dsi_command, req_id, writeOffset,
+                            g_vfs_afp_command_get_size (afp_command),
+                            g_vfs_afp_command_get_data (afp_command),
+                            cancellable, error);
 }
 
-GVfsAfpConnection *
-g_vfs_afp_connection_new (GSocketConnectable *addr,
-                          GCancellable       *cancellable,
-                          GError             **error)
+gboolean
+g_vfs_afp_connection_open (GVfsAfpConnection *afp_connection,
+                           GCancellable      *cancellable,
+                           GError            **error)
 {
-  GVfsAfpConnection *afp_connection;
-  GVfsAfpConnectionPrivate *priv;
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
 
   GSocketClient *client;
 
+  guint16 req_id;
+  gboolean res;
   char *reply;
   DSIHeader dsi_header;
   guint pos;
 
-  afp_connection = g_object_new (G_VFS_TYPE_AFP_CONNECTION, NULL);
-  priv = afp_connection->priv;
-
   client = g_socket_client_new ();
-  priv->conn = G_IO_STREAM (g_socket_client_connect (client, addr, cancellable, error));
+  priv->conn = G_IO_STREAM (g_socket_client_connect (client, priv->addr, cancellable, error));
   g_object_unref (client);
 
   if (!priv->conn)
-    goto error;
+    return FALSE;
 
-  if (!send_request_sync (afp_connection, DSI_OPEN_SESSION, 0, 0, NULL, NULL,
-                          cancellable, error))
-    goto error;
+  req_id = get_request_id (afp_connection);
+  res = send_request_sync (g_io_stream_get_output_stream (priv->conn),
+                           DSI_OPEN_SESSION, req_id, 0,  0, NULL,
+                           cancellable, error);
+  if (!res)
+    return FALSE;
 
-  reply = read_reply_sync (afp_connection, &dsi_header, cancellable, error);
-  if (!reply)
-    goto error;
+  res = read_reply_sync (g_io_stream_get_input_stream (priv->conn),
+                         &dsi_header, &reply, cancellable, error);
+  if (!res)
+    return FALSE;
 
   pos = 0;
   while ((dsi_header.totalDataLength - pos) > 2)
@@ -430,12 +764,63 @@ g_vfs_afp_connection_new (GSocketConnectable *addr,
 
     pos += optionLength;
   }
+  g_free (reply);
+
+  return TRUE;
+}
+
+GVfsAfpReply *
+g_vfs_afp_connection_get_server_info (GVfsAfpConnection *afp_connection,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+  GVfsAfpConnectionPrivate *priv = afp_connection->priv;
   
-  return afp_connection;
+  GSocketClient *client;
+  GIOStream *conn;
+  gboolean res;
+  DSIHeader dsi_header;
+  char *data;
+
+  client = g_socket_client_new ();
+  conn = G_IO_STREAM (g_socket_client_connect (client, priv->addr, cancellable, error));
+  g_object_unref (client);
+
+  if (!conn)
+    return NULL;
   
-error:
-  g_object_unref (afp_connection);
-  return NULL;   
+  res = send_request_sync (g_io_stream_get_output_stream (conn), DSI_GET_STATUS,
+                           0, 0, 0, NULL, cancellable, error);
+  if (!res)
+  {
+    g_object_unref (conn);
+    return NULL;
+  }
+
+  res = read_reply_sync (g_io_stream_get_input_stream (conn), &dsi_header,
+                         &data, cancellable, error);
+  if (!res)
+  {
+    g_object_unref (conn);
+    return NULL;
+  }
+
+  return g_vfs_afp_reply_new (dsi_header.errorCode, data,
+                              dsi_header.totalDataLength);
+}
+
+GVfsAfpConnection *
+g_vfs_afp_connection_new (GSocketConnectable *addr)
+{
+  GVfsAfpConnection        *afp_connection;
+  GVfsAfpConnectionPrivate *priv;
+
+  afp_connection = g_object_new (G_VFS_TYPE_AFP_CONNECTION, NULL);
+  priv = afp_connection->priv;
+
+  priv->addr = g_object_ref (addr);
+
+  return afp_connection;
 }
 
 static void
@@ -447,11 +832,19 @@ g_vfs_afp_connection_init (GVfsAfpConnection *afp_connection)
                                                               G_VFS_TYPE_AFP_CONNECTION,
                                                               GVfsAfpConnectionPrivate);
 
+  priv->addr = NULL;
   priv->conn = NULL;
   priv->request_id = 0;
 
   priv->kRequestQuanta = -1;
   priv->kServerReplayCacheSize = -1;
+
+  priv->request_queue = g_queue_new ();
+  priv->request_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                              NULL, (GDestroyNotify)free_request_data);
+
+  priv->send_loop_running = FALSE;
+  priv->read_loop_running = FALSE;
 }
 
 static void
@@ -460,6 +853,9 @@ g_vfs_afp_connection_finalize (GObject *object)
   GVfsAfpConnection *afp_connection = (GVfsAfpConnection *)object;
   GVfsAfpConnectionPrivate *priv = afp_connection->priv;
 
+  if (priv->addr)
+    g_object_unref (priv->addr);
+  
   if (priv->conn)
     g_object_unref (priv->conn);
 
diff --git a/daemon/gvfsafpconnection.h b/daemon/gvfsafpconnection.h
index 47bbf0c..4c1d518 100644
--- a/daemon/gvfsafpconnection.h
+++ b/daemon/gvfsafpconnection.h
@@ -30,6 +30,7 @@ G_BEGIN_DECLS
 typedef enum
 {
   AFP_COMMAND_GET_SRVR_INFO = 15,
+  AFP_COMMAND_GET_SRVR_PARMS = 16,
   AFP_COMMAND_LOGIN = 18,
   AFP_COMMAND_WRITE = 33,
   AFP_COMMAND_WRITE_EXT = 61
@@ -38,6 +39,7 @@ typedef enum
 typedef enum
 {
   AFP_ERROR_NONE = 0,
+  AFP_ERROR_USER_NOT_AUTH = -5023,
   AFP_ERROR_NO_MORE_SESSIONS = -1068
 } AfpErrorCode;
 
@@ -76,10 +78,13 @@ typedef struct _GVfsAfpCommandClass GVfsAfpCommandClass;
 typedef struct _GVfsAfpCommand GVfsAfpCommand;
 
 
-GVfsAfpCommand* g_vfs_afp_command_new        (AfpCommandType type);
+GVfsAfpCommand* g_vfs_afp_command_new         (AfpCommandType type);
 
-void            g_vfs_afp_command_put_pascal (GVfsAfpCommand *command, char *str);
+void            g_vfs_afp_command_put_pascal  (GVfsAfpCommand *command, const char *str);
+void            g_vfs_afp_command_pad_to_even (GVfsAfpCommand *command);
 
+gsize           g_vfs_afp_command_get_size    (GVfsAfpCommand *command);
+char*           g_vfs_afp_command_get_data    (GVfsAfpCommand *command);
 
 GType           g_vfs_afp_command_get_type (void) G_GNUC_CONST;
 
@@ -90,11 +95,11 @@ GType           g_vfs_afp_command_get_type (void) G_GNUC_CONST;
  * GVfsAfpConnection
  */
 #define G_VFS_TYPE_AFP_CONNECTION             (g_vfs_afp_connection_get_type ())
-#define G_VFS_AFP_CONNECTION(obj)             (G_VFS_TYPE_CHECK_INSTANCE_CAST ((obj), G_VFS_TYPE_VFS_AFP_CONNECTION, GVfsAfpConnection))
-#define G_VFS_AFP_CONNECTION_CLASS(klass)     (G_VFS_TYPE_CHECK_CLASS_CAST ((klass), G_VFS_TYPE_VFS_AFP_CONNECTION, GVfsAfpConnectionClass))
-#define G_IS_VFS_AFP_CONNECTION(obj)          (G_VFS_TYPE_CHECK_INSTANCE_TYPE ((obj), G_VFS_TYPE_VFS_AFP_CONNECTION))
-#define G_IS_VFS_AFP_CONNECTION_CLASS(klass)  (G_VFS_TYPE_CHECK_CLASS_TYPE ((klass), G_VFS_TYPE_VFS_AFP_CONNECTION))
-#define G_VFS_AFP_CONNECTION_GET_CLASS(obj)   (G_VFS_TYPE_INSTANCE_GET_CLASS ((obj), G_VFS_TYPE_VFS_AFP_CONNECTION, GVfsAfpConnectionClass))
+#define G_VFS_AFP_CONNECTION(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_VFS_TYPE_AFP_CONNECTION, GVfsAfpConnection))
+#define G_VFS_AFP_CONNECTION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), G_VFS_TYPE_AFP_CONNECTION, GVfsAfpConnectionClass))
+#define G_IS_VFS_AFP_CONNECTION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_VFS_TYPE_AFP_CONNECTION))
+#define G_IS_VFS_AFP_CONNECTION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), G_VFS_TYPE_AFP_CONNECTION))
+#define G_VFS_AFP_CONNECTION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), G_VFS_TYPE_AFP_CONNECTION, GVfsAfpConnectionClass))
 
 typedef struct _GVfsAfpConnectionClass GVfsAfpConnectionClass;
 typedef struct _GVfsAfpConnection GVfsAfpConnection;
@@ -111,12 +116,24 @@ struct _GVfsAfpConnection
 
   GVfsAfpConnectionPrivate *priv;
 };
-  
+
+typedef void (*GVfsAfpConnectionReplyCallback) (GVfsAfpConnection *afp_connection,
+                                                GVfsAfpReply      *reply,
+                                                GError            *error,
+                                                gpointer           user_data);
+
+
 GType g_vfs_afp_connection_get_type (void) G_GNUC_CONST;
 
-GVfsAfpConnection* g_vfs_afp_connection_new               (GSocketConnectable *addr,
-                                                           GCancellable       *cancellable,
-                                                           GError             **error);
+GVfsAfpConnection* g_vfs_afp_connection_new               (GSocketConnectable *addr);
+
+GVfsAfpReply*      g_vfs_afp_connection_get_server_info   (GVfsAfpConnection *afp_connection,
+                                                           GCancellable *cancellable,
+                                                           GError **error);
+
+gboolean           g_vfs_afp_connection_open              (GVfsAfpConnection *afp_connection,
+                                                           GCancellable      *cancellable,
+                                                           GError            **error);
 
 gboolean           g_vfs_afp_connection_send_command_sync (GVfsAfpConnection *afp_connection,
                                                            GVfsAfpCommand    *afp_command,
@@ -126,6 +143,12 @@ gboolean           g_vfs_afp_connection_send_command_sync (GVfsAfpConnection *af
 GVfsAfpReply*      g_vfs_afp_connection_read_reply_sync   (GVfsAfpConnection *afp_connection,
                                                            GCancellable *cancellable,
                                                            GError **error);
+
+void               g_vfs_afp_connection_queue_command     (GVfsAfpConnection *afp_connection,
+                                                           GVfsAfpCommand    *command,
+                                                           GVfsAfpConnectionReplyCallback reply_cb,
+                                                           GCancellable      *cancellable,                                                           
+                                                           gpointer user_data);
 G_END_DECLS
 
 #endif /* _GVFSAFPCONNECTION_H_ */
diff --git a/daemon/gvfsbackendafp.c b/daemon/gvfsbackendafp.c
index 6185264..6a723ae 100644
--- a/daemon/gvfsbackendafp.c
+++ b/daemon/gvfsbackendafp.c
@@ -28,6 +28,7 @@
 #include <gio/gio.h>
 
 #include "gvfsjobmount.h"
+#include "gvfsjobenumerate.h"
 
 
 #include "gvfsbackendafp.h"
@@ -36,17 +37,272 @@
 
 G_DEFINE_TYPE (GVfsBackendAfp, g_vfs_backend_afp, G_VFS_TYPE_BACKEND);
 
+
+#define AFP_UAM_NO_USER   "No User Authent"
+#define AFP_UAM_DHX       "DHCAST128"
+#define AFP_UAM_DHX2      "DHX2"
+
+static const char *
+afp_version_to_string (AfpVersion afp_version)
+{
+  const char *version_strings[] = { "AFPX03", "AFP3.1", "AFP3.2", "AFP3.3" };
+
+  return version_strings[afp_version - 1];
+}
+
+static AfpVersion
+string_to_afp_version (const char *str)
+{
+  gint i;
+  
+  const char *version_strings[] = { "AFPX03", "AFP3.1", "AFP3.2", "AFP3.3" };
+
+  for (i = 0; i < G_N_ELEMENTS (version_strings); i++)
+  {
+    if (g_str_equal (str, version_strings[i]))
+      return i + 1;
+  }
+
+  return AFP_VERSION_INVALID;
+}
+
 static void
-do_unmount (GVfsBackend *   backend,
-            GVfsJobUnmount *job,
-            GMountUnmountFlags flags,
-            GMountSource *mount_source)
+get_srvr_parms_cb (GVfsAfpConnection *afp_connection,
+                   GVfsAfpReply      *reply,
+                   GError            *error,
+                   gpointer           user_data)
 {
-  GVfsBackendAfp *afp = G_VFS_BACKEND_AFP (backend);
+  GVfsJobEnumerate *job = G_VFS_JOB_ENUMERATE (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
-  g_object_unref (afp->conn);
+  AfpErrorCode error_code;
+  guint16 num_volumes, i;
+
+  if (!reply)
+  {
+    g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+    return;
+  }
+
+  error_code = g_vfs_afp_reply_get_error_code (reply);
+  if (error_code != AFP_ERROR_NONE)
+  {
+    g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR,
+                              G_IO_ERROR_FAILED, _("Volume enumeration failed"));
+    return;
+  }
+
+  /* server time */
+  (void)g_data_input_stream_read_int32 (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+
+  num_volumes = g_data_input_stream_read_uint16 (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+  for (i = 0; i < num_volumes; i++)
+  {
+    guint8 flags;
+    char *vol_name;
+
+    GFileInfo *info;
+    GMountSpec *mount_spec;
+    char *uri;
+
+    flags = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+    vol_name = g_vfs_afp_reply_read_pascal (reply);
+
+    info = g_file_info_new ();
+    
+    g_file_info_set_name (info, vol_name);
+    g_file_info_set_display_name (info, vol_name);
+    g_file_info_set_edit_name (info, vol_name);
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE);
+    g_file_info_set_content_type (info, "inode/directory");
+    g_file_info_set_file_type (info, G_FILE_TYPE_MOUNTABLE);
+
+    g_file_info_set_attribute_boolean (info, "afp::volume-password-protected", (flags & 0x01));
+    
+    mount_spec = g_mount_spec_new ("afp-volume");
+    g_mount_spec_set (mount_spec, "host",
+                      g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
+    g_mount_spec_set (mount_spec, "volume", vol_name);
+
+    if (g_mount_tracker_has_mount_spec (afp_backend->mount_tracker, mount_spec))
+    {
+      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, FALSE);
+      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, TRUE);
+    }
+    else
+    {
+      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, TRUE);
+      g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, FALSE);
+    }
+    g_mount_spec_unref (mount_spec);
+
+    uri = g_strdup_printf ("afp://%s/%s",
+                           g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)),
+                           vol_name);
+    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
+                                      uri);
+    g_free (uri);
+
+    g_vfs_job_enumerate_add_info (job, info);
+    g_object_unref (info);
+  }
+  
+  g_vfs_job_enumerate_done (job);
+}
+
+
+static gboolean
+is_root (const char *filename)
+{
+  const char *p;
+
+  p = filename;
+  while (*p == '/')
+    p++;
+
+  return *p == 0;
+}
+
+static gboolean
+try_enumerate (GVfsBackend *backend,
+               GVfsJobEnumerate *job,
+               const char *filename,
+               GFileAttributeMatcher *attribute_matcher,
+               GFileQueryInfoFlags flags)
+{
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
+
+  GVfsAfpCommand *comm;
+  
+  if (!is_root(filename))
+  {
+    g_vfs_job_failed (G_VFS_JOB (job),
+                      G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                      _("File doesn't exist"));
+    return TRUE;
+  }
+
+  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_SRVR_PARMS);
+  /* pad byte */
+  g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (comm), 0, NULL, NULL);
+
+  g_vfs_afp_connection_queue_command (afp_backend->conn, comm,
+                                      get_srvr_parms_cb,
+                                      G_VFS_JOB (job)->cancellable, job);
+
+  return TRUE;
 }
 
+static gboolean
+do_login (GVfsBackendAfp *afp_backend,
+          const char *username,
+          const char *password,
+          GCancellable *cancellable,
+          GError **error)
+{
+  /* anonymous login */
+  if (!username)
+  {
+    GVfsAfpCommand *comm;
+    GVfsAfpReply *reply;
+    guint32 error_code;
+    
+    if (!g_slist_find_custom (afp_backend->uams, AFP_UAM_NO_USER, g_str_equal))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                           _("Server \"%s\" doesn't support anonymous login"),
+                   afp_backend->server_name);
+      return FALSE;
+    }
+
+    comm = g_vfs_afp_command_new (AFP_COMMAND_LOGIN);
+
+    g_vfs_afp_command_put_pascal (comm,
+                                  afp_version_to_string (afp_backend->version));
+    g_vfs_afp_command_put_pascal (comm, AFP_UAM_NO_USER);
+    if (!g_vfs_afp_connection_send_command_sync (afp_backend->conn, comm,
+                                                 cancellable, error))
+      return FALSE;
+
+    reply = g_vfs_afp_connection_read_reply_sync (afp_backend->conn, cancellable, error);
+    if (!reply)
+      return FALSE;
+
+    error_code = g_vfs_afp_reply_get_error_code (reply);
+    g_object_unref (reply);
+    
+    if (error_code != AFP_ERROR_NONE)
+    {
+      if (error_code == AFP_ERROR_USER_NOT_AUTH)
+      {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                     _("Anonymous login on server \"%s\" failed"),
+                     afp_backend->server_name);
+        return FALSE;
+      }
+      else
+      {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                     _("Login to server \"%s\" failed"), afp_backend->server_name);
+        return FALSE;
+      }
+    }
+  }
+
+  else {
+
+    /* Diffie-Hellman 2 */
+    if (g_slist_find_custom (afp_backend->uams, AFP_UAM_DHX2, g_str_equal))
+    {
+      g_debug ("Diffie-Hellman 2\n");
+    }
+
+    /* Diffie-Hellman */
+    else if (g_slist_find_custom (afp_backend->uams, AFP_UAM_DHX, g_str_equal))
+    {
+      g_debug ("Diffie-Hellman\n");
+    }
+
+    else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   _("Login to server \"%s\" failed, no suitable authentication mechanism found"),
+                   afp_backend->server_name);
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+static gboolean
+try_query_info (GVfsBackend *backend,
+                GVfsJobQueryInfo *job,
+                const char *filename,
+                GFileQueryInfoFlags flags,
+                GFileInfo *info,
+                GFileAttributeMatcher *matcher)
+{
+  g_debug ("filename: %s\n", filename);
+  
+  if (is_root (filename))
+  {
+    GIcon *icon;
+    
+    g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+    g_file_info_set_name (info, "/");
+    g_file_info_set_display_name (info, g_vfs_backend_get_display_name (backend));
+    g_file_info_set_content_type (info, "inode/directory");
+    icon = g_vfs_backend_get_icon (backend);
+    if (icon != NULL)
+      g_file_info_set_icon (info, icon);
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+  }
+  else
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+  
+  return TRUE;
+}
 static void
 do_mount (GVfsBackend *backend,
           GVfsJobMount *job,
@@ -56,37 +312,26 @@ do_mount (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
 
-  GError *err;
-  GVfsAfpCommand *command;
+  gboolean res;
+  GError *err = NULL;
   GVfsAfpReply *reply;
 
   guint16 MachineType_offset, AFPVersionCount_offset, UAMCount_offset;
   guint16 VolumeIconAndMask_offset, Flags;
-  char *ServerName;
-
-  /* UAM */
-  guint8 UAMCount;
-  GSList *UAMS = NULL;
+  guint8 count;
   guint i;
 
-  err = NULL;
-  afp_backend->conn = g_vfs_afp_connection_new (afp_backend->addr,
-                                                G_VFS_JOB (job)->cancellable,
-                                                &err);
-  if (!afp_backend->conn)
-    goto error;
-
-  command = g_vfs_afp_command_new (AFP_COMMAND_GET_SRVR_INFO);
-  if (!g_vfs_afp_connection_send_command_sync (afp_backend->conn, command,
-                                               G_VFS_JOB (job)->cancellable, &err))
-    goto error;
+  GMountSpec *afp_mount_spec;
+  char       *display_name;
 
-  reply = g_vfs_afp_connection_read_reply_sync (afp_backend->conn,
+  afp_backend->conn = g_vfs_afp_connection_new (afp_backend->addr);
+  
+  reply = g_vfs_afp_connection_get_server_info (afp_backend->conn,
                                                 G_VFS_JOB (job)->cancellable,
                                                 &err);
   if (!reply)
     goto error;
-
+  
   MachineType_offset =
     g_data_input_stream_read_uint16 (G_DATA_INPUT_STREAM (reply), NULL, NULL);
   AFPVersionCount_offset = 
@@ -98,86 +343,75 @@ do_mount (GVfsBackend *backend,
   Flags =
     g_data_input_stream_read_uint16 (G_DATA_INPUT_STREAM (reply), NULL, NULL);
   
-  ServerName = g_vfs_afp_reply_read_pascal (reply);
-  g_debug ("ServerName: %s\n", ServerName);
-  g_free (ServerName);
+  afp_backend->server_name = g_vfs_afp_reply_read_pascal (reply);
 
   /* Parse Versions */
   g_vfs_afp_reply_seek (reply, AFPVersionCount_offset, G_SEEK_SET);
-  UAMCount =
-    g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (reply), NULL, NULL);
-  g_debug ("Versions\n");
-  for (i = 0; i < UAMCount; i++)
+  count = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+  for (i = 0; i < count; i++)
   {
     char *version;
+    AfpVersion afp_version;
 
     version = g_vfs_afp_reply_read_pascal (reply);
-    g_debug ("%d. %s\n",i + 1, version);
-    g_free (version);
+    afp_version = string_to_afp_version (version);
+    if (afp_version > afp_backend->version)
+      afp_backend->version = afp_version;
+  }
+
+  if (afp_backend->version == AFP_VERSION_INVALID)
+  {
+    g_object_unref (reply);
+    g_vfs_job_failed (G_VFS_JOB (job),
+                      G_IO_ERROR, G_IO_ERROR_FAILED,
+                      _("Failed to connect to server (%s)"), "Server doesn't support AFP version 3.0 or later");
+    return;
   }
   
   /* Parse UAMs */
   g_vfs_afp_reply_seek (reply, UAMCount_offset, G_SEEK_SET);
-  UAMCount =
-    g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (reply), NULL, NULL);
-  for (i = 0; i < UAMCount; i++)
+  count = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (reply), NULL, NULL);
+  for (i = 0; i < count; i++)
   {
-    char *UAM;
+    char *uam;
 
-    UAM = g_vfs_afp_reply_read_pascal (reply);
-    UAMS = g_slist_prepend (UAMS, UAM);
+    uam = g_vfs_afp_reply_read_pascal (reply);
+    g_debug ("%s\n", uam);
+    afp_backend->uams = g_slist_prepend (afp_backend->uams, uam);
   }
-
   g_object_unref (reply);
 
 
-  /* Anonymous login */
-  if (!afp_backend->user)
-  {
-    if (!g_slist_find_custom (UAMS, "No User Authent", g_str_equal))
-    {
-      g_slist_free_full (UAMS, g_free);
-      
-      g_vfs_job_failed (G_VFS_JOB (job),
-			  G_IO_ERROR, G_IO_ERROR_FAILED,
-			  _("Failed to mount AFP share (%s)"), "Server doesn't support anonymous login");
-      return;
-    }
+  /* Open connection */
+  res = g_vfs_afp_connection_open (afp_backend->conn, G_VFS_JOB (job)->cancellable,
+                                   &err);
+  if (!res)
+    goto error;
 
-    
-  }
-  else
-  {
-    if (g_slist_find_custom (UAMS, "DHX2", g_str_equal))
-    {
-      g_debug ("Diffie-Hellman 2\n");
-    }
+  res = do_login (afp_backend, NULL, NULL, G_VFS_JOB (job)->cancellable,
+                  &err);
+  if (!res)
+    goto error;
 
-    else if (g_slist_find_custom (UAMS, "DHCAST128", g_str_equal))
-    {
-      g_debug ("Diffie-Hellman");
-    }
+  /* set mount info */
+  afp_mount_spec = g_mount_spec_new ("afp");
+  g_mount_spec_set (afp_mount_spec, "host",
+                    g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
+  if (afp_backend->user)
+    g_mount_spec_set (afp_mount_spec, "user", afp_backend->user);
 
-    else if (g_slist_find_custom (UAMS, "No User Authent", g_str_equal))
-    {
-      g_debug ("Anonymous login");
-    }
+  g_vfs_backend_set_mount_spec (backend, afp_mount_spec);
+  g_mount_spec_unref (afp_mount_spec);
+  
+  display_name = g_strdup_printf (_("AFP shares on %s"), afp_backend->server_name);
+  g_vfs_backend_set_display_name (backend, display_name);
+  g_free (display_name);
 
-    else
-    {
-      g_slist_free_full (UAMS, g_free);
-      
-      g_vfs_job_failed (G_VFS_JOB (job),
-                        G_IO_ERROR, G_IO_ERROR_FAILED,
-                        _("Failed to mount AFP share (%s)"),
-                        "No suitable authentication mechanism found");
-      return;
-    } 
-  }
+  g_vfs_backend_set_icon_name (backend, "network-server-afp");
 
-  g_slist_free_full (UAMS, g_free);
-  
+    
   g_vfs_job_succeeded (G_VFS_JOB (job));
+  return;
 
 error:
   g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -221,14 +455,23 @@ try_mount (GVfsBackend *backend,
 static void
 g_vfs_backend_afp_init (GVfsBackendAfp *object)
 {
-	/* TODO: Add initialization code here */
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (object);
+
+  afp_backend->mount_tracker = g_mount_tracker_new (NULL);
+  
+  afp_backend->server_name = NULL;
+  afp_backend->uams = NULL;
+  afp_backend->version = AFP_VERSION_INVALID;
 }
 
 static void
 g_vfs_backend_afp_finalize (GObject *object)
 {
-	/* TODO: Add deinitalization code here */
-
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (object);
+  
+  g_free (afp_backend->server_name);
+  g_slist_free_full (afp_backend->uams, g_free);
+  
 	G_OBJECT_CLASS (g_vfs_backend_afp_parent_class)->finalize (object);
 }
 
@@ -242,6 +485,7 @@ g_vfs_backend_afp_class_init (GVfsBackendAfpClass *klass)
 
 	backend_class->try_mount = try_mount;
   backend_class->mount = do_mount;
-  backend_class->unmount = do_unmount;
+  backend_class->try_query_info = try_query_info;
+  backend_class->try_enumerate = try_enumerate;
 }
 
diff --git a/daemon/gvfsbackendafp.h b/daemon/gvfsbackendafp.h
index ac9cd12..03d73bd 100644
--- a/daemon/gvfsbackendafp.h
+++ b/daemon/gvfsbackendafp.h
@@ -25,10 +25,21 @@
 
 #include <gvfsbackend.h>
 
+#include "gmounttracker.h"
+
 #include "gvfsafpconnection.h"
 
 G_BEGIN_DECLS
 
+typedef enum
+{
+  AFP_VERSION_INVALID,
+  AFP_VERSION_3_0,
+  AFP_VERSIOM_3_1,
+  AFP_VERSION_3_2,
+  AFP_VERSION_3_3
+} AfpVersion;
+
 #define G_VFS_TYPE_BACKEND_AFP             (g_vfs_backend_afp_get_type ())
 #define G_VFS_BACKEND_AFP(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_VFS_TYPE_BACKEND_AFP, GVfsBackendAfp))
 #define G_VFS_BACKEND_AFP_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), G_VFS_TYPE_BACKEND_AFP, GVfsBackendAfpClass))
@@ -51,7 +62,12 @@ struct _GVfsBackendAfp
 	GSocketConnectable *addr;
 	char               *user;
 
+  GMountTracker      *mount_tracker;
   GVfsAfpConnection  *conn;
+
+  char                *server_name;
+  GSList              *uams;
+  AfpVersion          version;
 };
 
 GType g_vfs_backend_afp_get_type (void) G_GNUC_CONST;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]