Client library rewrites.
authorPekka Riikonen <priikone@silcnet.org>
Sat, 25 Nov 2006 22:23:34 +0000 (22:23 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 25 Nov 2006 22:23:34 +0000 (22:23 +0000)
23 files changed:
lib/silcclient/Makefile.ad
lib/silcclient/README
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_channel.c
lib/silcclient/client_channel.h [new file with mode: 0644]
lib/silcclient/client_connect.c
lib/silcclient/client_connect.h
lib/silcclient/client_entry.c
lib/silcclient/client_internal.h
lib/silcclient/client_notify.c
lib/silcclient/client_prvmsg.c
lib/silcclient/client_prvmsg.h
lib/silcclient/client_register.c
lib/silcclient/client_register.h
lib/silcclient/command.c
lib/silcclient/command_reply.c
lib/silcclient/command_reply.h
lib/silcclient/protocol.c [deleted file]
lib/silcclient/protocol.h [deleted file]
lib/silcclient/silcclient.h
lib/silcclient/silcclient_entry.h
lib/silcclient/tests/test_silcclient.c

index 63027fc51eff6b849560941691f3f34413ab1b0e..a508a01a415467b2dc98fd05fdb0b7d4946af6a9 100644 (file)
@@ -23,8 +23,10 @@ libsilcclient_la_SOURCES =           \
        client.c                        \
        client_entry.c                  \
        client_prvmsg.c                 \
+       client_channel.c                \
        client_connect.c                \
        client_register.c               \
+       client_notify.c                 \
        command.c                       \
        command_reply.c
 
index a9c9d485bf64d21034f256db6f7d59e788607944..66a14e7d072148aa85b4744ca57376063a251623 100644 (file)
@@ -53,16 +53,6 @@ Using FSM
    peformance is not an issue, but only if there really are other FSM
    threads that need execution time also.
 
-   In packet processing do not use SILC_FSM_WAIT ever, since in current
-   design all packets are processed in one FSM thread, and if one packet
-   processor puts it into wait state, not other packets are received
-   in the mean time.  Instead go back to silc_client_connection_st_packet
-   with SILC_FSM_CONTINUE, and then in the async function's callback
-   set the old SilcPacket to the packet thread's state context and move back
-   to the packet processor with silc_fsm_next and silc_fsm_continue_sync
-   (always synchronously, never async).  This design may change later,
-   but for now this is it.
-
 
 When to use FSM semaphore signalling?
 
index 7f29c4ad5e4f1e90cba95e2a40479083d6c6bfdb..2d9e08a4f205363a43be5efc1c4282c51190d1fc 100644 (file)
 
 /************************** Types and definitions ***************************/
 
-SILC_FSM_STATE(silc_client_connection_st_run);
-SILC_FSM_STATE(silc_client_new_id);
-
 
 /************************ Static utility functions **************************/
 
+/* Connection machine FSM destructor.  This will finish the thread where
+   the machine was running and deletes the connection context. */
+
+static void silc_client_connection_destructor(SilcFSM fsm,
+                                             void *fsm_context,
+                                             void *destructor_context)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcFSMThread thread = destructor_context;
+
+  /* Delete connection */
+  silc_client_del_connection(conn->client, conn);
+
+  /* Finish the thread were this machine was running */
+  silc_fsm_finish(thread);
+}
+
+/* Packet FSM thread destructor */
+
+static void silc_client_packet_destructor(SilcFSMThread thread,
+                                         void *thread_context,
+                                         void *destructor_context)
+{
+  SilcClientConnection conn = thread_context;
+
+  /* Add thread back to thread pool */
+  silc_list_add(conn->internal->thread_pool, thread);
+  if (silc_list_count(conn->internal->thread_pool) == 1)
+    silc_list_start(conn->internal->thread_pool);
+}
+
 /* Packet engine callback to receive a packet */
 
 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
@@ -39,6 +67,7 @@ static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
                                           void *stream_context)
 {
   SilcClientConnection conn = stream_context;
+  SilcFSMThread thread;
 
   /* Packets we do not handle */
   switch (packet->type) {
@@ -57,10 +86,22 @@ static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
     break;
   }
 
-  /* Signal packet processor thread for a new packet */
-  conn->internal->new_packet = TRUE;
-  silc_fsm_set_state_context(&conn->internal->packet_thread, packet);
-  silc_fsm_continue_sync(&conn->internal->packet_thread);
+  /* Get packet processing thread */
+  thread = silc_list_get(conn->internal->thread_pool);
+  if (!thread) {
+    thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
+                                  silc_client_packet_destructor, NULL, FALSE);
+    if (!thread)
+      return FALSE;
+  } else {
+    silc_list_del(conn->internal->thread_pool, thread);
+    silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
+                        silc_client_packet_destructor, NULL, FALSE);
+  }
+
+  /* Process packet in thread */
+  silc_fsm_set_state_context(thread, packet);
+  silc_fsm_start_sync(thread, silc_client_connection_st_packet);
 
   return TRUE;
 }
@@ -111,21 +152,17 @@ void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
 SILC_FSM_STATE(silc_client_connection_st_start)
 {
   SilcClientConnection conn = fsm_context;
+  SilcFSM connfsm;
 
   /* Take scheduler for connection */
   conn->internal->schedule = silc_fsm_get_schedule(fsm);
 
   /*** Run connection machine */
-  silc_fsm_init(&conn->internal->fsm, conn, NULL, NULL,
-               conn->internal->schedule);
-  silc_fsm_sema_init(&conn->internal->wait_event, &conn->internal->fsm, 0);
-  silc_fsm_start_sync(&conn->internal->fsm, silc_client_connection_st_run);
-
-  /*** Run packet processor FSM thread */
-  silc_fsm_thread_init(&conn->internal->packet_thread, &conn->internal->fsm,
-                      conn, silc_client_fsm_destructor, NULL, FALSE);
-  silc_fsm_start_sync(&conn->internal->packet_thread,
-                     silc_client_connection_st_packet);
+  connfsm = &conn->internal->fsm;
+  silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
+               fsm, conn->internal->schedule);
+  silc_fsm_sema_init(&conn->internal->wait_event, connfsm, 0);
+  silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
 
   /* Schedule any events set in initialization */
   if (conn->internal->connect)
@@ -133,29 +170,33 @@ SILC_FSM_STATE(silc_client_connection_st_start)
   if (conn->internal->key_exchange)
     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
 
-  /* Wait until this thread is terminated */
+  /* Wait until this thread is terminated from the machine destructor */
   return SILC_FSM_WAIT;
 }
 
-/* Connection machine main state. */
+/* Connection machine main state.  This handles various connection related
+   events, but not packet processing.  It's done in dedicated packet
+   processing FSM thread. */
 
 SILC_FSM_STATE(silc_client_connection_st_run)
 {
   SilcClientConnection conn = fsm_context;
+  SilcFSMThread thread;
 
   /* Wait for events */
   SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
 
   /* Process events */
+  thread = &conn->internal->event_thread;
 
   if (conn->internal->connect) {
     SILC_LOG_DEBUG(("Event: connect"));
     conn->internal->connect = FALSE;
 
-    /** Connect remote host */
-    silc_fsm_thread_init(&conn->internal->event_thread, &conn->internal->fsm,
-                        conn, NULL, NULL, FALSE);
-    silc_fsm_start_sync(&conn->internal->event_thread, silc_client_st_connect);
+    /*** Event: connect */
+    silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
+                        NULL, NULL, FALSE);
+    silc_fsm_start_sync(thread, silc_client_st_connect);
     return SILC_FSM_CONTINUE;
   }
 
@@ -163,44 +204,33 @@ SILC_FSM_STATE(silc_client_connection_st_run)
     SILC_LOG_DEBUG(("Event: key exchange"));
     conn->internal->key_exchange = FALSE;
 
-    /** Start key exchange */
-    silc_fsm_thread_init(&conn->internal->event_thread, &conn->internal->fsm,
-                        conn, NULL, NULL, FALSE);
-    silc_fsm_start_sync(&conn->internal->event_thread,
-                       silc_client_st_connect_key_exchange);
+    /*** Event: key exchange */
+    silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
+                        NULL, NULL, FALSE);
+    silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
     return SILC_FSM_CONTINUE;
   }
 
   if (conn->internal->disconnected) {
+    /** Event: disconnected */
     SILC_LOG_DEBUG(("Event: disconnected"));
     conn->internal->disconnected = FALSE;
-
+    silc_fsm_next(fsm, silc_client_connection_st_close);
     return SILC_FSM_CONTINUE;
   }
 
   /* NOT REACHED */
-#if defined(SILC_DEBUG)
-  assert(FALSE);
-#endif /* SILC_DEBUG */
+  SILC_ASSERT(FALSE);
   return SILC_FSM_CONTINUE;
 }
 
-/* Connection's packet processor main state.  Packet processor thread waits
-   here for a new packet and processes received packets. */
+/* Packet processor thread.  Each incoming packet is processed in FSM
+   thread in this state.  The thread is run in the connection machine. */
 
 SILC_FSM_STATE(silc_client_connection_st_packet)
 {
-  SilcClientConnection conn = fsm_context;
-  SilcClient client = conn->client;
   SilcPacket packet = state_context;
 
-  /* Wait for packet to arrive */
-  if (!conn->internal->new_packet) {
-    SILC_LOG_DEBUG(("Wait for packet"));
-    return SILC_FSM_WAIT;
-  }
-  conn->internal->new_packet = FALSE;
-
   SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
 
   switch (packet->type) {
@@ -211,8 +241,8 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     break;
 
   case SILC_PACKET_CHANNEL_MESSAGE:
-    /* Channel message */
-    //    silc_client_channel_message(client, conn, packet);
+    /** Channel message */
+    silc_fsm_next(fsm, silc_client_channel_message);
     break;
 
   case SILC_PACKET_FTP:
@@ -221,8 +251,8 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     break;
 
   case SILC_PACKET_CHANNEL_KEY:
-    /* Received channel key */
-    //    silc_client_channel_key(client, conn, packet);
+    /** Channel key */
+    silc_fsm_next(fsm, silc_client_channel_key);
     break;
 
   case SILC_PACKET_COMMAND_REPLY:
@@ -231,23 +261,23 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     break;
 
   case SILC_PACKET_NOTIFY:
-    /* Notify */
-    //    silc_client_notify(client, conn, packet);
+    /** Notify */
+    silc_fsm_next(fsm, silc_client_notify);
     break;
 
   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
     /* Private message key indicator */
-    //    silc_client_private_message_key(client, conn, packet);
+    silc_fsm_next(fsm, silc_client_private_message_key);
     break;
 
   case SILC_PACKET_DISCONNECT:
-    /* Server disconnects */
-    //    silc_client_disconnect(client, conn, packet);
+    /** Disconnect */
+    silc_fsm_next(fsm, silc_client_disconnect);
     break;
 
   case SILC_PACKET_ERROR:
     /* Error by server */
-    //    silc_client_error(client, conn, packet);
+    silc_fsm_next(fsm, silc_client_error);
     break;
 
   case SILC_PACKET_KEY_AGREEMENT:
@@ -272,12 +302,94 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
 
   default:
     silc_packet_free(packet);
+    return SILC_FSM_FINISH;
     break;
   }
 
   return SILC_FSM_CONTINUE;
 }
 
+/* Disconnection even to close remote connection.  We close the connection
+   and finish the connection machine in this state.  The connection context
+   is deleted in the machine destructor.  The connection callback must be
+   already called back to application before getting here. */
+
+SILC_FSM_STATE(silc_client_connection_st_close)
+{
+  SilcClientConnection conn = fsm_context;
+
+  SILC_LOG_DEBUG(("Closing remote connection"));
+
+  /* XXX abort any ongoing events (protocols) */
+
+  /* Close connection */
+  silc_packet_stream_destroy(conn->stream);
+
+  SILC_LOG_DEBUG(("Finishing connection machine"));
+
+  return SILC_FSM_FINISH;
+}
+
+/* Received error packet from server.  Send it to application. */
+
+SILC_FSM_STATE(silc_client_error)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  char *msg;
+
+  msg = silc_memdup(silc_buffer_data(&packet->buffer),
+                   silc_buffer_len(&packet->buffer));
+  if (msg)
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
+
+  silc_free(msg);
+  silc_packet_free(packet);
+
+  return SILC_FSM_FINISH;
+}
+
+/* Received disconnect packet from server.  We close the connection and
+   send the disconnect message to application. */
+
+SILC_FSM_STATE(silc_client_disconnect)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcStatus status;
+  char *message = NULL;
+
+  SILC_LOG_DEBUG(("Server disconnected"));
+
+  if (silc_buffer_len(&packet->buffer) < 1) {
+    silc_packet_free(packet);
+    return SILC_FSM_FINISH;
+  }
+
+  status = (SilcStatus)packet->buffer.data[0];
+
+  silc_buffer_pull(&packet->buffer, 1);
+  if (silc_buffer_len(&packet->buffer) > 1 &&
+      silc_utf8_valid(silc_buffer_data(&packet->buffer),
+                     silc_buffer_len(&packet->buffer)))
+    message = silc_memdup(silc_buffer_data(&packet->buffer),
+                         silc_buffer_len(&packet->buffer));
+
+  /* Call connection callback */
+  conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
+                message, conn->context);
+
+  silc_free(message);
+  silc_packet_free(packet);
+
+  /* Signal to close connection */
+  conn->internal->disconnected = TRUE;
+  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+
+  return SILC_FSM_FINISH;
+}
 
 /*************************** Main client machine ****************************/
 
@@ -300,88 +412,15 @@ SILC_FSM_STATE(silc_client_st_run)
   }
 
   /* NOT REACHED */
-#if defined(SILC_DEBUG)
-  assert(FALSE);
-#endif /* SILC_DEBUG */
+  SILC_ASSERT(FALSE);
   return SILC_FSM_CONTINUE;
 }
 
+/******************************* Private API ********************************/
 
-/**************************** Packet Processing *****************************/
-
-/* Received new ID from server during registering to SILC network */
+/* Adds new connection.  Creates the connection context and returns it. */
 
-SILC_FSM_STATE(silc_client_new_id)
-{
-  SilcClientConnection conn = fsm_context;
-  SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcID id;
-
-  if (conn->local_id)
-    goto out;
-
-  SILC_LOG_DEBUG(("New ID received from server"));
-
-  if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
-                               silc_buffer_len(&packet->buffer), &id))
-    goto out;
-
-  /* Create local client entry */
-  conn->local_entry = silc_client_add_client(client, conn,
-                                            (client->nickname ?
-                                             client->nickname :
-                                             client->username),
-                                            client->username,
-                                            client->realname,
-                                            &id.u.client_id, 0);
-  if (!conn->local_entry)
-    goto out;
-
-  /* Save the ID */
-  conn->local_id = &conn->local_entry->id;
-  conn->local_idp = silc_buffer_copy(&packet->buffer);
-
-  /* Save cache entry */
-  silc_idcache_find_by_id_one(conn->internal->client_cache, conn->local_id,
-                             &conn->internal->local_entry);
-
-  /* Save remote ID */
-  if (packet->src_id_len) {
-    conn->remote_idp = silc_id_payload_encode_data(packet->src_id,
-                                                  packet->src_id_len,
-                                                  packet->src_id_type);
-    if (!conn->remote_idp)
-      goto out;
-    silc_id_payload_parse_id(silc_buffer_data(conn->remote_idp),
-                            silc_buffer_len(conn->remote_idp),
-                            &conn->remote_id);
-  }
-
-  /* Signal connection that new ID was received so it can continue
-     with the registering. */
-  if (conn->internal->registering)
-    silc_fsm_continue_sync(&conn->internal->event_thread);
-
- out:
-  /** Packet processed */
-  silc_packet_free(packet);
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
-}
-
-
-/******************************* Public API *********************************/
-
-/* 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
-   be added to the client using this function. User data `context' may
-   be sent as argument. This function is normally used only if the
-   application performed the connecting outside the library. The library
-   however may use this internally. */
-
-SilcClientConnection
+static SilcClientConnection
 silc_client_add_connection(SilcClient client,
                           SilcConnectionType conn_type,
                           SilcClientConnectionParams *params,
@@ -416,16 +455,24 @@ silc_client_add_connection(SilcClient client,
   conn->type = conn_type;
   conn->callback = callback;
   conn->context = context;
-  conn->internal->client_cache =
-    silc_idcache_alloc(0, SILC_ID_CLIENT, NULL, NULL);
-  conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL,
-                                                    NULL);
-  conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL,
-                                                   NULL);
-  conn->internal->ftp_sessions = silc_dlist_init();
   conn->internal->verbose = TRUE;
   silc_list_init(conn->internal->pending_commands,
                 struct SilcClientCommandContextStruct, next);
+  silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
+
+  conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
+                                                   NULL, NULL);
+  conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
+                                                    NULL, NULL);
+  conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
+                                                   NULL, NULL);
+  if (!conn->internal->client_cache || !conn->internal->channel_cache ||
+      !conn->internal->server_cache) {
+    silc_client_del_connection(client, conn);
+    return NULL;
+  }
+
+  conn->internal->ftp_sessions = silc_dlist_init();
 
   if (params) {
     if (params->detach_data)
@@ -435,9 +482,6 @@ silc_client_add_connection(SilcClient client,
     conn->internal->params.detach_data_len = params->detach_data_len;
   }
 
-  /* Add the connection to connections list */
-  //  silc_dlist_add(client->internal->conns, conn);
-
   /* Run the connection state machine.  If threads are in use the machine
      is always run in a real thread. */
   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
@@ -447,7 +491,7 @@ silc_client_add_connection(SilcClient client,
     silc_client_del_connection(client, conn);
     return NULL;
   }
-  silc_fsm_start_sync(thread, silc_client_connection_st_start);
+  silc_fsm_start(thread, silc_client_connection_st_start);
 
   return conn;
 }
@@ -544,29 +588,33 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 #endif /* 0 */
 }
 
-/* Connects to remote server. This is the main routine used to connect
-   to remote SILC server. Returns FALSE on error. */
 
-void silc_client_connect_to_server(SilcClient client,
-                                  SilcClientConnectionParams *params,
-                                  SilcPublicKey public_key,
-                                  SilcPrivateKey private_key,
-                                  char *remote_host, int port,
-                                  SilcClientConnectCallback callback,
-                                  void *context)
+/******************************* Client API *********************************/
+
+/* Connects to remote server.  This is the main routine used to connect
+   to remote SILC server.  Performs key exchange also.  Returns the
+   connection context to the connection callback. */
+
+SilcBool silc_client_connect_to_server(SilcClient client,
+                                      SilcClientConnectionParams *params,
+                                      SilcPublicKey public_key,
+                                      SilcPrivateKey private_key,
+                                      char *remote_host, int port,
+                                      SilcClientConnectCallback callback,
+                                      void *context)
 {
   SilcClientConnection conn;
 
   if (!client || !remote_host)
-    return;
+    return FALSE;
 
   /* Add new connection */
   conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
-    callback(client, NULL, SILC_CLIENT_CONN_ERROR, context);
-    return;
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
+    return FALSE;
   }
 
   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
@@ -575,80 +623,81 @@ void silc_client_connect_to_server(SilcClient client,
 
   /* Signal connection machine to start connecting */
   conn->internal->connect = TRUE;
+  return TRUE;
 }
 
-/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
-   key material between client and server.  This function can be called
-   directly if application is performing its own connecting and does not
-   use the connecting provided by this library. This function is normally
-   used only if the application performed the connecting outside the library.
-   The library however may use this internally. */
+/* Connects to remote client.  Performs key exchange also.  Returns the
+   connection context to the connection callback. */
 
-void silc_client_start_key_exchange(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcStream stream)
+SilcBool silc_client_connect_to_client(SilcClient client,
+                                      SilcClientConnectionParams *params,
+                                      SilcPublicKey public_key,
+                                      SilcPrivateKey private_key,
+                                      char *remote_host, int port,
+                                      SilcClientConnectCallback callback,
+                                      void *context)
 {
-#if 0
-  assert(conn && stream);
-  assert(client->public_key);
-  assert(client->private_key);
+  SilcClientConnection conn;
 
-  conn->nickname = (client->nickname ? strdup(client->nickname) :
-                   strdup(client->username));
-#endif /* 0 */
+  if (!client || !remote_host)
+    return FALSE;
 
-  /* Start */
+  /* Add new connection */
+  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
+                                   public_key, private_key, remote_host,
+                                   port, callback, context);
+  if (!conn) {
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
+    return FALSE;
+  }
 
+  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                            "Connecting to port %d of client host %s",
+                            port, remote_host);
+
+  /* Signal connection machine to start connecting */
+  conn->internal->connect = TRUE;
+  return TRUE;
 }
 
-#if 0
-/* Authentication method resolving callback. Application calls this function
-   after we've called the client->internal->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(SilcBool success,
-                                    SilcProtocolAuthMeth auth_meth,
-                                    const unsigned char *auth_data,
-                                    SilcUInt32 auth_data_len, void *context)
+/* Starts key exchange in the remote stream indicated by `stream'.  This
+   creates the connection context and returns it in the connection callback. */
+
+SilcBool silc_client_key_exchange(SilcClient client,
+                                 SilcClientConnectionParams *params,
+                                 SilcPublicKey public_key,
+                                 SilcPrivateKey private_key,
+                                 SilcStream stream,
+                                 SilcConnectionType conn_type,
+                                 SilcClientConnectCallback callback,
+                                 void *context)
 {
-  SilcClientConnAuthInternalContext *proto_ctx =
-    (SilcClientConnAuthInternalContext *)context;
-  SilcClient client = (SilcClient)proto_ctx->client;
+  SilcClientConnection conn;
+  const char *host;
+  SilcUInt16 port;
 
-  if (!success)
-    auth_meth = SILC_AUTH_NONE;
+  if (!client || !stream)
+    return FALSE;
 
-  proto_ctx->auth_meth = auth_meth;
-
-  if (success && auth_data && auth_data_len) {
-
-    /* Passphrase must be UTF-8 encoded, if it isn't encode it */
-    if (auth_meth == SILC_AUTH_PASSWORD &&
-       !silc_utf8_valid(auth_data, auth_data_len)) {
-      int payload_len = 0;
-      unsigned char *autf8 = NULL;
-      payload_len = silc_utf8_encoded_len(auth_data, auth_data_len,
-                                         SILC_STRING_ASCII);
-      autf8 = silc_calloc(payload_len, sizeof(*autf8));
-      auth_data_len = silc_utf8_encode(auth_data, auth_data_len,
-                                      SILC_STRING_ASCII, autf8, payload_len);
-      auth_data = autf8;
-    }
+  if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port))
+    return FALSE;
 
-    proto_ctx->auth_data = silc_memdup(auth_data, auth_data_len);
-    proto_ctx->auth_data_len = auth_data_len;
+  /* Add new connection */
+  conn = silc_client_add_connection(client, conn_type, params,
+                                   public_key, private_key,
+                                   (char *)host, port, callback, context);
+  if (!conn) {
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
+    return FALSE;
   }
+  conn->stream = (void *)stream;
 
-  /* Allocate the authenteication protocol and execute it. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
-                     &proto_ctx->sock->protocol, (void *)proto_ctx,
-                     silc_client_connect_to_server_final);
-
-  /* Execute the protocol */
-  silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
+  /* Signal connection to start key exchange */
+  conn->internal->key_exchange = TRUE;
+  return TRUE;
 }
 
+#if 0
 /* Finalizes the connection to the remote SILC server. This is called
    after authentication protocol has been completed. This send our
    user information to the server to receive our client ID from
@@ -781,293 +830,6 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
                         0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
-/* Packet processing callback. This is used to send and receive packets
-   from network. This is generic task. */
-
-SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
-{
-  SilcClient client = (SilcClient)context;
-  SilcClientConnection conn = NULL;
-  SilcClientConnection conn;
-  int ret;
-
-  SILC_LOG_DEBUG(("Processing packet"));
-
-  SILC_CLIENT_GET_SOCK(client, fd, sock);
-  if (sock == NULL)
-    return;
-
-  conn = (SilcClientConnection)sock->user_data;
-
-  /* Packet sending */
-  if (type == SILC_TASK_WRITE) {
-    /* Do not send data to disconnected connection */
-    if (SILC_IS_DISCONNECTED(sock))
-      return;
-
-    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(client->schedule, fd);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-
-    silc_buffer_clear(sock->outbuf);
-    return;
-  }
-
-  /* Packet receiving */
-  if (type == SILC_TASK_READ) {
-    /* Read data from network */
-    ret = silc_packet_receive(sock);
-    if (ret < 0)
-      return;
-
-    /* EOF */
-    if (ret == 0) {
-      SILC_LOG_DEBUG(("Read EOF"));
-
-      /* If connection is disconnecting already we will finally
-        close the connection */
-      if (SILC_IS_DISCONNECTING(sock)) {
-       if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-         client->internal->ops->disconnected(client, conn, 0, NULL);
-       silc_client_close_connection_real(client, sock, conn);
-       return;
-      }
-
-      SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
-      if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-       client->internal->ops->disconnected(client, conn, 0, NULL);
-      silc_client_close_connection_real(client, sock, conn);
-      return;
-    }
-
-    /* Process the packet. This will call the parser that will then
-       decrypt and parse the packet. */
-    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
-      silc_packet_receive_process(sock, FALSE, conn->internal->receive_key,
-                                 conn->internal->hmac_receive,
-                                 conn->internal->psn_receive,
-                                 silc_client_packet_parse, client);
-    else
-      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
-                                 silc_client_packet_parse, client);
-  }
-}
-
-/* Parser callback called by silc_packet_receive_process. Thie merely
-   registers timeout that will handle the actual parsing when appropriate. */
-
-static SilcBool silc_client_packet_parse(SilcPacketParserContext *parser_context,
-                                    void *context)
-{
-  SilcClient client = (SilcClient)context;
-  SilcClientConnection conn = parser_context->sock;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcPacketContext *packet = parser_context->packet;
-  SilcPacketType ret;
-
-  if (conn && conn->internal->hmac_receive && conn->sock == sock)
-    conn->internal->psn_receive = parser_context->packet->sequence + 1;
-
-  /* Parse the packet immediately */
-  if (parser_context->normal)
-    ret = silc_packet_parse(packet, conn->internal->receive_key);
-  else
-    ret = silc_packet_parse_special(packet, conn->internal->receive_key);
-
-  if (ret == SILC_PACKET_NONE) {
-    silc_packet_context_free(packet);
-    silc_free(parser_context);
-    return FALSE;
-  }
-
-  /* If protocol for this connection is key exchange or rekey then we'll
-     process all packets synchronously, since there might be packets in
-     queue that we are not able to decrypt without first processing the
-     packets before them. */
-  if (sock->protocol && sock->protocol->protocol &&
-      (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
-       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
-
-    /* Parse the incoming packet type */
-    silc_client_packet_parse_type(client, sock, packet);
-
-    /* Reprocess the buffer since we'll return FALSE. This is because
-       the `conn->internal->receive_key' might have become valid by processing
-       the previous packet */
-    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
-      silc_packet_receive_process(sock, FALSE, conn->internal->receive_key,
-                                 conn->internal->hmac_receive,
-                                 conn->internal->psn_receive,
-                                 silc_client_packet_parse, client);
-    else
-      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
-                                 silc_client_packet_parse, client);
-
-    silc_packet_context_free(packet);
-    silc_free(parser_context);
-
-    return FALSE;
-  }
-
-  /* Parse the incoming packet type */
-  silc_client_packet_parse_type(client, sock, packet);
-  silc_packet_context_free(packet);
-  silc_free(parser_context);
-  return TRUE;
-}
-#endif /* 0 */
-
-/* Closes connection to remote end. Free's all allocated data except
-   for some information such as nickname etc. that are valid at all time.
-   If the `sock' is NULL then the conn->sock will be used.  If `sock' is
-   provided it will be checked whether the sock and `conn->sock' are the
-   same (they can be different, ie. a socket can use `conn' as its
-   connection but `conn->sock' might be actually a different connection
-   than the `sock'). */
-
-#if 0
-void silc_client_close_connection_real(SilcClient client,
-                                      SilcClientConnection conn)
-{
-  int del = FALSE;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!sock && !conn)
-    return;
-
-  if (!sock || (sock && conn->sock == sock))
-    del = TRUE;
-  if (!sock)
-    sock = conn->sock;
-
-  if (!sock) {
-    if (del && conn)
-      silc_client_del_connection(client, conn);
-    return;
-  }
-
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(client->schedule, sock->sock);
-
-  /* Unregister all tasks */
-  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->connected 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)
-    silc_client_del_connection(client, conn);
-
-  silc_socket_free(sock);
-}
-
-/* Closes the connection to the remote end */
-
-void silc_client_close_connection(SilcClient client,
-                                 SilcClientConnection conn)
-{
-  //  silc_client_close_connection_real(client, NULL, conn);
-}
-
-/* Called when we receive disconnection packet from server. This
-   closes our end properly and displays the reason of the disconnection
-   on the screen. */
-
-void silc_client_disconnect(SilcClient client,
-                           SilcClientConnection conn,
-                           SilcBuffer packet)
-{
-  SilcClientConnection conn;
-  SilcStatus status;
-  char *message = NULL;
-
-  SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
-
-  if (packet->len < 1)
-    return;
-
-  status = (SilcStatus)packet->data[0];
-
-  if (packet->len > 1 &&
-      silc_utf8_valid(packet->data + 1, packet->len - 1))
-    message = silc_memdup(packet->data + 1, packet->len - 1);
-
-  conn = (SilcClientConnection)sock->user_data;
-  if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-    client->internal->ops->disconnected(client, conn, status, message);
-
-  silc_free(message);
-
-  SILC_SET_DISCONNECTED(sock);
-
-  /* Close connection through scheduler. */
-  silc_schedule_task_add(client->schedule, sock->sock,
-                        silc_client_disconnected_by_server_later,
-                        client, 0, 1, SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
-}
-
-/* Received error message from server. Display it on the screen.
-   We don't take any action what so ever of the error message. */
-
-void silc_client_error_by_server(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcBuffer message)
-{
-  char *msg;
-
-  msg = silc_memdup(message->data, message->len);
-  client->internal->ops->say(client, sock->user_data,
-                            SILC_CLIENT_MESSAGE_AUDIT, msg);
-  silc_free(msg);
-}
-
-/* Auto-nicking callback to send NICK command to server. */
-
-SILC_TASK_CALLBACK(silc_client_send_auto_nick)
-{
-  SilcClientConnection conn = (SilcClientConnection)context;
-  SilcClient client = conn->client;
-  if (client)
-    silc_client_command_send(client, conn, SILC_COMMAND_NICK,
-                            ++conn->cmd_ident, 1, 1,
-                            client->nickname, strlen(client->nickname));
-}
-
 /* Client session resuming callback.  If the session was resumed
    this callback is called after the resuming is completed.  This
    will call the `connect' client operation to the application
@@ -1098,157 +860,6 @@ static void silc_client_resume_session_cb(SilcClient client,
   }
 }
 
-/* Removes a client entry from all channels it has joined. */
-
-void silc_client_remove_from_channels(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry)
-{
-  SilcHashTableList htl;
-  SilcChannelUser chu;
-
-  silc_hash_table_list(client_entry->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
-    silc_hash_table_del(chu->client->channels, chu->channel);
-    silc_hash_table_del(chu->channel->user_list, chu->client);
-    silc_free(chu);
-  }
-
-  silc_hash_table_list_reset(&htl);
-}
-
-/* Replaces `old' client entries from all channels to `new' client entry.
-   This can be called for example when nickname changes and old ID entry
-   is replaced from ID cache with the new one. If the old ID entry is only
-   updated, then this fucntion needs not to be called. */
-
-void silc_client_replace_from_channels(SilcClient client,
-                                      SilcClientConnection conn,
-                                      SilcClientEntry old,
-                                      SilcClientEntry new)
-{
-  SilcHashTableList htl;
-  SilcChannelUser chu;
-
-  silc_hash_table_list(old->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
-    /* Replace client entry */
-    silc_hash_table_del(chu->client->channels, chu->channel);
-    silc_hash_table_del(chu->channel->user_list, chu->client);
-
-    chu->client = new;
-    silc_hash_table_add(chu->channel->user_list, chu->client, chu);
-    silc_hash_table_add(chu->client->channels, chu->channel, chu);
-  }
-  silc_hash_table_list_reset(&htl);
-}
-
-/* Registers failure timeout to process the received failure packet
-   with timeout. */
-
-void silc_client_process_failure(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcPacketContext *packet)
-{
-  SilcUInt32 failure = 0;
-
-  if (sock->protocol) {
-    if (packet->buffer->len >= 4)
-      SILC_GET32_MSB(failure, packet->buffer->data);
-
-    /* Notify application */
-    client->internal->ops->failure(client, sock->user_data, sock->protocol,
-                                  SILC_32_TO_PTR(failure));
-  }
-}
-
-/* A timeout callback for the re-key. We will be the initiator of the
-   re-key protocol. */
-
-SILC_TASK_CALLBACK_GLOBAL(silc_client_rekey_callback)
-{
-  SilcClientConnection conn = (SilcSocketConnection)context;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcClient client = (SilcClient)conn->internal->rekey->context;
-  SilcProtocol protocol;
-  SilcClientRekeyInternalContext *proto_ctx;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* If rekey protocol is active already wait for it to finish */
-  if (sock->protocol && sock->protocol->protocol &&
-      sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)
-    return;
-
-  /* Allocate internal protocol context. This is sent as context
-     to the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = (void *)client;
-  proto_ctx->sock = silc_socket_dup(sock);
-  proto_ctx->responder = FALSE;
-  proto_ctx->pfs = conn->internal->rekey->pfs;
-
-  /* Perform rekey protocol. Will call the final callback after the
-     protocol is over. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_REKEY,
-                     &protocol, proto_ctx, silc_client_rekey_final);
-  sock->protocol = protocol;
-
-  /* Run the protocol */
-  silc_protocol_execute(protocol, client->schedule, 0, 0);
-}
-
-/* The final callback for the REKEY protocol. This will actually take the
-   new key material into use. */
-
-SILC_TASK_CALLBACK(silc_client_rekey_final)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientRekeyInternalContext *ctx =
-    (SilcClientRekeyInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientConnection conn = ctx->sock;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
-    /* Error occured during protocol */
-    silc_protocol_cancel(protocol, client->schedule);
-    silc_protocol_free(protocol);
-    sock->protocol = NULL;
-    if (ctx->packet)
-      silc_packet_context_free(ctx->packet);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    silc_socket_free(ctx->sock);
-    silc_free(ctx);
-    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);
-
-  /* Re-register re-key timeout */
-  if (ctx->responder == FALSE)
-    silc_schedule_task_add(client->schedule, sock->sock,
-                          silc_client_rekey_callback,
-                          sock, conn->internal->rekey->timeout, 0,
-                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  /* Cleanup */
-  silc_protocol_free(protocol);
-  sock->protocol = NULL;
-  if (ctx->packet)
-    silc_packet_context_free(ctx->packet);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  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
@@ -1357,8 +968,6 @@ silc_client_request_authentication_method(SilcClient client,
 #endif /* 0 */
 
 
-/******************************* Client API *********************************/
-
 /* 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
@@ -1372,9 +981,15 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
   SilcClient new_client;
 
   new_client = silc_calloc(1, sizeof(*new_client));
+  if (!new_client)
+    return NULL;
   new_client->application = application;
 
   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
+  if (!new_client->internal) {
+    silc_free(new_client);
+    return NULL;
+  }
   new_client->internal->ops = ops;
   new_client->internal->params =
     silc_calloc(1, sizeof(*new_client->internal->params));
index 59bb12b1c73c971d9147c6a4209eebbb393a8d76..1f3d0d2bfa68dca3726eef8f59da4825ad418624 100644 (file)
 /* Forward declarations */
 typedef struct SilcClientStruct *SilcClient;
 typedef struct SilcClientConnectionStruct *SilcClientConnection;
-typedef struct SilcClientPingStruct SilcClientPing;
-typedef struct SilcClientAwayStruct SilcClientAway;
-typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement;
-typedef struct SilcClientFtpSessionStruct *SilcClientFtpSession;
 typedef struct SilcClientEntryStruct *SilcClientEntry;
 typedef struct SilcChannelEntryStruct *SilcChannelEntry;
 typedef struct SilcServerEntryStruct *SilcServerEntry;
+
+typedef struct SilcClientAwayStruct SilcClientAway;
+typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement;
+typedef struct SilcClientFtpSessionStruct *SilcClientFtpSession;
 typedef struct SilcClientCommandReplyContextStruct
                                            *SilcClientCommandReplyContext;
 typedef struct SilcChannelUserStruct *SilcChannelUser;
@@ -63,4 +63,29 @@ typedef struct SilcClientEntryInternalStruct {
   SilcAtomic8 refcnt;          /* Reference counter */
 } SilcClientEntryInternal;
 
+/* Internal channel entry context */
+typedef struct SilcChannelEntryInternalStruct {
+  /* SilcChannelEntry status information */
+  SilcDList old_channel_keys;
+  SilcDList old_hmacs;
+
+  /* Channel private keys */
+  SilcDList private_keys;                   /* List of private keys or NULL */
+  SilcChannelPrivateKey curr_key;           /* Current private key */
+
+  /* Channel keys */
+  SilcCipher channel_key;                    /* The channel key */
+  SilcHmac hmac;                            /* Current HMAC */
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; /* Current IV */
+
+  SilcUInt16 resolve_cmd_ident;                     /* Resolving identifier */
+  SilcAtomic8 refcnt;                       /* Reference counter */
+} SilcChannelEntryInternal;
+
+/* Internal server entry context */
+typedef struct SilcServerEntryInternalStruct {
+  SilcUInt16 resolve_cmd_ident;                     /* Resolving identifier */
+  SilcAtomic8 refcnt;                       /* Reference counter */
+} SilcServerEntryInternal;
+
 #endif /* CLIENT_H */
index 7587d07d27a9451fd5f2e54de2c4e8a37a5abce1..c843b63b9c3a558350f69e362efac5e73ed61336 100644 (file)
 
 */
 /* $Id$ */
-/* This file includes channel message sending and receiving routines,
-   channel key receiving and setting, and channel private key handling
-   routines. */
 
 #include "silc.h"
 #include "silcclient.h"
 #include "client_internal.h"
 
-/* Sends packet to the `channel'. Packet to channel is always encrypted
-   differently from "normal" packets. SILC header of the packet is
-   encrypted with the next receiver's key and the rest of the packet is
-   encrypted with the channel specific key. Padding and HMAC is computed
-   with the next receiver's key. The `data' is the channel message. If
-   the `force_send' is TRUE then the packet is sent immediately. */
+/************************** Channel Message Send ****************************/
+
+/* Sends channel message to `channel'. */
 
 SilcBool silc_client_send_channel_message(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcChannelEntry channel,
-                                     SilcChannelPrivateKey key,
-                                     SilcMessageFlags flags,
-                                     unsigned char *data,
-                                     SilcUInt32 data_len,
-                                     SilcBool force_send)
+                                         SilcClientConnection conn,
+                                         SilcChannelEntry channel,
+                                         SilcChannelPrivateKey key,
+                                         SilcMessageFlags flags,
+                                         SilcHash hash,
+                                         unsigned char *data,
+                                         SilcUInt32 data_len)
 {
-  SilcSocketConnection sock;
-  SilcBuffer payload;
-  SilcPacketContext packetdata;
-  const SilcBufferStruct packet;
+  SilcChannelUser chu;
+  SilcBuffer buffer;
   SilcCipher cipher;
   SilcHmac hmac;
-  unsigned char *id_string;
-  int block_len;
-  SilcChannelUser chu;
-  SilcBool ret = FALSE;
+  SilcBool ret;
 
-  assert(client && conn && channel);
-  sock = conn->sock;
-  SILC_LOG_DEBUG(("Sending packet to channel"));
+  SILC_LOG_DEBUG(("Sending channel message"));
+
+  if (!client || !conn || !channel)
+    return FALSE;
+  if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+    return FALSE;
 
   chu = silc_client_on_channel(channel, conn->local_entry);
   if (!chu) {
-    SILC_LOG_ERROR(("Cannot send message to channel we are not joined"));
+    client->internal->ops->say(conn->client, conn,
+                              SILC_CLIENT_MESSAGE_AUDIT,
+                              "Cannot talk to channel: not joined");
     return FALSE;
   }
 
@@ -73,36 +67,37 @@ SilcBool silc_client_send_channel_message(SilcClient client,
     return FALSE;
 
   /* Take the key to be used */
-  if (channel->private_keys) {
+  if (channel->internal.private_keys) {
     if (key) {
       /* Use key application specified */
       cipher = key->cipher;
       hmac = key->hmac;
     } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
-              channel->curr_key) {
+              channel->internal.curr_key) {
       /* Use current private key */
-      cipher = channel->curr_key->cipher;
-      hmac = channel->curr_key->hmac;
+      cipher = channel->internal.curr_key->cipher;
+      hmac = channel->internal.curr_key->hmac;
     } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
-              !channel->curr_key && channel->private_keys) {
+              !channel->internal.curr_key &&
+              channel->internal.private_keys) {
       /* Use just some private key since we don't know what to use
         and private keys are set. */
-      silc_dlist_start(channel->private_keys);
-      key = silc_dlist_get(channel->private_keys);
+      silc_dlist_start(channel->internal.private_keys);
+      key = silc_dlist_get(channel->internal.private_keys);
       cipher = key->cipher;
       hmac = key->hmac;
 
       /* Use this key as current private key */
-      channel->curr_key = key;
+      channel->internal.curr_key = key;
     } else {
       /* Use normal channel key generated by the server */
-      cipher = channel->channel_key;
-      hmac = channel->hmac;
+      cipher = channel->internal.channel_key;
+      hmac = channel->internal.hmac;
     }
   } else {
     /* Use normal channel key generated by the server */
-    cipher = channel->channel_key;
-    hmac = channel->hmac;
+    cipher = channel->internal.channel_key;
+    hmac = channel->internal.hmac;
   }
 
   if (!cipher || !hmac) {
@@ -110,198 +105,136 @@ SilcBool silc_client_send_channel_message(SilcClient client,
     return FALSE;
   }
 
-  block_len = silc_cipher_get_block_len(cipher);
-
   /* Encode the message payload. This also encrypts the message payload. */
-  payload = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
-                                       cipher, hmac, client->rng, NULL,
-                                       client->private_key, client->sha1hash);
-  if (!payload) {
+  buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
+                                      cipher, hmac, client->rng, NULL,
+                                      conn->private_key, hash, NULL);
+  if (!buffer) {
     SILC_LOG_ERROR(("Error encoding channel message"));
     return FALSE;
   }
 
-  /* Get data used in packet header encryption, keys and stuff. */
-  cipher = conn->internal->send_key;
-  hmac = conn->internal->hmac_send;
-  id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-
-  /* Set the packet context pointers. The destination ID is always
-     the Channel ID of the channel. Server and router will handle the
-     distribution of the packet. */
-  data = payload->data;
-  data_len = payload->len;
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  packetdata.dst_id = id_string;
-  packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
-                                packetdata.src_id_len +
-                                packetdata.dst_id_len);
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
-    packetdata.src_id_len + packetdata.dst_id_len;
-  SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                     packetdata.src_id_len +
-                     packetdata.dst_id_len), block_len, packetdata.padlen);
-
-  /* Create the outgoing packet */
-  if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock,
-                            data, data_len, (const SilcBuffer)&packet)) {
-    SILC_LOG_ERROR(("Error assembling packet"));
-    goto out;
-  }
-
-  /* Encrypt the header and padding of the packet. This is encrypted
-     with normal session key shared with our server. */
-  silc_packet_encrypt(cipher, hmac, conn->internal->psn_send++,
-                     (SilcBuffer)&packet, SILC_PACKET_HEADER_LEN +
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  SILC_LOG_HEXDUMP(("Packet to channel, len %d", packet.len),
-                   packet.data, packet.len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-
-  /* Check for mandatory rekey */
-  if (conn->internal->psn_send == SILC_CLIENT_REKEY_THRESHOLD)
-    silc_schedule_task_add(client->schedule, sock->sock,
-                          silc_client_rekey_callback, sock, 0, 1,
-                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  ret = TRUE;
-
- out:
-  silc_buffer_free(payload);
-  silc_free(id_string);
+  /* Send the channel message */
+  ret = silc_packet_send_ext(conn->stream, SILC_PACKET_CHANNEL_MESSAGE, 0,
+                            0, NULL, SILC_ID_CHANNEL, &channel->id,
+                            silc_buffer_datalen(buffer), NULL, NULL);
 
+  silc_buffer_free(buffer);
   return ret;
 }
 
-typedef struct {
-  SilcMessagePayload payload;
-  SilcChannelID *channel_id;
-  SilcChannelPrivateKey key;
-} *SilcChannelClientResolve;
-
-static void silc_client_channel_message_cb(SilcClient client,
-                                          SilcClientConnection conn,
-                                          SilcClientEntry *clients,
-                                          SilcUInt32 clients_count,
-                                          void *context)
-{
-  SilcChannelClientResolve res = (SilcChannelClientResolve)context;
-
-  if (clients_count == 1) {
-    SilcChannelEntry channel;
-    unsigned char *message;
-    SilcUInt32 message_len;
-
-    channel = silc_client_get_channel_by_id(client, conn, res->channel_id);
-    if (!channel)
-      goto out;
-
-    /* If this client is not on channel, add it there since it clearly
-       is there. */
-    if (!silc_client_on_channel(channel, clients[0])) {
-      SilcChannelUser chu = silc_calloc(1, sizeof(*chu));
-      chu->client = clients[0];
-      chu->channel = channel;
-      silc_hash_table_add(channel->user_list, clients[0], chu);
-      silc_hash_table_add(clients[0]->channels, channel, chu);
-    }
+/************************* Channel Message Receive **************************/
 
-    message = silc_message_get_data(res->payload, &message_len);
+/* Client resolving callback.  Continues with the channel message processing */
 
-    /* Pass the message to application */
-    client->internal->ops->channel_message(
-                           client, conn, clients[0], channel, res->payload,
-                           res->key, silc_message_get_flags(res->payload),
-                           message, message_len);
-  }
+static void silc_client_channel_message_resolved(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcStatus status,
+                                                SilcDList clients,
+                                                void *context)
+{
+  /* If no client found, ignore the channel message, a silent error */
+  if (!clients)
+    silc_fsm_next(context, silc_client_channel_message_error);
 
- out:
-  silc_message_payload_free(res->payload);
-  silc_free(res->channel_id);
-  silc_free(res);
+  /* Continue processing the channel message packet */
+  SILC_FSM_CALL_CONTINUE(context);
 }
 
-/* Process received message to a channel (or from a channel, really). This
-   decrypts the channel message with channel specific key and parses the
-   message payload. Finally it displays the message on the screen. */
+/* Process received channel message */
 
-void silc_client_channel_message(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_channel_message)
 {
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcBuffer buffer = packet->buffer;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcBuffer buffer = &packet->buffer;
   SilcMessagePayload payload = NULL;
-  SilcChannelID *id = NULL;
   SilcChannelEntry channel;
   SilcClientEntry client_entry;
-  SilcClientID *client_id = NULL;
+  SilcClientID remote_id;
+  SilcChannelID channel_id;
   unsigned char *message;
   SilcUInt32 message_len;
   SilcChannelPrivateKey key = NULL;
 
   SILC_LOG_DEBUG(("Received channel message"));
 
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL)
-    goto out;
+  if (packet->dst_id_type != SILC_ID_CHANNEL) {
+    /** Invalid packet */
+    silc_fsm_next(fsm, silc_client_channel_message_error);
+    return SILC_FSM_CONTINUE;
+  }
 
-  client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                            SILC_ID_CLIENT);
-  if (!client_id)
-    goto out;
-  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
-  if (!id)
-    goto out;
+  if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
+                     &remote_id, sizeof(remote_id))) {
+    /** Invalid source ID */
+    silc_fsm_next(fsm, silc_client_channel_message_error);
+    return SILC_FSM_CONTINUE;
+  }
 
