updates.
[silc.git] / lib / silcclient / client.c
index 583d0221f9216325078e1840c3d18259b0989576..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,19 +34,43 @@ 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
    the client. The `application' is application specific user data pointer
    and caller must free it. */
 
-SilcClient silc_client_alloc(SilcClientOperations *ops, void *application)
+SilcClient silc_client_alloc(SilcClientOperations *ops, 
+                            SilcClientParams *params,
+                            void *application,
+                            const char *silc_version)
 {
   SilcClient new_client;
 
   new_client = silc_calloc(1, sizeof(*new_client));
   new_client->application = application;
   new_client->ops = ops;
+  new_client->silc_client_version = strdup(silc_version);
+  new_client->params = silc_calloc(1, sizeof(*new_client->params));
+
+  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;
 }
@@ -59,6 +83,8 @@ void silc_client_free(SilcClient client)
     if (client->rng)
       silc_rng_free(client->rng);
 
+    silc_free(client->silc_client_version);
+    silc_free(client->params);
     silc_free(client);
   }
 }
@@ -87,8 +113,10 @@ int silc_client_init(SilcClient client)
   silc_client_protocols_register();
 
   /* Initialize the scheduler */
-  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;
 
   return TRUE;
 }
@@ -100,11 +128,8 @@ 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();
-  silc_schedule_uninit();
+  silc_schedule_stop(client->schedule);
+  silc_schedule_uninit(client->schedule);
 
   silc_client_protocols_unregister();
 
@@ -120,7 +145,13 @@ void silc_client_run(SilcClient client)
 
   /* Start the scheduler, the heart of the SILC client. When this returns
      the program will be terminated. */
-  silc_schedule();
+  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
@@ -142,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;
@@ -235,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(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;
 
@@ -274,7 +305,7 @@ int silc_client_connect_to_server(SilcClient client, int port,
 
   conn = silc_client_add_connection(client, host, port, context);
 
-  client->ops->say(client, conn, 
+  client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
                   "Connecting to port %d of server %s", port, host);
 
   /* Allocate internal context for connection process. This is
@@ -300,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;
@@ -311,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);
@@ -332,8 +369,8 @@ int silc_client_start_key_exchange(SilcClient client,
                      &protocol, (void *)proto_ctx,
                      silc_client_connect_to_server_second);
   if (!protocol) {
-    client->ops->say(client, conn, 
-                    "Error: Could not start authentication protocol");
+    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                    "Error: Could not start key exchange protocol");
     return FALSE;
   }
   conn->sock->protocol = protocol;
@@ -348,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;
 }
 
@@ -366,31 +403,33 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   SILC_LOG_DEBUG(("Start"));
 
   /* Check the socket status as it might be in error */
-  getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
   if (opt != 0) {
     if (ctx->tries < 2) {
       /* Connection failed but lets try again */
-      client->ops->say(client, conn, "Could not connect to server %s: %s",
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Could not connect to server %s: %s",
                       ctx->host, strerror(opt));
-      client->ops->say(client, conn, 
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
                       "Connecting to port %d of server %s resumed", 
                       ctx->port, ctx->host);
 
       /* Unregister old connection try */
-      silc_schedule_unset_listen_fd(fd);
+      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);
       ctx->tries++;
     } else {
       /* Connection failed and we won't try anymore */
-      client->ops->say(client, conn, "Could not connect to server %s: %s",
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Could not connect to server %s: %s",
                       ctx->host, strerror(opt));
-      silc_schedule_unset_listen_fd(fd);
+      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 */
@@ -400,8 +439,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
     return;
   }
 
-  silc_schedule_unset_listen_fd(fd);
-  silc_task_unregister(client->io_queue, ctx->task);
+  silc_schedule_unset_listen_fd(client->schedule, fd);
+  silc_schedule_task_del(client->schedule, ctx->task);
   silc_free(ctx);
 
   if (!silc_client_start_key_exchange(client, conn, fd)) {
@@ -463,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)
@@ -477,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
@@ -549,12 +614,12 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
 
   /* Register re-key timeout */
-  conn->rekey->timeout = 3600; /* XXX hardcoded */
+  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)
@@ -572,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 */
@@ -591,7 +659,7 @@ int silc_client_packet_send_real(SilcClient client,
      This call sets the connection both for input and output (the input
      is set always and this call keeps the input setting, actually). 
      Actual data sending is performed by silc_client_packet_process. */
-  SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(sock->sock);
+  SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(client->schedule, sock->sock);
 
   /* Mark to socket that data is pending in outgoing buffer. This flag
      is needed if new data is added to the buffer before the earlier
@@ -621,24 +689,29 @@ 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 
        available for this connection it will be set for output as well. 
        This call clears the output setting and sets it only for input. */
-    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(fd);
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, fd);
     SILC_UNSET_OUTBUF_PENDING(sock);
 
     silc_buffer_clear(sock->outbuf);
@@ -647,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)
@@ -768,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
@@ -799,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:
     /*
@@ -874,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."));
@@ -896,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;
@@ -912,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 "
@@ -934,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;
@@ -950,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 "
@@ -1015,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 "
@@ -1026,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;
@@ -1124,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
@@ -1147,31 +1248,78 @@ void silc_client_close_connection(SilcClient client,
     sock = conn->sock;
 
   /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
+  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);
 
+  /* Cancel any active protocol */
+  if (sock->protocol) {
+    if (sock->protocol->protocol->type == 
+       SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == 
+       SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      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->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);
+    }
 
-    client->ops->say(client, sock->user_data,
-                    "Closed connection to host %s", sock->hostname);
+    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);
+    }
 
-    /* Clear ID caches */
-    silc_idcache_del_all(conn->client_cache);
-    silc_idcache_del_all(conn->channel_cache);
+    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);
+    }
 
-    /* Free data */
+    /* Clear ID caches */
+    if (conn->client_cache)
+      silc_idcache_del_all(conn->client_cache);
+    if (conn->channel_cache)
+      silc_idcache_del_all(conn->channel_cache);
+    if (conn->server_cache)
+      silc_idcache_del_all(conn->server_cache);
+
+    /* 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)
@@ -1185,27 +1333,10 @@ void silc_client_close_connection(SilcClient client,
     if (conn->rekey)
       silc_free(conn->rekey);
 
-    conn->sock = NULL;
-    conn->remote_port = 0;
-    conn->remote_type = 0;
-    conn->send_key = NULL;
-    conn->receive_key = NULL;
-    conn->hmac_send = NULL;
-    conn->hmac_receive = NULL;
-    conn->local_id = NULL;
-    conn->local_id_data = NULL;
-    conn->remote_host = NULL;
-    conn->current_channel = NULL;
-    conn->pending_commands = NULL;
-    conn->rekey = NULL;
-
+    memset(conn, 0, sizeof(*conn));
     silc_client_del_connection(client, conn);
   }
 
-  if (sock->protocol) {
-    silc_protocol_free(sock->protocol);
-    sock->protocol = NULL;
-  }
   silc_socket_free(sock);
 }
 
@@ -1223,7 +1354,7 @@ void silc_client_disconnected_by_server(SilcClient client,
 
   msg = silc_calloc(message->len + 1, sizeof(char));
   memcpy(msg, message->data, message->len);
-  client->ops->say(client, sock->user_data, msg);
+  client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
   silc_free(msg);
 
   SILC_SET_DISCONNECTED(sock);
@@ -1241,7 +1372,7 @@ void silc_client_error_by_server(SilcClient client,
 
   msg = silc_calloc(message->len + 1, sizeof(char));
   memcpy(msg, message->data, message->len);
-  client->ops->say(client, sock->user_data, msg);
+  client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
   silc_free(msg);
 }
 
@@ -1254,6 +1385,7 @@ void silc_client_receive_new_id(SilcClient client,
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   int connecting = FALSE;
+  SilcBuffer sidp;
 
   if (!conn->local_entry)
     connecting = TRUE;
@@ -1277,20 +1409,25 @@ 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
+     and other stuff. */
+  sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
+  silc_client_send_command(client, conn, SILC_COMMAND_INFO,
+                          ++conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+  silc_buffer_free(sidp);
+
   /* Notify application of successful connection. We do it here now that
      we've received the Client ID and are allowed to send traffic. */
   if (connecting)
@@ -1299,7 +1436,8 @@ void silc_client_receive_new_id(SilcClient client,
 
 /* Processed received Channel ID for a channel. This is called when client
    joins to channel and server replies with channel ID. The ID is cached. 
-   Returns the created channel entry. */
+   Returns the created channel entry. This is also called when received
+   channel ID in for example USERS command reply that we do not have. */
 
 SilcChannelEntry silc_client_new_channel_id(SilcClient client,
                                            SilcSocketConnection sock,
@@ -1318,11 +1456,9 @@ SilcChannelEntry silc_client_new_channel_id(SilcClient client,
   channel->mode = mode;
   silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
 
-  conn->current_channel = channel;
-
   /* 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;
 }
@@ -1408,102 +1544,6 @@ void silc_client_replace_from_channels(SilcClient client,
   silc_idcache_list_free(list);
 }
 
-/* Parses mode mask and returns the mode as string. */
-
-char *silc_client_chmode(uint32 mode, SilcChannelEntry channel)
-{
-  char string[100];
-
-  if (!mode)
-    return NULL;
-
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_MODE_PRIVATE)
-    strncat(string, "p", 1);
-
-  if (mode & SILC_CHANNEL_MODE_SECRET)
-    strncat(string, "s", 1);
-
-  if (mode & SILC_CHANNEL_MODE_PRIVKEY)
-    strncat(string, "k", 1);
-
-  if (mode & SILC_CHANNEL_MODE_INVITE)
-    strncat(string, "i", 1);
-
-  if (mode & SILC_CHANNEL_MODE_TOPIC)
-    strncat(string, "t", 1);
-
-  if (mode & SILC_CHANNEL_MODE_ULIMIT)
-    strncat(string, "l", 1);
-
-  if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
-    strncat(string, "a", 1);
-
-  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
-    strncat(string, "f", 1);
-
-  if (mode & SILC_CHANNEL_MODE_CIPHER) {
-    char cipher[30];
-    memset(cipher, 0, sizeof(cipher));
-    snprintf(cipher, sizeof(cipher), " c (%s)", 
-            channel->channel_key->cipher->name);
-    strncat(string, cipher, strlen(cipher));
-  }
-
-  if (mode & SILC_CHANNEL_MODE_HMAC) {
-    char hmac[30];
-    memset(hmac, 0, sizeof(hmac));
-    snprintf(hmac, sizeof(hmac), " h (%s)", 
-            channel->hmac->hmac->name);
-    strncat(string, hmac, strlen(hmac));
-  }
-
-  /* Rest of mode is ignored */
-
-  return strdup(string);
-}
-
-/* Parses channel user mode mask and returns te mode as string */
-
-char *silc_client_chumode(uint32 mode)
-{
-  char string[4];
-
-  if (!mode)
-    return NULL;
-
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "f", 1);
-
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "o", 1);
-
-  return strdup(string);
-}
-
-/* Parses channel user mode and returns it as special mode character. */
-
-char *silc_client_chumode_char(uint32 mode)
-{
-  char string[4];
-
-  if (!mode)
-    return NULL;
-
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "*", 1);
-
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "@", 1);
-
-  return strdup(string);
-}
-
 /* Registers failure timeout to process the received failure packet
    with timeout. */
 
@@ -1551,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
@@ -1576,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)
@@ -1588,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;
@@ -1598,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);
+}