updates.
[silc.git] / lib / silcclient / client.c
index 8fa87ad8d7282542836264ec568340c90838df23..617682e17030e23d5c42587e99725b79f8166c50 100644 (file)
@@ -2,7 +2,7 @@
 
   client.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
   Copyright (C) 1997 - 2001 Pekka Riikonen
 
@@ -34,6 +34,10 @@ static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
 static void silc_client_packet_parse_type(SilcClient client, 
                                          SilcSocketConnection sock,
                                          SilcPacketContext *packet);
+void silc_client_resolve_auth_method(bool success,
+                                    SilcProtocolAuthMeth auth_meth,
+                                    const unsigned char *auth_data,
+                                    uint32 auth_data_len, void *context);
 
 /* Allocates new client object. This has to be done before client may
    work. After calling this one must call silc_client_init to initialize
@@ -56,9 +60,18 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
   if (params)
     memcpy(new_client->params, params, sizeof(*params));
 
+  if (!new_client->params->task_max)
+    new_client->params->task_max = 200;
+
   if (!new_client->params->rekey_secs)
     new_client->params->rekey_secs = 3600;
 
+  if (!new_client->params->connauth_request_secs)
+    new_client->params->connauth_request_secs = 2;
+
+  new_client->params->
+    nickname_format[sizeof(new_client->params->nickname_format) - 1] = 0;
+
   return new_client;
 }
 
@@ -100,9 +113,8 @@ int silc_client_init(SilcClient client)
   silc_client_protocols_register();
 
   /* Initialize the scheduler */
-  client->schedule = silc_schedule_init(&client->io_queue, 
-                                       &client->timeout_queue, 
-                                       &client->generic_queue, 5000);
+  client->schedule = silc_schedule_init(client->params->task_max ?
+                                       client->params->task_max : 200);
   if (!client->schedule)
     return FALSE;
 
@@ -116,9 +128,6 @@ void silc_client_stop(SilcClient client)
 {
   SILC_LOG_DEBUG(("Stopping client"));
 
-  /* Stop the scheduler, although it might be already stopped. This
-     doesn't hurt anyone. This removes all the tasks and task queues,
-     as well. */
   silc_schedule_stop(client->schedule);
   silc_schedule_uninit(client->schedule);
 
@@ -139,6 +148,12 @@ void silc_client_run(SilcClient client)
   silc_schedule(client->schedule);
 }
 
+static void silc_client_entry_destructor(SilcIDCache cache,
+                                        SilcIDCacheEntry entry)
+{
+  silc_free(entry->name);
+}
+
 /* Allocates and adds new connection to the client. This adds the allocated
    connection to the connection table and returns a pointer to it. A client
    can have multiple connections to multiple servers. Every connection must
@@ -158,7 +173,8 @@ SilcClientConnection silc_client_add_connection(SilcClient client,
   conn = silc_calloc(1, sizeof(*conn));
 
   /* Initialize ID caches */
-  conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT, NULL);
+  conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT, 
+                                         silc_client_entry_destructor);
   conn->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
   conn->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
   conn->client = client;
@@ -251,19 +267,18 @@ silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
   /* XXX In the future we should give up this non-blocking connect all
      together and use threads instead. */
   /* Create connection to server asynchronously */
-  sock = silc_net_create_connection_async(ctx->port, ctx->host);
+  sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
   if (sock < 0)
     return -1;
 
   /* Register task that will receive the async connect and will
      read the result. */
-  ctx->task = silc_task_register(ctx->client->io_queue, sock, 
-                                silc_client_connect_to_server_start,
-                                (void *)ctx, 0, 0, 
-                                SILC_TASK_FD,
-                                SILC_TASK_PRI_NORMAL);
-  silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
-  silc_schedule_set_listen_fd(ctx->client->schedule, sock, ctx->task->iomask);
+  ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, 
+                                    silc_client_connect_to_server_start,
+                                    (void *)ctx, 0, 0, 
+                                    SILC_TASK_FD,
+                                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
 
   ctx->sock = sock;
 