-  /* Find the channel entry from channels on this connection */
-  channel = silc_client_get_channel_by_id(client, conn, id);
-  if (!channel)
-    goto out;
+  /* Get sender client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &remote_id);
+  if (!client_entry || !client_entry->nickname[0]) {
+    /** Resolve client info */
+    silc_client_unref_client(client, conn, client_entry);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &remote_id, NULL,
+                                        silc_client_channel_message_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
+
+  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                     &channel_id, sizeof(channel_id))) {
+    /** Invalid destination ID */
+    silc_fsm_next(fsm, silc_client_channel_message_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Find the channel */
+  channel = silc_client_get_channel_by_id(client, conn, &channel_id);
+  if (!channel) {
+    /** Unknown channel */
+    silc_fsm_next(fsm, silc_client_channel_message_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* If there is no channel private key then just decrypt the message
      with the channel key. If private keys are set then just go through
      all private keys and check what decrypts correctly. */
-  if (!channel->private_keys) {
+  if (!channel->internal.private_keys) {
     /* Parse the channel message payload. This also decrypts the payload */
-    payload = silc_message_payload_parse(buffer->data, buffer->len, FALSE,
-                                        FALSE, channel->channel_key,
-                                        channel->hmac);
+    payload = silc_message_payload_parse(silc_buffer_data(buffer),
+                                        silc_buffer_len(buffer), FALSE,
+                                        FALSE, channel->internal.channel_key,
+                                        channel->internal.hmac, NULL,
+                                        FALSE, NULL);
 
     /* If decryption failed and we have just performed channel key rekey
        we will use the old key in decryption. If that fails too then we
        cannot do more and will drop the packet. */
     if (!payload) {
-      SilcCipher key;
+      SilcCipher cipher;
       SilcHmac hmac;
-      int i;
 
-      if (!channel->old_channel_keys ||
-         !silc_dlist_count(channel->old_channel_keys))
+      if (!channel->internal.old_channel_keys ||
+         !silc_dlist_count(channel->internal.old_channel_keys))
        goto out;
 
       SILC_LOG_DEBUG(("Attempting to decrypt with old channel key(s)"));
 
-      silc_dlist_end(channel->old_channel_keys);
-      silc_dlist_end(channel->old_hmacs);
-      for (i = 0; i < silc_dlist_count(channel->old_channel_keys); i++) {
-       key = silc_dlist_get(channel->old_channel_keys);
-       hmac = silc_dlist_get(channel->old_hmacs);
-       if (!key || !hmac)
+      silc_dlist_end(channel->internal.old_channel_keys);
+      silc_dlist_end(channel->internal.old_hmacs);
+      while ((cipher = silc_dlist_get(channel->internal.old_channel_keys))) {
+       hmac = silc_dlist_get(channel->internal.old_hmacs);
+       if (!hmac)
          break;
 
-       payload = silc_message_payload_parse(buffer->data, buffer->len,
-                                            FALSE, FALSE, key, hmac);
+       payload = silc_message_payload_parse(silc_buffer_data(buffer),
+                                            silc_buffer_len(buffer),
+                                            FALSE, FALSE, cipher, hmac,
+                                            NULL, FALSE, NULL);
        if (payload)
          break;
       }
@@ -309,21 +242,24 @@ void silc_client_channel_message(SilcClient client,
        goto out;
     }
   } else {
-    /* If the private key mode, however is not set on the channel then
-       try the actual channel key first before trying private keys. */
+    /* If the private key mode is not set on the channel then try the actual
+       channel key first before trying private keys. */
     if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
-      payload = silc_message_payload_parse(buffer->data, buffer->len, FALSE,
-                                          FALSE, channel->channel_key,
-                                          channel->hmac);
+      payload = silc_message_payload_parse(silc_buffer_data(buffer),
+                                          silc_buffer_len(buffer),
+                                          FALSE, FALSE,
+                                          channel->internal.channel_key,
+                                          channel->internal.hmac, NULL,
+                                          FALSE, NULL);
 
     if (!payload) {
-      silc_dlist_start(channel->private_keys);
-      while ((key = silc_dlist_get(channel->private_keys))
-            != SILC_LIST_END) {
+      silc_dlist_start(channel->internal.private_keys);
+      while ((key = silc_dlist_get(channel->internal.private_keys))) {
        /* Parse the message payload. This also decrypts the payload */
-       payload = silc_message_payload_parse(buffer->data, buffer->len,
-                                            FALSE, FALSE,
-                                            key->cipher, key->hmac);
+       payload = silc_message_payload_parse(silc_buffer_data(buffer),
+                                            silc_buffer_len(buffer),
+                                            FALSE, FALSE, key->cipher,
+                                            key->hmac, NULL, FALSE, NULL);
        if (payload)
          break;
       }
@@ -332,23 +268,6 @@ void silc_client_channel_message(SilcClient client,
     }
   }
 
-  /* Find client entry */
-  client_entry = silc_client_get_client_by_id(client, conn, client_id);
-  if (!client_entry || !client_entry->nickname ||
-      !silc_client_on_channel(channel, client_entry)) {
-    /* Resolve the client info */
-    SilcChannelClientResolve res = silc_calloc(1, sizeof(*res));
-    res->payload = payload;
-    res->channel_id = id;
-    res->key = key;
-    silc_client_get_client_by_id_resolve(client, conn, client_id, NULL,
-                                        silc_client_channel_message_cb,
-                                        res);
-    payload = NULL;
-    id = NULL;
-    goto out;
-  }
-
   message = silc_message_get_data(payload, &message_len);
 
   /* Pass the message to application */
@@ -358,12 +277,24 @@ void silc_client_channel_message(SilcClient client,
                             message, message_len);
 
  out:
-  silc_free(id);
-  silc_free(client_id);
+  silc_client_unref_client(client, conn, client_entry);
+  silc_client_unref_channel(client, conn, channel);
   if (payload)
     silc_message_payload_free(payload);
+  return SILC_FSM_FINISH;
+}
+
+/* Channel message error. */
+
+SILC_FSM_STATE(silc_client_channel_message_error)
+{
+  SilcPacket packet = state_context;
+  silc_packet_free(packet);
+  return SILC_FSM_FINISH;
 }
 
+/******************************* Channel Key ********************************/
+
 /* Timeout callback that is called after a short period of time after the
    new channel key has been created.  This removes the first channel key
    in the list. */
@@ -374,171 +305,162 @@ SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey)
   SilcCipher key;
   SilcHmac hmac;
 
-  if (channel->old_channel_keys) {
-    silc_dlist_start(channel->old_channel_keys);
-    key = silc_dlist_get(channel->old_channel_keys);
+  if (channel->internal.old_channel_keys) {
+    silc_dlist_start(channel->internal.old_channel_keys);
+    key = silc_dlist_get(channel->internal.old_channel_keys);
     if (key) {
-      silc_dlist_del(channel->old_channel_keys, key);
+      silc_dlist_del(channel->internal.old_channel_keys, key);
       silc_cipher_free(key);
     }
   }
 
-  if (channel->old_hmacs) {
-    silc_dlist_start(channel->old_hmacs);
-    hmac = silc_dlist_get(channel->old_hmacs);
+  if (channel->internal.old_hmacs) {
+    silc_dlist_start(channel->internal.old_hmacs);
+    hmac = silc_dlist_get(channel->internal.old_hmacs);
     if (hmac) {
-      silc_dlist_del(channel->old_hmacs, hmac);
+      silc_dlist_del(channel->internal.old_hmacs, hmac);
       silc_hmac_free(hmac);
     }
   }
 }
 
-/* Saves channel key from encoded `key_payload'. This is used when we
-   receive Channel Key Payload and when we are processing JOIN command
-   reply. */
+/* Saves channel key from encoded `key_payload'. This is used when we receive
+   Channel Key Payload and when we are processing JOIN command reply. */
 
-void silc_client_save_channel_key(SilcClient client,
-                                 SilcClientConnection conn,
-                                 SilcBuffer key_payload,
-                                 SilcChannelEntry channel)
+SilcBool silc_client_save_channel_key(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcBuffer key_payload,
+                                     SilcChannelEntry channel)
 {
   unsigned char *id_string, *key, *cipher, *hmac, hash[SILC_HASH_MAXLEN];
   SilcUInt32 tmp_len;
-  SilcChannelID *id;
+  SilcChannelID id;
   SilcChannelKeyPayload payload;
 
-  payload = silc_channel_key_payload_parse(key_payload->data,
-                                          key_payload->len);
+  payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
+                                          silc_buffer_len(key_payload));
   if (!payload)
-    return;
+    return FALSE;
 
   id_string = silc_channel_key_get_id(payload, &tmp_len);
   if (!id_string) {
     silc_channel_key_payload_free(payload);
-    return;
+    return FALSE;
   }
 
-  id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
-  if (!id) {
+  if (!silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
     silc_channel_key_payload_free(payload);
-    return;
+    return FALSE;
   }
 
   /* Find channel. */
   if (!channel) {
-    channel = silc_client_get_channel_by_id(client, conn, id);
-    if (!channel)
-      goto out;
+    channel = silc_client_get_channel_by_id(client, conn, &id);
+    if (!channel) {
+      silc_channel_key_payload_free(payload);
+      return FALSE;
+    }
+  } else {
+    silc_client_ref_channel(client, conn, channel);
   }
 
-  hmac = (channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) :
-         SILC_DEFAULT_HMAC);
-
   /* Save the old key for a short period of time so that we can decrypt
      channel message even after the rekey if some client would be sending
      messages with the old key after the rekey. */
-  if (!channel->old_channel_keys)
-    channel->old_channel_keys = silc_dlist_init();
-  if (!channel->old_hmacs)
-    channel->old_hmacs = silc_dlist_init();
-  silc_dlist_add(channel->old_channel_keys, channel->channel_key);
-  silc_dlist_add(channel->old_hmacs, channel->hmac);
-  silc_schedule_task_add(client->schedule, 0,
-                        silc_client_save_channel_key_rekey, channel,
-                        10, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
-  /* Free the old channel key data */
-  silc_free(channel->key);
+  if (!channel->internal.old_channel_keys)
+    channel->internal.old_channel_keys = silc_dlist_init();
+  if (!channel->internal.old_hmacs)
+    channel->internal.old_hmacs = silc_dlist_init();
+  if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
+    silc_dlist_add(channel->internal.old_channel_keys,
+                  channel->internal.channel_key);
+    silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
+    silc_schedule_task_add_timeout(client->schedule,
+                                  silc_client_save_channel_key_rekey,
+                                  channel, 15, 0);
+  }
 
-  /* Save the key */
-  key = silc_channel_key_get_key(payload, &tmp_len);
+  /* Get channel cipher */
   cipher = silc_channel_key_get_cipher(payload, NULL);
-  channel->key_len = tmp_len * 8;
-  channel->key = silc_memdup(key, tmp_len);
-
-  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+  if (!silc_cipher_alloc(cipher, &channel->internal.channel_key)) {
     client->internal->ops->say(
                           conn->client, conn,
                           SILC_CLIENT_MESSAGE_AUDIT,
                           "Cannot talk to channel: unsupported cipher %s",
                           cipher);
-    goto out;
+    silc_client_unref_channel(client, conn, channel);
+    silc_channel_key_payload_free(payload);
+    return FALSE;
   }
 
   /* Set the cipher key */
-  silc_cipher_set_key(channel->channel_key, key, channel->key_len);
+  key = silc_channel_key_get_key(payload, &tmp_len);
+  silc_cipher_set_key(channel->internal.channel_key, key, tmp_len * 8);
 
-  /* Generate HMAC key from the channel key data and set it */
-  silc_hmac_alloc(hmac, NULL, &channel->hmac);
-  silc_hash_make(silc_hmac_get_hash(channel->hmac), key, tmp_len, hash);
-  silc_hmac_set_key(channel->hmac, hash,
-                   silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+  /* Get channel HMAC */
+  hmac = (channel->internal.hmac ?
+         (char *)silc_hmac_get_name(channel->internal.hmac) :
+         SILC_DEFAULT_HMAC);
+  if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
+    client->internal->ops->say(
+                          conn->client, conn,
+                          SILC_CLIENT_MESSAGE_AUDIT,
+                          "Cannot talk to channel: unsupported HMAC %s",
+                          hmac);
+    silc_client_unref_channel(client, conn, channel);
+    silc_channel_key_payload_free(payload);
+    return FALSE;
+  }
+
+  /* Set HMAC key */
+  silc_hash_make(silc_hmac_get_hash(channel->internal.hmac), key,
+                tmp_len, hash);
+  silc_hmac_set_key(channel->internal.hmac, hash,
+                   silc_hash_len(silc_hmac_get_hash(channel->internal.hmac)));
   memset(hash, 0, sizeof(hash));
 
- out:
-  silc_free(id);
-  silc_channel_key_payload_free(payload);
+  silc_client_unref_channel(client, conn, channel);
+
+  return TRUE;
 }
 
-/* Processes received key for channel. The received key will be used
-   to protect the traffic on the channel for now on. Client must receive
-   the key to the channel before talking on the channel is possible.
-   This is the key that server has generated, this is not the channel
-   private key, it is entirely local setting. */
+/* Received channel key packet.  The key will replace old channel key. */
 
-void silc_client_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet)
+SILC_FSM_STATE(silc_client_channel_key)
 {
-  SILC_LOG_DEBUG(("Received key for channel"));
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+
+  SILC_LOG_DEBUG(("Received channel key"));
 
   /* Save the key */
-  silc_client_save_channel_key(client, sock->user_data, packet, NULL);
+  silc_client_save_channel_key(client, conn, &packet->buffer, NULL);
+  silc_packet_free(packet);
+
+  return SILC_FSM_FINISH;
 }
 
-/* Adds private key for channel. When channel has private key then the
-   messages are encrypted using that key. All clients on the channel must
-   also know the key in order to decrypt the messages. However, it is
-   possible to have several private keys per one channel. In this case
-   only some of the clients on the channel may know the one key and only
-   some the other key.
-
-   If `cipher' and/or `hmac' is NULL then default values will be used
-   (aes-256-cbc for cipher and hmac-sha1-96 for hmac).
-
-   The private key for channel is optional. If it is not set then the
-   channel messages are encrypted using the channel key generated by the
-   server. However, setting the private key (or keys) for the channel
-   significantly adds security. If more than one key is set the library
-   will automatically try all keys at the message decryption phase. Note:
-   setting many keys slows down the decryption phase as all keys has to
-   be tried in order to find the correct decryption key. However, setting
-   a few keys does not have big impact to the decryption performace.
-
-   NOTE: that this is entirely local setting. The key set using this function
-   is not sent to the network at any phase.
-
-   NOTE: If the key material was originated by the SKE protocol (using
-   silc_client_send_key_agreement) then the `key' MUST be the
-   key->send_enc_key as this is dictated by the SILC protocol. However,
-   currently it is not expected that the SKE key material would be used
-   as channel private key. However, this API allows it. */
+/**************************** Channel Private Key ***************************/
+
+/* Add new channel private key */
 
 SilcBool silc_client_add_channel_private_key(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcChannelEntry channel,
-                                        const char *name,
-                                        char *cipher,
-                                        char *hmac,
-                                        unsigned char *key,
-                                        SilcUInt32 key_len,
-                                        SilcChannelPrivateKey *ret_key)
+                                            SilcClientConnection conn,
+                                            SilcChannelEntry channel,
+                                            const char *name,
+                                            char *cipher,
+                                            char *hmac,
+                                            unsigned char *key,
+                                            SilcUInt32 key_len,
+                                            SilcChannelPrivateKey *ret_key)
 {
   SilcChannelPrivateKey entry;
   unsigned char hash[SILC_HASH_MAXLEN];
-  SilcSKEKeyMaterial *keymat;
+  SilcSKEKeyMaterial keymat;
 
-  assert(client && channel);
+  if (!client || !conn || !channel)
+    return FALSE;
 
   if (!cipher)
     cipher = SILC_DEFAULT_CIPHER;
@@ -547,43 +469,58 @@ SilcBool silc_client_add_channel_private_key(SilcClient client,
 
   if (!silc_cipher_is_supported(cipher))
     return FALSE;
-
   if (!silc_hmac_is_supported(hmac))
     return FALSE;
 
+  if (!channel->internal.private_keys) {
+    channel->internal.private_keys = silc_dlist_init();
+    if (!channel->internal.private_keys)
+      return FALSE;
+  }
+
   /* Produce the key material */
-  keymat = silc_calloc(1, sizeof(*keymat));
-  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
-                                        client->sha1hash, keymat)
-      != SILC_SKE_STATUS_OK)
+  keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
+                                             client->sha1hash);
+  if (!keymat)
     return FALSE;
 
-  if (!channel->private_keys)
-    channel->private_keys = silc_dlist_init();
-
   /* Save the key */
   entry = silc_calloc(1, sizeof(*entry));
+  if (!entry) {
+    silc_ske_free_key_material(keymat);
+    return FALSE;
+  }
   entry->name = name ? strdup(name) : NULL;
-  entry->key = silc_memdup(keymat->send_enc_key, keymat->enc_key_len / 8);
-  entry->key_len = keymat->enc_key_len / 8;
 
   /* Allocate the cipher and set the key*/
-  silc_cipher_alloc(cipher, &entry->cipher);
-  silc_cipher_set_key(entry->cipher, entry->key, keymat->enc_key_len);
+  if (!silc_cipher_alloc(cipher, &entry->cipher)) {
+    silc_free(entry);
+    silc_free(entry->name);
+    silc_ske_free_key_material(keymat);
+    return FALSE;
+  }
+  silc_cipher_set_key(entry->cipher, keymat->send_enc_key,
+                     keymat->enc_key_len);
 
   /* Generate HMAC key from the channel key data and set it */
-  silc_hmac_alloc(hmac, NULL, &entry->hmac);
-  silc_hash_make(silc_hmac_get_hash(entry->hmac), entry->key,
-                entry->key_len, hash);
+  if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
+    silc_free(entry);
+    silc_free(entry->name);
+    silc_cipher_free(entry->cipher);
+    silc_ske_free_key_material(keymat);
+    return FALSE;
+  }
+  silc_hash_make(silc_hmac_get_hash(entry->hmac), keymat->send_enc_key,
+                keymat->enc_key_len / 8, hash);
   silc_hmac_set_key(entry->hmac, hash,
                    silc_hash_len(silc_hmac_get_hash(entry->hmac)));
   memset(hash, 0, sizeof(hash));
 
   /* Add to the private keys list */
-  silc_dlist_add(channel->private_keys, entry);
+  silc_dlist_add(channel->internal.private_keys, entry);
 
-  if (!channel->curr_key)
-    channel->curr_key = entry;
+  if (!channel->internal.curr_key)
+    channel->internal.curr_key = entry;
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
@@ -604,26 +541,25 @@ SilcBool silc_client_del_channel_private_keys(SilcClient client,
 {
   SilcChannelPrivateKey entry;
 
-  assert(client && channel);
+  if (!client || !conn || !channel)
+    return FALSE;
 
-  if (!channel->private_keys)
+  if (!channel->internal.private_keys)
     return FALSE;
 
-  silc_dlist_start(channel->private_keys);
-  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
-    silc_dlist_del(channel->private_keys, entry);
-    memset(entry->key, 0, entry->key_len);
-    silc_free(entry->key);
+  silc_dlist_start(channel->internal.private_keys);
+  while ((entry = silc_dlist_get(channel->internal.private_keys))) {
+    silc_dlist_del(channel->internal.private_keys, entry);
     silc_free(entry->name);
     silc_cipher_free(entry->cipher);
     silc_hmac_free(entry->hmac);
     silc_free(entry);
   }
 
-  channel->curr_key = NULL;
+  channel->internal.curr_key = NULL;
 
-  silc_dlist_uninit(channel->private_keys);
-  channel->private_keys = NULL;
+  silc_dlist_uninit(channel->internal.private_keys);
+  channel->internal.private_keys = NULL;
 
   return TRUE;
 }
@@ -635,38 +571,38 @@ SilcBool silc_client_del_channel_private_keys(SilcClient client,
    returns FALSE on error, TRUE otherwise. */
 
 SilcBool silc_client_del_channel_private_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcChannelEntry channel,
-                                       SilcChannelPrivateKey key)
+                                            SilcClientConnection conn,
+                                            SilcChannelEntry channel,
+                                            SilcChannelPrivateKey key)
 {
   SilcChannelPrivateKey entry;
 
-  assert(client && channel);
+  if (!client || !conn || !channel)
+    return FALSE;
 
-  if (!channel->private_keys)
+  if (!channel->internal.private_keys)
     return FALSE;
 
-  silc_dlist_start(channel->private_keys);
-  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
-    if (entry == key) {
-      if (channel->curr_key == entry)
-       channel->curr_key = NULL;
-
-      silc_dlist_del(channel->private_keys, entry);
-      memset(entry->key, 0, entry->key_len);
-      silc_free(entry->key);
-      silc_free(entry->name);
-      silc_cipher_free(entry->cipher);
-      silc_hmac_free(entry->hmac);
-      silc_free(entry);
-
-      if (silc_dlist_count(channel->private_keys) == 0) {
-       silc_dlist_uninit(channel->private_keys);
-       channel->private_keys = NULL;
-      }
+  silc_dlist_start(channel->internal.private_keys);
+  while ((entry = silc_dlist_get(channel->internal.private_keys))) {
+    if (entry != key)
+      continue;
+
+    if (channel->internal.curr_key == entry)
+      channel->internal.curr_key = NULL;
+
+    silc_dlist_del(channel->internal.private_keys, entry);
+    silc_free(entry->name);
+    silc_cipher_free(entry->cipher);
+    silc_hmac_free(entry->hmac);
+    silc_free(entry);
 
-      return TRUE;
+    if (silc_dlist_count(channel->internal.private_keys) == 0) {
+      silc_dlist_uninit(channel->internal.private_keys);
+      channel->internal.private_keys = NULL;
     }
+
+    return TRUE;
   }
 
   return FALSE;
@@ -678,39 +614,28 @@ SilcBool silc_client_del_channel_private_key(SilcClient client,
    used to delete the specific key by giving the pointer as argument to the
    function silc_client_del_channel_private_key. */
 
-SilcChannelPrivateKey *
-silc_client_list_channel_private_keys(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcChannelEntry channel,
-                                     SilcUInt32 *key_count)
+SilcDList silc_client_list_channel_private_keys(SilcClient client,
+                                               SilcClientConnection conn,
+                                               SilcChannelEntry channel)
 {
-  SilcChannelPrivateKey *keys = NULL, entry;
-  SilcUInt32 count = 0;
+  SilcChannelPrivateKey entry;
+  SilcDList list;
 
-  assert(client && channel);
+  if (!client || !conn || !channel)
+    return FALSE;
 
-  if (!channel->private_keys)
+  if (!channel->internal.private_keys)
     return NULL;
 
-  silc_dlist_start(channel->private_keys);
-  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
-    keys = silc_realloc(keys, sizeof(*keys) * (count + 1));
-    keys[count] = entry;
-    count++;
-  }
-
-  if (key_count)
-    *key_count = count;
-
-  return keys;
-}
+  list = silc_dlist_init();
+  if (!list)
+    return NULL;
 
-/* Frees the SilcChannelPrivateKey array. */
+  silc_dlist_start(channel->internal.private_keys);
+  while ((entry = silc_dlist_get(channel->internal.private_keys)))
+    silc_dlist_add(list, entry);
 
-void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
-                                          SilcUInt32 key_count)
-{
-  silc_free(keys);
+  return list;
 }
 
 /* Sets the `key' to be used as current channel private key on the
@@ -722,10 +647,13 @@ void silc_client_current_channel_private_key(SilcClient client,
                                             SilcChannelEntry channel,
                                             SilcChannelPrivateKey key)
 {
-  assert(client && channel);
-  channel->curr_key = key;
+  if (!channel)
+    return;
+  channel->internal.curr_key = key;
 }
 
+/***************************** Utility routines *****************************/
+
 /* Returns the SilcChannelUser entry if the `client_entry' is joined on the
    channel indicated by the `channel'. NULL if client is not joined on
    the channel. */
@@ -741,3 +669,64 @@ SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
 
   return NULL;
 }
+
+/* Adds client to channel */
+
+SilcBool silc_client_add_to_channel(SilcChannelEntry channel,
+                                   SilcClientEntry client_entry,
+                                   SilcUInt32 cumode)
+{
+  SilcChannelUser chu;
+
+  if (silc_client_on_channel(channel, client_entry))
+    return FALSE;
+
+  chu = silc_calloc(1, sizeof(*chu));
+  if (!chu)
+    return FALSE;
+
+  chu->client = client_entry;
+  chu->channel = channel;
+  chu->mode = cumode;
+  silc_hash_table_add(channel->user_list, client_entry, chu);
+  silc_hash_table_add(client_entry->channels, channel, chu);
+
+  return TRUE;
+}
+
+/* Removes client from a channel */
+
+SilcBool silc_client_remove_from_channel(SilcChannelEntry channel,
+                                        SilcClientEntry client_entry)
+{
+  SilcChannelUser chu;
+
+  chu = silc_client_on_channel(channel, client_entry);
+  if (!chu)
+    return FALSE;
+
+  silc_hash_table_del(chu->client->channels, chu->channel);
+  silc_hash_table_del(chu->channel->user_list, chu->client);
+  silc_free(chu);
+
+  return TRUE;
+}
+
+/* Removes a client entry from all channels it has joined. */
+
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry)
+{
+  SilcHashTableList htl;
+  SilcChannelUser chu;
+
+  silc_hash_table_list(client_entry->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+    silc_hash_table_del(chu->client->channels, chu->channel);
+    silc_hash_table_del(chu->channel->user_list, chu->client);
+    silc_free(chu);
+  }
+
+  silc_hash_table_list_reset(&htl);
+}
diff --git a/lib/silcclient/client_channel.h b/lib/silcclient/client_channel.h
new file mode 100644 (file)
index 0000000..ac6b56a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+
+  client_channel.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2006 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_CHANNEL_H
+#define CLIENT_CHANNEL_H
+
+SILC_FSM_STATE(silc_client_channel_message);
+SILC_FSM_STATE(silc_client_channel_message_error);
+SILC_FSM_STATE(silc_client_channel_key);
+
+SilcBool silc_client_save_channel_key(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcBuffer key_payload,
+                                     SilcChannelEntry channel);
+SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
+                                      SilcClientEntry client_entry);
+SilcBool silc_client_add_to_channel(SilcChannelEntry channel,
+                                   SilcClientEntry client_entry,
+                                   SilcUInt32 cumode);
+SilcBool silc_client_remove_from_channel(SilcChannelEntry channel,
+                                        SilcClientEntry client_entry);
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry);
+
+#endif /* CLIENT_CHANNEL_H */
index 95e086c7dc902901390f751c72dbb79b02908a3b..bef89fa53da293e4245f3576fe4918a4ac68ff51 100644 (file)
@@ -88,28 +88,16 @@ static void silc_client_connect_callback(SilcNetStatus status,
   if (status != SILC_NET_OK) {
     /* Notify application of failure */
     SILC_LOG_DEBUG(("Connecting failed"));
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0,
+                  NULL, conn->context);
     silc_fsm_next(fsm, silc_client_st_connect_error);
     SILC_FSM_CALL_CONTINUE(fsm);
     return;
   }
 
-  SILC_LOG_DEBUG(("Connected"));
-
-  /* Create packet stream */
-  conn->stream = silc_packet_stream_create(client->internal->packet_engine,
-                                          conn->internal->schedule, stream);
-  if (!conn->stream) {
-    SILC_LOG_DEBUG(("Could not create packet stream"));
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
-    silc_fsm_next(fsm, silc_client_st_connect_error);
-    SILC_FSM_CALL_CONTINUE(fsm);
-    return;
-  }
-
-  silc_packet_set_context(conn->stream, conn);
-
   /* Connection created successfully */
+  SILC_LOG_DEBUG(("Connected"));
+  conn->stream = (void *)stream;
   SILC_FSM_CALL_CONTINUE(fsm);
 }
 
@@ -215,7 +203,8 @@ static void silc_client_ke_completion(SilcSKE ske,
                       "Error during key exchange with %s: cannot use keys",
                       conn->remote_host);
 
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, conn->context);
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
+                  conn->context);
 
     silc_fsm_next(fsm, silc_client_st_connect_error);
     SILC_FSM_CALL_CONTINUE(fsm);
@@ -270,7 +259,8 @@ static void silc_client_connect_auth_completion(SilcConnAuth connauth,
                        client, conn, SILC_CLIENT_MESSAGE_ERROR,
                        "Authentication failed");
 
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, conn->context);
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
+                  conn->context);
     silc_fsm_next(fsm, silc_client_st_connect_error);
   }
 
@@ -289,7 +279,8 @@ SILC_FSM_STATE(silc_client_st_connect)
   SILC_LOG_DEBUG(("Connecting to %s:%d", conn->remote_host,
                  conn->remote_port));
 
-  silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
+  /** Connect */
+  silc_fsm_next(fsm, silc_client_st_connect_set_stream);
 
   if (conn->internal->params.udp) {
     SilcStream stream;
@@ -297,12 +288,13 @@ SILC_FSM_STATE(silc_client_st_connect)
     if (!conn->internal->params.local_ip) {
       /** IP address not given */
       SILC_LOG_ERROR(("Local UDP IP address not specified"));
-      conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+      conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
+                    conn->context);
       silc_fsm_next(fsm, silc_client_st_connect_error);
       return SILC_FSM_CONTINUE;
     }
 
-    /** Connect (UDP) */
+    /* Connect (UDP) */
     stream = silc_net_udp_connect(conn->internal->params.local_ip,
                                  conn->internal->params.local_port,
                                  conn->remote_host, conn->remote_port,
@@ -312,7 +304,7 @@ SILC_FSM_STATE(silc_client_st_connect)
                                               SILC_NET_HOST_UNREACHABLE,
                                               stream, fsm));
   } else {
-    /** Connect (TCP) */
+    /* Connect (TCP) */
     SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host,
                                       conn->remote_port,
                                       conn->internal->schedule,
@@ -320,6 +312,33 @@ SILC_FSM_STATE(silc_client_st_connect)
   }
 }
 
+/* Sets the new connection stream into use and creates packet stream */
+
+SILC_FSM_STATE(silc_client_st_connect_set_stream)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+
+  /* Create packet stream */
+  conn->stream = silc_packet_stream_create(client->internal->packet_engine,
+                                          conn->internal->schedule,
+                                          (SilcStream)conn->stream);
+  if (!conn->stream) {
+    /** Cannot create packet stream */
+    SILC_LOG_DEBUG(("Could not create packet stream"));
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
+                  conn->context);
+    silc_fsm_next(fsm, silc_client_st_connect_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  silc_packet_set_context(conn->stream, conn);
+
+  /** Start key exchange */
+  silc_fsm_next(fsm, silc_client_st_connect_key_exchange);
+  return SILC_FSM_CONTINUE;
+}
+
 /* Starts key exchange protocol with remote host */
 
 SILC_FSM_STATE(silc_client_st_connect_key_exchange)
@@ -337,7 +356,8 @@ SILC_FSM_STATE(silc_client_st_connect_key_exchange)
                   conn->public_key, conn->private_key, fsm);
   if (!conn->internal->ske) {
     /** Out of memory */
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, conn->context);
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_KE, 0, NULL,
+                  conn->context);
     silc_fsm_next(fsm, silc_client_st_connect_error);
     return SILC_FSM_CONTINUE;
   }
@@ -356,12 +376,14 @@ SILC_FSM_STATE(silc_client_st_connect_key_exchange)
     params.session_port = conn->internal->params.local_port;
   }
 
-  /** Start key exchange */
   if (conn->internal->params.no_authentication)
+    /** Run key exchange (no auth) */
     silc_fsm_next(fsm, silc_client_st_connected);
   else if (conn->internal->params.udp)
+    /** Run key exchange (UDP)*/
     silc_fsm_next(fsm, silc_client_st_connect_setup_udp);
   else
+    /** Run key exchange (TCP) */
     silc_fsm_next(fsm, silc_client_st_connect_auth);
 
   SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream,
@@ -388,7 +410,8 @@ SILC_FSM_STATE(silc_client_st_connect_setup_udp)
                                conn->internal->schedule);
   if (!stream) {
     /** Cannot create UDP stream */
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
+                  conn->context);
     silc_fsm_next(fsm, silc_client_st_connect_error);
     return SILC_FSM_CONTINUE;
   }
@@ -421,10 +444,11 @@ SILC_FSM_STATE(silc_client_st_connect_auth)
 
   /* If authentication data not provided, ask from application */
   if (!conn->internal->params.auth_set)
-    SILC_FSM_CALL(client->internal->ops->
-                 get_auth_method(client, conn,
-                                 conn->remote_host, conn->remote_port,
-                                 silc_client_connect_auth_method, fsm));
+    SILC_FSM_CALL(client->internal->ops->get_auth_method(
+                                   client, conn,
+                                   conn->remote_host,
+                                   conn->remote_port,
+                                   silc_client_connect_auth_method, fsm));
 
   if (conn->internal->params.auth_method == SILC_AUTH_PUBLIC_KEY)
     conn->internal->params.auth = conn->private_key;
@@ -449,7 +473,8 @@ SILC_FSM_STATE(silc_client_st_connect_auth_start)
                                 client->internal->params->rekey_secs);
   if (!connauth) {
     /** Out of memory */
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, conn->context);
+    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR_AUTH, 0, NULL,
+                  conn->context);
     silc_fsm_next(fsm, silc_client_st_connect_error);
     return SILC_FSM_CONTINUE;
   }
@@ -491,7 +516,8 @@ SILC_FSM_STATE(silc_client_st_connected)
   }
 
   /* Call connection callback */
-  conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, conn->context);
+  conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
+                conn->context);
 
   return SILC_FSM_FINISH;
 }
index c375ae06498cd2d8a358f03293eedc83c6bfd03b..00d60f2087423da2fcde9be677fef096d4f3ae95 100644 (file)
@@ -22,6 +22,7 @@
 
 /* States */
 SILC_FSM_STATE(silc_client_st_connect);
+SILC_FSM_STATE(silc_client_st_connect_set_stream);
 SILC_FSM_STATE(silc_client_st_connect_key_exchange);
 SILC_FSM_STATE(silc_client_st_connect_setup_udp);
 SILC_FSM_STATE(silc_client_st_connect_auth);
index 87f2c6589ec650c9f0347196e90e99b98d6877da..ce3717aaf0cd98d68e49a7d6c9bbf9496ee3141f 100644 (file)
@@ -46,14 +46,16 @@ SilcClientEntry silc_client_get_client_by_id(SilcClient client,
 
   /* Find ID from cache */
   if (!silc_idcache_find_by_id_one(conn->internal->client_cache, client_id,
-                                  &id_cache))
+                                  &id_cache)) {
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
+  }
 
   client_entry = id_cache->context;
 
   /* Reference */
   silc_client_ref_client(client, conn, client_entry);
-  silc_mutex_lock(conn->internal->lock);
+  silc_mutex_unlock(conn->internal->lock);
 
   SILC_LOG_DEBUG(("Found"));
 
@@ -161,6 +163,7 @@ static SilcBool silc_client_get_clients_cb(SilcClient client,
     client_entry = va_arg(ap, SilcClientEntry);
     silc_client_ref_client(client, conn, client_entry);
     silc_dlist_add(i->clients, client_entry);
+    client_entry->internal.resolve_cmd_ident = 0;
   }
 
   if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
@@ -191,7 +194,9 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
                                          void *context)
 {
   SilcClientGetClientInternal i;
+  SilcClientEntry client_entry;
   SilcBuffer idp;
+  SilcUInt16 cmd_ident;
 
   if (!client || !conn | !client_id)
     return;
@@ -204,13 +209,36 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
     return;
   i->completion = completion;
   i->context = context;
+  i->clients = silc_dlist_init();
+  if (!i->clients) {
+    silc_free(i);
+    return;
+  }
+
+  /* Attach to resolving, if on going */
+  client_entry = silc_client_get_client_by_id(client, conn, client_id);
+  if (client_entry && client_entry->internal.resolve_cmd_ident) {
+    SILC_LOG_DEBUG(("Attach to existing resolving"));
+    silc_client_unref_client(client, conn, client_entry);
+    silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                               client_entry->internal.resolve_cmd_ident,
+                               silc_client_get_clients_cb, i);
+    return;
+  }
 
   /* Send the command */
   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-  silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
-                          silc_client_get_clients_cb, i,
-                          2, 3, silc_buffer_datalen(attributes),
-                          4, silc_buffer_datalen(idp));
+  cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_WHOIS,
+                                      silc_client_get_clients_cb, i,
+                                      2, 3, silc_buffer_datalen(attributes),
+                                      4, silc_buffer_datalen(idp));
+  if (!cmd_ident)
+    completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
+
+  if (client_entry && cmd_ident)
+    client_entry->internal.resolve_cmd_ident = cmd_ident;
+
+  silc_client_unref_client(client, conn, client_entry);
   silc_buffer_free(idp);
 }
 
@@ -243,8 +271,10 @@ static SilcUInt16 silc_client_get_clients_i(SilcClient client,
   if (!i)
     return 0;
   i->clients = silc_dlist_init();
-  if (!i->clients)
+  if (!i->clients) {
+    silc_free(i);
     return 0;
+  }
   i->completion = completion;
   i->context = context;
 
@@ -692,6 +722,8 @@ SilcClientEntry silc_client_add_client(SilcClient client,
   /* Format the nickname */
   silc_client_nickname_format(client, conn, client_entry);
 
+  silc_mutex_lock(conn->internal->lock);
+
   /* Add client to cache, the normalized nickname is saved to cache */
   if (!silc_idcache_add(conn->internal->client_cache, nick,
                        &client_entry->id, client_entry)) {
@@ -699,11 +731,14 @@ SilcClientEntry silc_client_add_client(SilcClient client,
     silc_free(client_entry->realname);
     silc_hash_table_free(client_entry->channels);
     silc_free(client_entry);
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
   }
 
   client_entry->nickname_normalized = nick;
 
+  silc_mutex_unlock(conn->internal->lock);
+
   return client_entry;
 }
 
@@ -833,147 +868,154 @@ void silc_client_list_free(SilcClient client, SilcClientConnection conn,
 }
 
 
-/************************* Channel Entry Routines ***************************/
-
-/* Add new channel entry to the ID Cache */
+/* Formats the nickname of the client specified by the `client_entry'.
+   If the format is specified by the application this will format the
+   nickname and replace the old nickname in the client entry. If the
+   format string is not specified then this function has no effect. */
 
-SilcChannelEntry silc_client_add_channel(SilcClient client,
-                                        SilcClientConnection conn,
-                                        const char *channel_name,
-                                        SilcUInt32 mode,
-                                        SilcChannelID *channel_id)
+void silc_client_nickname_format(SilcClient client,
+                                SilcClientConnection conn,
+                                SilcClientEntry client_entry)
 {
-  SilcChannelEntry channel;
-  char *channel_namec;
+  char *cp;
+  char newnick[128 + 1];
+  int i, off = 0, len;
+  SilcBool freebase;
+  SilcDList clients;
+  SilcClientEntry entry, unformatted = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  channel = silc_calloc(1, sizeof(*channel));
-  channel->channel_name = strdup(channel_name);
-  channel->id = channel_id;
-  channel->mode = mode;
-  channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
-                                            NULL, NULL, NULL, TRUE);
-
-  /* Normalize channel name */
-  channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
-                                         SILC_STRING_UTF8, 256, NULL);
-  if (!channel_namec) {
-    silc_free(channel->channel_name);
-    silc_hash_table_free(channel->user_list);
-    silc_free(channel);
-    return NULL;
-  }
-
-  /* Put it to the ID cache */
-  if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
-                       (void *)channel->id, (void *)channel)) {
-    silc_free(channel_namec);
-    silc_free(channel->channel_name);
-    silc_hash_table_free(channel->user_list);
-    silc_free(channel);
-    return NULL;
-  }
+  if (!client->internal->params->nickname_format[0])
+    return;
 
-  return channel;
-}
+  if (!client_entry->nickname[0])
+    return;
 
-/* Foreach callbcak to free all users from the channel when deleting a
-   channel entry. */
+  /* Get all clients with same nickname. Do not perform the formatting
+     if there aren't any clients with same nickname unless the application
+     is forcing us to do so. */
+  clients = silc_client_get_clients_local(client, conn,
+                                         client_entry->nickname, NULL);
+  if (!clients && !client->internal->params->nickname_force_format)
+    return;
 
-static void silc_client_del_channel_foreach(void *key, void *context,
-                                           void *user_context)
-{
-  SilcChannelUser chu = (SilcChannelUser)context;
+  len = 0;
+  freebase = TRUE;
+  while ((entry = silc_dlist_get(clients))) {
+    if (entry->internal.valid && entry != client_entry)
+      len++;
+    if (entry->internal.valid && entry != client_entry &&
+       silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
+      freebase = FALSE;
+      unformatted = entry;
+    }
+  }
+  if (!len || freebase)
+    return;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* If we are changing nickname of our local entry we'll enforce
+     that we will always get the unformatted nickname.  Give our
+     format number to the one that is not formatted now. */
+  if (unformatted && client_entry == conn->local_entry)
+    client_entry = unformatted;
 
-  /* Remove the context from the client's channel hash table as that
-     table and channel's user_list hash table share this same context. */
-  silc_hash_table_del(chu->client->channels, chu->channel);
-  silc_free(chu);
-}
+  memset(newnick, 0, sizeof(newnick));
+  cp = client->internal->params->nickname_format;
+  while (*cp) {
+    if (*cp == '%') {
+      cp++;
+      continue;
+    }
 
-/* Removes channel from the cache by the channel entry. */
+    switch(*cp) {
+    case 'n':
+      /* Nickname */
+      if (!client_entry->nickname[0])
+       break;
+      len = strlen(client_entry->nickname);
+      memcpy(&newnick[off], client_entry->nickname, len);
+      off += len;
+      break;
+    case 'h':
+      /* Stripped hostname */
+      if (!client_entry->hostname[0])
+       break;
+      len = strcspn(client_entry->hostname, ".");
+      i = strcspn(client_entry->hostname, "-");
+      if (i < len)
+        len = i;
+      memcpy(&newnick[off], client_entry->hostname, len);
+      off += len;
+      break;
+    case 'H':
+      /* Full hostname */
+      if (!client_entry->hostname[0])
+       break;
+      len = strlen(client_entry->hostname);
+      memcpy(&newnick[off], client_entry->hostname, len);
+      off += len;
+      break;
+    case 's':
+      /* Stripped server name */
+      if (!client_entry->server)
+       break;
+      len = strcspn(client_entry->server, ".");
+      memcpy(&newnick[off], client_entry->server, len);
+      off += len;
+      break;
+    case 'S':
+      /* Full server name */
+      if (!client_entry->server)
+       break;
+      len = strlen(client_entry->server);
+      memcpy(&newnick[off], client_entry->server, len);
+      off += len;
+      break;
+    case 'a':
+      /* Ascending number */
+      {
+       char tmp[6];
+       int num, max = 1;
 
-SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
-                                SilcChannelEntry channel)
-{
-  SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
-                                            channel, NULL);
+       if (silc_dlist_count(clients) == 1)
+         break;
 
-  SILC_LOG_DEBUG(("Start"));
+       silc_dlist_start(clients);
+       while ((entry = silc_dlist_get(clients))) {
+         if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
+           continue;
+         if (strlen(entry->nickname) <= off)
+           continue;
+         num = atoi(&entry->nickname[off]);
+         if (num > max)
+           max = num;
+       }
 
-  /* Free all client entrys from the users list. The silc_hash_table_free
-     will free all the entries so they are not freed at the foreach
-     callback. */
-  silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
-                         NULL);
-  silc_hash_table_free(channel->user_list);
+       memset(tmp, 0, sizeof(tmp));
+       snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
+       len = strlen(tmp);
+       memcpy(&newnick[off], tmp, len);
+       off += len;
+      }
+      break;
+    default:
+      /* Some other character in the string */
+      memcpy(&newnick[off], cp, 1);
+      off++;
+      break;
+    }
 
-  silc_free(channel->channel_name);
-  silc_free(channel->topic);
-  silc_free(channel->id);
-  if (channel->founder_key)
-    silc_pkcs_public_key_free(channel->founder_key);
-  silc_free(channel->key);
-  if (channel->channel_key)
-    silc_cipher_free(channel->channel_key);
-  if (channel->hmac)
-    silc_hmac_free(channel->hmac);
-  if (channel->old_channel_keys) {
-    SilcCipher key;
-    silc_dlist_start(channel->old_channel_keys);
-    while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
-      silc_cipher_free(key);
-    silc_dlist_uninit(channel->old_channel_keys);
-  }
-  if (channel->old_hmacs) {
-    SilcHmac hmac;
-    silc_dlist_start(channel->old_hmacs);
-    while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
-      silc_hmac_free(hmac);
-    silc_dlist_uninit(channel->old_hmacs);
+    cp++;
   }
-  silc_schedule_task_del_by_context(conn->client->schedule, channel);
-  silc_client_del_channel_private_keys(client, conn, channel);
-  silc_free(channel);
-  return ret;
-}
-
-/* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
-   if the ID could not be changed. */
-
-SilcBool silc_client_replace_channel_id(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcChannelEntry channel,
-                                       SilcChannelID *new_id)
-{
-  char *channel_namec;
 
-  if (!new_id)
-    return FALSE;
-
-  SILC_LOG_DEBUG(("Old Channel ID id(%s)",
-                 silc_id_render(channel->id, SILC_ID_CHANNEL)));
-  SILC_LOG_DEBUG(("New Channel ID id(%s)",
-                 silc_id_render(new_id, SILC_ID_CHANNEL)));
-
-  silc_idcache_del_by_id(conn->internal->channel_cache, channel->id, NULL);
-  silc_free(channel->id);
-  channel->id = new_id;
-
-  /* Normalize channel name */
-  channel_namec = silc_channel_name_check(channel->channel_name,
-                                         strlen(channel->channel_name),
-                                         SILC_STRING_UTF8, 256, NULL);
-  if (!channel_namec)
-    return FALSE;
-
-  return silc_idcache_add(conn->internal->channel_cache, channel_namec,
-                         channel->id, channel) != NULL;
+  newnick[off] = 0;
+  memcpy(client_entry->nickname, newnick, strlen(newnick));
+  silc_client_list_free(client, conn, clients);
 }
 
+/************************ Channel Searching Locally *************************/
+
 /* Finds entry for channel by the channel name. Returns the entry or NULL
    if the entry was not found. It is found only if the client is joined
    to the channel. */
@@ -985,11 +1027,10 @@ SilcChannelEntry silc_client_get_channel(SilcClient client,
   SilcIDCacheEntry id_cache;
   SilcChannelEntry entry;
 
-  assert(client && conn);
-  if (!channel)
+  if (!client || !conn || !channel)
     return NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Find channel %s", channel));
 
   /* Normalize name for search */
   channel = silc_channel_name_check(channel, strlen(channel), SILC_STRING_UTF8,
@@ -997,16 +1038,23 @@ SilcChannelEntry silc_client_get_channel(SilcClient client,
   if (!channel)
     return NULL;
 
+  silc_mutex_lock(conn->internal->lock);
+
   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
                                     &id_cache)) {
+    silc_mutex_unlock(conn->internal->lock);
     silc_free(channel);
     return NULL;
   }
 
-  entry = (SilcChannelEntry)id_cache->context;
-
   SILC_LOG_DEBUG(("Found"));
 
+  entry = id_cache->context;
+
+  /* Reference */
+  silc_client_ref_channel(client, conn, entry);
+  silc_mutex_unlock(conn->internal->lock);
+
   silc_free(channel);
 
   return entry;
@@ -1023,52 +1071,83 @@ SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
   SilcIDCacheEntry id_cache;
   SilcChannelEntry entry;
 
-  assert(client && conn);
-  if (!channel_id)
+  if (!client || !conn || !channel_id)
     return NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Find channel by id %s",
+                 silc_id_render(channel_id, SILC_ID_CHANNEL)));
+
+  silc_mutex_lock(conn->internal->lock);
 
   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
-                                  &id_cache))
+                                  &id_cache)) {
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
-
-  entry = (SilcChannelEntry)id_cache->context;
+  }
 
   SILC_LOG_DEBUG(("Found"));
 
+  entry = id_cache->context;
+
+  /* Reference */
+  silc_client_ref_channel(client, conn, entry);
+  silc_mutex_unlock(conn->internal->lock);
+
   return entry;
 }
 
-#if 0
+/********************** Channel Resolving from Server ***********************/
+
+/* Channel resolving context */
 typedef struct {
-  SilcClient client;
-  SilcClientConnection conn;
-  union {
-    SilcChannelID *channel_id;
-    char *channel_name;
-  } u;
+  SilcDList channels;
   SilcGetChannelCallback completion;
   void *context;
-} *GetChannelInternal;
+} *SilcClientGetChannelInternal;
+
+/* Resolving command callback */
 
-SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
+static SilcBool silc_client_get_channel_cb(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcCommand command,
+                                          SilcStatus status,
+                                          SilcStatus error,
+                                          void *context,
+                                          va_list ap)
 {
-  GetChannelInternal i = (GetChannelInternal)context;
+  SilcClientGetChannelInternal i = context;
   SilcChannelEntry entry;
 
-  SILC_LOG_DEBUG(("Start"));
+  if (error != SILC_STATUS_OK) {
+    SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
+    if (i->completion)
+      i->completion(client, conn, error, NULL, i->context);
+    goto out;
+  }
 
-  /* Get the channel */
-  entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
-  if (entry) {
-    i->completion(i->client, i->conn, &entry, 1, i->context);
-  } else {
-    i->completion(i->client, i->conn, NULL, 0, i->context);
+  /* Add the returned channel to list */
+  if (i->completion) {
+    entry = va_arg(ap, SilcChannelEntry);
+    silc_client_ref_channel(client, conn, entry);
+    silc_dlist_add(i->channels, entry);
   }
 
-  silc_free(i->u.channel_name);
+  if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
+    /* Deliver the channels to the caller */
+    if (i->completion) {
+      SILC_LOG_DEBUG(("Resolved %d channels", silc_dlist_count(i->channels)));
+      silc_dlist_start(i->channels);
+      i->completion(client, conn, SILC_STATUS_OK, i->channels, i->context);
+    }
+    goto out;
+  }
+
+  return TRUE;
+
+ out:
+  silc_client_list_free_channels(client, conn, i->channels);
   silc_free(i);
+  return FALSE;
 }
 
 /* Resolves channel entry from the server by the channel name. */
@@ -1079,51 +1158,29 @@ void silc_client_get_channel_resolve(SilcClient client,
                                     SilcGetChannelCallback completion,
                                     void *context)
 {
-  GetChannelInternal i = silc_calloc(1, sizeof(*i));
+  SilcClientGetChannelInternal i;
 
-  assert(client && conn && channel_name);
+  if (!client || !conn || !channel_name || !completion)
+    return;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Resolve channel %s", channel_name));
 
-  i->client = client;
-  i->conn = conn;
-  i->u.channel_name = strdup(channel_name);
+  i = silc_calloc(1, sizeof(*i));
+  if (!i)
+    return;
   i->completion = completion;
   i->context = context;
-
-  /* Register our own command reply for this command */
-  silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
-                              silc_client_command_reply_identify_i, 0,
-                              ++conn->cmd_ident);
-
-  /* Send the command */
-  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
-                          conn->cmd_ident,
-                          1, 3, channel_name, strlen(channel_name));
-
-  /* Add pending callback */
-  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
-                             silc_client_command_get_channel_resolve_callback,
-                             (void *)i);
-}
-
-SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
-{
-  GetChannelInternal i = (GetChannelInternal)context;
-  SilcChannelEntry entry;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Get the channel */
-  entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
-  if (entry) {
-    i->completion(i->client, i->conn, &entry, 1, i->context);
-  } else {
-    i->completion(i->client, i->conn, NULL, 0, i->context);
+  i->channels = silc_dlist_init();
+  if (!i->channels) {
+    silc_free(i);
+    return;
   }
 
-  silc_free(i->u.channel_id);
-  silc_free(i);
+  /* Send the command */
+  if (!silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                               silc_client_get_channel_cb, i, 1,
+                               3, channel_name, strlen(channel_name)))
+    completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
 }
 
 /* Resolves channel information from the server by the channel ID. */
@@ -1134,37 +1191,245 @@ void silc_client_get_channel_by_id_resolve(SilcClient client,
                                           SilcGetChannelCallback completion,
                                           void *context)
 {
+  SilcClientGetChannelInternal i;
+  SilcChannelEntry channel;
   SilcBuffer idp;
-  GetChannelInternal i = silc_calloc(1, sizeof(*i));
+  SilcUInt16 cmd_ident;
 
-  assert(client && conn && channel_id);
+  if (!client || !conn || !channel_id || !completion)
+    return;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Resolve channel by id %s",
+                 silc_id_render(channel_id, SILC_ID_CHANNEL)));
 
-  i->client = client;
-  i->conn = conn;
-  i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
+  i = silc_calloc(1, sizeof(*i));
+  if (!i)
+    return;
   i->completion = completion;
   i->context = context;
+  i->channels = silc_dlist_init();
+  if (!i->channels) {
+    silc_free(i);
+    return;
+  }
 
-  /* Register our own command reply for this command */
-  silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
-                              silc_client_command_reply_identify_i, 0,
-                              ++conn->cmd_ident);
+  /* Attach to resolving, if on going */
+  channel = silc_client_get_channel_by_id(client, conn, channel_id);
+  if (channel && channel->internal.resolve_cmd_ident) {
+    SILC_LOG_DEBUG(("Attach to existing resolving"));
+    silc_client_unref_channel(client, conn, channel);
+    silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                               channel->internal.resolve_cmd_ident,
+                               silc_client_get_channel_cb, i);
+    return;
+  }
 
   /* Send the command */
   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
-                          conn->cmd_ident,
-                          1, 5, idp->data, idp->len);
+  cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                                      silc_client_get_channel_cb, i, 1,
+                                      5, silc_buffer_datalen(idp));
   silc_buffer_free(idp);
+  if (!cmd_ident)
+    completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
+
+  if (channel && cmd_ident)
+    channel->internal.resolve_cmd_ident = cmd_ident;
 
-  /* Add pending callback */
-  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
-                             silc_client_command_get_channel_by_id_callback,
-                             (void *)i);
+  silc_client_unref_channel(client, conn, channel);
 }
-#endif /* 0 */
+
+/************************* Channel Entry Routines ***************************/
+
+/* Add new channel entry to the ID Cache */
+
+SilcChannelEntry silc_client_add_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        const char *channel_name,
+                                        SilcUInt32 mode,
+                                        SilcChannelID *channel_id)
+{
+  SilcChannelEntry channel;
+  char *channel_namec;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  channel = silc_calloc(1, sizeof(*channel));
+  if (!channel)
+    return NULL;
+
+  channel->id = *channel_id;
+  channel->mode = mode;
+
+  channel->channel_name = strdup(channel_name);
+  if (!channel->channel_name) {
+    silc_free(channel);
+    return NULL;
+  }
+
+  channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
+                                            NULL, NULL, NULL, TRUE);
+  if (!channel->user_list) {
+    silc_free(channel->channel_name);
+    silc_free(channel);
+    return NULL;
+  }
+
+  /* Normalize channel name */
+  channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
+                                         SILC_STRING_UTF8, 256, NULL);
+  if (!channel_namec) {
+    silc_free(channel->channel_name);
+    silc_hash_table_free(channel->user_list);
+    silc_free(channel);
+    return NULL;
+  }
+
+  /* Add channel to cache, the normalized channel name is saved to cache */
+  if (!silc_idcache_add(conn->internal->channel_cache, channel_namec,
+                       &channel->id, channel)) {
+    silc_free(channel_namec);
+    silc_free(channel->channel_name);
+    silc_hash_table_free(channel->user_list);
+    silc_free(channel);
+    return NULL;
+  }
+
+  return channel;
+}
+
+/* Foreach callbcak to free all users from the channel when deleting a
+   channel entry. */
+
+static void silc_client_del_channel_foreach(void *key, void *context,
+                                           void *user_context)
+{
+  SilcChannelUser chu = (SilcChannelUser)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Remove the context from the client's channel hash table as that
+     table and channel's user_list hash table share this same context. */
+  silc_hash_table_del(chu->client->channels, chu->channel);
+  silc_free(chu);
+}
+
+/* Removes channel from the cache by the channel entry. */
+
+SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
+                                SilcChannelEntry channel)
+{
+  SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
+                                            channel, NULL);
+#if 0
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Free all client entrys from the users list. The silc_hash_table_free
+     will free all the entries so they are not freed at the foreach
+     callback. */
+  silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
+                         NULL);
+  silc_hash_table_free(channel->user_list);
+
+  silc_free(channel->channel_name);
+  silc_free(channel->topic);
+  if (channel->founder_key)
+    silc_pkcs_public_key_free(channel->founder_key);
+  silc_free(channel->key);
+  if (channel->channel_key)
+    silc_cipher_free(channel->channel_key);
+  if (channel->hmac)
+    silc_hmac_free(channel->hmac);
+  if (channel->old_channel_keys) {
+    SilcCipher key;
+    silc_dlist_start(channel->old_channel_keys);
+    while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
+      silc_cipher_free(key);
+    silc_dlist_uninit(channel->old_channel_keys);
+  }
+  if (channel->old_hmacs) {
+    SilcHmac hmac;
+    silc_dlist_start(channel->old_hmacs);
+    while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
+      silc_hmac_free(hmac);
+    silc_dlist_uninit(channel->old_hmacs);
+  }
+  silc_schedule_task_del_by_context(conn->client->schedule, channel);
+  silc_client_del_channel_private_keys(client, conn, channel);
+  silc_free(channel);
+#endif
+  return ret;
+}
+
+/* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
+   if the ID could not be changed. */
+
+SilcBool silc_client_replace_channel_id(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       SilcChannelID *new_id)
+{
+  SilcIDCacheEntry id_cache;
+  SilcBool ret = FALSE;
+
+  if (!new_id)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Old Channel ID id(%s)",
+                 silc_id_render(&channel->id, SILC_ID_CHANNEL)));
+  SILC_LOG_DEBUG(("New Channel ID id(%s)",
+                 silc_id_render(new_id, SILC_ID_CHANNEL)));
+
+  silc_mutex_lock(conn->internal->lock);
+
+  /* Update the ID */
+  if (silc_idcache_find_by_id_one(conn->internal->channel_cache,
+                                  &channel->id, &id_cache))
+    ret = silc_idcache_update(conn->internal->channel_cache, id_cache,
+                             &channel->id, new_id, NULL, NULL, FALSE);
+
+  silc_mutex_unlock(conn->internal->lock);
+
+  return ret;
+}
+
+/* Take reference of channel entry */
+
+void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
+                            SilcChannelEntry channel_entry)
+{
+  silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
+}
+
+/* Release reference of channel entry */
+
+void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
+                              SilcChannelEntry channel_entry)
+{
+  if (channel_entry &&
+      silc_atomic_sub_int8(&channel_entry->internal.refcnt, 1) == 0)
+    silc_client_del_channel(client, conn, channel_entry);
+}
+
+/* Free channel entry list */
+
+void silc_client_list_free_channels(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcDList channel_list)
+{
+  SilcChannelEntry channel_entry;
+
+  if (channel_list) {
+    silc_dlist_start(channel_list);
+    while ((channel_entry = silc_dlist_get(channel_list)))
+      silc_client_unref_channel(client, conn, channel_entry);
+
+    silc_dlist_uninit(channel_list);
+  }
+}
+
+/************************* Server Searching Locally *************************/
 
 /* Finds entry for server by the server name. */
 
@@ -1175,11 +1440,10 @@ SilcServerEntry silc_client_get_server(SilcClient client,
   SilcIDCacheEntry id_cache;
   SilcServerEntry entry;
 
-  assert(client && conn);
-  if (!server_name)
+  if (!client || !conn || !server_name)
     return NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Find server by name %s", server_name));
 
   /* Normalize server name for search */
   server_name = silc_identifier_check(server_name, strlen(server_name),
@@ -1187,13 +1451,21 @@ SilcServerEntry silc_client_get_server(SilcClient client,
   if (!server_name)
     return NULL;
 
+  silc_mutex_lock(conn->internal->lock);
+
   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
                                     server_name, &id_cache)) {
     silc_free(server_name);
     return NULL;
   }
 
-  entry = (SilcServerEntry)id_cache->context;
+  SILC_LOG_DEBUG(("Found"));
+
+  /* Reference */
+  entry = id_cache->context;
+  silc_client_ref_server(client, conn, entry);
+
+  silc_mutex_unlock(conn->internal->lock);
 
   silc_free(server_name);
 
@@ -1209,21 +1481,142 @@ SilcServerEntry silc_client_get_server_by_id(SilcClient client,
   SilcIDCacheEntry id_cache;
   SilcServerEntry entry;
 
-  assert(client && conn);
-  if (!server_id)
+  if (!client || !conn || !server_id)
     return NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Find server by id %s",
+                 silc_id_render(server_id, SILC_ID_SERVER)));
+
+  silc_mutex_lock(conn->internal->lock);
 
   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
                                   (void *)server_id, &id_cache))
     return NULL;
 
+  SILC_LOG_DEBUG(("Found"));
+
+  /* Reference */
   entry = (SilcServerEntry)id_cache->context;
+  silc_client_ref_server(client, conn, entry);
+
+  silc_mutex_unlock(conn->internal->lock);
 
   return entry;
 }
 
+/*********************** Server Resolving from Server ***********************/
+
+/* Resolving context */
+typedef struct {
+  SilcDList servers;
+  SilcGetServerCallback completion;
+  void *context;
+} *SilcClientGetServerInternal;
+
+/* Resolving command callback */
+
+static SilcBool silc_client_get_server_cb(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcCommand command,
+                                         SilcStatus status,
+                                         SilcStatus error,
+                                         void *context,
+                                         va_list ap)
+{
+  SilcClientGetServerInternal i = context;
+  SilcServerEntry server;
+
+  if (error != SILC_STATUS_OK) {
+    SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
+    if (i->completion)
+      i->completion(client, conn, error, NULL, i->context);
+    goto out;
+  }
+
+  /* Add the returned servers to list */
+  if (i->completion) {
+    server = va_arg(ap, SilcServerEntry);
+    silc_client_ref_server(client, conn, server);
+    silc_dlist_add(i->servers, server);
+    server->internal.resolve_cmd_ident = 0;
+  }
+
+  if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END) {
+    /* Deliver the servers to the caller */
+    if (i->completion) {
+      SILC_LOG_DEBUG(("Resolved %d servers", silc_dlist_count(i->servers)));
+      silc_dlist_start(i->servers);
+      i->completion(client, conn, SILC_STATUS_OK, i->servers, i->context);
+    }
+    goto out;
+  }
+
+  return TRUE;
+
+ out:
+  silc_client_list_free_servers(client, conn, i->servers);
+  silc_free(i);
+  return FALSE;
+}
+
+/* Resolve server by server ID */
+
+void silc_client_get_server_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcServerID *server_id,
+                                         SilcGetServerCallback completion,
+                                         void *context)
+{
+  SilcClientGetServerInternal i;
+  SilcServerEntry server;
+  SilcBuffer idp;
+  SilcUInt16 cmd_ident;
+
+  if (!client || !conn || !server_id || !completion)
+    return;
+
+  SILC_LOG_DEBUG(("Resolve server by id %s",
+                 silc_id_render(server_id, SILC_ID_SERVER)));
+
+  i = silc_calloc(1, sizeof(*i));
+  if (!i)
+    return;
+  i->completion = completion;
+  i->context = context;
+  i->servers = silc_dlist_init();
+  if (!i->servers) {
+    silc_free(i);
+    return;
+  }
+
+  /* Attach to resolving, if on going */
+  server = silc_client_get_server_by_id(client, conn, server_id);
+  if (server && server->internal.resolve_cmd_ident) {
+    SILC_LOG_DEBUG(("Attach to existing resolving"));
+    silc_client_unref_server(client, conn, server);
+    silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                               server->internal.resolve_cmd_ident,
+                               silc_client_get_server_cb, i);
+    return;
+  }
+
+  /* Send the command */
+  idp = silc_id_payload_encode(server_id, SILC_ID_SERVER);
+  cmd_ident = silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                                      silc_client_get_server_cb, i, 1,
+                                      5, silc_buffer_datalen(idp));
+  silc_buffer_free(idp);
+  if (!cmd_ident)
+    completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
+
+  if (server && cmd_ident)
+    server->internal.resolve_cmd_ident = cmd_ident;
+
+  silc_client_unref_server(client, conn, server);
+}
+
+/************************** Server Entry Routines ***************************/
+
 /* Add new server entry */
 
 SilcServerEntry silc_client_add_server(SilcClient client,
@@ -1241,7 +1634,7 @@ SilcServerEntry silc_client_add_server(SilcClient client,
   if (!server_entry || !server_id)
     return NULL;
 
-  server_entry->server_id = server_id;
+  server_entry->id = *server_id;
   if (server_name)
     server_entry->server_name = strdup(server_name);
   if (server_info)
@@ -1252,7 +1645,6 @@ SilcServerEntry silc_client_add_server(SilcClient client,
     server_namec = silc_identifier_check(server_name, strlen(server_name),
                                         SILC_STRING_UTF8, 256, NULL);
     if (!server_namec) {
-      silc_free(server_entry->server_id);
       silc_free(server_entry->server_name);
       silc_free(server_entry->server_info);
       silc_free(server_entry);
@@ -1260,30 +1652,33 @@ SilcServerEntry silc_client_add_server(SilcClient client,
     }
   }
 
+  silc_mutex_lock(conn->internal->lock);
+
   /* Add server to cache */
   if (!silc_idcache_add(conn->internal->server_cache, server_namec,
-                       server_entry->server_id, server_entry)) {
+                       &server_entry->id, server_entry)) {
     silc_free(server_namec);
-    silc_free(server_entry->server_id);
     silc_free(server_entry->server_name);
     silc_free(server_entry->server_info);
     silc_free(server_entry);
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
   }
 
+  silc_mutex_unlock(conn->internal->lock);
+
   return server_entry;
 }
 
 /* Removes server from the cache by the server entry. */
 
 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
-                           SilcServerEntry server)
+                               SilcServerEntry server)
 {
   SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
                                             server, NULL);
   silc_free(server->server_name);
   silc_free(server->server_info);
-  silc_free(server->server_id);
   silc_free(server);
   return ret;
 }
@@ -1317,7 +1712,7 @@ void silc_client_update_server(SilcClient client,
        return;
 
       silc_idcache_add(conn->internal->server_cache, server_namec,
-                      server_entry->server_id, server_entry);
+                      &server_entry->id, server_entry);
     }
   }
 
@@ -1329,148 +1724,37 @@ void silc_client_update_server(SilcClient client,
   }
 }
 
-/* Formats the nickname of the client specified by the `client_entry'.
-   If the format is specified by the application this will format the
-   nickname and replace the old nickname in the client entry. If the
-   format string is not specified then this function has no effect. */
+/* Take reference of server entry */
 