@@ -316,9 +331,9 @@ int silc_client_connect_to_server(SilcClient client, int port,
    used only if the application performed the connecting outside the library.
    The library however may use this internally. */
 
-int silc_client_start_key_exchange(SilcClient client,
-                                  SilcClientConnection conn,
-                                   int fd)
+bool silc_client_start_key_exchange(SilcClient client,
+                                   SilcClientConnection conn,
+                                   int fd)
 {
   SilcProtocol protocol;
   SilcClientKEInternalContext *proto_ctx;
@@ -327,6 +342,12 @@ int silc_client_start_key_exchange(SilcClient client,
   /* Allocate new socket connection object */
   silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
 
+  /* Sometimes when doing quick reconnects the new socket may be same as
+     the old one and there might be pending stuff for the old socket. 
+     If new one is same then those pending sutff might cause problems.
+     Make sure they do not do that. */
+  silc_schedule_task_del_by_fd(client->schedule, fd);
+
   conn->nickname = strdup(client->username);
   conn->sock->hostname = conn->remote_host;
   conn->sock->ip = strdup(conn->remote_host);
@@ -349,7 +370,7 @@ int silc_client_start_key_exchange(SilcClient client,
                      silc_client_connect_to_server_second);
   if (!protocol) {
     client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                    "Error: Could not start authentication protocol");
+                    "Error: Could not start key exchange protocol");
     return FALSE;
   }
   conn->sock->protocol = protocol;
@@ -364,7 +385,7 @@ int silc_client_start_key_exchange(SilcClient client,
   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
 
   /* Execute the protocol */
-  silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
   return TRUE;
 }
 
@@ -396,7 +417,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
       /* Unregister old connection try */
       silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
-      silc_task_unregister(client->io_queue, ctx->task);
+      silc_schedule_task_del(client->schedule, ctx->task);
 
       /* Try again */
       silc_client_connect_to_server_internal(ctx);
@@ -408,7 +429,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
                       ctx->host, strerror(opt));
       silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
-      silc_task_unregister(client->io_queue, ctx->task);
+      silc_schedule_task_del(client->schedule, ctx->task);
       silc_free(ctx);
 
       /* Notify application of failure */
@@ -419,7 +440,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   }
 
   silc_schedule_unset_listen_fd(client->schedule, fd);
-  silc_task_unregister(client->io_queue, ctx->task);
+  silc_schedule_task_del(client->schedule, ctx->task);
   silc_free(ctx);
 
   if (!silc_client_start_key_exchange(client, conn, fd)) {
@@ -481,13 +502,6 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
 
-  /* Resolve the authentication method to be used in this connection */
-  if (!client->ops->get_auth_method(client, sock->user_data, sock->hostname,
-                                   sock->port, &proto_ctx->auth_meth,
-                                   &proto_ctx->auth_data, 
-                                   &proto_ctx->auth_data_len))
-    proto_ctx->auth_meth = SILC_AUTH_NONE;
-
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
   if (ctx->packet)
@@ -495,13 +509,46 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   silc_free(ctx);
   sock->protocol = NULL;
 
+  /* Resolve the authentication method to be used in this connection. The
+     completion callback is called after the application has resolved
+     the authentication method. */
+  client->ops->get_auth_method(client, sock->user_data, sock->hostname,
+                              sock->port, silc_client_resolve_auth_method,
+                              proto_ctx);
+}
+
+/* Authentication method resolving callback. Application calls this function
+   after we've called the client->ops->get_auth_method client operation
+   to resolve the authentication method. We will continue the executiong
+   of the protocol in this function. */
+
+void silc_client_resolve_auth_method(bool success,
+                                    SilcProtocolAuthMeth auth_meth,
+                                    const unsigned char *auth_data,
+                                    uint32 auth_data_len, void *context)
+{
+  SilcClientConnAuthInternalContext *proto_ctx =
+    (SilcClientConnAuthInternalContext *)context;
+  SilcClient client = (SilcClient)proto_ctx->client;
+
+  if (!success)
+    auth_meth = SILC_AUTH_NONE;
+
+  proto_ctx->auth_meth = auth_meth;
+
+  if (auth_data && auth_data_len) {
+    proto_ctx->auth_data = silc_calloc(auth_data_len, sizeof(*auth_data));
+    memcpy(proto_ctx->auth_data, auth_data, auth_data_len);
+    proto_ctx->auth_data_len = auth_data_len;
+  }
+
   /* Allocate the authenteication protocol and execute it. */
   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, 
-                     &sock->protocol, (void *)proto_ctx, 
+                     &proto_ctx->sock->protocol, (void *)proto_ctx, 
                      silc_client_connect_to_server_final);
 
   /* Execute the protocol */
-  silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+  silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
 }
 
 /* Finalizes the connection to the remote SILC server. This is called
@@ -569,10 +616,10 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   /* Register re-key timeout */
   conn->rekey->timeout = client->params->rekey_secs;
   conn->rekey->context = (void *)client;