-void silc_client_nickname_format(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcClientEntry client_entry)
+void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
+                           SilcServerEntry server_entry)
 {
-  char *cp;
-  char newnick[128 + 1];
-  int i, off = 0, len;
-  SilcBool freebase;
-  SilcDList clients;
-  SilcClientEntry entry, unformatted = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!client->internal->params->nickname_format[0])
-    return;
-
-  if (!client_entry->nickname[0])
-    return;
-
-  /* Get all clients with same nickname. Do not perform the formatting
-     if there aren't any clients with same nickname unless the application
-     is forcing us to do so. */
-  clients = silc_client_get_clients_local(client, conn,
-                                         client_entry->nickname, NULL);
-  if (!clients && !client->internal->params->nickname_force_format)
-    return;
-
-  len = 0;
-  freebase = TRUE;
-  while ((entry = silc_dlist_get(clients))) {
-    if (entry->internal.valid && entry != client_entry)
-      len++;
-    if (entry->internal.valid && entry != client_entry &&
-       silc_utf8_strcasecmp(entry->nickname, client_entry->nickname)) {
-      freebase = FALSE;
-      unformatted = entry;
-    }
-  }
-  if (!len || freebase)
-    return;
-
-  /* If we are changing nickname of our local entry we'll enforce
-     that we will always get the unformatted nickname.  Give our
-     format number to the one that is not formatted now. */
-  if (unformatted && client_entry == conn->local_entry)
-    client_entry = unformatted;
+  silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
+}
 
-  memset(newnick, 0, sizeof(newnick));
-  cp = client->internal->params->nickname_format;
-  while (*cp) {
-    if (*cp == '%') {
-      cp++;
-      continue;
-    }
+/* Release reference of server entry */
 
-    switch(*cp) {
-    case 'n':
-      /* Nickname */
-      if (!client_entry->nickname[0])
-       break;
-      len = strlen(client_entry->nickname);
-      memcpy(&newnick[off], client_entry->nickname, len);
-      off += len;
-      break;
-    case 'h':
-      /* Stripped hostname */
-      if (!client_entry->hostname[0])
-       break;
-      len = strcspn(client_entry->hostname, ".");
-      i = strcspn(client_entry->hostname, "-");
-      if (i < len)
-        len = i;
-      memcpy(&newnick[off], client_entry->hostname, len);
-      off += len;
-      break;
-    case 'H':
-      /* Full hostname */
-      if (!client_entry->hostname[0])
-       break;
-      len = strlen(client_entry->hostname);
-      memcpy(&newnick[off], client_entry->hostname, len);
-      off += len;
-      break;
-    case 's':
-      /* Stripped server name */
-      if (!client_entry->server)
-       break;
-      len = strcspn(client_entry->server, ".");
-      memcpy(&newnick[off], client_entry->server, len);
-      off += len;
-      break;
-    case 'S':
-      /* Full server name */
-      if (!client_entry->server)
-       break;
-      len = strlen(client_entry->server);
-      memcpy(&newnick[off], client_entry->server, len);
-      off += len;
-      break;
-    case 'a':
-      /* Ascending number */
-      {
-       char tmp[6];
-       int num, max = 1;
+void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
+                             SilcServerEntry server_entry)
+{
+  if (server_entry &&
+      silc_atomic_sub_int8(&server_entry->internal.refcnt, 1) == 0)
+    silc_client_del_server(client, conn, server_entry);
+}
 
-       if (silc_dlist_count(clients) == 1)
-         break;
+/* Free server entry list */
 
-       silc_dlist_start(clients);
-       while ((entry = silc_dlist_get(clients))) {
-         if (!silc_utf8_strncasecmp(entry->nickname, newnick, off))
-           continue;
-         if (strlen(entry->nickname) <= off)
-           continue;
-         num = atoi(&entry->nickname[off]);
-         if (num > max)
-           max = num;
-       }
+void silc_client_list_free_servers(SilcClient client,
+                                  SilcClientConnection conn,
+                                  SilcDList server_list)
+{
+  SilcServerEntry server_entry;
 
-       memset(tmp, 0, sizeof(tmp));
-       snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
-       len = strlen(tmp);
-       memcpy(&newnick[off], tmp, len);
-       off += len;
-      }
-      break;
-    default:
-      /* Some other character in the string */
-      memcpy(&newnick[off], cp, 1);
-      off++;
-      break;
-    }
+  if (server_list) {
+    silc_dlist_start(server_list);
+    while ((server_entry = silc_dlist_get(server_list)))
+      silc_client_unref_server(client, conn, server_entry);
 
-    cp++;
+    silc_dlist_uninit(server_list);
   }
-
-  newnick[off] = 0;
-  memcpy(client_entry->nickname, newnick, strlen(newnick));
-  silc_client_list_free(client, conn, clients);
 }
index 0016a73241a5fc4fe3d3895b76248cd6921b8da8..99a28733e7265fae3e7bc8a042ed2c6ddfbbb734 100644 (file)
@@ -26,6 +26,8 @@
 #include "client_register.h"
 #include "client_entry.h"
 #include "client_prvmsg.h"
+#include "client_channel.h"
+#include "client_notify.h"
 
 /* Context to hold the connection authentication request callbacks that
    will be called when the server has replied back to our request about
@@ -60,15 +62,6 @@ typedef struct {
   void *context;
 } SilcClientInternalConnectContext;
 
-/* Structure to hold ping time information. Every PING command will
-   add entry of this structure and is removed after reply to the ping
-   as been received. */
-struct SilcClientPingStruct {
-  time_t start_time;
-  void *dest_id;
-  char *dest_name;
-};
-
 /* Structure to hold away messages set by user. This is mainly created
    for future extensions where away messages could be set according filters
    such as nickname and hostname. For now only one away message can
@@ -117,7 +110,6 @@ typedef struct SilcClientCommandContextStruct {
   SilcStatus error;                  /* Current command reply error */
 
   void *context;                     /* Context for free use */
-  unsigned int processed     : 1;     /* Set when reply was processed  */
   unsigned int called        : 1;     /* Set when called by application */
   unsigned int verbose       : 1;     /* Verbose with 'say' client operation */
   unsigned int resolved      : 1;     /* Set when resolving something */
@@ -161,10 +153,6 @@ struct SilcClientConnectionInternalStruct {
   /* Pending command queue for this connection */
   SilcList pending_commands;
 
-  /* Requested pings. */
-  SilcClientPing *ping;
-  SilcUInt32 ping_count;
-
   /* Set away message */
   SilcClientAway *away;
 
@@ -180,7 +168,6 @@ struct SilcClientConnectionInternalStruct {
   SilcHashTable attrs;
 
   SilcFSMStruct fsm;                    /* Connection FSM */
-  SilcFSMThreadStruct packet_thread;     /* FSM thread for packet processor */
   SilcFSMThreadStruct event_thread;      /* FSM thread for events */
   SilcFSMSemaStruct wait_event;                 /* Event signaller */
   SilcMutex lock;                       /* Connection lock */
@@ -191,6 +178,7 @@ struct SilcClientConnectionInternalStruct {
   SilcClientConnectionParams params;    /* Connection parameters */
   SilcAtomic16 cmd_ident;               /* Current command identifier */
   SilcIDCacheEntry local_entry;                 /* Local client cache entry */
+  SilcList thread_pool;                         /* Packet thread pool */
 
   SilcHashTable privmsg_wait;           /* Waited private messages */
 
@@ -198,38 +186,25 @@ struct SilcClientConnectionInternalStruct {
   unsigned int connect            : 1;  /* Connect remote host */
   unsigned int disconnected       : 1;  /* Disconnected by remote host */
   unsigned int key_exchange       : 1;   /* Start key exchange */
-  unsigned int new_packet         : 1;  /* New packet received */
 
   /* Flags */
   unsigned int verbose            : 1;   /* Notify application */
   unsigned int registering        : 1;  /* Set when registering to network */
 };
 
+SILC_FSM_STATE(silc_client_connection_st_run);
 SILC_FSM_STATE(silc_client_connection_st_packet);
-
-void silc_client_channel_message(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcPacket packet);
-void silc_client_ftp(SilcClient client, SilcClientConnection conn,
-                    SilcPacket packet);
-void silc_client_channel_key(SilcClient client,
-                            SilcClientConnection conn,
-                            SilcPacket packet);
-void silc_client_notify(SilcClient client,
-                       SilcClientConnection conn,
-                       SilcPacket packet);
-void silc_client_disconnect(SilcClient client,
-                           SilcClientConnection conn,
-                           SilcPacket packet);
-void silc_client_error(SilcClient client,
-                      SilcClientConnection conn,
-                      SilcPacket packet);
-void silc_client_key_agreement(SilcClient client,
-                              SilcClientConnection conn,
-                              SilcPacket packet);
-void silc_client_connection_auth_request(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcPacket packet);
+SILC_FSM_STATE(silc_client_connection_st_close);
+SILC_FSM_STATE(silc_client_error);
+SILC_FSM_STATE(silc_client_disconnect);
+
+void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
+SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+                               SilcClientEntry client_entry);
+SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
+                                SilcChannelEntry channel);
+SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
+                               SilcServerEntry server);
 SilcUInt16 silc_client_command_send_argv(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcCommand command,
@@ -240,126 +215,13 @@ SilcUInt16 silc_client_command_send_argv(SilcClient client,
                                         SilcUInt32 *argv_lens,
                                         SilcUInt32 *argv_types);
 
-#if 0
-/* Session resuming callback */
-typedef void (*SilcClientResumeSessionCallback)(SilcClient client,
-                                               SilcClientConnection conn,
-                                               SilcBool success,
-                                               void *context);
-
-/* Rekey must be performed at the lastest when this many packets is sent */
-#define SILC_CLIENT_REKEY_THRESHOLD 0xfffffe00
-
-/* Macros */
-
-/* Registers generic task for file descriptor for reading from network and
-   writing to network. As being generic task the actual task is allocated
-   only once and after that the same task applies to all registered fd's. */
-#define SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd)     \
-do {                                                   \
-  silc_schedule_task_add(client->schedule, (fd),       \
-                        silc_client_packet_process,    \
-                        context, 0, 0,                 \
-                        SILC_TASK_GENERIC,             \
-                        SILC_TASK_PRI_NORMAL);         \
-} while(0)
-
-#define SILC_CLIENT_SET_CONNECTION_FOR_INPUT(s, fd)                    \
-do {                                                                   \
-  silc_schedule_set_listen_fd((s), (fd), SILC_TASK_READ, FALSE);       \
-} while(0)
-
-#define SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(s, fd)                   \
-do {                                                                   \
-  silc_schedule_set_listen_fd((s), (fd), (SILC_TASK_READ |             \
-                                         SILC_TASK_WRITE), FALSE);     \
-} while(0)
-
-/* Finds socket connection object by file descriptor */
-#define SILC_CLIENT_GET_SOCK(__x, __fd, __sock)                        \
-do {                                                           \
-  int __i;                                                     \
-                                                               \
-  for (__i = 0; __i < (__x)->internal->conns_count; __i++)     \
-    if ((__x)->internal->conns[__i] &&                         \
-       (__x)->internal->conns[__i]->sock &&                    \
-       (__x)->internal->conns[__i]->sock->sock == (__fd))      \
-      break;                                                   \
-                                                               \
-  if (__i >= (__x)->internal->conns_count) {                   \
-    (__sock) = NULL;                                           \
-    for (__i = 0; __i < (__x)->internal->sockets_count; __i++) \
-      if ((__x)->internal->sockets[__i] &&                     \
-         (__x)->internal->sockets[__i]->sock == (__fd))        \
-        (__sock) = (__x)->internal->sockets[__i];              \
-  } else                                                       \
-    (__sock) = (__x)->internal->conns[__i]->sock;              \
-} while(0)
-
-/* Check whether rekey protocol is active */
-#define SILC_CLIENT_IS_REKEY(sock)                                     \
-  (sock->protocol && sock->protocol->protocol &&                       \
-   sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)
-
-/* Prototypes */
-
-SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
-void silc_client_packet_send(SilcClient client,
-                             SilcClientConnection conn,
-                             SilcPacketType type,
-                             void *dst_id,
-                             SilcIdType dst_id_type,
-                             SilcCipher cipher,
-                             SilcHmac hmac,
-                             unsigned char *data,
-                             SilcUInt32 data_len,
-                             SilcBool force_send);
-int silc_client_packet_send_real(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcBool force_send);
-void silc_client_ftp_free_sessions(SilcClient client,
-                                  SilcClientConnection conn);
-void silc_client_ftp_session_free(SilcClientFtpSession session);
-void silc_client_ftp_session_free_client(SilcClientConnection conn,
-                                        SilcClientEntry client_entry);
-void silc_client_close_connection_real(SilcClient client,
-                                      SilcClientConnection conn,
-                                      SilcClientConnection conn);
-
-void silc_client_save_channel_key(SilcClient client,
-                                 SilcClientConnection conn,
-                                 SilcBuffer key_payload,
-                                 SilcChannelEntry channel);
-void silc_client_remove_from_channels(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcClientEntry client_entry);
-void silc_client_replace_from_channels(SilcClient client,
-                                      SilcClientConnection conn,
-                                      SilcClientEntry old,
-                                      SilcClientEntry newclient);
-void silc_client_process_failure(SilcClient client,
-                                SilcClientConnection conn,
-                                SilcPacket packet);
-SilcBuffer silc_client_get_detach_data(SilcClient client,
-                                      SilcClientConnection conn);
-SilcBool silc_client_process_detach_data(SilcClient client,
-                                    SilcClientConnection conn,
-                                    unsigned char **old_id,
-                                    SilcUInt16 *old_id_len);
-void silc_client_resume_session(SilcClient client,
-                               SilcClientConnection conn,
-                               SilcClientResumeSessionCallback callback,
-                               void *context);
-SilcBuffer silc_client_attributes_process(SilcClient client,
-                                         SilcClientConnection conn,
-                                         SilcDList attrs);
-void silc_client_packet_queue_purge(SilcClient client,
-                                   SilcClientConnection conn);
-SILC_TASK_CALLBACK_GLOBAL(silc_client_rekey_callback);
-void
-silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
-                                    SilcStatus status,
-                                    SilcBool notify);
-#endif /* 0 */
+void silc_client_ftp(SilcClient client, SilcClientConnection conn,
+                    SilcPacket packet);
+void silc_client_key_agreement(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcPacket packet);
+void silc_client_connection_auth_request(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcPacket packet);
 
 #endif
index 004d4331c546d3419ae7fc11c2d4f38e6b43ea5e..d54e6ccc811e20919f1f9e5fda96fce1b64d3c79 100644 (file)
 #include "silcclient.h"
 #include "client_internal.h"
 
-/* Context used for resolving client, channel and server info. */
-typedef struct {
-  void *packet;
-  void *context;
-  SilcSocketConnection sock;
-} *SilcClientNotifyResolve;
-
-SILC_TASK_CALLBACK(silc_client_notify_check_client)
+/************************** Types and definitions ***************************/
+
+#define NOTIFY conn->client->internal->ops->notify
+
+/************************ Static utility functions **************************/
+
+/* Entry resolving callback.  This will continue processing the notify. */
+
+static void silc_client_notify_resolved(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcStatus status,
+                                       SilcDList entries,
+                                       void *context)
 {
-  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
-  SilcClient client = res->context;
-  SilcClientConnection conn = res->sock->user_data;
-  SilcClientID *client_id = res->packet;
-  silc_client_get_client_by_id_resolve(client, conn, client_id,
-                                      NULL, NULL, NULL);
-  silc_free(client_id);
-  silc_socket_free(res->sock);
-  silc_free(res);
+  /* If no entries found, just finish the notify processing, a silent error */
+  if (!entries)
+    silc_fsm_next(context, silc_client_notify_processed);
+
+  /* Continue processing the notify */
+  SILC_FSM_CALL_CONTINUE_SYNC(context);
 }
 
-SILC_TASK_CALLBACK(silc_client_notify_del_client_cb)
+/********************************* Notify ***********************************/
+
+/* Process received notify packet */
+
+SILC_FSM_STATE(silc_client_notify)
 {
-  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
-  SilcClient client = res->context;
-  SilcClientConnection conn = res->sock->user_data;
-  SilcClientID *client_id = res->packet;
-  SilcClientEntry client_entry;
-  client_entry = silc_client_get_client_by_id(client, conn, client_id);
-  if (client_entry)
-    silc_client_del_client(client, conn, client_entry);
-  silc_free(client_id);
-  silc_socket_free(res->sock);
-  silc_free(res);
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload;
+
+  payload = silc_notify_payload_parse(silc_buffer_data(&packet->buffer),
+                                     silc_buffer_len(&packet->buffer));
+  if (!payload) {
+    SILC_LOG_DEBUG(("Malformed notify payload"));
+    silc_packet_free(packet);
+    return SILC_FSM_FINISH;
+  }
+
+  if (!silc_notify_get_args(payload)) {
+    SILC_LOG_DEBUG(("Malformed notify"));
+    silc_notify_payload_free(payload);
+    silc_packet_free(packet);
+    return SILC_FSM_FINISH;
+  }
+
+  /* Save notify payload to packet context during processing */
+  packet->next = (void *)payload;
+
+  /* Process the notify */
+  switch (silc_notify_get_type(payload)) {
+
+  case SILC_NOTIFY_TYPE_NONE:
+    /** NONE */
+    silc_fsm_next(fsm, silc_client_notify_none);
+    break;
+
+  case SILC_NOTIFY_TYPE_INVITE:
+    /** INVITE */
+    silc_fsm_next(fsm, silc_client_notify_invite);
+    break;
+
+  case SILC_NOTIFY_TYPE_JOIN:
+    /** JOIN */
+    silc_fsm_next(fsm, silc_client_notify_join);
+    break;
+
+  case SILC_NOTIFY_TYPE_LEAVE:
+    /** LEAVE */
+    silc_fsm_next(fsm, silc_client_notify_leave);
+    break;
+
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    /** SIGNOFF */
+    silc_fsm_next(fsm, silc_client_notify_signoff);
+    break;
+
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    /** TOPIC_SET */
+    silc_fsm_next(fsm, silc_client_notify_topic_set);
+    break;
+
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+    /** NICK_CHANGE */
+    silc_fsm_next(fsm, silc_client_notify_nick_change);
+    break;
+
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    /** CMODE_CHANGE */
+    silc_fsm_next(fsm, silc_client_notify_cmode_change);
+    break;
+
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    /** CUMODE_CHANGE */
+    silc_fsm_next(fsm, silc_client_notify_cumode_change);
+    break;
+
+  case SILC_NOTIFY_TYPE_MOTD:
+    /** MOTD */
+    silc_fsm_next(fsm, silc_client_notify_motd);
+    break;
+
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    /** CHANNEL_CHANGE */
+    silc_fsm_next(fsm, silc_client_notify_channel_change);
+    break;
+
+  case SILC_NOTIFY_TYPE_KICKED:
+    /** KICKED */
+    silc_fsm_next(fsm, silc_client_notify_kicked);
+    break;
+
+  case SILC_NOTIFY_TYPE_KILLED:
+    /** KILLED */
+    silc_fsm_next(fsm, silc_client_notify_killed);
+    break;
+
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    /** SERVER_SIGNOFF */
+    silc_fsm_next(fsm, silc_client_notify_server_signoff);
+    break;
+
+  case SILC_NOTIFY_TYPE_ERROR:
+    /** ERROR */
+    silc_fsm_next(fsm, silc_client_notify_error);
+    break;
+
+  case SILC_NOTIFY_TYPE_WATCH:
+    /** WATCH */
+    silc_fsm_next(fsm, silc_client_notify_watch);
+    break;
+
+  default:
+    /** Unknown notify */
+    silc_notify_payload_free(payload);
+    silc_packet_free(packet);
+    return SILC_FSM_FINISH;
+    break;
+  }
+
+  return SILC_FSM_YIELD;
 }
 
-/* Called when notify is received and some async operation (such as command)
-   is required before processing the notify message. This calls again the
-   silc_client_notify_by_server and reprocesses the original notify packet. */
+/* Notify processed, finish the packet processing thread */
 
-static void silc_client_notify_by_server_pending(void *context, void *context2)
+SILC_FSM_STATE(silc_client_notify_processed)
 {
-  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
-  SilcClientCommandReplyContext reply =
-    (SilcClientCommandReplyContext)context2;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  silc_notify_payload_free(payload);
+  silc_packet_free(packet);
+  return SILC_FSM_FINISH;
+}
+
+/********************************** NONE ************************************/
+
+SILC_FSM_STATE(silc_client_notify_none)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+
+  /* Notify application */
+  NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL));
+
+  /** Notify processed */
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
+
+/********************************* INVITE ***********************************/
 
-  SILC_LOG_DEBUG(("Start"));
+/* Someone invite me to a channel */
 
-  if (reply && !silc_command_get_status(reply->payload, NULL, NULL))
+SILC_FSM_STATE(silc_client_notify_invite)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry;
+  SilcChannelEntry channel = NULL;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcID id;
+
+  SILC_LOG_DEBUG(("Notify: INVITE"));
+
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Get the channel name */
+  tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+  if (!tmp)
     goto out;
 
-  silc_client_notify_by_server(res->context, res->sock, res->packet);
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+
+  /* Get sender Client ID */
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Find Client entry and if not found query it */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry || !client_entry->nickname[0]) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
+
+  /* Notify application */
+  NOTIFY(client, conn, type, channel, tmp, client_entry);
+
+  silc_client_unref_client(client, conn, client_entry);
 
  out:
-  silc_socket_free(res->sock);
-  silc_packet_context_free(res->packet);
-  silc_free(res);
+  /** Notify processed */
+  silc_client_unref_channel(client, conn, channel);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Resets the channel entry's resolve_cmd_ident after whatever-thing
-   was resolved is completed. */
+/********************************** JOIN ************************************/
 
-static void silc_client_channel_cond(void *context, void *context2)
+/* Someone joined a channel */
+
+SILC_FSM_STATE(silc_client_notify_join)
 {
-  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
-  SilcClient client = res->context;
-  SilcClientConnection conn = res->sock->user_data;
-  SilcChannelID *channel_id = res->packet;
-  SilcChannelEntry channel;
-  channel = silc_client_get_channel_by_id(client, conn, channel_id);
-  if (channel)
-    channel->resolve_cmd_ident = 0;
-  silc_free(channel_id);
-  silc_socket_free(res->sock);
-  silc_free(res);
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry;
+  SilcChannelEntry channel = NULL;
+  SilcID id;
+
+  SILC_LOG_DEBUG(("Notify: JOIN"));
+
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Get channel entry */
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
+
+  /* Get Client ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Find Client entry and if not found query it */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry || !client_entry->nickname[0] ||
+      !client_entry->username[0]) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
+
+  if (client_entry != conn->local_entry)
+    silc_client_nickname_format(client, conn, client_entry);
+
+  /* Join the client to channel */
+  silc_client_add_to_channel(channel, client_entry, 0);
+
+  /* Notify application. */
+  NOTIFY(client, conn, type, client_entry, channel);
+
+  silc_client_unref_client(client, conn, client_entry);
+
+ out:
+  /** Notify processed */
+  silc_client_unref_channel(client, conn, channel);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Function that starts waiting for the `cmd_ident' to arrive and
-   marks the channel info being resolved.  */
+/********************************** LEAVE ***********************************/
+
+/* Someone left a channel */
 
-static void silc_client_channel_set_wait(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcChannelEntry channel,
-                                        SilcUInt16 cmd_ident)
+SILC_FSM_STATE(silc_client_notify_leave)
 {
-  SilcClientNotifyResolve res;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry = NULL;
+  SilcChannelEntry channel = NULL;
+  SilcChannelUser chu;
+  SilcID id;
+
+  SILC_LOG_DEBUG(("Notify: LEAVE"));
+
+  /* Get channel entry */
+  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                     &id.u.channel_id, sizeof(id.u.channel_id)))
+    goto out;
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
+
+  /* Get Client ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Find Client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry)
+    goto out;
 
-  if (!channel->resolve_cmd_ident) {
-    res = silc_calloc(1, sizeof(*res));
+  /* Remove client from channel */
+  chu = silc_client_on_channel(channel, client_entry);
+  if (chu) {
+    silc_hash_table_del(client_entry->channels, channel);
+    silc_hash_table_del(channel->user_list, client_entry);
+    silc_free(chu);
+  }
+
+#if 0 /* Kind of useless, server will return error if client keeps using
+        non-existing client, and the entry is removed then. */
+  /* Some client implementations actually quit network by first doing
+     LEAVE and then immediately SIGNOFF.  We'll check for this by doing
+     check for the client after 5 - 34 seconds.  If it is not valid after
+     that we'll remove the client from cache. */
+  if (!silc_hash_table_count(client_entry->channels)) {
+    SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
     res->context = client;
     res->sock = silc_socket_dup(conn->sock);
-    res->packet = silc_id_dup(channel->id, SILC_ID_CHANNEL);
-    silc_client_command_pending(conn, SILC_COMMAND_NONE, cmd_ident,
-                               silc_client_channel_cond, res);
-    channel->resolve_cmd_ident = cmd_ident;
+    res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
+    silc_schedule_task_add(client->schedule, conn->sock->sock,
+                          silc_client_notify_check_client, res,
+                          (5 + (silc_rng_get_rn16(client->rng) % 29)),
+                          0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
   }
+#endif
+
+  /* Notify application. */
+  NOTIFY(client, conn, type, client_entry, channel);
+
+  silc_client_unref_client(client, conn, client_entry);
+
+ out:
+  /** Notify processed */
+  silc_client_unref_channel(client, conn, channel);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Attaches to the channel's resolving cmd ident and calls the
-   notify handling with `packet' after it's received. */
+/********************************* SIGNOFF **********************************/
+
+/* Someone quit SILC network */
 
-static void silc_client_channel_wait(SilcClient client,
-                                    SilcClientConnection conn,
-                                    SilcChannelEntry channel,
-                                    SilcPacketContext *packet)
+SILC_FSM_STATE(silc_client_notify_signoff)
 {
-  SilcClientNotifyResolve res;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry;
+   unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcID id;
 
-  if (!channel->resolve_cmd_ident)
-    return;
+  SILC_LOG_DEBUG(("Notify: SIGNOFF"));
 
-  res = silc_calloc(1, sizeof(*res));
-  res->packet = silc_packet_context_dup(packet);
-  res->context = client;
-  res->sock = silc_socket_dup(conn->sock);
+  /* Get Client ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-  silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                             channel->resolve_cmd_ident,
-                             silc_client_notify_by_server_pending, res);
+  /* Find Client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry)
+    goto out;
+
+  /* Get signoff message */
+  tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+  if (tmp_len > 128)
+    tmp[128] = '\0';
+
+  /* Notify application */
+  NOTIFY(client, conn, type, client_entry, tmp);
+
+  /* Remove from all channels */
+  silc_client_remove_from_channels(client, conn, client_entry);
+
+#if 0
+  /* Remove from cache */
+  silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
+#endif
+
+  /* Free data */
+  silc_client_unref_client(client, conn, client_entry);
+  silc_client_del_client_entry(client, conn, client_entry);
+
+ out:
+  /** Notify processed */
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Resolve client, channel or server information. */
+/******************************** TOPIC_SET *********************************/
 
-static void silc_client_notify_by_server_resolve(SilcClient client,
-                                                SilcClientConnection conn,
-                                                SilcPacketContext *packet,
-                                                SilcIdType id_type,
-                                                void *id)
+/* Someone set topic on a channel */
+
+SILC_FSM_STATE(silc_client_notify_topic_set)
 {
-  SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-  SilcBuffer idp = silc_id_payload_encode(id, id_type);
-
-  res->packet = silc_packet_context_dup(packet);
-  res->context = client;
-  res->sock = silc_socket_dup(conn->sock);
-
-  /* For client resolving use WHOIS, and otherwise use IDENTIFY */
-  if (id_type == SILC_ID_CLIENT) {
-    silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
-                                silc_client_command_reply_whois_i, 0,
-                                ++conn->cmd_ident);
-    silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
-                            1, 4, idp->data, idp->len);
-    silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
-                               silc_client_notify_by_server_pending, res);
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry = NULL;
+  SilcChannelEntry channel = NULL, channel_entry = NULL;
+  SilcServerEntry server = NULL;
+  void *entry;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcID id;
+
+  SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
+
+  /* Get channel entry */
+  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                     &id.u.channel_id, sizeof(id.u.channel_id)))
+    goto out;
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
+
+  /* Get ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Get topic */
+  tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+  if (!tmp)
+    goto out;
+
+  if (id.type == SILC_ID_CLIENT) {
+    /* Find Client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (!client_entry || !client_entry->nickname[0]) {
+      /** Resolve client */
+      silc_client_unref_client(client, conn, client_entry);
+      SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                          client, conn, &id.u.client_id, NULL,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
+    }
+    entry = client_entry;
+  } else if (id.type == SILC_ID_SERVER) {
+    /* Find Server entry */
+    server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
+    if (!server) {
+      /** Resolve server */
+      SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
+                                          client, conn, &id.u.server_id,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
+    }
+    entry = server;
   } else {
-    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
-                                silc_client_command_reply_identify_i, 0,
-                                ++conn->cmd_ident);
-    silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
-                            conn->cmd_ident, 1, 5, idp->data, idp->len);
-    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
-                               silc_client_notify_by_server_pending, res);
+    /* Find Channel entry */
+    channel_entry = silc_client_get_channel_by_id(client, conn,
+                                                 &id.u.channel_id);
+    if (!channel_entry) {
+      /** Resolve channel */
+      SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+                                   client, conn, &id.u.channel_id,
+                                   silc_client_notify_resolved,
+                                   fsm));
+      /* NOT REACHED */
+    }
+    entry = channel_entry;
   }
-  silc_buffer_free(idp);
+
+  silc_free(channel->topic);
+  channel->topic = silc_memdup(tmp, strlen(tmp));
+
+  /* Notify application. */
+  NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
+
+  if (client_entry)
+    silc_client_unref_client(client, conn, client_entry);
+  if (server)
+    silc_client_unref_server(client, conn, server);
+  if (channel_entry)
+    silc_client_unref_channel(client, conn, channel_entry);
+
+ out:
+  /** Notify processed */
+  silc_client_unref_channel(client, conn, channel);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Received notify message from server */
+/****************************** NICK_CHANGE *********************************/
 
-void silc_client_notify_by_server(SilcClient client,
-                                 SilcSocketConnection sock,
-                                 SilcPacketContext *packet)
+/* Someone changed their nickname on a channel */
+
+SILC_FSM_STATE(silc_client_notify_nick_change)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcNotifyPayload payload;
-  SilcNotifyType type;
-  SilcArgumentPayload args;
-
-  void *id;
-  SilcIdType id_type;
-  SilcClientID *client_id = NULL;
-  SilcChannelID *channel_id = NULL;
-  SilcServerID *server_id = NULL;
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
-  SilcClientEntry client_entry2 = NULL;
-  SilcChannelEntry channel;
-  SilcChannelUser chu;
-  SilcServerEntry server;
-  unsigned char *tmp;
-  SilcUInt32 tmp_len, mode;
+  unsigned char *tmp, *nick, oldnick[128 + 1];
+  SilcUInt32 tmp_len;
+  SilcID id;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
 
-  payload = silc_notify_payload_parse(buffer->data, buffer->len);
-  if (!payload)
+  /* Get ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  type = silc_notify_get_type(payload);
-  args = silc_notify_get_args(payload);
-  if (!args)
+  /* Ignore my ID */
+  if (conn->local_id &&
+      SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
     goto out;
 
-  switch(type) {
-  case SILC_NOTIFY_TYPE_NONE:
-    /* Notify application */
-    client->internal->ops->notify(client, conn, type,
-                                 silc_argument_get_arg_type(args, 1, NULL));
-    break;
+  /* Find old Client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry || !client_entry->nickname[0]) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
 
-  case SILC_NOTIFY_TYPE_INVITE:
-    /*
-     * Someone invited me to a channel. Find Client and Channel entries
-     * for the application.
-     */
+  /* Get new Client ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    SILC_LOG_DEBUG(("Notify: INVITE"));
+  /* Take the new nickname */
+  tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+  if (!tmp)
+    goto out;
 
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
+  /* Check whether nickname changed at all.  It is possible that nick
+     change notify is received but nickname didn't change, only the
+     ID changes.  If Client ID hash match, nickname didn't change. */
+  if (SILC_ID_COMPARE_HASH(&client_entry->id, &id.u.client_id) &&
+      silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
+    /* Nickname didn't change.  Update only Client ID.  We don't notify
+       application because nickname didn't change. */
+    silc_idcache_update(conn->internal->client_cache, client_entry,
+                       &client_entry->id, &id.u.client_id, NULL, NULL, FALSE);
+    goto out;
+  }
 
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!channel_id)
-      goto out;
+  /* Normalize nickname */
+  nick = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, 128, NULL);
+  if (!nick)
+    goto out;
 
-    /* Get the channel entry */
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+  /* Update nickname */
+  if (!silc_idcache_update(conn->internal->client_cache, client_entry,
+                          NULL, NULL, client_entry->nickname_normalized,
+                          nick, TRUE)) {
+    silc_free(nick);
+    goto out;
+  }
+  memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
+  memcpy(client_entry->nickname, tmp, tmp_len);
+  client_entry->nickname_normalized = nick;
+  silc_client_nickname_format(client, conn, client_entry);
 
-    /* Get sender Client ID */
-    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (!tmp)
-      goto out;
+  /* Notify application */
+  NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+ out:
+  /** Notify processed */
+  silc_client_unref_client(client, conn, client_entry);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-    /* Find Client entry and if not found query it */
-    client_entry = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry) {
-      silc_client_notify_by_server_resolve(client, conn, packet,
-                                          SILC_ID_CLIENT, client_id);
-      goto out;
-    }
+/****************************** CMODE_CHANGE ********************************/
 
-    /* Get the channel name */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
+/* Someone changed channel mode */
 
-    /* Notify application */
-    client->internal->ops->notify(client, conn, type, channel, tmp,
-                                 client_entry);
-    break;
+SILC_FSM_STATE(silc_client_notify_cmode_change)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry = NULL;
+  SilcChannelEntry channel = NULL, channel_entry = NULL;
+  SilcServerEntry server = NULL;
+  void *entry;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len, mode;
+  SilcID id;
+  char *passphrase, *cipher, *hmac;
+  SilcPublicKey founder_key = NULL;
+  SilcDList chpks = NULL;
 
-  case SILC_NOTIFY_TYPE_JOIN:
-    /*
-     * Someone has joined to a channel. Get their ID and nickname and
-     * cache them for later use.
-     */
+  SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
 
-    SILC_LOG_DEBUG(("Notify: JOIN"));
+  /* Get channel entry */
+  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                     &id.u.channel_id, sizeof(id.u.channel_id)))
+    goto out;
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
 
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
+  /* Get the mode */
+  tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(mode, tmp);
 
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!channel_id)
-      goto out;
+  /* Get ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    /* Get channel entry */
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+  if (id.type == SILC_ID_CLIENT) {
+    /* Find Client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (!client_entry || !client_entry->nickname[0]) {
+      /** Resolve client */
+      silc_client_unref_client(client, conn, client_entry);
+      SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                          client, conn, &id.u.client_id, NULL,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
+    }
+    entry = client_entry;
+  } else if (id.type == SILC_ID_SERVER) {
+    /* Find Server entry */
+    server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
+    if (!server) {
+      /** Resolve server */
+      SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
+                                          client, conn, &id.u.server_id,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
+    }
+    entry = server;
+  } else {
+    /* Find Channel entry */
+    channel_entry = silc_client_get_channel_by_id(client, conn,
+                                                 &id.u.channel_id);
+    if (!channel_entry) {
+      /** Resolve channel */
+      SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+                                   client, conn, &id.u.channel_id,
+                                   silc_client_notify_resolved,
+                                   fsm));
+      /* NOT REACHED */
+    }
+    entry = channel_entry;
+  }
 
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
+  /* Get the channel founder key if it was set */
+  tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
+  if (tmp) {
+    if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key))
       goto out;
+    if (!channel->founder_key) {
+      channel->founder_key = founder_key;
+      founder_key = NULL;
+    }
+  }
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+  /* Get the cipher */
+  cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
 
-    /* Find Client entry and if not found query it */
-    client_entry = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry) {
-      silc_client_channel_set_wait(client, conn, channel,
-                                  conn->cmd_ident + 1);
-      silc_client_notify_by_server_resolve(client, conn, packet,
-                                          SILC_ID_CLIENT, client_id);
-      goto out;
-    }
+  /* Get the hmac */
+  hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
+  if (hmac) {
+    unsigned char hash[SILC_HASH_MAXLEN];
+    SilcHmac newhmac;
 
-    /* If nickname or username hasn't been resolved, do so */
-    if (!client_entry->nickname || !client_entry->username) {
-      if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-       /* Attach to existing resolving */
-       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-       res->packet = silc_packet_context_dup(packet);
-       res->context = client;
-       res->sock = silc_socket_dup(conn->sock);
-       silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                   client_entry->resolve_cmd_ident,
-                                   silc_client_notify_by_server_pending,
-                                   res);
-       goto out;
-      }
-
-      /* Do new resolving */
-      silc_client_channel_set_wait(client, conn, channel,
-                                  conn->cmd_ident + 1);
-      silc_client_notify_by_server_resolve(client, conn, packet,
-                                          SILC_ID_CLIENT, client_id);
-      client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
-      client_entry->resolve_cmd_ident = conn->cmd_ident;
+    if (!silc_hmac_alloc(hmac, NULL, &newhmac))
       goto out;
-    } else {
-      if (client_entry != conn->local_entry)
-       silc_client_nickname_format(client, conn, client_entry);
-    }
 
-    /* If information is being resolved for this channel, wait for it */
-    if (channel->resolve_cmd_ident) {
-      silc_client_channel_wait(client, conn, channel, packet);
-      goto out;
+    /* Get HMAC key from the old HMAC context, and update it to the new one */
+    tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
+    if (tmp) {
+      silc_hash_make(silc_hmac_get_hash(newhmac), tmp, tmp_len, hash);
+      silc_hmac_set_key(newhmac, hash,
+                       silc_hash_len(silc_hmac_get_hash(newhmac)));
+      if (channel->internal.hmac)
+       silc_hmac_free(channel->internal.hmac);
+      channel->internal.hmac = newhmac;
+      memset(hash, 0, sizeof(hash));
     }
+  }
 
-    /* Join the client to channel */
-    if (!silc_client_on_channel(channel, client_entry)) {
-      chu = silc_calloc(1, sizeof(*chu));
-      chu->client = client_entry;
-      chu->channel = channel;
-      silc_hash_table_add(channel->user_list, client_entry, chu);
-      silc_hash_table_add(client_entry->channels, channel, chu);
-    }
+  /* Get the passphrase if it was set */
+  passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->internal->ops->notify(client, conn, type, client_entry, channel);
-    break;
+  /* Get user limit */
+  tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
+  if (tmp && tmp_len == 4)
+    SILC_GET32_MSB(channel->user_limit, tmp);
+  if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
+    channel->user_limit = 0;
 
-  case SILC_NOTIFY_TYPE_LEAVE:
-    /*
-     * Someone has left a channel. We will remove it from the channel but
-     * we'll keep it in the cache in case we'll need it later.
-     */
+  /* Save the new mode */
+  channel->mode = mode;
 
-    SILC_LOG_DEBUG(("Notify: LEAVE"));
+  /* Get the channel public key that was added or removed */
+  tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
+  if (tmp)
+    chpks = silc_argument_list_parse_decoded(tmp, tmp_len,
+                                            SILC_ARGUMENT_PUBLIC_KEY);
 
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
+  /* Notify application. */
+  NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
+        passphrase, channel->founder_key, chpks, channel);
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+ out:
+  if (founder_key)
+    silc_pkcs_public_key_free(founder_key);
+  if (chpks)
+    silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
+  if (client_entry)
+    silc_client_unref_client(client, conn, client_entry);
+  if (server)
+    silc_client_unref_server(client, conn, server);
+  if (channel_entry)
+    silc_client_unref_channel(client, conn, channel_entry);
+  silc_client_unref_channel(client, conn, channel);
+
+  /** Notify processed */
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-    /* Find Client entry */
-    client_entry =
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
+/***************************** CUMODE_CHANGE ********************************/
 
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+/* Someone changed a user's mode on a channel */
 
-    /* Remove client from channel */
-    chu = silc_client_on_channel(channel, client_entry);
-    if (chu) {
-      silc_hash_table_del(client_entry->channels, channel);
-      silc_hash_table_del(channel->user_list, client_entry);
-      silc_free(chu);
-    }
+SILC_FSM_STATE(silc_client_notify_cumode_change)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry = NULL, client_entry2 = NULL;
+  SilcChannelEntry channel = NULL, channel_entry = NULL;
+  SilcServerEntry server = NULL;
+  SilcChannelUser chu;
+  void *entry;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len, mode;
+  SilcID id, id2;
+
+  SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
+
+  /* Get channel entry */
+  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                     &id.u.channel_id, sizeof(id.u.channel_id)))
+    goto out;
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
 
-    /* Some client implementations actually quit network by first doing
-       LEAVE and then immediately SIGNOFF.  We'll check for this by doing
-       check for the client after 5 - 34 seconds.  If it is not valid after
-       that we'll remove the client from cache. */
-    if (!silc_hash_table_count(client_entry->channels)) {
-      SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-      res->context = client;
-      res->sock = silc_socket_dup(conn->sock);
-      res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
-      silc_schedule_task_add(client->schedule, conn->sock->sock,
-                            silc_client_notify_check_client, res,
-                            (5 + (silc_rng_get_rn16(client->rng) % 29)),
-                            0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  /* Get the mode */
+  tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(mode, tmp);
+
+  /* Get ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  if (id.type == SILC_ID_CLIENT) {
+    /* Find Client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (!client_entry || !client_entry->nickname[0]) {
+      /** Resolve client */
+      silc_client_unref_client(client, conn, client_entry);
+      SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                          client, conn, &id.u.client_id, NULL,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
+    }
+    entry = client_entry;
+  } else if (id.type == SILC_ID_SERVER) {
+    /* Find Server entry */
+    server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
+    if (!server) {
+      /** Resolve server */
+      SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
+                                          client, conn, &id.u.server_id,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
     }
+    entry = server;
+  } else {
+    /* Find Channel entry */
+    channel_entry = silc_client_get_channel_by_id(client, conn,
+                                                 &id.u.channel_id);
+    if (!channel_entry) {
+      /** Resolve channel */
+      SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+                                   client, conn, &id.u.channel_id,
+                                   silc_client_notify_resolved,
+                                   fsm));
+      /* NOT REACHED */
+    }
+    entry = channel_entry;
+  }
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->internal->ops->notify(client, conn, type, client_entry, channel);
-    break;
+  /* Get target Client ID */
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
+    goto out;
 
-  case SILC_NOTIFY_TYPE_SIGNOFF:
-    /*
-     * Someone left SILC. We'll remove it from all channels and from cache.
-     */
+  /* Find target Client entry */
+  client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
+  if (!client_entry2 || !client_entry2->nickname[0]) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry);
+    silc_client_unref_client(client, conn, client_entry2);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id2.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
 
-    SILC_LOG_DEBUG(("Notify: SIGNOFF"));
+  /* Save the mode */
+  chu = silc_client_on_channel(channel, client_entry2);
+  if (chu)
+    chu->mode = mode;
 
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
+  /* Notify application. */
+  NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+ out:
+  silc_client_unref_client(client, conn, client_entry2);
+  if (client_entry)
+    silc_client_unref_client(client, conn, client_entry);
+  if (server)
+    silc_client_unref_server(client, conn, server);
+  if (channel_entry)
+    silc_client_unref_channel(client, conn, channel_entry);
+  silc_client_unref_channel(client, conn, channel);
+
+  /** Notify processed */
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-    /* Find Client entry */
-    client_entry =
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
+/********************************* MOTD *************************************/
 
-    /* Get signoff message */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (tmp_len > 128)
-      tmp = NULL;
+/* Received Message of the day */
 
-    /* Notify application */
-    client->internal->ops->notify(client, conn, type, client_entry, tmp);
+SILC_FSM_STATE(silc_client_notify_motd)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
 
-    /* Remove from all channels */
-    silc_client_remove_from_channels(client, conn, client_entry);
+  SILC_LOG_DEBUG(("Notify: MOTD"));
 
-    /* Remove from cache */
-    silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
+  /* Get motd */
+  tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+  if (!tmp)
+    goto out;
 
-    /* Free data */
-    silc_client_del_client_entry(client, conn, client_entry);
-    break;
+  /* Notify application */
+  NOTIFY(client, conn, type, tmp);
 
-  case SILC_NOTIFY_TYPE_TOPIC_SET:
-    /*
-     * Someone set the topic on a channel.
-     */
+ out:
+  /** Notify processed */
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-    SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
+/**************************** CHANNEL CHANGE ********************************/
 
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+/* Router has enforced a new ID to a channel, change it */
 
-    /* Get ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
-    if (!id)
-      goto out;
+SILC_FSM_STATE(silc_client_notify_channel_change)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcChannelEntry channel = NULL;
+  SilcID id;
+
+  SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
+
+  /* Get the old ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    /* Find Client entry */
-    if (id_type == SILC_ID_CLIENT) {
-      /* Find Client entry */
-      client_id = id;
-      client_entry = silc_client_get_client_by_id(client, conn, client_id);
-      if (!client_entry) {
-       silc_client_channel_set_wait(client, conn, channel,
-                                    conn->cmd_ident + 1);
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_CLIENT, client_id);
-       goto out;
-      }
-    } else if (id_type == SILC_ID_SERVER) {
-      /* Find Server entry */
-      server_id = id;
-      server = silc_client_get_server_by_id(client, conn, server_id);
-      if (!server) {
-       silc_client_channel_set_wait(client, conn, channel,
-                                    conn->cmd_ident + 1);
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_SERVER, server_id);
-       server = silc_client_add_server(client, conn, NULL, NULL, server_id);
-       if (!server)
-         goto out;
-
-       server->resolve_cmd_ident = conn->cmd_ident;
-       server_id = NULL;
-       goto out;
-      }
-
-      /* If entry being resoled, wait for it before processing this notify */
-      if (server->resolve_cmd_ident) {
-       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-       res->packet = silc_packet_context_dup(packet);
-       res->context = client;
-       res->sock = silc_socket_dup(conn->sock);
-       silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                   server->resolve_cmd_ident,
-                                   silc_client_notify_by_server_pending, res);
-       goto out;
-      }
-
-      /* Save the pointer to the client_entry pointer */
-      client_entry = (SilcClientEntry)server;
-    } else {
-      /* Find Channel entry */
-      silc_free(channel_id);
-      channel_id = id;
-      client_entry = (SilcClientEntry)
-       silc_client_get_channel_by_id(client, conn, channel_id);
-      if (!client_entry) {
-       silc_client_channel_set_wait(client, conn, channel,
-                                    conn->cmd_ident + 1);
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_CHANNEL, channel_id);
-       goto out;
-      }
-    }
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
 
-    /* Get topic */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
+  /* Get the new ID */
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    /* If information is being resolved for this channel, wait for it */
-    if (channel->resolve_cmd_ident) {
-      silc_client_channel_wait(client, conn, channel, packet);
-      goto out;
-    }
+  /* Replace the Channel ID */
+  if (!silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id))
+    goto out;
 
-    if (tmp) {
-      silc_free(channel->topic);
-      channel->topic = silc_memdup(tmp, strlen(tmp));
-    }
+  /* Notify application */
+  NOTIFY(client, conn, type, channel, channel);
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->internal->ops->notify(client, conn, type, id_type,
-                                 client_entry, tmp, channel);
+ out:
+  /** Notify processed */
+  silc_client_unref_channel(client, conn, channel);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-    break;
+/******************************** KICKED ************************************/
 
-  case SILC_NOTIFY_TYPE_NICK_CHANGE:
-    /*
-     * Someone changed their nickname. If we don't have entry for the new
-     * ID we will query it and return here after it's done. After we've
-     * returned we fetch the old entry and free it and notify the
-     * application.
-     */
-
-    SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
-
-    /* Get old Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
+/* Some client was kicked from a channel */
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+SILC_FSM_STATE(silc_client_notify_kicked)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry, client_entry2;
+  SilcChannelEntry channel = NULL;
+  SilcChannelUser chu;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcID id;
 
-    /* Ignore my ID */
-    if (conn->local_id && SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
-      break;
+  SILC_LOG_DEBUG(("Notify: KICKED"));
 
-    /* Find old Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
-    silc_free(client_id);
-    client_id = NULL;
-
-    /* Wait for resolving if necessary */
-    if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-      SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-      res->packet = silc_packet_context_dup(packet);
-      res->context = client;
-      res->sock = silc_socket_dup(conn->sock);
-      silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                 client_entry->resolve_cmd_ident,
-                                 silc_client_notify_by_server_pending, res);
-      goto out;
-    }
+  /* Get Client ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    client_entry->valid = FALSE;
+  /* Find Client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry)
+    goto out;
 
-    /* Get new Client ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
+  /* Get channel entry */
+  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                     &id.u.channel_id, sizeof(id.u.channel_id)))
+    goto out;
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+  /* Get kicker's Client ID */
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    /* Take the nickname */
-    tmp = silc_argument_get_arg_type(args, 3, NULL);
-    if (!tmp)
-      goto out;
+  /* Find kicker's client entry and if not found resolve it */
+  client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry2 || !client_entry2->nickname[0]) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry);
+    silc_client_unref_client(client, conn, client_entry2);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
+
+  /* Get comment */
+  tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 
-    /* Check whether nickname changed at all.  It is possible that nick
-       change notify is received but nickname didn't change, only the
-       ID changes.  Check whether the hashes in the Client ID match, if
-       they do nickname didn't change. */
-    if (SILC_ID_COMPARE_HASH(client_entry->id, client_id) &&
-       silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
-      /* Nickname didn't change.  Update only Client ID. */
-
-      /* Normalize nickname */
-      tmp = silc_identifier_check(tmp, strlen(tmp),
-                                 SILC_STRING_UTF8, 128, NULL);
-      if (!tmp)
-       goto out;
-
-      silc_idcache_del_by_context(conn->internal->client_cache,
-                                 client_entry);
-      silc_free(client_entry->id);
-      client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
-      silc_idcache_add(conn->internal->client_cache, tmp,
-                      client_entry->id, client_entry, 0, NULL);
-
-      /* Notify application */
-      client->internal->ops->notify(client, conn, type,
-                                   client_entry, client_entry);
-      break;
+  /* Remove kicked client from channel */
+  if (client_entry != conn->local_entry) {
+    chu = silc_client_on_channel(channel, client_entry);
+    if (chu) {
+      silc_hash_table_del(client_entry->channels, channel);
+      silc_hash_table_del(channel->user_list, client_entry);
+      silc_free(chu);
     }
+  }
 
-    /* Create new client entry, and save all old information with the
-       new nickname and client ID */
-    client_entry2 = silc_client_add_client(client, conn, NULL, NULL,
-                                          client_entry->realname,
-                                          silc_id_dup(client_id,
-                                                      SILC_ID_CLIENT), 0);
-    if (!client_entry2)
-      goto out;
+  /* Notify application. */
+  NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
 
-    if (client_entry->server)
-      client_entry2->server = strdup(client_entry->server);
-    if (client_entry->username)
-      client_entry2->username = strdup(client_entry->username);
-    if (client_entry->hostname)
-      client_entry2->hostname = strdup(client_entry->hostname);
-    client_entry2->fingerprint = client_entry->fingerprint;
-    client_entry2->fingerprint_len = client_entry->fingerprint_len;
-    client_entry->fingerprint = NULL;
-    client_entry->fingerprint_len = 0;
-    silc_client_update_client(client, conn, client_entry2, tmp, NULL, NULL,
-                             client_entry->mode);
-
-    /* Remove the old from cache */
-    silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
-
-    /* Replace old ID entry with new one on all channels. */
-    silc_client_replace_from_channels(client, conn, client_entry,
-                                     client_entry2);
-
-    /* Notify application */
-    client->internal->ops->notify(client, conn, type,
-                                 client_entry, client_entry2);
-
-    /* Free old client entry */
-    silc_client_del_client_entry(client, conn, client_entry);
+  /* If I was kicked from channel, remove the channel */
+  if (client_entry == conn->local_entry) {
+    if (conn->current_channel == channel)
+      conn->current_channel = NULL;
+    silc_client_del_channel(client, conn, channel);
+  }
 
-    break;
+  silc_client_unref_client(client, conn, client_entry);
+  silc_client_unref_client(client, conn, client_entry2);
 
-  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
-    {
-      /*
-       * Someone changed a channel mode
-       */
-      char *passphrase, *cipher, *hmac;
-      SilcPublicKey founder_key = NULL;
-      SilcBufferStruct chpks;
-
-      SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
-
-      /* Get channel entry */
-      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                                 SILC_ID_CHANNEL);
-      if (!channel_id)
-       goto out;
-      channel = silc_client_get_channel_by_id(client, conn, channel_id);
-      if (!channel)
-       goto out;
-
-      /* Get ID */
-      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-      if (!tmp)
-       goto out;
-      id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
-      if (!id)
-       goto out;
-
-      /* Find Client entry */
-      if (id_type == SILC_ID_CLIENT) {
-       /* Find Client entry */
-       client_id = id;
-       client_entry = silc_client_get_client_by_id(client, conn, client_id);
-       if (!client_entry) {
-         silc_client_channel_set_wait(client, conn, channel,
-                                      conn->cmd_ident + 1);
-         silc_client_notify_by_server_resolve(client, conn, packet,
-                                              SILC_ID_CLIENT, client_id);
-         goto out;
-       }
-
-       if (!client_entry->nickname) {
-         if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-           /* Attach to existing resolving */
-           SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-           res->packet = silc_packet_context_dup(packet);
-           res->context = client;
-           res->sock = silc_socket_dup(conn->sock);
-           silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                       client_entry->resolve_cmd_ident,
-                                       silc_client_notify_by_server_pending,
-                                       res);
-           goto out;
-         }
-
-         /* Do new resolving */
-         silc_client_channel_set_wait(client, conn, channel,
-                                      conn->cmd_ident + 1);
-         silc_client_notify_by_server_resolve(client, conn, packet,
-                                              SILC_ID_CLIENT, client_id);
-         client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
-         client_entry->resolve_cmd_ident = conn->cmd_ident;
-         goto out;
-       }
-      } else if (id_type == SILC_ID_SERVER) {
-       /* Find Server entry */
-       server_id = id;
-       server = silc_client_get_server_by_id(client, conn, server_id);
-       if (!server) {
-         silc_client_channel_set_wait(client, conn, channel,
-                                      conn->cmd_ident + 1);
-         silc_client_notify_by_server_resolve(client, conn, packet,
-                                              SILC_ID_SERVER, server_id);
-         server = silc_client_add_server(client, conn, NULL, NULL, server_id);
-         if (!server)
-           goto out;
-
-         server->resolve_cmd_ident = conn->cmd_ident;
-         server_id = NULL;
-         goto out;
-       }
-
-       /* If entry being resoled, wait for it before processing this notify */
-       if (server->resolve_cmd_ident) {
-         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-         res->packet = silc_packet_context_dup(packet);
-         res->context = client;
-         res->sock = silc_socket_dup(conn->sock);
-         silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                     server->resolve_cmd_ident,
-                                     silc_client_notify_by_server_pending,
-                                     res);
-         goto out;
-       }
-
-       /* Save the pointer to the client_entry pointer */
-       client_entry = (SilcClientEntry)server;
-      } else {
-       /* Find Channel entry */
-       silc_free(channel_id);
-       channel_id = id;
-       client_entry = (SilcClientEntry)
-         silc_client_get_channel_by_id(client, conn, channel_id);
-       if (!client_entry) {
-         silc_client_channel_set_wait(client, conn, channel,
-                                      conn->cmd_ident + 1);
-         silc_client_notify_by_server_resolve(client, conn, packet,
-                                              SILC_ID_CHANNEL, channel_id);
-         goto out;
-       }
-      }
-
-      /* Get the mode */
-      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-      if (!tmp)
-       goto out;
-
-      SILC_GET32_MSB(mode, tmp);
-
-      /* If information is being resolved for this channel, wait for it */
-      if (channel->resolve_cmd_ident) {
-       silc_client_channel_wait(client, conn, channel, packet);
-       goto out;
-      }
-
-      /* Save the new mode */
-      channel->mode = mode;
-
-      /* Get the cipher */
-      cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
-
-      /* Get the hmac */
-      hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
-      if (hmac) {
-       unsigned char hash[SILC_HASH_MAXLEN];
-
-       if (channel->hmac)
-         silc_hmac_free(channel->hmac);
-       if (!silc_hmac_alloc(hmac, NULL, &channel->hmac))
-         goto out;
-
-       silc_hash_make(silc_hmac_get_hash(channel->hmac),
-                      channel->key, channel->key_len / 8,
-                      hash);
-       silc_hmac_set_key(channel->hmac, hash,
-                         silc_hash_len(silc_hmac_get_hash(channel->hmac)));
-       memset(hash, 0, sizeof(hash));
-      }
-
-      /* Get the passphrase if it was set */
-      passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
-
-      /* Get the channel founder key if it was set */
-      tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
-      if (tmp) {
-       if (!silc_pkcs_public_key_payload_decode(tmp, tmp_len, &founder_key))
-         founder_key = NULL;
-      }
-
-      /* Get user limit */
-      tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
-      if (tmp && tmp_len == 4)
-        SILC_GET32_MSB(channel->user_limit, tmp);
-      if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
-        channel->user_limit = 0;
-
-      /* Get the channel public key that was added or removed */
-      tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
-      if (tmp)
-       silc_buffer_set(&chpks, tmp, tmp_len);
-
-      /* Notify application. The channel entry is sent last as this notify
-        is for channel but application don't know it from the arguments
-        sent by server. */
-      client->internal->ops->notify(client, conn, type, id_type,
-                                   client_entry, mode, cipher, hmac,
-                                   passphrase, founder_key,
-                                   tmp ? &chpks : NULL, channel);
-
-      if (founder_key)
-       silc_pkcs_public_key_free(founder_key);
-    }
-    break;
+ out:
+  /** Notify processed */
+  silc_client_unref_channel(client, conn, channel);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
-    /*
-     * Someone changed user's mode on a channel
-     */
+/******************************** KILLED ************************************/
 
-    SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
+/* Some client was killed from the network */
 
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+SILC_FSM_STATE(silc_client_notify_killed)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry = NULL, client_entry2 = NULL;
+  SilcChannelEntry channel_entry = NULL;
+  SilcServerEntry server = NULL;
+  void *entry;
+  char *comment;
+  SilcUInt32 comment_len;
+  SilcID id;
+
+  SILC_LOG_DEBUG(("Notify: KILLED"));
+
+  /* Get Client ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    /* Get ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
-    if (!id)
-      goto out;
+  /* Find Client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry)
+    goto out;
+
+  /* Get comment */
+  comment = silc_argument_get_arg_type(args, 2, &comment_len);
 
+  /* Get killer's ID */
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
-    if (id_type == SILC_ID_CLIENT) {
-      /* Find Client entry */
-      client_id = id;
-      client_entry = silc_client_get_client_by_id(client, conn, client_id);
-      if (!client_entry) {
-       silc_client_channel_set_wait(client, conn, channel,
-                                    conn->cmd_ident + 1);
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_CLIENT, client_id);
-       goto out;
-      }
-
-      if (!client_entry->nickname) {
-       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-         /* Attach to existing resolving */
-         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-         res->packet = silc_packet_context_dup(packet);
-         res->context = client;
-         res->sock = silc_socket_dup(conn->sock);
-         silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                     client_entry->resolve_cmd_ident,
-                                     silc_client_notify_by_server_pending,
-                                     res);
-         goto out;
-       }
-
-       /* Do new resolving */
-       silc_client_channel_set_wait(client, conn, channel,
-                                    conn->cmd_ident + 1);
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_CLIENT, client_id);
-        client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
-        client_entry->resolve_cmd_ident = conn->cmd_ident;
-       goto out;
-      }
-    } else if (id_type == SILC_ID_SERVER) {
-      /* Find Server entry */
-      server_id = id;
-      server = silc_client_get_server_by_id(client, conn, server_id);
-      if (!server) {
-       silc_client_channel_set_wait(client, conn, channel,
-                                    conn->cmd_ident + 1);
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_SERVER, server_id);
-       server = silc_client_add_server(client, conn, NULL, NULL, server_id);
-       if (!server)
-         goto out;
-
-       server->resolve_cmd_ident = conn->cmd_ident;
-       server_id = NULL;
-       goto out;
-      }
-
-      /* If entry being resoled, wait for it before processing this notify */
-      if (server->resolve_cmd_ident) {
-       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-       res->packet = silc_packet_context_dup(packet);
-       res->context = client;
-       res->sock = silc_socket_dup(conn->sock);
-       silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                   server->resolve_cmd_ident,
-                                   silc_client_notify_by_server_pending, res);
-       goto out;
-      }
-
-      /* Save the pointer to the client_entry pointer */
-      client_entry = (SilcClientEntry)server;
-    } else {
-      /* Find Channel entry */
-      silc_free(channel_id);
-      channel_id = id;
-      client_entry = (SilcClientEntry)
-       silc_client_get_channel_by_id(client, conn, channel_id);
-      if (!client_entry) {
-       silc_client_channel_set_wait(client, conn, channel,
-                                    conn->cmd_ident + 1);
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_CHANNEL, channel_id);
-       goto out;
-      }
+    client_entry2 = silc_client_get_client_by_id(client, conn,
+                                                &id.u.client_id);
+    if (!client_entry2 || !client_entry2->nickname[0]) {
+      /** Resolve client */
+      silc_client_unref_client(client, conn, client_entry);
+      silc_client_unref_client(client, conn, client_entry2);
+      SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                          client, conn, &id.u.client_id, NULL,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
     }
+    entry = client_entry2;
+  } else if (id.type == SILC_ID_SERVER) {
+    /* Find Server entry */
+    server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
+    if (!server) {
+      /** Resolve server */
+      SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
+                                          client, conn, &id.u.server_id,
+                                          silc_client_notify_resolved,
+                                          fsm));
+      /* NOT REACHED */
+    }
+    entry = server;
+  } else {
+    /* Find Channel entry */
+    channel_entry = silc_client_get_channel_by_id(client, conn,
+                                                 &id.u.channel_id);
+    if (!channel_entry) {
+      /** Resolve channel */
+      SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+                                   client, conn, &id.u.channel_id,
+                                   silc_client_notify_resolved,
+                                   fsm));
+      /* NOT REACHED */
+    }
+    entry = channel_entry;
+  }
 
-    /* Get the mode */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    SILC_GET32_MSB(mode, tmp);
+  /* Notify application. */
+  NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
 
-    /* If information is being resolved for this channel, wait for it */
-    if (channel->resolve_cmd_ident) {
-      silc_client_channel_wait(client, conn, channel, packet);
-      goto out;
-    }
+  /* Delete the killed client */
+  if (client_entry != conn->local_entry)
+    silc_client_del_client(client, conn, client_entry);
 
-    /* Get target Client ID */
-    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (!tmp)
-      goto out;
+ out:
+  silc_client_unref_client(client, conn, client_entry);
+  if (client_entry2)
+    silc_client_unref_client(client, conn, client_entry2);
+  if (server)
+    silc_client_unref_server(client, conn, server);
+  if (channel_entry)
+    silc_client_unref_channel(client, conn, channel_entry);
+
+  /** Notify processed */
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-    silc_free(client_id);
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+/**************************** SERVER SIGNOFF ********************************/
 
-    /* Find target Client entry */
-    client_entry2 =
-      silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry2) {
-      silc_client_notify_by_server_resolve(client, conn, packet,
-                                          SILC_ID_CLIENT, client_id);
-      goto out;
-    }
+/* Some server quit SILC network.  Remove its clients from channels. */
 
-    /* Save the mode */
-    chu = silc_client_on_channel(channel, client_entry2);
-    if (chu)
-      chu->mode = mode;
-
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->internal->ops->notify(client, conn, type,
-                                 id_type, client_entry, mode,
-                                 client_entry2, channel);
-    break;
+SILC_FSM_STATE(silc_client_notify_server_signoff)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry;
+  SilcDList clients;
+  SilcID id;
+  int i;
 
-  case SILC_NOTIFY_TYPE_MOTD:
-    /*
-     * Received Message of the day
-     */
+  SILC_LOG_DEBUG(("Notify: SIGNOFF"));
 
-    SILC_LOG_DEBUG(("Notify: MOTD"));
+  clients = silc_dlist_init();
+  if (!clients)
+    goto out;
 
-    /* Get motd */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
+  for (i = 1; i < silc_argument_get_arg_num(args); i++) {
+    /* Get Client ID */
+    if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
       goto out;
 
-    /* Notify application */
-    client->internal->ops->notify(client, conn, type, tmp);
-    break;
+    /* Get the client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (client_entry)
+      silc_dlist_add(clients, client_entry);
+  }
 
-  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
-    /*
-     * Router has enforced a new ID to a channel. Let's change the old
-     * ID to the one provided here.
-     */
+  /* Notify application.  We don't keep server entries so the server
+     entry is returned as NULL. The client's are returned as list. */
+  NOTIFY(client, conn, type, NULL, clients);
 
-    SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
+  /* Delete the clients */
+  silc_dlist_start(clients);
+  while ((client_entry = silc_dlist_get(clients)))
+    silc_client_del_client(client, conn, client_entry);
 
-    /* Get the old ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!channel_id)
-      goto out;
+ out:
+  /** Notify processed */
+  silc_client_list_free(client, conn, clients);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-    /* Get the channel entry */
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      goto out;
+/******************************** ERROR *************************************/
 
-    silc_free(channel_id);
-    channel_id = NULL;
+/* Some error occurred */
 
-    /* Get the new ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
-      goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!channel_id)
+SILC_FSM_STATE(silc_client_notify_error)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcID id;
+  SilcStatus error;
+
+  /* Get error */
+  tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+  if (!tmp && tmp_len != 1)
+    goto out;
+  error = (SilcStatus)tmp[0];
+
+  SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
+
+  /* Handle the error */
+  if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
       goto out;
+    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+    if (client_entry) {
+      silc_client_del_client(client, conn, client_entry);
+      silc_client_unref_client(client, conn, client_entry);
+    }
+  }
 
-    /* Replace the Channel ID */
-    if (silc_client_replace_channel_id(client, conn, channel, channel_id))
-      channel_id = NULL;
+  /* Notify application. */
+  NOTIFY(client, conn, type, error);
 
-    /* Notify application */
-    client->internal->ops->notify(client, conn, type, channel, channel);
-    break;
+ out:
+  /** Notify processed */
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
+}
 
-  case SILC_NOTIFY_TYPE_KICKED:
-    /*
-     * A client (maybe me) was kicked from a channel
-     */
+/******************************** WATCH *************************************/
 
-    SILC_LOG_DEBUG(("Notify: KICKED"));
+/* Received notify about some client we are watching */
 
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
+SILC_FSM_STATE(silc_client_notify_watch)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcNotifyType type = silc_notify_get_type(payload);
+  SilcArgumentPayload args = silc_notify_get_args(payload);
+  SilcClientEntry client_entry = NULL;
+  SilcNotifyType notify = 0;
+  SilcBool del_client = FALSE;
+  unsigned char *pk, *tmp;
+  SilcUInt32 mode, pk_len, tmp_len;
+  SilcPublicKey public_key = NULL;
+  SilcID id;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-    if (!client_id)
-      goto out;
+  SILC_LOG_DEBUG(("Notify: WATCH"));
 
-    /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
+  /* Get sender Client ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
 
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id)
-      goto out;
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+  /* Find Client entry and if not found resolve it */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry || !client_entry->nickname[0]) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
 
-    /* From protocol version 1.1 we get the kicker's client ID as well */
-    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (tmp) {
-      silc_free(client_id);
-      client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-      if (!client_id)
-       goto out;
-
-      /* Find kicker's client entry and if not found resolve it */
-      client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
-      if (!client_entry2) {
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_CLIENT, client_id);
-       goto out;
-      } else {
-       if (client_entry2 != conn->local_entry)
-         silc_client_nickname_format(client, conn, client_entry2);
-      }
-    }
+  /* Get user mode */
+  tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+  if (!tmp || tmp_len != 4)
+    goto out;
+  SILC_GET32_MSB(mode, tmp);
 
-    /* Get comment */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-
-    /* Remove kicked client from channel */
-    if (client_entry != conn->local_entry) {
-      chu = silc_client_on_channel(channel, client_entry);
-      if (chu) {
-       silc_hash_table_del(client_entry->channels, channel);
-       silc_hash_table_del(channel->user_list, client_entry);
-       silc_free(chu);
-      }
-    }
+  /* Get notify type */
+  tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+  if (tmp && tmp_len != 2)
+    goto out;
+  if (tmp)
+    SILC_GET16_MSB(notify, tmp);
+
+  /* Get nickname */
+  tmp = silc_argument_get_arg_type(args, 2, NULL);
+  if (tmp) {
+    char *tmp_nick = NULL;
+
+    if (client->internal->params->nickname_parse)
+      client->internal->params->nickname_parse(client_entry->nickname,
+                                              &tmp_nick);
+    else
+      tmp_nick = strdup(tmp);
+
+    /* If same nick, the client was new to us and has become "present"
+       to network.  Send NULL as nick to application. */
+    if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
+      tmp = NULL;
 
-    /* Notify application. The channel entry is sent last as this notify
-       is for channel but application don't know it from the arguments
-       sent by server. */
-    client->internal->ops->notify(client, conn, type, client_entry, tmp,
-                                 client_entry2, channel);
-
-    /* Remove kicked client (us) from channel */
-    if (client_entry == conn->local_entry) {
-      /* If I was kicked from channel, remove the channel */
-      if (conn->current_channel == channel)
-       conn->current_channel = NULL;
-      silc_client_del_channel(client, conn, channel);
-    } else {
-      if (!silc_hash_table_count(client_entry->channels)) {
-       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-       res->context = client;
-       res->sock = silc_socket_dup(conn->sock);
-       res->packet = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
-       silc_schedule_task_add(client->schedule, conn->sock->sock,
-                              silc_client_notify_check_client, res,
-                              (5 + (silc_rng_get_rn16(client->rng) % 529)),
-                              0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-      }
-    }
-    break;
+    silc_free(tmp_nick);
+  }
 
-  case SILC_NOTIFY_TYPE_KILLED:
-    {
-      /*
-       * A client (maybe me) was killed from the network.
-       */
-      char *comment;
-      SilcUInt32 comment_len;
-
-      SILC_LOG_DEBUG(("Notify: KILLED"));
-
-      /* Get Client ID */
-      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-      if (!tmp)
-       goto out;
-
-      client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-      if (!client_id)
-       goto out;
-
-      /* Find Client entry */
-      client_entry = silc_client_get_client_by_id(client, conn, client_id);
-      if (!client_entry)
-       goto out;
-
-      /* Get comment */
-      comment = silc_argument_get_arg_type(args, 2, &comment_len);
-
-      /* From protocol version 1.1 we get killer's client ID as well */
-      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-      if (tmp) {
-       silc_free(client_id);
-       client_id = NULL;
-       id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
-       if (!id)
-         goto out;
-
-       /* Find Client entry */
-       if (id_type == SILC_ID_CLIENT) {
-         /* Find Client entry */
-         client_id = id;
-         client_entry2 = silc_client_get_client_by_id(client, conn,
-                                                      client_id);
-         if (!client_entry) {
-           silc_client_notify_by_server_resolve(client, conn, packet,
-                                                SILC_ID_CLIENT, client_id);
-           goto out;
-         }
-       } else if (id_type == SILC_ID_SERVER) {
-         /* Find Server entry */
-         server_id = id;
-         server = silc_client_get_server_by_id(client, conn, server_id);
-         if (!server) {
-           silc_client_notify_by_server_resolve(client, conn, packet,
-                                                SILC_ID_SERVER, server_id);
-           server = silc_client_add_server(client, conn, NULL, NULL,
-                                           server_id);
-           if (!server)
-             goto out;
-
-           server->resolve_cmd_ident = conn->cmd_ident;
-           server_id = NULL;
-           goto out;
-         }
-
-         if (server->resolve_cmd_ident) {
-           SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-           res->packet = silc_packet_context_dup(packet);
-           res->context = client;
-           res->sock = silc_socket_dup(conn->sock);
-           silc_client_command_pending(conn, SILC_COMMAND_NONE,
-                                       server->resolve_cmd_ident,
-                                       silc_client_notify_by_server_pending,
-                                       res);
-           goto out;
-         }
-
-         /* Save the pointer to the client_entry pointer */
-         client_entry2 = (SilcClientEntry)server;
-       } else {
-         /* Find Channel entry */
-         channel_id = id;
-         channel = silc_client_get_channel_by_id(client, conn, channel_id);
-         if (!channel) {
-           silc_client_notify_by_server_resolve(client, conn, packet,
-                                                SILC_ID_CHANNEL, channel_id);
-           goto out;
-         }
-
-         /* Save the pointer to the client_entry pointer */
-         client_entry2 = (SilcClientEntry)channel;
-         silc_free(channel_id);
-         channel_id = NULL;
-       }
-      }
-
-      /* Notify application. */
-      client->internal->ops->notify(client, conn, type, client_entry,
-                                   comment, id_type, client_entry2);
-
-      if (client_entry != conn->local_entry)
-       /* Remove the client from all channels and free it */
-       silc_client_del_client(client, conn, client_entry);
+  /* Get public key, if present */
+  pk = silc_argument_get_arg_type(args, 5, &pk_len);
+  if (pk && !client_entry->public_key) {
+    if (silc_public_key_payload_decode(pk, pk_len, &public_key)) {
+      client_entry->public_key = public_key;
+      public_key = NULL;
     }
-    break;
+  }
 
-  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
-    {
-      /*
-       * A server quit the SILC network and some clients must be removed
-       * from channels as they quit as well.
-       */
-      SilcClientEntry *clients = NULL;
-      SilcUInt32 clients_count = 0;
-      int i;
-
-      SILC_LOG_DEBUG(("Notify: SIGNOFF"));
-
-      for (i = 1; i < silc_argument_get_arg_num(args); i++) {
-       /* Get Client ID */
-       tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
-       if (tmp) {
-         client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-         if (!client_id)
-           goto out;
-
-         /* Get the client entry */
-         client_entry = silc_client_get_client_by_id(client, conn, client_id);
-         if (client_entry) {
-           clients = silc_realloc(clients, sizeof(*clients) *
-                                  (clients_count + 1));
-           clients[clients_count] = client_entry;
-           clients_count++;
-         }
-         silc_free(client_id);
-       }
-      }
-      client_id = NULL;
-
-      /* Notify application. We don't keep server entries so the server
-        entry is returned as NULL. The client's are returned as array
-        of SilcClientEntry pointers. */
-      client->internal->ops->notify(client, conn, type, NULL,
-                                   clients, clients_count);
-
-      for (i = 0; i < clients_count; i++) {
-       /* Remove client from all channels */
-       client_entry = clients[i];
-       if (client_entry == conn->local_entry)
-         continue;
-
-       /* Remove the client from all channels and free it */
-       silc_client_del_client(client, conn, client_entry);
-      }
-      silc_free(clients);
+  /* Notify application. */
+  NOTIFY(client, conn, type, client_entry, tmp, mode, notify,
+        client_entry->public_key);
 
-    }
-    break;
+  client_entry->mode = mode;
 
-  case SILC_NOTIFY_TYPE_ERROR:
-    {
-      /*
-       * Some has occurred and server is notifying us about it.
-       */
-      SilcStatus error;
-
-      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-      if (!tmp && tmp_len != 1)
-       goto out;
-      error = (SilcStatus)tmp[0];
-
-      SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
-
-      if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-       tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-       if (tmp) {
-         client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-         if (!client_id)
-           goto out;
-         client_entry = silc_client_get_client_by_id(client, conn,
-                                                     client_id);
-         if (client_entry)
-           silc_client_del_client(client, conn, client_entry);
-       }
-      }
-
-      /* Notify application. */
-      client->internal->ops->notify(client, conn, type, error);
-    }
-    break;
+  /* If nickname was changed, remove the client entry unless the
+     client is on some channel */
+  /* XXX, why do we need to remove the client entry?? */
+  if (tmp && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
+      !silc_hash_table_count(client_entry->channels))
+    del_client = TRUE;
+  else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
+          notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
+          notify == SILC_NOTIFY_TYPE_KILLED)
+    del_client = TRUE;
 