-  silc_task_register(client->timeout_queue, conn->sock->sock, 
-                    silc_client_rekey_callback,
-                    (void *)conn->sock, conn->rekey->timeout, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(client->schedule, conn->sock->sock, 
+                        silc_client_rekey_callback,
+                        (void *)conn->sock, conn->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
   silc_protocol_free(protocol);
   if (ctx->auth_data)
@@ -590,14 +637,17 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 
 int silc_client_packet_send_real(SilcClient client,
                                 SilcSocketConnection sock,
-                                bool force_send,
-                                bool flush)
+                                bool force_send)
 {
   int ret;
 
   /* If rekey protocol is active we must assure that all packets are
      sent through packet queue. */
-  if (flush == FALSE && SILC_CLIENT_IS_REKEY(sock))
+  if (SILC_CLIENT_IS_REKEY(sock))
+    force_send = FALSE;
+
+  /* If outbound data is already pending do not force send */
+  if (SILC_IS_OUTBUF_PENDING(sock))
     force_send = FALSE;
 
   /* Send the packet */
@@ -639,18 +689,23 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 
   /* Packet sending */
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    ret = silc_client_packet_send_real(client, sock, TRUE, TRUE);
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
+
+    /* Error */
+    if (ret == -1)
+      return;
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
@@ -665,8 +720,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 
   /* Packet receiving */
   if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
-
     /* Read data from network */
     ret = silc_packet_receive(sock);
     if (ret < 0)
@@ -786,11 +839,11 @@ void silc_client_packet_parse(SilcPacketParserContext *parser_context)
   SilcClient client = (SilcClient)parser_context->context;
 
   /* Parse the packet */
-  silc_task_register(client->timeout_queue, parser_context->sock->sock, 
-                    silc_client_packet_parse_real,
-                    (void *)parser_context, 0, 1, 
-                    SILC_TASK_TIMEOUT,
-                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(client->schedule, parser_context->sock->sock, 
+                        silc_client_packet_parse_real,
+                        (void *)parser_context, 0, 1, 
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 }
 
 /* Parses the packet type and calls what ever routines the packet type
@@ -817,7 +870,7 @@ void silc_client_packet_parse_type(SilcClient client,
      * success message is for whatever protocol is executing currently.
      */
     if (sock->protocol)
-      silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+      silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
     break;
   case SILC_PACKET_FAILURE:
     /*
@@ -892,7 +945,7 @@ void silc_client_packet_parse_type(SilcClient client,
        break;
 
       /* Let the protocol handle the packet */
-      silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+      silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -914,7 +967,7 @@ void silc_client_packet_parse_type(SilcClient client,
        proto_ctx->packet = silc_packet_context_dup(packet);
 
        /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
       } else {
        SilcClientKEInternalContext *proto_ctx = 
          (SilcClientKEInternalContext *)sock->protocol->context;
@@ -930,7 +983,7 @@ void silc_client_packet_parse_type(SilcClient client,
          break;
        
        /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
@@ -952,7 +1005,7 @@ void silc_client_packet_parse_type(SilcClient client,
        proto_ctx->packet = silc_packet_context_dup(packet);
 
        /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
       } else {
        SilcClientKEInternalContext *proto_ctx = 
          (SilcClientKEInternalContext *)sock->protocol->context;
@@ -968,7 +1021,7 @@ void silc_client_packet_parse_type(SilcClient client,
          break;
        
        /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
@@ -1033,10 +1086,10 @@ void silc_client_packet_parse_type(SilcClient client,
 
       /* Let the protocol handle the packet */
       if (proto_ctx->responder == FALSE)
-       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
       else
        /* Let the protocol handle the packet */
-       silc_protocol_execute(sock->protocol, client->timeout_queue, 
+       silc_protocol_execute(sock->protocol, client->schedule, 
                              0, 100000);
     } else {
       SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
@@ -1044,6 +1097,20 @@ void silc_client_packet_parse_type(SilcClient client,
     }
     break;
 
+  case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+    /*
+     * Reveived reply to our connection authentication method request
+     * packet. This is used to resolve the authentication method for the
+     * current session from the server if the client does not know it.
+     */
+    silc_client_connection_auth_request(client, sock, packet);
+    break;
+
+  case SILC_PACKET_FTP:
+    /* Received file transfer packet. */
+    silc_client_ftp(client, sock, packet);
+    break;
+
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1142,7 +1209,23 @@ void silc_client_packet_send(SilcClient client,
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send, FALSE);
+  silc_client_packet_send_real(client, sock, force_send);
+}
+
+void silc_client_packet_queue_purge(SilcClient client,
+                                   SilcSocketConnection sock)
+{
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
 }
 
 /* Closes connection to remote end. Free's all allocated data except
@@ -1168,8 +1251,8 @@ void silc_client_close_connection(SilcClient client,
   silc_schedule_unset_listen_fd(client->schedule, sock->sock);
 
   /* Unregister all tasks */
-  silc_task_unregister_by_fd(client->io_queue, sock->sock);
-  silc_task_unregister_by_fd(client->timeout_queue, sock->sock);
+  silc_schedule_task_del_by_fd(client->schedule, sock->sock);
+  silc_schedule_task_del_by_fd(client->schedule, sock->sock);
 
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
@@ -1181,21 +1264,50 @@ void silc_client_close_connection(SilcClient client,
        sock->protocol->protocol->type == 
        SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
       sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      silc_protocol_execute_final(sock->protocol, client->timeout_queue);
-      sock->protocol = NULL;
+      silc_protocol_execute_final(sock->protocol, client->schedule);
       /* The application will recall this function with these protocols
         (the ops->connect client operation). */
       return;
     } else {
       sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      silc_protocol_execute_final(sock->protocol, client->timeout_queue);
+      silc_protocol_execute_final(sock->protocol, client->schedule);
       sock->protocol = NULL;
     }
   }
 
   /* Free everything */
   if (del && sock->user_data) {
-    /* XXX Free all client entries and channel entries. */
+    /* Free all cache entries */
+    SilcIDCacheList list;
+    SilcIDCacheEntry entry;
+    bool ret;
+
+    if (silc_idcache_get_all(conn->client_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_client(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
+      }
+      silc_idcache_list_free(list);
+    }
+
+    if (silc_idcache_get_all(conn->channel_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_channel(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
+      }
+      silc_idcache_list_free(list);
+    }
+
+    if (silc_idcache_get_all(conn->server_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_server(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
+      }
+      silc_idcache_list_free(list);
+    }
 
     /* Clear ID caches */
     if (conn->client_cache)
@@ -1205,11 +1317,9 @@ void silc_client_close_connection(SilcClient client,
     if (conn->server_cache)
       silc_idcache_del_all(conn->server_cache);
 
-    /* Free data */
+    /* Free data (my ID is freed in above silc_client_del_client) */
     if (conn->remote_host)
       silc_free(conn->remote_host);
-    if (conn->local_id)
-      silc_free(conn->local_id);
     if (conn->local_id_data)
       silc_free(conn->local_id_data);
     if (conn->send_key)
@@ -1299,18 +1409,16 @@ void silc_client_receive_new_id(SilcClient client,
     conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
 
   conn->local_entry->nickname = conn->nickname;
-  if (!conn->local_entry->username) {
-    conn->local_entry->username = 
-      silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
-                 sizeof(conn->local_entry->username));
-    sprintf(conn->local_entry->username, "%s@%s", client->username,
-           client->hostname);
-  }
+  if (!conn->local_entry->username)
+    conn->local_entry->username = strdup(client->username);
+  if (!conn->local_entry->hostname)
+    conn->local_entry->hostname = strdup(client->hostname);
   conn->local_entry->server = strdup(conn->remote_host);
   conn->local_entry->id = conn->local_id;
+  conn->local_entry->valid = TRUE;
   
   /* Put it to the ID cache */
-  silc_idcache_add(conn->client_cache, conn->nickname, conn->local_id, 
+  silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id, 
                   (void *)conn->local_entry, FALSE);
 
   /* Issue INFO command to fetch the real server name and server information
@@ -1349,8 +1457,8 @@ SilcChannelEntry silc_client_new_channel_id(SilcClient client,
   silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
 
   /* Put it to the ID cache */
-  silc_idcache_add(conn->channel_cache, channel_name, (void *)channel->id
-                  (void *)channel, FALSE);
+  silc_idcache_add(conn->channel_cache, channel->channel_name
+                  (void *)channel->id, (void *)channel, FALSE);
 
   return channel;
 }
@@ -1483,13 +1591,13 @@ SILC_TASK_CALLBACK(silc_client_rekey_callback)
   sock->protocol = protocol;
       
   /* Run the protocol */
-  silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
 
   /* Re-register re-key timeout */
-  silc_task_register(client->timeout_queue, sock->sock, 
-                    silc_client_rekey_callback,
-                    context, conn->rekey->timeout, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add(client->schedule, sock->sock, 
+                        silc_client_rekey_callback,
+                        context, conn->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* The final callback for the REKEY protocol. This will actually take the
@@ -1508,7 +1616,7 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    silc_protocol_cancel(protocol, client->timeout_queue);
+    silc_protocol_cancel(protocol, client->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
     if (ctx->packet)
@@ -1520,6 +1628,10 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
     return;
   }
 
+  /* Purge the outgoing data queue to assure that all rekey packets really
+     go to the network before we quit the protocol. */
+  silc_client_packet_queue_purge(client, sock);
+
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
@@ -1530,3 +1642,135 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
   silc_socket_free(ctx->sock);
   silc_free(ctx);
 }
+
+/* Processes incoming connection authentication method request packet.
+   It is a reply to our previously sent request. The packet can be used
+   to resolve the authentication method for the current session if the
+   client does not know it beforehand. */
+
+void silc_client_connection_auth_request(SilcClient client,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  uint16 conn_type, auth_meth;
+  int ret;
+
+  /* If we haven't send our request then ignore this one. */
+  if (!conn->connauth)
+    return;
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_SHORT(&conn_type),
+                            SILC_STR_UI_SHORT(&auth_meth),
+                            SILC_STR_END);
+  if (ret == -1)
+    auth_meth = SILC_AUTH_NONE;
+
+  /* Call the request callback to notify application for received 
+     authentication method information. */
+  if (conn->connauth->callback)
+    (*conn->connauth->callback)(client, conn, auth_meth,
+                               conn->connauth->context);
+
+  silc_schedule_task_del(client->schedule, conn->connauth->timeout);
+
+  silc_free(conn->connauth);
+  conn->connauth = NULL;
+}
+
+/* Timeout task callback called if the server does not reply to our 
+   connection authentication method request in the specified time interval. */
+
+SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
+{
+  SilcClientConnection conn = (SilcClientConnection)context;
+  SilcClient client = conn->client;
+
+  if (!conn->connauth)
+    return;
+
+  /* Call the request callback to notify application */
+  if (conn->connauth->callback)
+    (*conn->connauth->callback)(client, conn, SILC_AUTH_NONE,
+                               conn->connauth->context);
+
+  silc_free(conn->connauth);
+  conn->connauth = NULL;
+}
+
+/* This function can be used to request the current authentication method
+   from the server. This may be called when connecting to the server
+   and the client library requests the authentication data from the
+   application. If the application does not know the current authentication
+   method it can request it from the server using this function.
+   The `callback' with `context' will be called after the server has
+   replied back with the current authentication method. */
+
+void 
+silc_client_request_authentication_method(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcConnectionAuthRequest callback,
+                                         void *context)
+{
+  SilcClientConnAuthRequest connauth;
+  SilcBuffer packet;
+
+  connauth = silc_calloc(1, sizeof(*connauth));
+  connauth->callback = callback;
+  connauth->context = context;
+
+  if (conn->connauth)
+    silc_free(conn->connauth);
+
+  conn->connauth = connauth;
+
+  /* Assemble the request packet and send it to the server */
+  packet = silc_buffer_alloc(4);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
+                    SILC_STR_UI_SHORT(SILC_AUTH_NONE),
+                    SILC_STR_END);
+  silc_client_packet_send(client, conn->sock, 
+                         SILC_PACKET_CONNECTION_AUTH_REQUEST,
+                         NULL, 0, NULL, NULL, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+
+  /* Register a timeout in case server does not reply anything back. */
+  connauth->timeout =
+    silc_schedule_task_add(client->schedule, conn->sock->sock, 
+                          silc_client_request_authentication_method_timeout,
+                          conn, client->params->connauth_request_secs, 0,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* Called when file transfer packet is received. This will parse the
+   packet and give it to the file transfer protocol. */
+
+void silc_client_ftp(SilcClient client,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  uint8 type;
+  int ret;
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_CHAR(&type),
+                            SILC_STR_END);
+  if (ret == -1)
+    return;
+
+  /* We support only type number 1 (== SFTP) */
+  if (type != 1)
+    return;
+
+  silc_buffer_pull(packet->buffer, 1);
+
+  /* Give it to the file transfer protocol processor. */
+  //silc_sftp_client_receive_process(xxx, sock, packet);
+}