-  case SILC_NOTIFY_TYPE_WATCH:
-    {
-      /*
-       * Received notify about some client we are watching
-       */
-      SilcNotifyType notify = 0;
-      SilcBool del_client = FALSE;
-      unsigned char *pk;
-      SilcUInt32 pk_len;
-      SilcPublicKey public_key = NULL;
-
-      SILC_LOG_DEBUG(("Notify: WATCH"));
-
-      /* Get sender Client ID */
-      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-      if (!tmp)
-       goto out;
-      client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-      if (!client_id)
-       goto out;
-
-      /* Find Client entry and if not found query it */
-      client_entry = silc_client_get_client_by_id(client, conn, client_id);
-      if (!client_entry) {
-       silc_client_notify_by_server_resolve(client, conn, packet,
-                                            SILC_ID_CLIENT, client_id);
-       goto out;
-      }
-
-      /* Get user mode */
-      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-      if (!tmp || tmp_len != 4)
-       goto out;
-      SILC_GET32_MSB(mode, tmp);
-
-      /* Get notify type */
-      tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
-      if (tmp && tmp_len != 2)
-       goto out;
-      if (tmp)
-       SILC_GET16_MSB(notify, tmp);
-
-      /* Get nickname */
-      tmp = silc_argument_get_arg_type(args, 2, NULL);
-      if (tmp) {
-       char *tmp_nick = NULL;
-
-       if (client->internal->params->nickname_parse)
-         client->internal->params->nickname_parse(client_entry->nickname,
-                                                  &tmp_nick);
-       else
-         tmp_nick = strdup(tmp);
-
-       /* If same nick, the client was new to us and has become "present"
-          to network.  Send NULL as nick to application. */
-       if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
-         tmp = NULL;
-
-       silc_free(tmp_nick);
-      }
-
-      /* Get public key, if present */
-      pk = silc_argument_get_arg_type(args, 5, &pk_len);
-      if (pk && !client_entry->public_key) {
-        if (silc_pkcs_public_key_payload_decode(pk, pk_len, &public_key)) {
-         client_entry->public_key = public_key;
-         public_key = NULL;
-       }
-      }
-
-      /* Notify application. */
-      client->internal->ops->notify(client, conn, type, client_entry,
-                                   tmp, mode, notify,
-                                   client_entry->public_key);
-
-      client_entry->mode = mode;
-
-      /* If nickname was changed, remove the client entry unless the
-        client is on some channel */
-      if (tmp && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
-         !silc_hash_table_count(client_entry->channels))
-       del_client = TRUE;
-      else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
-              notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
-              notify == SILC_NOTIFY_TYPE_KILLED)
-       del_client = TRUE;
-
-      if (del_client) {
-       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-       res->context = client;
-       res->sock = silc_socket_dup(conn->sock);
-       res->packet = client_id;
-        client_id = NULL;
-       silc_schedule_task_add(client->schedule, conn->sock->sock,
-                              silc_client_notify_del_client_cb, res,
-                              1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-      }
-
-      silc_pkcs_public_key_free(public_key);
-    }
-    break;
+  if (del_client)
+    silc_client_del_client(client, conn, client_entry);
 
-  default:
-    break;
-  }
+  if (public_key)
+    silc_pkcs_public_key_free(public_key);
 
  out:
-  silc_notify_payload_free(payload);
-  silc_free(client_id);
-  silc_free(channel_id);
-  silc_free(server_id);
+  /** Notify processed */
+  silc_client_unref_client(client, conn, client_entry);
+  silc_fsm_next(fsm, silc_client_notify_processed);
+  return SILC_FSM_CONTINUE;
 }
index ec0e3b8de39a9b8dac782d6d47b42995ba225da1..a932fbf7822451b96d8731c54c8e64fa1e56096b 100644 (file)
@@ -30,6 +30,7 @@ SilcBool silc_client_send_private_message(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientEntry client_entry,
                                          SilcMessageFlags flags,
+                                         SilcHash hash,
                                          unsigned char *data,
                                          SilcUInt32 data_len)
 {
@@ -40,6 +41,8 @@ SilcBool silc_client_send_private_message(SilcClient client,
 
   if (!client || !conn || !client_entry)
     return FALSE;
+  if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+    return FALSE;
 
   /* Encode private message payload */
   buffer =
@@ -49,7 +52,7 @@ SilcBool silc_client_send_private_message(SilcClient client,
                                TRUE, client_entry->internal.send_key,
                                client_entry->internal.hmac_send,
                                client->rng, NULL, conn->private_key,
-                               client->sha1hash, NULL);
+                               hash, NULL);
   if (!buffer) {
     SILC_LOG_ERROR(("Error encoding private message"));
     return FALSE;
@@ -76,10 +79,7 @@ typedef struct {
   unsigned int stopped      : 1;
 } *SilcClientPrivateMessageWait;
 
-/* Client resolving callback.  This continues the private message packet
-   processing in the packet processor thread, which is in waiting state
-   (for incoming packets) when we get here.  We can safely continue in
-   the thread and then return back to waiting when we do it synchronously. */
+/* Client resolving callback.  Continues with the private message processing */
 
 static void silc_client_private_message_resolved(SilcClient client,
                                                 SilcClientConnection conn,
@@ -87,15 +87,12 @@ static void silc_client_private_message_resolved(SilcClient client,
                                                 SilcDList clients,
                                                 void *context)
 {
-  if (!clients) {
-    silc_packet_free(context);
-    return;
-  }
+  /* If no client found, ignore the private message, a silent error */
+  if (!clients)
+    silc_fsm_next(context, silc_client_private_message_error);
 
   /* Continue processing the private message packet */
-  silc_fsm_set_state_context(&conn->internal->packet_thread, context);
-  silc_fsm_next(&conn->internal->packet_thread, silc_client_private_message);
-  silc_fsm_continue_sync(&conn->internal->packet_thread);
+  SILC_FSM_CALL_CONTINUE(context);
 }
 
 /* Private message received. */
@@ -113,24 +110,31 @@ SILC_FSM_STATE(silc_client_private_message)
   SilcUInt32 message_len;
   SilcClientPrivateMessageWait pmw;
 
-  if (packet->src_id_type != SILC_ID_CLIENT)
-    goto out;
+  SILC_LOG_DEBUG(("Received private message"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT) {
+    /** Invalid packet */
+    silc_fsm_next(fsm, silc_client_private_message_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
-                     &remote_id, sizeof(remote_id)))
-    goto out;
+                     &remote_id, sizeof(remote_id))) {
+    /** Invalid source ID */
+    silc_fsm_next(fsm, silc_client_private_message_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Check whether we know this client already */
   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
   if (!remote_client || !remote_client->nickname[0]) {
-    /* Resolve the client info.  We return back to packet thread to receive
-       other packets while we wait for the resolving to finish. */
+    /** Resolve client info */
     silc_client_unref_client(client, conn, remote_client);
-    silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &remote_id, NULL,
                                         silc_client_private_message_resolved,
-                                        packet);
-    silc_fsm_next(fsm, silc_client_connection_st_packet);
-    return SILC_FSM_CONTINUE;
+                                        fsm));
+    /* NOT REACHED */
   }
 
   if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
@@ -189,19 +193,27 @@ SILC_FSM_STATE(silc_client_private_message)
     /* Send the away message */
     silc_client_send_private_message(client, conn, remote_client,
                                     SILC_MESSAGE_FLAG_AUTOREPLY |
-                                    SILC_MESSAGE_FLAG_NOREPLY,
+                                    SILC_MESSAGE_FLAG_NOREPLY, NULL,
                                     conn->internal->away->away,
                                     strlen(conn->internal->away->away));
   }
 
  out:
   /** Packet processed */
+  silc_packet_free(packet);
   silc_client_unref_client(client, conn, remote_client);
   if (payload)
     silc_message_payload_free(payload);
+  return SILC_FSM_FINISH;
+}
+
+/* Private message error. */
+
+SILC_FSM_STATE(silc_client_private_message_error)
+{
+  SilcPacket packet = state_context;
   silc_packet_free(packet);
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
+  return SILC_FSM_FINISH;
 }
 
 #if 0 /* XXX we need to rethink this */
@@ -385,24 +397,21 @@ SILC_FSM_STATE(silc_client_private_message_key)
 
   if (packet->src_id_type != SILC_ID_CLIENT) {
     silc_packet_free(packet);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
                      &remote_id, sizeof(remote_id))) {
     silc_packet_free(packet);
-    goto out;
+    return SILC_FSM_FINISH;
   }
 
   /* Always resolve the remote client.  The actual packet is processed
      in the resolving callback. */
-  silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+  SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                      client, conn, &remote_id, NULL,
                                       silc_client_private_message_key_cb,
-                                      packet);
-
- out:
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
+                                      fsm));
 }
 
 /* Adds private message key to the client library. The key will be used to
@@ -584,10 +593,7 @@ silc_client_send_private_message_key_request(SilcClient client,
                                             SilcClientConnection conn,
                                             SilcClientEntry client_entry)
 {
-  SilcBufferStruct buffer;
-  int cipher_len, hmac_len;
   const char *cipher, *hmac;
-  SilcBool ret;
 
   if (!client || !conn || !client_entry)
     return FALSE;
@@ -598,29 +604,18 @@ silc_client_send_private_message_key_request(SilcClient client,
   SILC_LOG_DEBUG(("Sending private message key indicator"));
 
   cipher = silc_cipher_get_name(client_entry->internal.send_key);
-  cipher_len = strlen(cipher);
   hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
-  hmac_len = strlen(hmac);
-
-  /* Create private message key payload */
-  memset(&buffer, 0, sizeof(buffer));
-  if (silc_buffer_format(&buffer,
-                        SILC_STR_UI_SHORT(cipher_len),
-                        SILC_STR_UI_XNSTRING(cipher,
-                                             cipher_len),
-                        SILC_STR_UI_SHORT(hmac_len),
-                        SILC_STR_UI_XNSTRING(hmac,
-                                             hmac_len),
-                        SILC_STR_END) < 0)
-    return FALSE;
 
   /* Send the packet */
-  ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE_KEY,
-                            0, 0, NULL, SILC_ID_CLIENT, &client_entry->id,
-                            silc_buffer_datalen(&buffer), NULL, NULL);
-  silc_buffer_purge(&buffer);
-
-  return ret;
+  return silc_packet_send_va_ext(conn->stream,
+                                SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                                0, 0, NULL, SILC_ID_CLIENT,
+                                &client_entry->id, NULL, NULL,
+                                SILC_STR_UI_SHORT(strlen(cipher)),
+                                SILC_STR_DATA(cipher, strlen(cipher)),
+                                SILC_STR_UI_SHORT(strlen(hmac)),
+                                SILC_STR_DATA(hmac, strlen(hmac)),
+                                SILC_STR_END);
 }
 
 /* Removes the private message from the library. The key won't be used
index b7b8b7f921049f91e0be3c5655919c4d2a277ae9..93e5532d114087fa12f1c256b44761bcfdbd0e05 100644 (file)
@@ -21,6 +21,7 @@
 #define CLIENT_PRVMSG_H
 
 SILC_FSM_STATE(silc_client_private_message);
+SILC_FSM_STATE(silc_client_private_message_error);
 SILC_FSM_STATE(silc_client_private_message_key);
 
 #endif /* CLIENT_PRVMSG_H */
index 395933d927ee402e2bc876ef7d2f9e01820f5d03..98a86f513f228519c0b0d14be21231f09a862a9b 100644 (file)
 /************************ Static utility functions **************************/
 
 
+/****************************** NEW_ID packet *******************************/
+
+/* Received new ID packet from server during registering to SILC network */
+
+SILC_FSM_STATE(silc_client_new_id)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcID id;
+
+  if (conn->local_id)
+    goto out;
+
+  SILC_LOG_DEBUG(("New ID received from server"));
+
+  if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
+                               silc_buffer_len(&packet->buffer), &id))
+    goto out;
+
+  /* Create local client entry */
+  conn->local_entry = silc_client_add_client(client, conn,
+                                            (client->nickname ?
+                                             client->nickname :
+                                             client->username),
+                                            client->username,
+                                            client->realname,
+                                            &id.u.client_id, 0);
+  if (!conn->local_entry)
+    goto out;
+
+  /* Save the ID */
+  conn->local_id = &conn->local_entry->id;
+  conn->local_idp = silc_buffer_copy(&packet->buffer);
+
+  /* Save cache entry */
+  silc_idcache_find_by_id_one(conn->internal->client_cache, conn->local_id,
+                             &conn->internal->local_entry);
+
+  /* Save remote ID */
+  if (packet->src_id_len) {
+    conn->remote_idp = silc_id_payload_encode_data(packet->src_id,
+                                                  packet->src_id_len,
+                                                  packet->src_id_type);
+    if (!conn->remote_idp)
+      goto out;
+    silc_id_payload_parse_id(silc_buffer_data(conn->remote_idp),
+                            silc_buffer_len(conn->remote_idp),
+                            &conn->remote_id);
+  }
+
+  /* Signal connection that new ID was received so it can continue
+     with the registering. */
+  if (conn->internal->registering)
+    silc_fsm_continue_sync(&conn->internal->event_thread);
+
+ out:
+  /** Packet processed */
+  silc_packet_free(packet);
+  return SILC_FSM_FINISH;
+}
+
+
 /************************ Register to SILC network **************************/
 
 /* Register to network */
@@ -35,37 +98,23 @@ SILC_FSM_STATE(silc_client_st_register)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcBufferStruct buf;
-  int ret;
 
   SILC_LOG_DEBUG(("Register to network"));
 
-  memset(&buf, 0, sizeof(buf));
-  ret = silc_buffer_format(&buf,
+  /* Send NEW_CLIENT packet to register to network */
+  if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
                           SILC_STR_UI_SHORT(strlen(client->username)),
                           SILC_STR_DATA(client->username,
                                         strlen(client->username)),
                           SILC_STR_UI_SHORT(strlen(client->realname)),
                           SILC_STR_DATA(client->realname,
                                         strlen(client->realname)),
-                          SILC_STR_END);
-  if (ret < 0) {
-    /** Out of memory */
-    silc_fsm_next(fsm, silc_client_st_register_error);
-    return SILC_FSM_CONTINUE;
-  }
-
-  /* Send the packet */
-  if (!silc_packet_send(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
-                       silc_buffer_data(&buf), silc_buffer_len(&buf))) {
+                          SILC_STR_END)) {
     /** Error sending packet */
-    silc_buffer_purge(&buf);
     silc_fsm_next(fsm, silc_client_st_register_error);
     return SILC_FSM_CONTINUE;
   }
 
-  silc_buffer_purge(&buf);
-
   /** Wait for new ID */
   conn->internal->registering = TRUE;
   silc_fsm_next_later(fsm, silc_client_st_register_complete, 15, 0);
@@ -108,7 +157,8 @@ SILC_FSM_STATE(silc_client_st_register_complete)
                           silc_buffer_len(conn->remote_idp));
 
   /* Call connection callback.  We are now inside SILC network. */
-  conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, conn->context);
+  conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
+                conn->context);
 
   conn->internal->registering = FALSE;
   return SILC_FSM_FINISH;
@@ -122,7 +172,7 @@ SILC_FSM_STATE(silc_client_st_register_error)
   /* XXX */
   /* Close connection */
 
-  conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, conn->context);
+  conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL, conn->context);
 
   return SILC_FSM_FINISH;
 }
index 79ef0529c239bcfd739fca6cd495f1746fb38b50..0aff00a6c7d396ff23fa4663f04e18057d41026e 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef CLIENT_REGISTER_H
 #define CLIENT_REGISTER_H
 
+SILC_FSM_STATE(silc_client_new_id);
 SILC_FSM_STATE(silc_client_st_register);
 SILC_FSM_STATE(silc_client_st_register_complete);
 SILC_FSM_STATE(silc_client_st_register_error);
index de95e743720c1c22cfd3ad544a1b161a65a15402..68e9eed62fdd04794363438368bee6c031968ce3 100644 (file)
@@ -567,7 +567,7 @@ SilcBool silc_client_command_pending(SilcClientConnection conn,
 
   silc_mutex_unlock(conn->internal->lock);
 
-  return FALSE;
+  return TRUE;
 }
 
 /******************************** WHOIS *************************************/
@@ -596,10 +596,10 @@ SILC_FSM_STATE(silc_client_command_whois)
 
   for (i = 1; i < cmd->argc; i++) {
     if (!strcasecmp(cmd->argv[i], "-details")) {
-       details = TRUE;
+      details = TRUE;
     } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
-       pubkey = cmd->argv[i + 1];
-       i++;
+      pubkey = cmd->argv[i + 1];
+      i++;
     } else {
       /* We assume that the first parameter is the nickname, if it isn't
          -details or -pubkey. The last parameter should always be the count */
@@ -826,7 +826,7 @@ SILC_FSM_STATE(silc_client_command_list)
     /* Get the Channel ID of the channel */
     channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
     if (channel)
-      idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+      idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
   }
 
   if (!idp)
@@ -887,7 +887,7 @@ SILC_FSM_STATE(silc_client_command_topic)
     goto out;
   }
 
-  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
 
   /* Send TOPIC command to the server */
   if (cmd->argc > 2)
@@ -1006,7 +1006,7 @@ SILC_FSM_STATE(silc_client_command_invite)
   }
 
   /* Send the command */
-  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
   if (client_entry) {
     clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
@@ -1052,10 +1052,13 @@ SILC_FSM_STATE(silc_client_command_quit_final)
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
-  /* Close connection */
+  /* Call connection callback */
   conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
-                conn->context);
-  //  silc_client_close_connection(q->client, q->conn->sock->user_data);
+                0, NULL, conn->context);
+
+  /* Signal to close connection */
+  conn->internal->disconnected = TRUE;
+  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
 
   return SILC_FSM_FINISH;
 }
@@ -1737,7 +1740,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
 
        if (cmd->argc == 3) {
          /* Send empty command to receive the public key list. */
-         chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+         chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
          silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
                                      NULL, NULL, 1,
                                      1, silc_buffer_datalen(chidp));
@@ -1791,7 +1794,7 @@ SILC_FSM_STATE(silc_client_command_cmode)
     }
   }
 
-  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
   SILC_PUT32_MSB(mode, modebuf);
 
   /* Send the command. We support sending only one mode at once that
@@ -1975,7 +1978,7 @@ SILC_FSM_STATE(silc_client_command_cumode)
     }
   }
 
-  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
   SILC_PUT32_MSB(mode, modebuf);
   clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
 
@@ -1991,10 +1994,16 @@ SILC_FSM_STATE(silc_client_command_cumode)
   silc_buffer_free(clidp);
   if (auth)
     silc_buffer_free(auth);
+  silc_free(nickname);
+  silc_client_list_free(client, conn, clients);
 
   /* Notify application */
   COMMAND(SILC_STATUS_OK);
 
+  /** Wait for command reply */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+
  out:
   silc_client_list_free(client, conn, clients);
   silc_free(nickname);
@@ -2064,7 +2073,7 @@ SILC_FSM_STATE(silc_client_command_kick)
   target = silc_dlist_get(clients);
 
   /* Send KICK command to the server */
-  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
   idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
   if (cmd->argc == 3)
     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
@@ -2286,7 +2295,7 @@ SILC_FSM_STATE(silc_client_command_ban)
     }
   }
 
-  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
 
   /* Send the command */
   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
@@ -2437,7 +2446,7 @@ SILC_FSM_STATE(silc_client_command_leave)
     goto out;
   }
 
-  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
 
   /* Send LEAVE command to the server */
   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
@@ -2529,8 +2538,10 @@ SILC_FSM_STATE(silc_client_command_getkey)
     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
   else
     nickname = strdup(cmd->argv[1]);
-  if (!nickname)
+  if (!nickname) {
+    COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
     return SILC_FSM_FINISH;
+  }
 
   /* Find client entry */
   clients = silc_client_get_clients_local(client, conn, nickname,
@@ -2549,7 +2560,7 @@ SILC_FSM_STATE(silc_client_command_getkey)
                                             2, cmd->argv[1],
                                             strlen(cmd->argv[1])));
     }
-    idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
+    idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
   } else {
     client_entry = silc_dlist_get(clients);
     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
@@ -2748,10 +2759,8 @@ SILC_FSM_STATE(silc_client_command)
   payload = silc_command_payload_parse(packet->buffer.data,
                                       silc_buffer_len(&packet->buffer));
   if (!payload) {
-    /** Bad command payload */
     SILC_LOG_DEBUG(("Bad command packet"));
-    silc_fsm_next(fsm, silc_client_connection_st_packet);
-    return SILC_FSM_CONTINUE;
+    return SILC_FSM_FINISH;
   }
 
   /* Get arguments */
@@ -2774,8 +2783,5 @@ SILC_FSM_STATE(silc_client_command)
   }
 
   silc_command_payload_free(payload);
-
-  /** Packet processed */
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
+  return SILC_FSM_FINISH;
 }
index 9eea6bf1a53177cbb896377b39304d77b5896524..dd4c2f1aaef7e3e7ceda3e1a7b9dfa65ce811c1b 100644 (file)
@@ -135,10 +135,8 @@ SILC_FSM_STATE(silc_client_command_reply)
   payload = silc_command_payload_parse(silc_buffer_datalen(&packet->buffer));
   silc_packet_free(packet);
   if (!payload) {
-    /** Bad reply payload */
     SILC_LOG_DEBUG(("Bad command reply packet"));
-    silc_fsm_next(fsm, silc_client_connection_st_packet);
-    return SILC_FSM_CONTINUE;
+    return SILC_FSM_FINISH;
   }
 
   cmd_ident = silc_command_get_ident(payload);
@@ -148,16 +146,15 @@ SILC_FSM_STATE(silc_client_command_reply)
   silc_mutex_lock(conn->internal->lock);
   silc_list_start(conn->internal->pending_commands);
   while ((cmd = silc_list_get(conn->internal->pending_commands)))
-    if (cmd->cmd == command && cmd->cmd_ident == cmd_ident)
+    if ((cmd->cmd == command || cmd->cmd == SILC_COMMAND_NONE)
+       && cmd->cmd_ident == cmd_ident)
       break;
   silc_mutex_unlock(conn->internal->lock);
 
   if (!cmd) {
-    /** Unknown command reply */
     SILC_LOG_DEBUG(("Unknown command reply"));
     silc_command_payload_free(payload);
-    silc_fsm_next(fsm, silc_client_connection_st_packet);
-    return SILC_FSM_CONTINUE;
+    return SILC_FSM_FINISH;
   }
 
   /* Signal command thread that command reply has arrived */
@@ -165,56 +162,41 @@ SILC_FSM_STATE(silc_client_command_reply)
   silc_fsm_next(&cmd->thread, silc_client_command_reply_process);
   silc_fsm_continue_sync(&cmd->thread);
 
-  /** Packet processed */
-  silc_fsm_next(fsm, silc_client_connection_st_packet);
-  return SILC_FSM_CONTINUE;
+  return SILC_FSM_FINISH;
 }
 
 /* Wait here for command reply to arrive from remote host */
 
 SILC_FSM_STATE(silc_client_command_reply_wait)
 {
-  SilcClientCommandContext cmd = fsm_context;
-
   SILC_LOG_DEBUG(("Wait for command reply"));
 
   /** Wait for command reply */
-  cmd->processed = FALSE;
   silc_fsm_set_state_context(fsm, NULL);
-  silc_fsm_next_later(fsm, silc_client_command_reply_process, 20, 0);
+  silc_fsm_next_later(fsm, silc_client_command_reply_timeout, 20, 0);
   return SILC_FSM_WAIT;
 }
 
-/* Process received command reply payload */
+/* Timeout occurred while waiting command reply */
 
-SILC_FSM_STATE(silc_client_command_reply_process)
+SILC_FSM_STATE(silc_client_command_reply_timeout)
 {
   SilcClientCommandContext cmd = fsm_context;
-  SilcCommandPayload payload = state_context;
-
-  if (!payload) {
-    /* Timeout, reply not received in timely fashion */
-    SilcArgumentPayload args = NULL;
-    ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT);
-    return SILC_FSM_FINISH;
-  }
+  SilcArgumentPayload args = NULL;
 
-  if (cmd->processed) {
-    /* Command reply processed */
-    silc_command_payload_free(payload);
+  /* Timeout, reply not received in timely fashion */
+  ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT);
+  return SILC_FSM_FINISH;
+}
 
-    if (cmd->status == SILC_STATUS_OK || cmd->status == SILC_STATUS_LIST_END ||
-       SILC_STATUS_IS_ERROR(cmd->status))
-      return SILC_FSM_FINISH;
+/* Process received command reply payload */
 
-    /** Wait more command payloads */
-    silc_fsm_next(fsm, silc_client_command_reply_wait);
-    return SILC_FSM_CONTINUE;
-  }
+SILC_FSM_STATE(silc_client_command_reply_process)
+{
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
 
   silc_command_get_status(payload, &cmd->status, &cmd->error);
-  silc_fsm_set_state_context(fsm, payload);
-  cmd->processed = TRUE;
 
   switch (cmd->cmd) {
   case SILC_COMMAND_WHOIS:
@@ -290,7 +272,7 @@ SILC_FSM_STATE(silc_client_command_reply_process)
     silc_fsm_next(fsm, silc_client_command_reply_cumode);
     break;
   case SILC_COMMAND_KICK:
-    /** kick */
+    /** KICK */
     silc_fsm_next(fsm, silc_client_command_reply_kick);
     break;
   case SILC_COMMAND_BAN:
@@ -332,6 +314,24 @@ SILC_FSM_STATE(silc_client_command_reply_process)
   return SILC_FSM_CONTINUE;
 }
 
+/* Completes command reply processing */
+
+SILC_FSM_STATE(silc_client_command_reply_processed)
+{
+  SilcClientCommandContext cmd = fsm_context;
+  SilcCommandPayload payload = state_context;
+
+  silc_command_payload_free(payload);
+
+  if (cmd->status == SILC_STATUS_OK || cmd->status == SILC_STATUS_LIST_END ||
+      SILC_STATUS_IS_ERROR(cmd->status))
+    return SILC_FSM_FINISH;
+
+  /** Wait more command payloads */
+  silc_fsm_next(fsm, silc_client_command_reply_wait);
+  return SILC_FSM_CONTINUE;
+}
+
 /******************************** WHOIS *************************************/
 
 /* Received reply for WHOIS command. */
@@ -447,7 +447,7 @@ SILC_FSM_STATE(silc_client_command_reply_whois)
   }
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -495,7 +495,7 @@ SILC_FSM_STATE(silc_client_command_reply_whowas)
 
  out:
   silc_client_unref_client(client, conn, client_entry);
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -571,7 +571,7 @@ SILC_FSM_STATE(silc_client_command_reply_identify)
     } else {
       silc_client_update_server(client, conn, server_entry, name, info);
     }
-    server_entry->resolve_cmd_ident = 0;
+    server_entry->internal.resolve_cmd_ident = 0;
 
     /* Notify application */
     silc_client_command_callback(cmd, server_entry, name, info);
@@ -606,7 +606,7 @@ SILC_FSM_STATE(silc_client_command_reply_identify)
   }
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -679,7 +679,7 @@ SILC_FSM_STATE(silc_client_command_reply_nick)
                               conn->local_entry->nickname, &old_client_id);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -705,7 +705,7 @@ SILC_FSM_STATE(silc_client_command_reply_list)
   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
     /* There were no channels in the network. */
     silc_client_command_callback(cmd, NULL, NULL, NULL, 0);
-    silc_fsm_next(fsm, silc_client_command_reply_process);
+    silc_fsm_next(fsm, silc_client_command_reply_processed);
     return SILC_FSM_CONTINUE;
   }
 
@@ -739,7 +739,7 @@ SILC_FSM_STATE(silc_client_command_reply_list)
   silc_client_command_callback(cmd, channel_entry, name, topic, usercount);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -787,7 +787,7 @@ SILC_FSM_STATE(silc_client_command_reply_topic)
   silc_client_command_callback(cmd, channel, channel->topic);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -834,7 +834,7 @@ SILC_FSM_STATE(silc_client_command_reply_invite)
   silc_client_command_callback(cmd, channel, tmp ? &buf : NULL);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -874,7 +874,7 @@ SILC_FSM_STATE(silc_client_command_reply_kill)
   }
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -933,7 +933,7 @@ SILC_FSM_STATE(silc_client_command_reply_info)
                               server->server_info);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -990,7 +990,7 @@ SILC_FSM_STATE(silc_client_command_reply_stats)
   silc_client_command_callback(cmd, &stats);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1013,7 +1013,7 @@ SILC_FSM_STATE(silc_client_command_reply_ping)
   /* Notify application */
   silc_client_command_callback(cmd);
 
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1029,7 +1029,6 @@ SILC_FSM_STATE(silc_client_command_reply_join)
   SilcCommandPayload payload = state_context;
   SilcArgumentPayload args = silc_command_get_args(payload);
   SilcChannelEntry channel;
-  SilcChannelUser chu;
   SilcUInt32 mode = 0, len, list_count;
   char *topic, *tmp, *channel_name = NULL, *hmac;
   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
@@ -1073,7 +1072,7 @@ SILC_FSM_STATE(silc_client_command_reply_join)
   /* Check whether we have this channel entry already. */
   channel = silc_client_get_channel(client, conn, channel_name);
   if (channel) {
-    if (!SILC_ID_CHANNEL_COMPARE(channel->id, &id.u.channel_id))
+    if (!SILC_ID_CHANNEL_COMPARE(&channel->id, &id.u.channel_id))
       silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id);
   } else {
     /* Create new channel entry */
@@ -1091,7 +1090,7 @@ SILC_FSM_STATE(silc_client_command_reply_join)
   /* Get hmac */
   hmac = silc_argument_get_arg_type(args, 11, NULL);
   if (hmac) {
-    if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+    if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
       if (cmd->verbose)
        SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
            "Cannot join channel: Unsupported HMAC `%s'", hmac);
@@ -1158,17 +1157,9 @@ SILC_FSM_STATE(silc_client_command_reply_join)
     }
 
     /* Join client to the channel */
-    if (!silc_client_on_channel(channel, client_entry)) {
-      chu = silc_calloc(1, sizeof(*chu));
-      if (!chu) {
-       silc_client_unref_client(client, conn, client_entry);
-       goto out;
-      }
-      chu->client = client_entry;
-      chu->channel = channel;
-      chu->mode = mode;
-      silc_hash_table_add(channel->user_list, client_entry, chu);
-      silc_hash_table_add(client_entry->channels, channel, chu);
+    if (!silc_client_add_to_channel(channel, client_entry, mode)) {
+      silc_client_unref_client(client, conn, client_entry);
+      goto out;
     }
     silc_client_unref_client(client, conn, client_entry);
 
@@ -1223,7 +1214,7 @@ SILC_FSM_STATE(silc_client_command_reply_join)
   silc_buffer_free(keyp);
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1277,13 +1268,13 @@ SILC_FSM_STATE(silc_client_command_reply_motd)
   silc_client_command_callback(cmd, motd);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
 /********************************** UMODE ***********************************/
 
-/* Received reply tohe UMODE command. Save the current user mode */
+/* Received reply to the UMODE command. Save the current user mode */
 
 SILC_FSM_STATE(silc_client_command_reply_umode)
 {
@@ -1311,7 +1302,7 @@ SILC_FSM_STATE(silc_client_command_reply_umode)
   silc_client_command_callback(cmd, mode);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1331,7 +1322,7 @@ SILC_FSM_STATE(silc_client_command_reply_cmode)
   SilcChannelEntry channel;
   SilcUInt32 len;
   SilcPublicKey public_key = NULL;
-  SilcBufferStruct channel_pubkeys;
+  SilcDList channel_pubkeys = NULL;
   SilcID id;
 
   /* Sanity checks */
@@ -1377,17 +1368,19 @@ SILC_FSM_STATE(silc_client_command_reply_cmode)
   /* Get channel public key(s) */
   tmp = silc_argument_get_arg_type(args, 5, &len);
   if (tmp)
-    silc_buffer_set(&channel_pubkeys, tmp, len);
+    channel_pubkeys =
+      silc_argument_list_parse_decoded(tmp, len, SILC_ARGUMENT_PUBLIC_KEY);
 
   /* Notify application */
   silc_client_command_callback(cmd, channel, mode, public_key,
-                              tmp ? &channel_pubkeys : NULL,
-                              channel->user_limit);
+                              channel_pubkeys, channel->user_limit);
+
+  silc_argument_list_free(channel_pubkeys, SILC_ARGUMENT_PUBLIC_KEY);
 
  out:
   if (public_key)
     silc_pkcs_public_key_free(public_key);
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1458,7 +1451,7 @@ SILC_FSM_STATE(silc_client_command_reply_cumode)
   silc_client_unref_client(client, conn, client_entry);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1511,7 +1504,7 @@ SILC_FSM_STATE(silc_client_command_reply_kick)
   silc_client_unref_client(client, conn, client_entry);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1530,7 +1523,7 @@ SILC_FSM_STATE(silc_client_command_reply_silcoper)
   /* Notify application */
   silc_client_command_callback(cmd);
 
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1549,7 +1542,7 @@ SILC_FSM_STATE(silc_client_command_reply_oper)
   /* Notify application */
   silc_client_command_callback(cmd);
 
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1582,7 +1575,7 @@ SILC_FSM_STATE(silc_client_command_reply_detach)
   }
 #endif /* 0 */
 
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1601,7 +1594,7 @@ SILC_FSM_STATE(silc_client_command_reply_watch)
   /* Notify application */
   silc_client_command_callback(cmd);
 
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1646,7 +1639,7 @@ SILC_FSM_STATE(silc_client_command_reply_ban)
   silc_client_command_callback(cmd, channel, tmp ? &buf : NULL);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1662,7 +1655,6 @@ SILC_FSM_STATE(silc_client_command_reply_leave)
   SilcCommandPayload payload = state_context;
   SilcArgumentPayload args = silc_command_get_args(payload);
   SilcChannelEntry channel;
-  SilcChannelUser chu;
   SilcID id;
 
   /* Sanity checks */
@@ -1683,12 +1675,7 @@ SILC_FSM_STATE(silc_client_command_reply_leave)
   }
 
   /* Remove us from this channel. */
-  chu = silc_client_on_channel(channel, conn->local_entry);
-  if (chu) {
-    silc_hash_table_del(chu->client->channels, chu->channel);
-    silc_hash_table_del(chu->channel->user_list, chu->client);
-    silc_free(chu);
-  }
+  silc_client_remove_from_channel(channel, conn->local_entry);
 
   /* Notify application */
   silc_client_command_callback(cmd, channel);
@@ -1697,37 +1684,46 @@ SILC_FSM_STATE(silc_client_command_reply_leave)
   silc_client_del_channel(client, conn, channel);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
 /********************************* USERS ************************************/
 
-static SilcBool
-silc_client_command_reply_users_continue(SilcClient client,
+/* Continue USERS command after resolving unknown users */
+
+static void
+silc_client_command_reply_users_resolved(SilcClient client,
                                         SilcClientConnection conn,
-                                        SilcCommand command,
                                         SilcStatus status,
-                                        SilcStatus error,
-                                        void *context,
-                                        va_list ap)
+                                        SilcDList clients,
+                                        void *context)
 {
   SilcClientCommandContext cmd = context;
-
-  return TRUE;
+  SILC_FSM_CALL_CONTINUE(&cmd->thread);
 }
 
-/* Continue USERS command after resolving unknown users */
+/* Continue USERS command after resolving unknown channel */
 
 static void
-silc_client_command_reply_users_resolved(SilcClient client,
+silc_client_command_reply_users_continue(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcStatus status,
-                                        SilcDList clients,
+                                        SilcDList channels,
                                         void *context)
 {
   SilcClientCommandContext cmd = context;
-  SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
+
+  if (!channels) {
+    SilcCommandPayload payload = silc_fsm_get_state_context(&cmd->thread);
+    SilcArgumentPayload args = silc_command_get_args(payload);
+
+    cmd->status = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID;
+    ERROR_CALLBACK(cmd->status);
+    silc_fsm_next(&cmd->thread, silc_client_command_reply_processed);
+  }
+
+  SILC_FSM_CALL_CONTINUE(&cmd->thread);
 }
 
 /* Reply to USERS command. Received list of client ID's and theirs modes
@@ -1747,7 +1743,6 @@ SILC_FSM_STATE(silc_client_command_reply_users)
   SilcBufferStruct client_id_list, client_mode_list;
   SilcChannelEntry channel;
   SilcClientEntry client_entry;
-  SilcChannelUser chu;
   SilcID id;
   int i;
 
@@ -1765,11 +1760,9 @@ SILC_FSM_STATE(silc_client_command_reply_users)
   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
   if (!channel) {
     /* Resolve the channel from server */
-#if 0
     SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
                        client, conn, &id.u.channel_id,
                        silc_client_command_reply_users_continue, cmd));
-#endif /* 0 */
     /* NOT REACHED */
   }
 
@@ -1821,18 +1814,8 @@ SILC_FSM_STATE(silc_client_command_reply_users)
     /* Save the client on this channel.  Unknown clients are ignored as they
        clearly do not exist since the resolving didn't find them. */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (client_entry && !silc_client_on_channel(channel, client_entry)) {
-      chu = silc_calloc(1, sizeof(*chu));
-      if (!chu) {
-       silc_client_unref_client(client, conn, client_entry);
-       goto out;
-      }
-      chu->client = client_entry;
-      chu->mode = mode;
-      chu->channel = channel;
-      silc_hash_table_add(channel->user_list, client_entry, chu);
-      silc_hash_table_add(client_entry->channels, channel, chu);
-    }
+    if (client_entry)
+      silc_client_add_to_channel(channel, client_entry, mode);
     silc_client_unref_client(client, conn, client_entry);
 
     if (!silc_buffer_pull(&client_id_list, idp_len))
@@ -1847,7 +1830,7 @@ SILC_FSM_STATE(silc_client_command_reply_users)
   silc_hash_table_list_reset(&htl);
 
  out:
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1920,15 +1903,20 @@ SILC_FSM_STATE(silc_client_command_reply_getkey)
       goto out;
     }
 
+    if (!server_entry->public_key) {
+      server_entry->public_key = public_key;
+      public_key = NULL;
+    }
+
     /* Notify application */
     silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
-                                public_key);
+                                server_entry->public_key);
   }
 
  out:
   if (public_key)
     silc_pkcs_public_key_free(public_key);
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1957,7 +1945,7 @@ SILC_FSM_STATE(silc_client_command_reply_service)
   /* Notify application */
   silc_client_command_callback(cmd, service_list, name);
 
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
 
@@ -1967,6 +1955,6 @@ SILC_FSM_STATE(silc_client_command_reply_service)
 
 SILC_FSM_STATE(silc_client_command_reply_quit)
 {
-  silc_fsm_next(fsm, silc_client_command_reply_process);
+  silc_fsm_next(fsm, silc_client_command_reply_processed);
   return SILC_FSM_CONTINUE;
 }
index 383f6d2e6b04b2cfa9ad5d75adeeeed9512b1348..be1b56434ae5afb00f233e55316347aae743c0b3 100644 (file)
@@ -22,7 +22,9 @@
 
 SILC_FSM_STATE(silc_client_command_reply);
 SILC_FSM_STATE(silc_client_command_reply_wait);
+SILC_FSM_STATE(silc_client_command_reply_timeout);
 SILC_FSM_STATE(silc_client_command_reply_process);
+SILC_FSM_STATE(silc_client_command_reply_processed);
 SILC_FSM_STATE(silc_client_command_reply_whois);
 SILC_FSM_STATE(silc_client_command_reply_whowas);
 SILC_FSM_STATE(silc_client_command_reply_identify);
diff --git a/lib/silcclient/protocol.c b/lib/silcclient/protocol.c
deleted file mode 100644 (file)
index 078c832..0000000
+++ /dev/null
@@ -1,1187 +0,0 @@
-/*
-
-  protocol.c
-
-  Author: Pekka Riikonen <priikone@silcnet.org>
-
-  Copyright (C) 1997 - 2004, 2006 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; version 2 of the License.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/* $Id$ */
-
-#include "silc.h"
-#include "silcclient.h"
-#include "client_internal.h"
-
-SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
-SILC_TASK_CALLBACK(silc_client_protocol_key_exchange);
-SILC_TASK_CALLBACK(silc_client_protocol_rekey);
-
-/*
- * Key Exhange protocol functions
- */
-
-/* Function that is called when SKE protocol sends packets to network. */
-
-void silc_client_protocol_ke_send_packet(SilcSKE ske,
-                                        SilcBuffer packet,
-                                        SilcPacketType type,
-                                        void *context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  /* Send the packet immediately */
-  silc_client_packet_send(client, ske->sock, type, NULL, 0, NULL, NULL,
-                         packet->data, packet->len, TRUE);
-}
-
-/* Public key verification callback. Called by the application. */
-
-typedef struct {
-  SilcSKE ske;
-  SilcSKEVerifyCbCompletion completion;
-  void *completion_context;
-} *VerifyKeyContext;
-
-static void silc_client_verify_key_cb(SilcBool success, void *context)
-{
-  VerifyKeyContext verify = (VerifyKeyContext)context;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Call the completion callback back to the SKE */
-  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
-                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
-                    verify->completion_context);
-
-  silc_free(verify);
-}
-
-/* Callback that is called when we have received KE payload from
-   responder. We try to verify the public key now. */
-
-void silc_client_protocol_ke_verify_key(SilcSKE ske,
-                                       unsigned char *pk_data,
-                                       SilcUInt32 pk_len,
-                                       SilcSKEPKType pk_type,
-                                       void *context,
-                                       SilcSKEVerifyCbCompletion completion,
-                                       void *completion_context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  VerifyKeyContext verify;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  verify = silc_calloc(1, sizeof(*verify));
-  verify->ske = ske;
-  verify->completion = completion;
-  verify->completion_context = completion_context;
-
-  /* Verify public key from user. */
-  client->internal->ops->verify_public_key(client, ctx->sock->user_data,
-                                          ctx->sock->type,
-                                          pk_data, pk_len, pk_type,
-                                          silc_client_verify_key_cb, verify);
-}
-
-/* Sets the negotiated key material into use for particular connection. */
-
-void silc_client_protocol_ke_set_keys(SilcSKE ske,
-                                     SilcSocketConnection sock,
-                                     SilcSKEKeyMaterial *keymat,
-                                     SilcCipher cipher,
-                                     SilcPKCS pkcs,
-                                     SilcHash hash,
-                                     SilcHmac hmac,
-                                     SilcSKEDiffieHellmanGroup group,
-                                     SilcBool is_responder)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  const char *cname = silc_cipher_get_name(cipher);
-
-  SILC_LOG_DEBUG(("Setting new keys into use"));
-
-  /* Allocate cipher to be used in the communication */
-  silc_cipher_alloc((char *)cname, &conn->internal->send_key);
-  silc_cipher_alloc((char *)cname, &conn->internal->receive_key);
-  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL,
-                 &conn->internal->hmac_send);
-  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL,
-                 &conn->internal->hmac_receive);
-
-  if (is_responder == TRUE) {
-    silc_cipher_set_key(conn->internal->send_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(conn->internal->send_key, keymat->receive_iv);
-    silc_cipher_set_key(conn->internal->receive_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(conn->internal->receive_key, keymat->send_iv);
-    silc_hmac_set_key(conn->internal->hmac_send, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(conn->internal->hmac_receive, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-  } else {
-    silc_cipher_set_key(conn->internal->send_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(conn->internal->send_key, keymat->send_iv);
-    silc_cipher_set_key(conn->internal->receive_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(conn->internal->receive_key, keymat->receive_iv);
-    silc_hmac_set_key(conn->internal->hmac_send, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(conn->internal->hmac_receive, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-  }
-
-  /* Rekey stuff */
-  conn->internal->rekey = silc_calloc(1, sizeof(*conn->internal->rekey));
-  conn->internal->rekey->send_enc_key = silc_memdup(keymat->send_enc_key,
-                                                   keymat->enc_key_len / 8);
-  conn->internal->rekey->enc_key_len = keymat->enc_key_len / 8;
-
-  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
-    conn->internal->rekey->pfs = TRUE;
-  conn->internal->rekey->ske_group = silc_ske_group_get_number(group);
-
-  /* Save the HASH function */
-  silc_hash_alloc(silc_hash_get_name(hash), &conn->internal->hash);
-}
-
-/* Checks the version string of the server. */
-
-SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
-                                    SilcUInt32 len, void *context)
-{
-  SilcClientConnection conn = (SilcClientConnection)ske->sock->user_data;
-  SilcClient client = (SilcClient)ske->user_data;
-  SilcUInt32 l_protocol_version = 0, r_protocol_version = 0;
-
-  if (!silc_parse_version_string(version, &r_protocol_version, NULL, NULL,
-                                NULL, NULL)) {
-    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                              "We don't support server version `%s'",
-                              version);
-    return SILC_SKE_STATUS_BAD_VERSION;
-  }
-
-  if (!silc_parse_version_string(client->internal->silc_client_version,
-                                &l_protocol_version, NULL, NULL,
-                                NULL, NULL)) {
-    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                              "We don't support server version `%s'",
-                              version);
-    return SILC_SKE_STATUS_BAD_VERSION;
-  }
-
-  /* If remote is too new, don't connect */
-  if (l_protocol_version < r_protocol_version) {
-    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                              "We don't support server version `%s'",
-                              version);
-    return SILC_SKE_STATUS_BAD_VERSION;
-  }
-
-  ske->sock->version = r_protocol_version;
-
-  return SILC_SKE_STATUS_OK;
-}
-
-/* Callback that is called by the SKE to indicate that it is safe to
-   continue the execution of the protocol. Is given as argument to the
-   silc_ske_initiator_finish or silc_ske_responder_phase_2 functions.
-   This is called due to the fact that the public key verification
-   process is asynchronous and we must not continue the protocl until
-   the public key has been verified and this callback is called. */
-
-static void silc_client_protocol_ke_continue(SilcSKE ske,
-                                            void *context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientConnection conn = ctx->sock->user_data;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (ske->status != SILC_SKE_STATUS_OK) {
-    /* Call failure client operation */
-    client->internal->ops->failure(client, conn, protocol,
-                                  (void *)ske->status);
-    protocol->state = SILC_PROTOCOL_STATE_ERROR;
-    silc_protocol_execute(protocol, client->schedule, 0, 0);
-    return;
-  }
-
-  /* Send Ok to the other end. We will end the protocol as server
-     sends Ok to us when we will take the new keys into use. Do this
-     if we are initiator. This is happens when this callback was sent
-     to silc_ske_initiator_finish function. */
-  if (ctx->responder == FALSE) {
-    silc_ske_end(ctx->ske);
-
-    /* End the protocol on the next round */
-    protocol->state = SILC_PROTOCOL_STATE_END;
-  }
-
-  /* Advance protocol state and call the next state if we are responder.
-     This happens when this callback was sent to silc_ske_responder_phase_2
-     function. */
-  if (ctx->responder == TRUE) {
-    protocol->state++;
-    silc_protocol_execute(protocol, client->schedule, 0, 1);
-  }
-}
-
-/* Performs key exchange protocol. This is used for both initiator
-   and responder key exchange. This may be called recursively. */
-
-SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx =
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientConnection conn = ctx->sock->user_data;
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
-    protocol->state = SILC_PROTOCOL_STATE_START;
-
-  switch(protocol->state) {
-  case SILC_PROTOCOL_STATE_START:
-    {
-      /*
-       * Start Protocol
-       */
-      SilcSKE ske;
-
-      /* Allocate Key Exchange object */
-      ctx->ske = ske = silc_ske_alloc(client->rng, client);
-
-      silc_ske_set_callbacks(ske, ctx->send_packet, NULL,
-                            ctx->verify,
-                            silc_client_protocol_ke_continue,
-                            silc_ske_check_version,
-                            context);
-
-      if (ctx->responder == TRUE) {
-       if (!ctx->packet) {
-         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                           status));
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         silc_protocol_execute(protocol, client->schedule, 0, 0);
-         return;
-       }
-
-       /* Start the key exchange by processing the received security
-          properties packet from initiator. */
-       status =
-         silc_ske_responder_start(ske, ctx->rng, ctx->sock,
-                                  client->internal->silc_client_version,
-                                  ctx->packet->buffer, TRUE);
-      } else {
-       SilcSKEStartPayload *start_payload;
-
-       /* Assemble security properties. */
-       silc_ske_assemble_security_properties(
-                                 ske, SILC_SKE_SP_FLAG_MUTUAL,
-                                 client->internal->silc_client_version,
-                                 &start_payload);
-
-       /* Start the key exchange by sending our security properties
-          to the remote end. */
-       status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock,
-                                         start_payload);
-      }
-
-      /* Return now if the procedure is pending */
-      if (status == SILC_SKE_STATUS_PENDING)
-       return;
-
-      if (status != SILC_SKE_STATUS_OK) {
-       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                         status));
-       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
-                       status));
-
-       protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       silc_protocol_execute(protocol, client->schedule, 0, 0);
-       return;
-      }
-
-      /* Advance protocol state and call the next state if we are responder */
-      protocol->state++;
-      if (ctx->responder == TRUE)
-       silc_protocol_execute(protocol, client->schedule, 0, 1);
-    }
-    break;
-  case 2:
-    {
-      /*
-       * Phase 1
-       */
-      if (ctx->responder == TRUE) {
-       /* Sends the selected security properties to the initiator. */
-       status = silc_ske_responder_phase_1(ctx->ske);
-      } else {
-       if (!ctx->packet) {
-         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                           status));
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         silc_protocol_execute(protocol, client->schedule, 0, 0);
-         return;
-       }
-
-       /* Call Phase-1 function. This processes the Key Exchange Start
-          paylaod reply we just got from the responder. The callback
-          function will receive the processed payload where we will
-          save it. */
-       status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet->buffer);
-      }
-
-      if (status != SILC_SKE_STATUS_OK) {
-       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                         status));
-       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
-                       status));
-
-       protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       silc_protocol_execute(protocol, client->schedule, 0, 0);
-       return;
-      }
-
-      /* Advance protocol state and call next state if we are initiator */
-      protocol->state++;
-      if (ctx->responder == FALSE)
-       silc_protocol_execute(protocol, client->schedule, 0, 1);
-    }
-    break;
-  case 3:
-    {
-      /*
-       * Phase 2
-       */
-      if (ctx->responder == TRUE) {
-       if (!ctx->packet) {
-         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                           status));
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         silc_protocol_execute(protocol, client->schedule, 0, 0);
-         return;
-       }
-
-       /* Process the received Key Exchange 1 Payload packet from
-          the initiator. This also creates our parts of the Diffie
-          Hellman algorithm. The silc_client_protocol_ke_continue will
-          be called after the public key has been verified. */
-       status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
-      } else {
-       /* Call the Phase-2 function. This creates Diffie Hellman
-          key exchange parameters and sends our public part inside
-          Key Exhange 1 Payload to the responder. */
-       status = silc_ske_initiator_phase_2(ctx->ske,
-                                           client->public_key,
-                                           client->private_key,
-                                           SILC_SKE_PK_TYPE_SILC);
-       protocol->state++;
-      }
-
-      /* Return now if the procedure is pending */
-      if (status == SILC_SKE_STATUS_PENDING)
-       return;
-
-      if (status != SILC_SKE_STATUS_OK) {
-       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                         status));
-       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
-                       status));
-
-       protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       silc_protocol_execute(protocol, client->schedule, 0, 0);
-       return;
-      }
-    }
-    break;
-  case 4:
-    {
-      /*
-       * Finish protocol
-       */
-      if (ctx->responder == TRUE) {
-       /* This creates the key exchange material and sends our
-          public parts to the initiator inside Key Exchange 2 Payload. */
-       status =
-         silc_ske_responder_finish(ctx->ske,
-                                   client->public_key, client->private_key,
-                                   SILC_SKE_PK_TYPE_SILC);
-
-       /* End the protocol on the next round */
-       protocol->state = SILC_PROTOCOL_STATE_END;
-      } else {
-       if (!ctx->packet) {
-         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                           status));
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         silc_protocol_execute(protocol, client->schedule, 0, 0);
-         return;
-       }
-
-       /* Finish the protocol. This verifies the Key Exchange 2 payload
-          sent by responder. The silc_client_protocol_ke_continue will
-          be called after the public key has been verified. */
-       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
-      }
-
-      /* Return now if the procedure is pending */
-      if (status == SILC_SKE_STATUS_PENDING)
-       return;
-
-      if (status != SILC_SKE_STATUS_OK) {
-        if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) {
-          client->internal->ops->say(
-                            client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                            "Received unsupported server %s public key",
-                            ctx->sock->hostname);
-        } else {
-          client->internal->ops->say(
-                          client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                          "Error during key exchange protocol with server %s",
-                          ctx->sock->hostname);
-        }
-       protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       silc_protocol_execute(protocol, client->schedule, 0, 0);
-       return;
-      }
-    }
-    break;
-
-  case SILC_PROTOCOL_STATE_END:
-    {
-      /*
-       * End protocol
-       */
-      SilcSKEKeyMaterial *keymat;
-      int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher);
-      int hash_len = silc_hash_len(ctx->ske->prop->hash);
-
-      /* Process the key material */
-      keymat = silc_calloc(1, sizeof(*keymat));
-      status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
-                                            keymat);
-      if (status != SILC_SKE_STATUS_OK) {
-       protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       silc_protocol_execute(protocol, client->schedule, 0, 300000);
-       silc_ske_free_key_material(keymat);
-       return;
-      }
-      ctx->keymat = keymat;
-
-      /* Send Ok to the other end if we are responder. If we are initiator
-        we have sent this already. */
-      if (ctx->responder == TRUE)
-       silc_ske_end(ctx->ske);
-
-      /* Unregister the timeout task since the protocol has ended.
-        This was the timeout task to be executed if the protocol is
-        not completed fast enough. */
-      if (ctx->timeout_task)
-       silc_schedule_task_del(client->schedule, ctx->timeout_task);
-
-      /* Protocol has ended, call the final callback */
-      if (protocol->final_callback)
-       silc_protocol_execute_final(protocol, client->schedule);
-      else
-       silc_protocol_free(protocol);
-    }
-    break;
-
-  case SILC_PROTOCOL_STATE_ERROR:
-    /*
-     * Error during protocol
-     */
-
-    /* Send abort notification */
-    silc_ske_abort(ctx->ske, ctx->ske->status);
-
-    /* On error the final callback is always called. */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, client->schedule);
-    else
-      silc_protocol_free(protocol);
-    break;
-
-  case SILC_PROTOCOL_STATE_FAILURE:
-    /*
-     * Received failure from remote.
-     */
-
-    /* Unregister the timeout task since the protocol has ended.
-       This was the timeout task to be executed if the protocol is
-       not completed fast enough. */
-    if (ctx->timeout_task)
-      silc_schedule_task_del(client->schedule, ctx->timeout_task);
-
-    /* On error the final callback is always called. */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, client->schedule);
-    else
-      silc_protocol_free(protocol);
-    break;
-  case SILC_PROTOCOL_STATE_UNKNOWN:
-    break;
-  }
-}
-
-/*
- * Connection Authentication protocol functions
- */
-
-static int
-silc_client_get_public_key_auth(SilcClient client,
-                               SilcClientConnection conn,
-                               unsigned char *auth_data,
-                               SilcUInt32 *auth_data_len,
-                               SilcSKE ske)
-{
-  int len;
-  SilcPKCS pkcs;
-  SilcBuffer auth;
-
-  /* Use our default key */
-  pkcs = client->pkcs;
-
-  /* Make the authentication data. Protocol says it is HASH plus
-     KE Start Payload. */
-  len = ske->hash_len + ske->start_payload_copy->len;
-  auth = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(auth, len);
-  silc_buffer_format(auth,
-                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
-                    SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
-                                         ske->start_payload_copy->len),
-                    SILC_STR_END);
-
-  if (silc_pkcs_sign_with_hash(pkcs, ske->prop->hash, auth->data,
-                              auth->len, auth_data, auth_data_len)) {
-    silc_buffer_free(auth);
-    return TRUE;
-  }
-
-  silc_buffer_free(auth);
-  return FALSE;
-}
-
-/* Continues the connection authentication protocol. This funtion may
-   be called directly or used as SilcAskPassphrase callback. */
-
-static void
-silc_client_conn_auth_continue(unsigned char *auth_data,
-                              SilcUInt32 auth_data_len, void *context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx =
-    (SilcClientConnAuthInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcBuffer packet;
-  int payload_len = 0;
-  unsigned char *autf8 = NULL;
-
-  SILC_LOG_DEBUG(("Sending authentication to server"));
-
-  /* Passphrase must be UTF-8 encoded, if it isn't encode it */
-  if (ctx->auth_meth == SILC_AUTH_PASSWORD &&
-      !silc_utf8_valid(auth_data, auth_data_len)) {
-    payload_len = silc_utf8_encoded_len(auth_data, auth_data_len,
-                                       SILC_STRING_ASCII);
-    autf8 = silc_calloc(payload_len, sizeof(*autf8));
-    auth_data_len = silc_utf8_encode(auth_data, auth_data_len,
-                                    SILC_STRING_ASCII, autf8, payload_len);
-    auth_data = autf8;
-  }
-
-  payload_len = 4 + auth_data_len;
-  packet = silc_buffer_alloc(payload_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(payload_len),
-                    SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
-                    SILC_STR_UI_XNSTRING(auth_data, auth_data_len),
-                    SILC_STR_END);
-
-  /* Send the packet to server */
-  silc_client_packet_send(client, ctx->sock,
-                         SILC_PACKET_CONNECTION_AUTH,
-                         NULL, 0, NULL, NULL,
-                         packet->data, packet->len, TRUE);
-  silc_buffer_free(packet);
-  silc_free(autf8);
-
-  /* Next state is end of protocol */
-  protocol->state = SILC_PROTOCOL_STATE_END;
-}
-
-SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx =
-    (SilcClientConnAuthInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientConnection conn = ctx->sock->user_data;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
-    protocol->state = SILC_PROTOCOL_STATE_START;
-
-  switch(protocol->state) {
-  case SILC_PROTOCOL_STATE_START:
-    {
-      /*
-       * Start protocol. We send authentication data to the server
-       * to be authenticated.
-       */
-      unsigned char *auth_data = NULL;
-      SilcUInt32 auth_data_len = 0;
-      unsigned char sign[2048 + 1];
-
-      switch(ctx->auth_meth) {
-      case SILC_AUTH_NONE:
-       /* No authentication required */
-       break;
-
-      case SILC_AUTH_PASSWORD:
-       /* Password authentication */
-       if (ctx->auth_data && ctx->auth_data_len) {
-         auth_data = ctx->auth_data;
-         auth_data_len = ctx->auth_data_len;
-         break;
-       }
-
-       client->internal->ops->say(
-                       client, conn, SILC_CLIENT_MESSAGE_INFO,
-                       "Password authentication required by server %s",
-                       ctx->sock->hostname);
-       client->internal->ops->ask_passphrase(client, conn,
-                                             silc_client_conn_auth_continue,
-                                             protocol);
-       return;
-       break;
-
-      case SILC_AUTH_PUBLIC_KEY:
-       if (!ctx->auth_data) {
-         /* Public key authentication */
-         silc_client_get_public_key_auth(client, conn, sign, &auth_data_len,
-                                         ctx->ske);
-         auth_data = sign;
-       } else {
-         auth_data = ctx->auth_data;
-         auth_data_len = ctx->auth_data_len;
-       }
-
-       break;
-      }
-
-      silc_client_conn_auth_continue(auth_data,
-                                    auth_data_len, protocol);
-    }
-    break;
-
-  case SILC_PROTOCOL_STATE_END:
-    {
-      /*
-       * End protocol. Nothing special to be done here.
-       */
-
-      /* Protocol has ended, call the final callback */
-      if (protocol->final_callback)
-       silc_protocol_execute_final(protocol, client->schedule);
-      else
-       silc_protocol_free(protocol);
-    }
-    break;
-
-  case SILC_PROTOCOL_STATE_ERROR:
-    {
-      /*
-       * Error. Send notify to remote.
-       */
-      unsigned char error[4];
-
-      SILC_PUT32_MSB(SILC_AUTH_FAILED, error);
-
-      /* Error in protocol. Send FAILURE packet. Although I don't think
-        this could ever happen on client side. */
-      silc_client_packet_send(client, ctx->sock, SILC_PACKET_FAILURE,
-                             NULL, 0, NULL, NULL, error, 4, TRUE);
-
-      /* On error the final callback is always called. */
-      if (protocol->final_callback)
-       silc_protocol_execute_final(protocol, client->schedule);
-      else
-       silc_protocol_free(protocol);
-    }
-
-  case SILC_PROTOCOL_STATE_FAILURE:
-    /*
-     * Received failure from remote.
-     */
-
-    /* On error the final callback is always called. */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, client->schedule);
-    else
-      silc_protocol_free(protocol);
-    break;
-
-  case SILC_PROTOCOL_STATE_UNKNOWN:
-    break;
-  }
-}
-
-/*
- * Re-key protocol routines
- */
-
-/* Actually takes the new keys into use. */
-
-static void
-silc_client_protocol_rekey_validate(SilcClient client,
-                                   SilcClientRekeyInternalContext *ctx,
-                                   SilcSocketConnection sock,
-                                   SilcSKEKeyMaterial *keymat,
-                                   SilcBool send)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-
-  if (ctx->responder == TRUE) {
-    if (send) {
-      silc_cipher_set_key(conn->internal->send_key, keymat->receive_enc_key,
-                         keymat->enc_key_len);
-      silc_cipher_set_iv(conn->internal->send_key, keymat->receive_iv);
-      silc_hmac_set_key(conn->internal->hmac_send, keymat->receive_hmac_key,
-                       keymat->hmac_key_len);
-    } else {
-      silc_cipher_set_key(conn->internal->receive_key, keymat->send_enc_key,
-                         keymat->enc_key_len);
-      silc_cipher_set_iv(conn->internal->receive_key, keymat->send_iv);
-      silc_hmac_set_key(conn->internal->hmac_receive, keymat->send_hmac_key,
-                       keymat->hmac_key_len);
-    }
-  } else {
-    if (send) {
-      silc_cipher_set_key(conn->internal->send_key, keymat->send_enc_key,
-                         keymat->enc_key_len);
-      silc_cipher_set_iv(conn->internal->send_key, keymat->send_iv);
-      silc_hmac_set_key(conn->internal->hmac_send, keymat->send_hmac_key,
-                       keymat->hmac_key_len);
-    } else {
-      silc_cipher_set_key(conn->internal->receive_key,
-                         keymat->receive_enc_key, keymat->enc_key_len);
-      silc_cipher_set_iv(conn->internal->receive_key, keymat->receive_iv);
-      silc_hmac_set_key(conn->internal->hmac_receive,
-                       keymat->receive_hmac_key, keymat->hmac_key_len);
-    }
-  }
-
-  /* Save the current sending encryption key */
-  if (!send) {
-    memset(conn->internal->rekey->send_enc_key, 0,
-          conn->internal->rekey->enc_key_len);
-    silc_free(conn->internal->rekey->send_enc_key);
-    conn->internal->rekey->send_enc_key = silc_memdup(keymat->send_enc_key,
-                                                     keymat->enc_key_len / 8);
-    conn->internal->rekey->enc_key_len = keymat->enc_key_len / 8;
-  }
-}
-
-/* This function actually re-generates (when not using PFS) the keys and
-   takes them into use. */
-
-static void
-silc_client_protocol_rekey_generate(SilcClient client,
-                                   SilcClientRekeyInternalContext *ctx,
-                                   SilcBool send)
-{
-  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
-  SilcSKEKeyMaterial *keymat;
-  SilcUInt32 key_len = silc_cipher_get_key_len(conn->internal->send_key);
-  SilcUInt32 hash_len = silc_hash_len(conn->internal->hash);
-
-  SILC_LOG_DEBUG(("Generating new %s session keys (no PFS)",
-                 send ? "sending" : "receiving"));
-
-  /* Generate the new key */
-  keymat = silc_calloc(1, sizeof(*keymat));
-  silc_ske_process_key_material_data(conn->internal->rekey->send_enc_key,
-                                    conn->internal->rekey->enc_key_len,
-                                    16, key_len, hash_len,
-                                    conn->internal->hash, keymat);
-
-  /* Set the keys into use */
-  silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat, send);
-
-  silc_ske_free_key_material(keymat);
-}
-
-/* This function actually re-generates (with PFS) the keys and
-   takes them into use. */
-
-static void
-silc_client_protocol_rekey_generate_pfs(SilcClient client,
-                                       SilcClientRekeyInternalContext *ctx,
-                                       SilcBool send)
-{
-  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
-  SilcSKEKeyMaterial *keymat;
-  SilcUInt32 key_len = silc_cipher_get_key_len(conn->internal->send_key);
-  SilcUInt32 hash_len = silc_hash_len(conn->internal->hash);
-  unsigned char *tmpbuf;
-  SilcUInt32 klen;
-
-  SILC_LOG_DEBUG(("Generating new %s session keys (with PFS)",
-                 send ? "sending" : "receiving"));
-
-  /* Encode KEY to binary data */
-  tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
-
-  /* Generate the new key */
-  keymat = silc_calloc(1, sizeof(*keymat));
-  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len,
-                                    conn->internal->hash, keymat);
-
-  /* Set the keys into use */
-  silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat, send);
-
-  memset(tmpbuf, 0, klen);
-  silc_free(tmpbuf);
-  silc_ske_free_key_material(keymat);
-}
-
-/* Packet sending callback. This function is provided as packet sending
-   routine to the Key Exchange functions. */
-
-static void
-silc_client_protocol_rekey_send_packet(SilcSKE ske,
-                                      SilcBuffer packet,
-                                      SilcPacketType type,
-                                      void *context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientRekeyInternalContext *ctx =
-    (SilcClientRekeyInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  /* Send the packet immediately */
-  silc_client_packet_send(client, ctx->sock, type, NULL, 0, NULL, NULL,
-                         packet->data, packet->len, FALSE);
-}
-
-/* Performs re-key as defined in the SILC protocol specification. */
-
-SILC_TASK_CALLBACK(silc_client_protocol_rekey)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientRekeyInternalContext *ctx =
-    (SilcClientRekeyInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
-  SilcSKEStatus status;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
-    protocol->state = SILC_PROTOCOL_STATE_START;
-
-  SILC_LOG_DEBUG(("State=%d", protocol->state));
-
-  switch(protocol->state) {
-  case SILC_PROTOCOL_STATE_START:
-    {
-      /*
-       * Start protocol.
-       */
-
-      if (ctx->responder == TRUE) {
-       /*
-        * We are receiving party
-        */
-
-       if (ctx->pfs == TRUE) {
-         /*
-          * Use Perfect Forward Secrecy, ie. negotiate the key material
-          * using the SKE protocol.
-          */
-
-         if (!ctx->packet) {
-           SILC_LOG_WARNING(("Error during Re-key"));
-           protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           silc_protocol_execute(protocol, client->schedule, 0, 300000);
-           return;
-         }
-
-         if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
-           /* Error in protocol */
-           protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           silc_protocol_execute(protocol, client->schedule, 0, 300000);
-         }
-
-         ctx->ske = silc_ske_alloc(client->rng, client);
-         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
-         silc_ske_group_get_by_number(conn->internal->rekey->ske_group,
-                                      &ctx->ske->prop->group);
-
-         silc_ske_set_callbacks(ctx->ske,
-                                silc_client_protocol_rekey_send_packet,
-                                NULL,  NULL, NULL, silc_ske_check_version,
-                                context);
-
-         status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
-         if (status != SILC_SKE_STATUS_OK) {
-           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
-                             status));
-
-           protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           silc_protocol_execute(protocol, client->schedule, 0, 300000);
-           return;
-         }
-
-         /* Advance the protocol state */
-         protocol->state++;
-         silc_protocol_execute(protocol, client->schedule, 0, 0);
-       } else {
-         /*
-          * Do normal and simple re-key.
-          */
-
-         /* Send the REKEY_DONE to indicate we will take new keys into use */
-         silc_client_packet_queue_purge(client, ctx->sock);
-         silc_client_packet_send(client, ctx->sock,
-                                 SILC_PACKET_REKEY_DONE,
-                                 NULL, 0, NULL, NULL, NULL, 0, FALSE);
-
-         /* After we send REKEY_DONE we must set the sending encryption
-            key to the new key since all packets after this packet must
-            encrypted with the new key. */
-         silc_client_protocol_rekey_generate(client, ctx, TRUE);
-         silc_client_packet_queue_purge(client, ctx->sock);
-
-         /* The protocol ends in next stage. */
-         protocol->state = SILC_PROTOCOL_STATE_END;
-       }
-
-      } else {
-       /*
-        * We are the initiator of this protocol
-        */
-
-       /* Start the re-key by sending the REKEY packet */
-       silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY,
-                               NULL, 0, NULL, NULL, NULL, 0, FALSE);
-
-       if (ctx->pfs == TRUE) {
-         /*
-          * Use Perfect Forward Secrecy, ie. negotiate the key material
-          * using the SKE protocol.
-          */
-         ctx->ske = silc_ske_alloc(client->rng, client);
-         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
-         silc_ske_group_get_by_number(conn->internal->rekey->ske_group,
-                                      &ctx->ske->prop->group);
-
-         silc_ske_set_callbacks(ctx->ske,
-                                silc_client_protocol_rekey_send_packet,
-                                NULL,  NULL, NULL, silc_ske_check_version,
-                                context);
-
-         status =  silc_ske_initiator_phase_2(ctx->ske, NULL, NULL, 0);
-         if (status != SILC_SKE_STATUS_OK) {
-           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
-                             status));
-
-           protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           silc_protocol_execute(protocol, client->schedule, 0, 300000);
-           return;
-         }
-
-         /* Advance the protocol state */
-         protocol->state++;
-       } else {
-         /*
-          * Do normal and simple re-key.
-          */
-
-         /* Send the REKEY_DONE to indicate we will take new keys into use
-            now. */
-         silc_client_packet_queue_purge(client, ctx->sock);
-         silc_client_packet_send(client, ctx->sock,
-                                 SILC_PACKET_REKEY_DONE,
-                                 NULL, 0, NULL, NULL, NULL, 0, FALSE);
-
-         /* After we send REKEY_DONE we must set the sending encryption
-            key to the new key since all packets after this packet must
-            encrypted with the new key. */
-         silc_client_protocol_rekey_generate(client, ctx, TRUE);
-         silc_client_packet_queue_purge(client, ctx->sock);
-
-         /* The protocol ends in next stage. */
-         protocol->state = SILC_PROTOCOL_STATE_END;
-       }
-      }
-    }
-    break;
-
-  case 2:
-    /*
-     * Second state, used only when oding re-key with PFS.
-     */
-    if (ctx->responder == TRUE) {
-      if (ctx->pfs == TRUE) {
-       /*
-        * Send our KE packe to the initiator now that we've processed
-        * the initiator's KE packet.
-        */
-       status = silc_ske_responder_finish(ctx->ske, NULL, NULL,
-                                          SILC_SKE_PK_TYPE_SILC);
-
-         if (status != SILC_SKE_STATUS_OK) {
-           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
-                             status));
-
-           protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           silc_protocol_execute(protocol, client->schedule, 0, 300000);
-           return;
-         }
-      }
-
-    } else {
-      if (ctx->pfs == TRUE) {
-       /*
-        * The packet type must be KE packet
-        */
-       if (!ctx->packet) {
-         SILC_LOG_WARNING(("Error during Re-key"));
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         silc_protocol_execute(protocol, client->schedule, 0, 300000);
-         return;
-       }
-
-       if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
-         /* Error in protocol */
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         silc_protocol_execute(protocol, client->schedule, 0, 300000);
-       }
-
-       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
-       if (status != SILC_SKE_STATUS_OK) {
-         SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
-                           status));
-
-         protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         silc_protocol_execute(protocol, client->schedule, 0, 300000);
-         return;
-       }
-      }
-    }
-
-    /* Send the REKEY_DONE to indicate we will take new keys into use
-       now. */
-    silc_client_packet_queue_purge(client, ctx->sock);
-    silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY_DONE,
-                           NULL, 0, NULL, NULL, NULL, 0, FALSE);
-
-    /* After we send REKEY_DONE we must set the sending encryption
-       key to the new key since all packets after this packet must
-       encrypted with the new key. */
-    silc_client_protocol_rekey_generate_pfs(client, ctx, TRUE);
-    silc_client_packet_queue_purge(client, ctx->sock);
-
-    /* The protocol ends in next stage. */
-    protocol->state = SILC_PROTOCOL_STATE_END;
-    break;
-
-  case SILC_PROTOCOL_STATE_END:
-    /*
-     * End protocol
-     */
-
-    if (!ctx->packet) {
-      SILC_LOG_WARNING(("Error during Re-key"));
-      protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      silc_protocol_execute(protocol, client->schedule, 0, 300000);
-      return;
-    }
-
-    if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
-      /* Error in protocol */
-      protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      silc_protocol_execute(protocol, client->schedule, 0, 0);
-    }
-
-    /* We received the REKEY_DONE packet and all packets after this is
-       encrypted with the new key so set the decryption key to the new key */
-    if (ctx->pfs == TRUE)
-      silc_client_protocol_rekey_generate_pfs(client, ctx, FALSE);
-    else
-      silc_client_protocol_rekey_generate(client, ctx, FALSE);
-    silc_client_packet_queue_purge(client, ctx->sock);
-
-    /* Protocol has ended, call the final callback */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, client->schedule);
-    else
-      silc_protocol_free(protocol);
-    break;
-
-  case SILC_PROTOCOL_STATE_ERROR:
-    /*
-     * Error occured
-     */
-
-    if (ctx->pfs == TRUE) {
-      /* Send abort notification */
-      silc_ske_abort(ctx->ske, ctx->ske->status);
-    }
-
-    /* On error the final callback is always called. */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, client->schedule);
-    else
-      silc_protocol_free(protocol);
-    break;
-
-  case SILC_PROTOCOL_STATE_FAILURE:
-    /*
-     * We have received failure from remote
-     */
-
-    /* On error the final callback is always called. */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, client->schedule);
-    else
-      silc_protocol_free(protocol);
-    break;
-
-  case SILC_PROTOCOL_STATE_UNKNOWN:
-    break;
-  }
-
-}
diff --git a/lib/silcclient/protocol.h b/lib/silcclient/protocol.h
deleted file mode 100644 (file)
index 246a3cd..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-
-  protocol.h
-
-  Author: Pekka Riikonen <priikone@silcnet.org>
-
-  Copyright (C) 1997 - 2004 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; version 2 of the License.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
-
-/* SILC client protocol types */
-#define SILC_PROTOCOL_CLIENT_NONE               0
-#define SILC_PROTOCOL_CLIENT_CONNECTION_AUTH    1
-#define SILC_PROTOCOL_CLIENT_KEY_EXCHANGE       2
-#define SILC_PROTOCOL_CLIENT_REKEY              3
-/* #define SILC_PROTOCOL_CLIENT_MAX             255 */
-
-/* Internal context for key exchange protocol */
-typedef struct {
-  void *client;
-  SilcSocketConnection sock;
-  SilcRng rng;
-  int responder;
-
-  void *dest_id;                   /* Destination ID from packet */
-  SilcIdType dest_id_type;         /* Destination ID type */
-
-  SilcTask timeout_task;
-  SilcPacketContext *packet;
-
-  SilcSKESendPacketCb send_packet;  /* SKE's packet sending callback */
-  SilcSKEVerifyCb verify;          /* SKE's key verify callback */
-  SilcSKE ske;                     /* The SKE object */
-  SilcSKEKeyMaterial *keymat;      /* The negotiated key material */
-  void *context;                   /* Internal context */
-} SilcClientKEInternalContext;
-
-/* Internal context for connection authentication protocol */
-typedef struct {
-  void *client;
-  SilcSocketConnection sock;
-  SilcClientConnectionStatus status;
-
-  /* SKE object from Key Exchange protocol. */
-  SilcSKE ske;
-
-  /* Auth method that must be used. This is resolved before this
-     connection authentication protocol is started. */
-  SilcProtocolAuthMeth auth_meth;
-
-  /* Destinations ID from KE protocol context */
-  void *dest_id;
-  SilcIdType dest_id_type;
-
-  /* Authentication data if we alreay know it. This is filled before
-     starting the protocol if we know the authentication data. Otherwise
-     these are and remain NULL. */
-  unsigned char *auth_data;
-  SilcUInt32 auth_data_len;
-
-  SilcTask timeout_task;
-} SilcClientConnAuthInternalContext;
-
-/* Internal context for the rekey protocol */
-typedef struct {
-  void *client;
-  void *context;
-  SilcSocketConnection sock;
-  SilcBool responder;              /* TRUE if we are receiving party */
-  SilcBool pfs;                            /* TRUE if PFS is to be used */
-  SilcSKE ske;                     /* Defined if PFS is used */
-  SilcPacketContext *packet;
-} SilcClientRekeyInternalContext;
-
-/* Prototypes */
-void silc_client_protocols_register(void);
-void silc_client_protocols_unregister(void);
-void silc_client_protocol_ke_send_packet(SilcSKE ske,
-                                        SilcBuffer packet,
-                                        SilcPacketType type,
-                                        void *context);
-void silc_client_protocol_ke_verify_key(SilcSKE ske,
-                                       unsigned char *pk_data,
-                                       SilcUInt32 pk_len,
-                                       SilcSKEPKType pk_type,
-                                       void *context,
-                                       SilcSKEVerifyCbCompletion completion,
-                                       void *completion_context);
-void silc_client_protocol_ke_set_keys(SilcSKE ske,
-                                     SilcSocketConnection sock,
-                                     SilcSKEKeyMaterial *keymat,
-                                     SilcCipher cipher,
-                                     SilcPKCS pkcs,
-                                     SilcHash hash,
-                                     SilcHmac hmac,
-                                     SilcSKEDiffieHellmanGroup group,
-                                     SilcBool is_responder);
-
-#endif
index 56852d83361230f5ed4a41f356571701dab44da5..254241ff8f0dcf5c4dc1111326ee414f1925942f 100644 (file)
@@ -90,22 +90,30 @@ typedef enum {
  *    void (*SilcClientConnectCallback)(SilcClient client,
  *                                      SilcClientConnection conn,
  *                                      SilcClientConnectionStatus status,
+ *                                      SilcStatus error,
+ *                                      const char *message,
  *                                      void *context);
  *
  * DESCRIPTION
  *
  *    Connect callbak given as argument to silc_client_connect_to_server,
- *    silc_client_connect_to_client and silc_client_add_connection functions.
+ *    silc_client_connect_to_client and silc_client_key_exchange functions.
  *    It is called to indicate the status of the connection, indicated
  *    by the `status'.  It is called after the connection has been
  *    established to the remote host and when connection is disconnected
  *    by the remote host.  The `context' is the context given as argument
  *    to the connecting function.
  *
+ *    When the `status' is SILC_CLIENT_CONN_DISCONNECTED the `error' will
+ *    indicate the reason for disconnection.  If the `message' is non-NULL
+ *    it delivers error or disconnection message.
+ *
  ***/
 typedef void (*SilcClientConnectCallback)(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientConnectionStatus status,
+                                         SilcStatus error,
+                                         const char *message,
                                          void *context);
 
 /****s* silcclient/SilcClientAPI/SilcClient
@@ -330,8 +338,8 @@ typedef void (*SilcKeyAgreementCallback)(SilcClient client,
  *
  * DESCRIPTION
  *
- *    Structure to hold the list of private message keys. The array of this
- *    structure is returned by the silc_client_list_private_message_keys
+ *    Structure to hold the list of private message keys. The list of these
+ *    structures is returned by the silc_client_list_private_message_keys
  *    function.
  *
  * SOURCE
@@ -365,8 +373,6 @@ struct SilcChannelPrivateKeyStruct {
   char *name;                        /* Application given name */
   SilcCipher cipher;                 /* The cipher and key */
   SilcHmac hmac;                     /* The HMAC and hmac key */
-  unsigned char *key;                /* The key data */
-  SilcUInt32 key_len;                /* The key length */
 };
 /***/
 
@@ -497,8 +503,7 @@ typedef struct {
      message). */
   void (*private_message)(SilcClient client, SilcClientConnection conn,
                          SilcClientEntry sender, SilcMessagePayload payload,
-                         SilcMessageFlags flags,
-                         const unsigned char *message,
+                         SilcMessageFlags flags, const unsigned char *message,
                          SilcUInt32 message_len);
 
   /* Notify message to the client. The notify arguments are sent in the
@@ -608,14 +613,14 @@ typedef struct {
      responsible of saving the `detach_data', to for example in a file.
 
      The detachment data can be given as argument to the functions
-     silc_client_connect_to_server, or silc_client_add_connection when
-     creating connection to remote server, inside SilcClientConnectionParams
-     structure.  If it is provided the client library will attempt to resume
-     the session in the network.  After the connection is created
-     successfully, the application is responsible of setting the user
-     interface for user into the same state it was before detaching (showing
-     same channels, channel modes, etc).  It can do this by fetching the
-     information (like joined channels) from the client library. */
+     silc_client_connect_to_server or silc_client_key_exchange when creating
+     connection to remote host, inside SilcClientConnectionParams structure.
+     If it is provided the client library will attempt to resume the session
+     in the network.  After the connection is created successfully, the
+     application is responsible of setting the user interface for user into
+     the same state it was before detaching (showing same channels, channel
+     modes, etc).  It can do this by fetching the information (like joined
+     channels) from the client library. */
   void (*detach)(SilcClient client, SilcClientConnection conn,
                 const unsigned char *detach_data,
                 SilcUInt32 detach_data_len);
@@ -868,8 +873,8 @@ void silc_client_stop(SilcClient client);
  * DESCRIPTION
  *
  *    Client connection parameters.  This can be filled by the application
- *    and given as argument to silc_client_connect_to_server or to
- *    silc_client_add_connection.
+ *    and given as argument to silc_client_connect_to_server,
+ *    silc_client_connect_to_client or silc_client_key_exchange.
  *
  * SOURCE
  */
@@ -879,7 +884,9 @@ typedef struct {
      this is not provided then the `verify_public_key' client operation will
      be called back to application.  If the boolean `verify_notfound' is set
      to TRUE then the `verify_public_key' client operation will be called
-     in case the public key is not found in `repository'. */
+     in case the public key is not found in `repository'.  Only public keys
+     added with at least SILC_SKR_USAGE_KEY_AGREEMENT in the repository will
+     be checked, other keys will be ignored. */
   SilcSKR repository;
   SilcBool verify_notfound;
 
@@ -938,7 +945,7 @@ typedef struct {
  *
  * SYNOPSIS
  *
- *    void
+ *    SilcBool
  *    silc_client_connect_to_server(SilcClient client,
  *                                  SilcClientConnectionParams *params,
  *                                  SilcPublicKey public_key,
@@ -959,23 +966,26 @@ typedef struct {
  *    host disconnects.
  *
  *    If application wishes to create the network connection itself, use
- *    the silc_client_add_connection after creating the connection, instead
- *    of using this function.
+ *    the silc_client_key_exchange after creating the connection to start
+ *    key exchange and authentication with the server.
+ *
+ *    Returns when connecting is started and FALSE if connection was not
+ *    created at all.
  *
  ***/
-void silc_client_connect_to_server(SilcClient client,
-                                  SilcClientConnectionParams *params,
-                                  SilcPublicKey public_key,
-                                  SilcPrivateKey private_key,
-                                  char *remote_host, int port,
-                                  SilcClientConnectCallback callback,
-                                  void *context);
+SilcBool silc_client_connect_to_server(SilcClient client,
+                                      SilcClientConnectionParams *params,
+                                      SilcPublicKey public_key,
+                                      SilcPrivateKey private_key,
+                                      char *remote_host, int port,
+                                      SilcClientConnectCallback callback,
+                                      void *context);
 
 /****f* silcclient/SilcClientAPI/silc_client_connect_to_client
  *
  * SYNOPSIS
  *
- *    void
+ *    SilcBool
  *    silc_client_connect_to_client(SilcClient client,
  *                                  SilcClientConnectionParams *params,
  *                                  SilcPublicKey public_key,
@@ -995,100 +1005,93 @@ void silc_client_connect_to_server(SilcClient client,
  *    when remote host disconnects.
  *
  *    If application wishes to create the network connection itself, use
- *    the silc_client_add_connection after creating the connection, instead
- *    of using this function.
- *
- ***/
-void silc_client_connect_to_client(SilcClient client,
-                                  SilcClientConnectionParams *params,
-                                  SilcPublicKey public_key,
-                                  SilcPrivateKey private_key,
-                                  char *remote_host, int port,
-                                  SilcClientConnectCallback callback,
-                                  void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_add_connection
- *
- * SYNOPSIS
- *
- *
- *    SilcClientConnection
- *    silc_client_add_connection(SilcClient client,
- *                               SilcConnectionType conn_type,
- *                               SilcClientConnectionParams *params,
- *                               char *hostname, int port, void *context);
- *
- * DESCRIPTION
- *
- *    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 hosts. Every connection must
- *    be added to the client using this function. User data `context' may
- *    be sent as argument.  If the `params' is provided they are used by
- *    the routine.
- *
- * NOTES
- *
- *    This function is normally used only if the application performed
- *    the connecting outside the library, and did not called the
- *    silc_client_connect_to_server function at all.
- *
- ***/
-SilcClientConnection
-silc_client_add_connection(SilcClient client,
-                          SilcConnectionType conn_type,
-                          SilcClientConnectionParams *params,
-                          SilcPublicKey public_key,
-                          SilcPrivateKey private_key,
-                          char *remote_host, int port,
-                          SilcClientConnectCallback callback,
-                          void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_del_connection
- *
- * SYNOPSIS
- *
- *    void silc_client_del_connection(SilcClient client,
- *                                    SilcClientConnection conn);
- *
- * DESCRIPTION
+ *    the silc_client_key_exchange after creating the connection to start
+ *    key exchange with the client.
  *
- *    Removes connection from client. Frees all memory. The library
- *    call this function automatically for all connection contexts.
- *    The application however may free the connection contexts it has
- *    allocated.
+ *    Returns when connecting is started and FALSE if connection was not
+ *    created at all.
  *
  ***/
-void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
+SilcBool silc_client_connect_to_client(SilcClient client,
+                                      SilcClientConnectionParams *params,
+                                      SilcPublicKey public_key,
+                                      SilcPrivateKey private_key,
+                                      char *remote_host, int port,
+                                      SilcClientConnectCallback callback,
+                                      void *context);
 
-/****f* silcclient/SilcClientAPI/silc_client_start_key_exchange
+/****f* silcclient/SilcClientAPI/silc_client_key_exchange
  *
  * SYNOPSIS
  *
- *    void silc_client_start_key_exchange(SilcClient client,
- *                                        SilcClientConnection conn,
- *                                        SilcStream stream);
+ *    SilcBool
+ *    silc_client_key_exchange(SilcClient client,
+ *                             SilcClientConnectionParams *params,
+ *                             SilcPublicKey public_key,
+ *                             SilcPrivateKey private_key,
+ *                             SilcStream stream,
+ *                             SilcConnectionType conn_type,
+ *                             SilcClientConnectCallback callback,
+ *                             void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Starts key exchange protocol and authentication protocol in the
+ *    connection indicated by `stream'.  This function can be be used to
+ *    start SILC session with remote host (usually server) when the caller
+ *    has itself created the connection, instead of calling the function
+ *    silc_client_connect_to_server or silc_client_connect_to_client.  If
+ *    one of those functions was used this function must not be called as
+ *    in that case the key exchange is performed automatically.
+ *
+ *    Use this function only if you have created the connection by yourself.
+ *    After creating the connection the socket must be wrapped into a
+ *    socket stream.  See silcsocketstream.h for more information.  Note that
+ *    the `stream' must have valid remote IP address (and optionally also
+ *    hostname) and port set.
+ *
+ *    The `params' may be provided to provide various connection parameters.
+ *    The `public_key' and the `private_key' is your identity used in this
+ *    session.  The `callback' with `context' will be called after the session
+ *    has been set up.  It will also be called later when remote host
+ *    disconnects.  The `conn_type' is the type of session this is going to
+ *    be.
+ *
+ *    Returns TRUE when key exchange is started and FALSE if it is not started
+ *    at all.
  *
- * DESCRIPTION
+ * EXAMPLE
  *
- *    Start SILC Key Exchange (SKE) protocol to negotiate shared secret
- *    key material between client and server.  This function can be called
- *    directly if application is performing its own connecting and does not
- *    use the connecting provided by this library. This function is normally
- *    used only if the application performed the connecting outside the
- *    library. The library however may use this internally.  After the
- *    key exchange is performed the `connect' client operation is called.
+ *    int sock;
  *
- * NOTES
+ *    // Create remote connection stream.  Resolve hostname and IP also.
+ *    sock = create_connection(remote_host, port);
+ *    silc_socket_tcp_stream_create(sock, TRUE, FALSE, schedule,
+ *                                  stream_create_cb, app);
+ *
+ *    // Stream callback delivers our new SilcStream context
+ *    void stream_create_cb(SilcSocketStreamStatus status, SilcStream stream,
+ *                          void *context)
+ *    {
+ *      ...
+ *      if (status != SILC_SOCKET_OK)
+ *        error(status);
  *
- *    The silc_client_add_connection must be called before calling this
- *    function to create the SilcClientConnection context for this
- *    connection.
+ *      // Start key exchange
+ *      silc_client_key_exchange(client, NULL, public_key, private_key,
+ *                               stream, SILC_CONN_SERVER, connection_cb, app);
+ *      ...
+ *    }
  *
  ***/
-void silc_client_start_key_exchange(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcStream stream);
+SilcBool silc_client_key_exchange(SilcClient client,
+                                 SilcClientConnectionParams *params,
+                                 SilcPublicKey public_key,
+                                 SilcPrivateKey private_key,
+                                 SilcStream stream,
+                                 SilcConnectionType conn_type,
+                                 SilcClientConnectCallback callback,
+                                 void *context);
 
 /****f* silcclient/SilcClientAPI/silc_client_close_connection
  *
@@ -1122,6 +1125,7 @@ void silc_client_close_connection(SilcClient client,
  *                                              SilcChannelEntry channel,
  *                                              SilcChannelPrivateKey key,
  *                                              SilcMessageFlags flags,
+ *                                              SilcHash hash,
  *                                              unsigned char *data,
  *                                              SilcUInt32 data_len);
  *
@@ -1153,6 +1157,7 @@ SilcBool silc_client_send_channel_message(SilcClient client,
                                          SilcChannelEntry channel,
                                          SilcChannelPrivateKey key,
                                          SilcMessageFlags flags,
+                                         SilcHash hash,
                                          unsigned char *data,
                                          SilcUInt32 data_len);
 
@@ -1174,6 +1179,7 @@ silc_client_receive_channel_message(SilcClient client,
  *                                              SilcClientConnection conn,
  *                                              SilcClientEntry client_entry,
  *                                              SilcMessageFlags flags,
+ *                                              SilcHash hash,
  *                                              unsigned char *data,
  *                                              SilcUInt32 data_len);
  *
@@ -1182,7 +1188,9 @@ silc_client_receive_channel_message(SilcClient client,
  *    Sends private message to remote client. If private message key has
  *    not been set with this client then the message will be encrypted using
  *    normal session keys.  If the `flags' includes SILC_MESSAGE_FLAG_SIGNED
- *    the message will be digitally signed with the SILC key pair.
+ *    the message will be digitally signed with the SILC key pair.  In this
+ *    case the caller must also provide the `hash' pointer.  By default, the
+ *    hash function must be SHA-1.
  *
  *    Returns TRUE if the message was sent, and FALSE if error occurred.
  *    This function is thread safe and private messages can be sent from
@@ -1193,195 +1201,10 @@ SilcBool silc_client_send_private_message(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientEntry client_entry,
                                          SilcMessageFlags flags,
+                                         SilcHash hash,
                                          unsigned char *data,
                                          SilcUInt32 data_len);
 
-/****f* silcclient/SilcClientAPI/SilcGetChannelCallback
- *
- * SYNOPSIS
- *
- *    typedef void (*SilcGetChannelCallback)(SilcClient client,
- *                                           SilcClientConnection conn,
- *                                           SilcChannelEntry *channels,
- *                                           SilcUInt32 channels_count,
- *                                           void *context);
- *
- * DESCRIPTION
- *
- *    Callback function given to the silc_client_get_channel_* functions.
- *    The found entries are allocated into the `channels' array. The array
- *    must not be freed by the receiver, the library will free it later.
- *    If the `channel' is NULL, no such channel exist in the SILC Network.
- *
- ***/
-typedef void (*SilcGetChannelCallback)(SilcClient client,
-                                      SilcClientConnection conn,
-                                      SilcChannelEntry *channels,
-                                      SilcUInt32 channels_count,
-                                      void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_channel
- *
- * SYNOPSIS
- *
- *    SilcChannelEntry silc_client_get_channel(SilcClient client,
- *                                             SilcClientConnection conn,
- *                                             char *channel_name);
- *
- * DESCRIPTION
- *
- *    Finds entry for channel by the channel name. Returns the entry or NULL
- *    if the entry was not found. It is found only if the client is joined
- *    to the channel.  Use silc_client_get_channel_resolve or
- *    silc_client_get_channel_by_id_resolve to resolve channel that client
- *    is not joined.
- *
- ***/
-SilcChannelEntry silc_client_get_channel(SilcClient client,
-                                        SilcClientConnection conn,
-                                        char *channel_name);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_channel_resolve
- *
- * SYNOPSIS
- *
- *    void silc_client_get_channel_resolve(SilcClient client,
- *                                         SilcClientConnection conn,
- *                                         char *channel_name,
- *                                         SilcGetChannelCallback completion,
- *                                         void *context);
- *
- * DESCRIPTION
- *
- *    Resolves entry for channel by the channel name from the server.
- *    The resolving is done with IDENTIFY command. Note that users on
- *    the channel are not resolved at the same time. Use for example
- *    silc_client_get_clients_by_channel to resolve all users on a channel.
- *
- ***/
-void silc_client_get_channel_resolve(SilcClient client,
-                                    SilcClientConnection conn,
-                                    char *channel_name,
-                                    SilcGetChannelCallback completion,
-                                    void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_channel_by_id
- *
- * SYNOPSIS
- *
- *    SilcChannelEntry
- *    silc_client_get_channel_by_id(SilcClient client,
- *                                  SilcClientConnection conn,
- *                                  SilcChannelID *channel_id);
- *
- * DESCRIPTION
- *
- *    Finds channel entry by the channel ID. Returns the entry or NULL
- *    if the entry was not found.  This checks the local cache and does
- *    not resolve anything from server.
- *
- ***/
-SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
-                                              SilcClientConnection conn,
-                                              SilcChannelID *channel_id);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_channel_by_id_resolve
- *
- * SYNOPSIS
- *
- *    void
- *    silc_client_get_channel_by_id_resolve(SilcClient client,
- *                                          SilcClientConnection conn,
- *                                          SilcChannelID *channel_id,
- *                                          SilcGetClientCallback completion,
- *                                          void *context);
- *
- * DESCRIPTION
- *
- *    Resolves the channel information (its name mainly) from the server
- *    by the `channel_id'. Use this only if you know that you do not have
- *    the entry cached locally. The resolving is done with IDENTIFY command.
- *
- *    Note that users on the channel are not resolved at the same time.
- *    Use for example silc_client_get_clients_by_channel to resolve all
- *    users on a channel.
- *
- ***/
-void silc_client_get_channel_by_id_resolve(SilcClient client,
-                                          SilcClientConnection conn,
-                                          SilcChannelID *channel_id,
-                                          SilcGetChannelCallback completion,
-                                          void *context);
-
-/****f* silcclient/SilcClientAPI/silc_client_del_channel
- *
- * SYNOPSIS
- *
- *    SilcBool silc_client_del_channel(SilcClient client,
- *                                 SilcClientConnection conn,
- *                                 SilcChannelEntry channel)
- *
- * DESCRIPTION
- *
- *    Removes channel from local cache by the channel entry indicated by
- *    the `channel'.  Returns TRUE if the deletion were successful.
- *
- ***/
-SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
-                                SilcChannelEntry channel);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_server
- *
- * SYNOPSIS
- *
- *    SilcServerEntry silc_client_get_server(SilcClient client,
- *                                           SilcClientConnection conn,
- *                                           char *server_name)
- *
- * DESCRIPTION
- *
- *    Finds entry for server by the server name. Returns the entry or NULL
- *    if the entry was not found.
- *
- ***/
-SilcServerEntry silc_client_get_server(SilcClient client,
-                                      SilcClientConnection conn,
-                                      char *server_name);
-
-/****f* silcclient/SilcClientAPI/silc_client_get_server_by_id
- *
- * SYNOPSIS
- *
- *    SilcServerEntry silc_client_get_server_by_id(SilcClient client,
- *                                                 SilcClientConnection conn,
- *                                                 SilcServerID *server_id);
- *
- * DESCRIPTION
- *
- *    Finds entry for server by the server ID. Returns the entry or NULL
- *    if the entry was not found.
- *
- ***/
-SilcServerEntry silc_client_get_server_by_id(SilcClient client,
-                                            SilcClientConnection conn,
-                                            SilcServerID *server_id);
-
-/****f* silcclient/SilcClientAPI/silc_client_del_server
- *
- * SYNOPSIS
- *
- *    SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
- *                                SilcServerEntry server);
- *
- * DESCRIPTION
- *
- *    Removes server from local cache by the server entry indicated by
- *    the `server'.  Returns TRUE if the deletion were successful.
- *
- ***/
-SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
-                               SilcServerEntry server);
-
 /****f* silcclient/SilcClientAPI/silc_client_on_channel
  *
  * SYNOPSIS
@@ -1453,7 +1276,9 @@ SilcUInt16 silc_client_command_call(SilcClient client,
                                    const char *command_line, ...);
 
 /* If FALSE is returned the callback will not be called again, even if there
-   is more data coming in in the command reply. */
+   is more data coming in in the command reply.  If there are other pending
+   commands waiting for the reply, they will receive it even if some other
+   command reply has returned FALSE. */
 typedef SilcBool (*SilcClientCommandReply)(SilcClient client,
                                           SilcClientConnection conn,
                                           SilcCommand command,
@@ -1490,7 +1315,7 @@ typedef SilcBool (*SilcClientCommandReply)(SilcClient client,
  *    The `reply' callback must be provided, and it is called when the
  *    command reply is received from the server.  Note that, when using this
  *    function the default `command_reply' client operation will not be
- *    called, when reply is received.  Note however that, `reply' is
+ *    called, when reply is received.  Note however that, `reply' is almost
  *    identical with `command_reply' callback, and application may forward
  *    the reply from `reply' to `command_reply' callback, if desired.
  *
@@ -1854,41 +1679,23 @@ SilcBool silc_client_del_channel_private_key(SilcClient client,
  *
  * SYNOPSIS
  *
- *    SilcChannelPrivateKey *
+ *    SilcDList
  *    silc_client_list_channel_private_keys(SilcClient client,
  *                                          SilcClientConnection conn,
- *                                          SilcChannelEntry channel,
- *                                          SilcUInt32 *key_count);
+ *                                          SilcChannelEntry channel);
  *
  * DESCRIPTION
  *
- *    Returns array (pointers) of private keys associated to the `channel'.
- *    The caller must free the array by calling the function
- *    silc_client_free_channel_private_keys. The pointers in the array may be
+ *    Returns list of private keys associated to the `channel'.  The caller
+ *    must free the returned list.  The pointers in the list may be
  *    used to delete the specific key by giving the pointer as argument to the
- *    function silc_client_del_channel_private_key.
+ *    function silc_client_del_channel_private_key.  Each entry in the list
+ *    is SilcChannelPrivateKey.
  *
  ***/
-SilcChannelPrivateKey *
-silc_client_list_channel_private_keys(SilcClient client,
-                                     SilcClientConnection conn,
-                                     SilcChannelEntry channel,
-                                     SilcUInt32 *key_count);
-
-/****f* silcclient/SilcClientAPI/silc_client_free_channel_private_keys
- *
- * SYNOPSIS
- *
- *    void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
- *                                               SilcUInt32 key_count);
- *
- * DESCRIPTION
- *
- *    Frees the SilcChannelPrivateKey array.
- *
- ***/
-void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
-                                          SilcUInt32 key_count);
+SilcDList silc_client_list_channel_private_keys(SilcClient client,
+                                               SilcClientConnection conn,
+                                               SilcChannelEntry channel);
 
 /****f* silcclient/SilcClientAPI/silc_client_current_channel_private_key
  *
index abc078b58cbb8bd2d3d07a215f487f502875b606..60ba5f0a0533a7fcd1780c9122e04071ee546ba0 100644 (file)
@@ -91,40 +91,31 @@ struct SilcClientEntryStruct {
  *    represented as SilcChannelEntry.  The structure includes information
  *    about the channel.  All strings in the structure are UTF-8 encoded.
  *
+ *    Application may store its own pointer into the context pointer in
+ *    this structure.
+ *
+ * NOTES
+ *
+ *    If application stores the SilcChannelEntry it must always take
+ *    a reference of it by calling silc_client_ref_channel function.  The
+ *    reference must be released after it is not needed anymore by calling
+ *    silc_client_unref_channel function.
+ *
  * SOURCE
  */
 struct SilcChannelEntryStruct {
-  /* General information */
-  char *channel_name;                       /* Channel name */
-  SilcChannelID *id;                        /* Channel ID */
-  SilcUInt32 mode;                          /* Channel mode, ChannelModes. */
-  char *topic;                              /* Current topic, may be NULL */
-  SilcPublicKey founder_key;                /* Founder key, may be NULL */
-  SilcUInt32 user_limit;                    /* User limit on channel */
-
-  /* All clients that has joined this channel.  The key to the table is the
-     SilcClientEntry and the context is SilcChannelUser context. */
-  SilcHashTable user_list;
-
-  /* Channel keys */
-  SilcCipher channel_key;                    /* The channel key */
-  unsigned char *key;                       /* Raw key data */
-  SilcUInt32 key_len;                       /* Raw key data length */
-  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; /* Current IV */
-  SilcHmac hmac;                            /* Current HMAC */
-
-  /* Channel private keys */
-  SilcDList private_keys;                   /* List of private keys or NULL */
-  SilcChannelPrivateKey curr_key;           /* Current private key */
-
-  /* SilcChannelEntry status information */
-  SilcDList old_channel_keys;
-  SilcDList old_hmacs;
-  SilcUInt16 resolve_cmd_ident;                     /* Command identifier when
-                                               resolving this entry */
-
-  /* Application specific data.  Application may set here whatever it wants. */
-  void *context;
+  char *channel_name;               /* Channel name */
+  char *topic;                      /* Current topic, may be NULL */
+  SilcPublicKey founder_key;        /* Founder key, may be NULL */
+  SilcChannelID id;                 /* Channel ID */
+  SilcUInt32 mode;                  /* Channel mode, ChannelModes. */
+  SilcUInt32 user_limit;            /* User limit on channel */
+  SilcHashTable user_list;          /* Joined users.  Key to hash table is
+                                       SilcClientEntry, context is
+                                       SilcChannelUser. */
+
+  void *context;                    /* Application specific context */
+  SilcChannelEntryInternal internal;
 };
 /***/
 
@@ -132,27 +123,38 @@ struct SilcChannelEntryStruct {
  *
  * NAME
  *
- *    typedef struct SilcServerEntryStruct { ... } *SilcServerEntry
+ *    typedef struct SilcServerEntryStruct { ... } *SilcServerEntry;
  *
  * DESCRIPTION
  *
  *    This structure represents a server in the SILC network.  All servers
  *    that the client is aware of and have for example resolved with
  *    SILC_COMMAND_INFO command have their on SilcServerEntry structure.
- *    All strings in the structure are UTF-8 encoded.
+ *    Server's public key is present only if it has been retrieved using
+ *    SILC_COMMAND_GETKEY command.  All strings in the structure are UTF-8
+ *    encoded.
+ *
+ *    Application may store its own pointer into the context pointer in
+ *    this structure.
+ *
+ * NOTES
+ *
+ *    If application stores the SilcServerEntry it must always take
+ *    a reference of it by calling silc_client_ref_server function.  The
+ *    reference must be released after it is not needed anymore by calling
+ *    silc_client_unref_server function.
  *
  * SOURCE
  */
 struct SilcServerEntryStruct {
   /* General information */
-  char *server_name;                        /* Server name */
-  char *server_info;                        /* Server info */
-  SilcServerID *server_id;                  /* Server ID */
-  SilcUInt16 resolve_cmd_ident;                     /* Command identifier when
-                                               resolving this entry */
-
-  /* Application specific data.  Application may set here whatever it wants. */
-  void *context;
+  char *server_name;                /* Server name */
+  char *server_info;                /* Server info */
+  SilcServerID id;                  /* Server ID */
+  SilcPublicKey public_key;         /* Server public key, may be NULL */
+
+  void *context;                    /* Application specific context */
+  SilcServerEntryInternal internal;
 };
 /***/
 
@@ -173,8 +175,9 @@ struct SilcServerEntryStruct {
  *    Callback function given to various client search functions.  The
  *    found entries are allocated into the `clients' list.  The list must
  *    not be freed by the receiver, the library will free it later.  If the
- *    `clients' is NULL, no such clients exist in the SILC Network, and
- *    the `status' will include the error.
+ *    `clients' is NULL, no such clients exist in the SILC network, and
+ *    the `status' will include the error.  Each entry in the `clients'
+ *    is SilcClientEntry.
  *
  * NOTES
  *
@@ -499,21 +502,340 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
                                          SilcGetClientCallback completion,
                                          void *context);
 
-/****f* silcclient/SilcClientAPI/silc_client_del_client
+/* SilcChannelEntry routines */
+
+/****f* silcclient/SilcClientAPI/SilcGetChannelCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcGetChannelCallback)(SilcClient client,
+ *                                           SilcClientConnection conn,
+ *                                           SilcStatus status,
+ *                                           SilcDList channels,
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Callback function given to various channel resolving functions.
+ *    The found entries are included in the `channels' list and each entry
+ *    in the list is SilcChannelEntry.  If `channels' is NULL then no such
+ *    channel exist in the network and the `status' will indicate the error.
+ *
+ * NOTES
+ *
+ *    If the application stores any of the SilcChannelEntry pointers from
+ *    the `channels' list it must reference it with silc_client_ref_channel
+ *    function.
+ *
+ *    Application must not free the returned `channels' list.
+ *
+ ***/
+typedef void (*SilcGetChannelCallback)(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcStatus status,
+                                      SilcDList channels,
+                                      void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_ref_channel
  *
  * SYNOPSIS
  *
- *    SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
- *                                SilcClientEntry client_entry)
+ *    void silc_client_ref_channel(SilcClient client,
+ *                                 SilcClientConnection conn,
+ *                                 SilcChannelEntry channel_entry);
  *
  * DESCRIPTION
  *
- *    Removes client from local cache by the client entry indicated by
- *    the `client_entry'.  Returns TRUE if the deletion were successful.
+ *    Takes a reference of the channel entry indicated by `channel_entry'
+ *    The reference must be released by calling silc_client_unref_channel
+ *    after it is not needed anymore.
  *
  ***/
-SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
-                               SilcClientEntry client_entry);
+void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
+                            SilcChannelEntry channel_entry);
 
+/****f* silcclient/SilcClientAPI/silc_client_unref_channel
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_unref_channel(SilcClient client,
+ *                                   SilcClientConnection conn,
+ *                                   SilcChannelEntry channel_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Releases the channel entry reference indicated by `channel_entry'.
+ *
+ ***/
+void silc_client_unref_channel(SilcClient client, SilcClientConnection conn,
+                              SilcChannelEntry channel_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_list_free_channel
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_list_free_channel(SilcClient client,
+ *                                       SilcClientConnection conn,
+ *                                       SilcDList channel_list);
+ *
+ * DESCRIPTION
+ *
+ *    Free's channel entry list that has been returned by various library
+ *    routines.
+ *
+ ***/
+void silc_client_list_free_channels(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcDList channel_list);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelEntry silc_client_get_channel(SilcClient client,
+ *                                             SilcClientConnection conn,
+ *                                             char *channel_name);
+ *
+ * DESCRIPTION
+ *
+ *    Finds entry for channel by the channel name. Returns the entry or NULL
+ *    if the entry was not found. It is found only if the client is joined
+ *    to the channel.  Use silc_client_get_channel_resolve or
+ *    silc_client_get_channel_by_id_resolve to resolve channel that client
+ *    is not joined.
+ *
+ * NOTES
+ *
+ *    The returned SilcChannelEntry has been referenced by the library and
+ *    the caller must call silc_client_unref_channel after the entry is not
+ *    needed anymore.
+ *
+ ***/
+SilcChannelEntry silc_client_get_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        char *channel_name);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel_resolve
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_get_channel_resolve(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         char *channel_name,
+ *                                         SilcGetChannelCallback completion,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Resolves entry for channel by the channel name from the server.
+ *    The resolving is done with IDENTIFY command. Note that users on
+ *    the channel are not resolved at the same time. Use for example
+ *    silc_client_get_clients_by_channel to resolve all users on a channel.
+ *
+ ***/
+void silc_client_get_channel_resolve(SilcClient client,
+                                    SilcClientConnection conn,
+                                    char *channel_name,
+                                    SilcGetChannelCallback completion,
+                                    void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel_by_id
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelEntry
+ *    silc_client_get_channel_by_id(SilcClient client,
+ *                                  SilcClientConnection conn,
+ *                                  SilcChannelID *channel_id);
+ *
+ * DESCRIPTION
+ *
+ *    Finds channel entry by the channel ID. Returns the entry or NULL
+ *    if the entry was not found.  This checks the local cache and does
+ *    not resolve anything from server.
+ *
+ * NOTES
+ *
+ *    The returned SilcChannelEntry has been referenced by the library and
+ *    the caller must call silc_client_unref_channel after the entry is not
+ *    needed anymore.
+ *
+ ***/
+SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel_by_id_resolve
+ *
+ * SYNOPSIS
+ *
+ *    void
+ *    silc_client_get_channel_by_id_resolve(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcChannelID *channel_id,
+ *                                          SilcGetClientCallback completion,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Resolves the channel information (its name mainly) from the server
+ *    by the `channel_id'. Use this only if you know that you do not have
+ *    the entry cached locally. The resolving is done with IDENTIFY command.
+ *
+ *    Note that users on the channel are not resolved at the same time.
+ *    Use for example silc_client_get_clients_by_channel to resolve all
+ *    users on a channel.
+ *
+ ***/
+void silc_client_get_channel_by_id_resolve(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcChannelID *channel_id,
+                                          SilcGetChannelCallback completion,
+                                          void *context);
+
+/* SilcServerEntry routines */
+
+/****f* silcclient/SilcClientAPI/SilcGetServerCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcGetServerCallback)(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcStatus status,
+ *                                          SilcDList servers,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Callback function given to various server resolving functions.
+ *    The found entries are included in the `servers' list and each entry
+ *    in the list is SilcServerEntry.  If `server' is NULL then no such
+ *    server exist in the network and the `status' will indicate the error.
+ *
+ * NOTES
+ *
+ *    If the application stores any of the SilcServerEntry pointers from
+ *    the `server' list it must reference it with silc_client_ref_server
+ *    function.
+ *
+ *    Application must not free the returned `server' list.
+ *
+ ***/
+typedef void (*SilcGetServerCallback)(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcStatus status,
+                                     SilcDList servers,
+                                     void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_ref_server
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_ref_server(SilcClient client,
+ *                                SilcClientConnection conn,
+ *                                SilcServerEntry server_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Takes a reference of the server entry indicated by `server_entry'
+ *    The reference must be released by calling silc_client_unref_server
+ *    after it is not needed anymore.
+ *
+ ***/
+void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
+                            SilcServerEntry server_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_unref_server
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_unref_server(SilcClient client,
+ *                                  SilcClientConnection conn,
+ *                                  SilcServerEntry server_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Releases the server entry reference indicated by `server_entry'.
+ *
+ ***/
+void silc_client_unref_server(SilcClient client, SilcClientConnection conn,
+                             SilcServerEntry server_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_list_free_server
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_list_free_server(SilcClient client,
+ *                                      SilcClientConnection conn,
+ *                                      SilcDList server_list);
+ *
+ * DESCRIPTION
+ *
+ *    Free's server entry list that has been returned by various library
+ *    routines.
+ *
+ ***/
+void silc_client_list_free_servers(SilcClient client,
+                                  SilcClientConnection conn,
+                                  SilcDList server_list);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_server
+ *
+ * SYNOPSIS
+ *
+ *    SilcServerEntry silc_client_get_server(SilcClient client,
+ *                                           SilcClientConnection conn,
+ *                                           char *server_name)
+ *
+ * DESCRIPTION
+ *
+ *    Finds entry for server by the server name. Returns the entry or NULL
+ *    if the entry was not found.
+ *
+ ***/
+SilcServerEntry silc_client_get_server(SilcClient client,
+                                      SilcClientConnection conn,
+                                      char *server_name);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_server_by_id
+ *
+ * SYNOPSIS
+ *
+ *    SilcServerEntry silc_client_get_server_by_id(SilcClient client,
+ *                                                 SilcClientConnection conn,
+ *                                                 SilcServerID *server_id);
+ *
+ * DESCRIPTION
+ *
+ *    Finds entry for server by the server ID. Returns the entry or NULL
+ *    if the entry was not found.
+ *
+ ***/
+SilcServerEntry silc_client_get_server_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcServerID *server_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_server_by_id_resolve
+ *
+ * SYNOPSIS
+ *
+ *    void
+ *    silc_client_get_server_by_id_resolve(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         SilcServerID *server_id,
+ *                                         SilcGetServerCallback completion,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Resolves the server information by the `server_id'.  The resolved
+ *    server is returned into the `completion' callback.
+ *
+ ***/
+void silc_client_get_server_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcServerID *server_id,
+                                         SilcGetServerCallback completion,
+                                         void *context);
 
 #endif /* SILCCLIENT_ENTRY_H */
index 68d5f90cb6e5feb2778a2825dd3cd4d7425b912b..f93a36f135ad93c79f5c6a0107c5e7c42daf798a 100644 (file)
@@ -6,25 +6,11 @@
 SilcBool success;
 SilcClientOperations ops;
 
-/* XXX */
-SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
-                                       SilcClientEntry client_entry)
-{
-  return NULL;
-}
-
 SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...)
 {
   return NULL;
 }
 
-SilcBool silc_client_del_channel_private_keys(SilcClient client,
-                                              SilcClientConnection conn,
-                                              SilcChannelEntry channel)
-{
-  return FALSE;
-}
-
 
 /******* MyBot code **********************************************************/
 
@@ -40,12 +26,13 @@ typedef struct {
 
 static void
 silc_connected(SilcClient client, SilcClientConnection conn,
-              SilcClientConnectionStatus status, void *context)
+              SilcClientConnectionStatus status, const char *message,
+              void *context)
 {
   MyBot mybot = client->application;
 
   if (status == SILC_CLIENT_CONN_DISCONNECTED) {
-    SILC_LOG_DEBUG(("Disconnected"));
+    SILC_LOG_DEBUG(("Disconnected %s", message ? message : ""));
     silc_client_stop(client);
     return;
   }
@@ -189,7 +176,7 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
 
 
 /* Private message to the client. The `sender' is the sender of the
-   message. The message is `message'and maybe NULL.  The `flags'  
+   message. The message is `message'and maybe NULL.  The `flags'
    indicates message flags  and it is used to determine how the message
    can be interpreted (like it may tell the message is multimedia
    message). */