Merge commit 'origin/silc.1.1.branch'
[silc.git] / lib / silcclient / client_register.c
index f029c480079c3fcabb022d257229329e7fc90bce..efbeaa24d9cae4534d01d65ad53e221b9ec83fb7 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2006 Pekka Riikonen
+  Copyright (C) 2006 - 2007 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
@@ -28,26 +28,15 @@ typedef struct {
   SilcClient client;
   SilcClientConnection conn;
   SilcBufferStruct detach;
+  SilcBuffer auth;
   char *nickname;
+  unsigned char *id;
+  SilcUInt32 id_len;
   SilcUInt32 channel_count;
 } *SilcClientResumeSession;
 
 /************************ Static utility functions **************************/
 
-/* Command callback.  Nothing interesting to do here. */
-
-static SilcBool
-silc_client_register_command_called(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcCommand command,
-                                   SilcStatus status,
-                                   SilcStatus error,
-                                   void *context,
-                                   va_list ap)
-{
-  return FALSE;
-}
-
 /* Continues resuming after resolving.  Continue after last reply. */
 
 static SilcBool
@@ -82,6 +71,23 @@ silc_client_resume_command_callback(SilcClient client,
   va_end(ap);
 }
 
+/* Resume authentication data generation callback */
+
+static void silc_client_resume_auth_generated(const SilcBuffer data,
+                                             void *context)
+{
+  SilcClientConnection conn = context;
+  SilcClientResumeSession resume =
+    silc_fsm_get_state_context(&conn->internal->event_thread);
+
+  if (!data)
+    silc_fsm_next(&conn->internal->event_thread, silc_client_st_resume_error);
+  else
+    resume->auth = silc_buffer_copy(data);
+
+  SILC_FSM_CALL_CONTINUE_SYNC(&conn->internal->event_thread);
+}
+
 
 /****************************** NEW_ID packet *******************************/
 
@@ -92,6 +98,7 @@ SILC_FSM_STATE(silc_client_new_id)
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
   SilcPacket packet = state_context;
+  char *nick;
   SilcID id;
 
   if (conn->local_id)
@@ -106,9 +113,15 @@ SILC_FSM_STATE(silc_client_new_id)
   SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
                                              SILC_ID_CLIENT)));
 
+  /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
+  if (conn->internal->remote_version >= 13)
+    nick = (conn->internal->params.nickname ?
+           conn->internal->params.nickname : client->username);
+  else
+    nick = client->username;
+
   /* Create local client entry */
-  conn->local_entry = silc_client_add_client(client, conn,
-                                            client->username,
+  conn->local_entry = silc_client_add_client(client, conn, nick,
                                             client->username,
                                             client->realname,
                                             &id.u.client_id, 0);
@@ -144,7 +157,7 @@ SILC_FSM_STATE(silc_client_new_id)
  out:
   /** Packet processed */
   silc_packet_free(packet);
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 
@@ -156,9 +169,15 @@ SILC_FSM_STATE(silc_client_st_register)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
+  char *nick = NULL;
 
   SILC_LOG_DEBUG(("Register to network"));
 
+  /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
+  if (conn->internal->remote_version >= 13)
+    nick = (conn->internal->params.nickname ?
+           conn->internal->params.nickname : client->username);
+
   /* 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)),
@@ -167,17 +186,19 @@ SILC_FSM_STATE(silc_client_st_register)
                           SILC_STR_UI_SHORT(strlen(client->realname)),
                           SILC_STR_DATA(client->realname,
                                         strlen(client->realname)),
+                          SILC_STR_UI_SHORT(nick ? strlen(nick) : 0),
+                          SILC_STR_DATA(nick, nick ? strlen(nick) : 0),
                           SILC_STR_END)) {
     /** Error sending packet */
     silc_fsm_next(fsm, silc_client_st_register_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   /** Wait for new ID */
   conn->internal->registering = TRUE;
   silc_fsm_next_later(fsm, silc_client_st_register_complete,
                      conn->internal->retry_timer, 0);
-  SILC_FSM_WAIT;
+  return SILC_FSM_WAIT;
 }
 
 /* Wait for NEW_ID packet to arrive */
@@ -190,7 +211,7 @@ SILC_FSM_STATE(silc_client_st_register_complete)
   if (conn->internal->disconnected) {
     /** Disconnected */
     silc_fsm_next(fsm, silc_client_st_register_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (!conn->local_id) {
@@ -200,7 +221,7 @@ SILC_FSM_STATE(silc_client_st_register_complete)
       conn->internal->retry_count = 0;
       conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
       silc_fsm_next(fsm, silc_client_st_register_error);
-      SILC_FSM_CONTINUE;
+      return SILC_FSM_CONTINUE;
     }
 
     /** Resend registering packet */
@@ -209,7 +230,7 @@ SILC_FSM_STATE(silc_client_st_register_complete)
                                    SILC_CLIENT_RETRY_MUL) +
                                   (silc_rng_get_rn16(client->rng) %
                                    SILC_CLIENT_RETRY_RAND));
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   SILC_LOG_DEBUG(("Registered to network"));
@@ -217,13 +238,13 @@ SILC_FSM_STATE(silc_client_st_register_complete)
   /* Issue IDENTIFY command for itself to get resolved hostname
      correctly from server. */
   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
-                          silc_client_register_command_called, NULL,
+                          silc_client_command_called_dummy, NULL,
                           1, 5, silc_buffer_data(conn->internal->local_idp),
                           silc_buffer_len(conn->internal->local_idp));
 
-  /* Call NICK command if the nickname was set by the application (and is
-     not same as the username). */
-  if (conn->internal->params.nickname &&
+  /* With SILC protocol version 1.2 call NICK command if the nickname was
+     set by the application. */
+  if (conn->internal->params.nickname && conn->internal->remote_version < 13 &&
       !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
     silc_client_command_call(client, conn, NULL,
                             "NICK", conn->internal->params.nickname, NULL);
@@ -231,7 +252,7 @@ SILC_FSM_STATE(silc_client_st_register_complete)
   /* Issue INFO command to fetch the real server name and server
      information and other stuff. */
   silc_client_command_send(client, conn, SILC_COMMAND_INFO,
-                          silc_client_register_command_called, NULL,
+                          silc_client_command_called_dummy, NULL,
                           1, 2, silc_buffer_data(conn->internal->remote_idp),
                           silc_buffer_len(conn->internal->remote_idp));
 
@@ -242,8 +263,10 @@ SILC_FSM_STATE(silc_client_st_register_complete)
   conn->internal->registering = FALSE;
   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
                                silc_client_connect_timeout, conn);
+  silc_async_free(conn->internal->cop);
+  conn->internal->cop = NULL;
 
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 /* Error registering to network */
@@ -251,26 +274,20 @@ SILC_FSM_STATE(silc_client_st_register_complete)
 SILC_FSM_STATE(silc_client_st_register_error)
 {
   SilcClientConnection conn = fsm_context;
-  SilcClient client = conn->client;
 
   SILC_LOG_DEBUG(("Error registering to network"));
 
   /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_ERROR;
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   }
 
-  /* Call connect callback */
-  if (conn->internal->callback_called)
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
-                  conn->callback_context);
-  conn->internal->callback_called = TRUE;
-
   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
                                silc_client_connect_timeout, conn);
 
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 /************************* Resume detached session **************************/
@@ -282,7 +299,6 @@ SILC_FSM_STATE(silc_client_st_resume)
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
   SilcClientResumeSession resume;
-  SilcBuffer auth;
   unsigned char *id;
   SilcUInt16 id_len;
   SilcClientID client_id;
@@ -294,7 +310,7 @@ SILC_FSM_STATE(silc_client_st_resume)
   if (!resume) {
     /** Out of memory */
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
   silc_fsm_set_state_context(fsm, resume);
 
@@ -316,7 +332,7 @@ SILC_FSM_STATE(silc_client_st_resume)
     /** Malformed detach data */
     SILC_LOG_DEBUG(("Malformed detachment data"));
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id,
@@ -324,38 +340,47 @@ SILC_FSM_STATE(silc_client_st_resume)
     /** Malformed ID */
     SILC_LOG_DEBUG(("Malformed ID"));
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
+  resume->id = id;
+  resume->id_len = id_len;
 
   /* Generate authentication data that server will verify */
-  auth = silc_auth_public_key_auth_generate(conn->public_key,
-                                           conn->private_key,
-                                           client->rng,
-                                           conn->internal->hash,
-                                           &client_id, SILC_ID_CLIENT);
-  if (!auth) {
-    /** Out of memory */
-    silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
-  }
+  silc_fsm_next(fsm, silc_client_st_resume_send);
+  SILC_FSM_CALL(silc_auth_public_key_auth_generate(
+                                conn->public_key, conn->private_key,
+                                client->rng, conn->internal->hash,
+                                &client_id, SILC_ID_CLIENT,
+                                silc_client_resume_auth_generated, conn));
+  /* NOT REACHED */
+}
+
+/* Send RESUME_CLIENT packet */
+
+SILC_FSM_STATE(silc_client_st_resume_send)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClientResumeSession resume = state_context;
+
+  SILC_LOG_DEBUG(("Send RESUME_CLIENT packet"));
 
   /* Send RESUME_CLIENT packet to resume to network */
   if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
-                          SILC_STR_UI_SHORT(id_len),
-                          SILC_STR_DATA(id, id_len),
-                          SILC_STR_DATA(silc_buffer_data(auth),
-                                        silc_buffer_len(auth)),
+                          SILC_STR_UI_SHORT(resume->id_len),
+                          SILC_STR_DATA(resume->id, resume->id_len),
+                          SILC_STR_DATA(silc_buffer_data(resume->auth),
+                                        silc_buffer_len(resume->auth)),
                           SILC_STR_END)) {
     /** Error sending packet */
     SILC_LOG_DEBUG(("Error sending packet"));
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   /** Wait for new ID */
   conn->internal->registering = TRUE;
   silc_fsm_next_later(fsm, silc_client_st_resume_resolve_channels, 15, 0);
-  SILC_FSM_WAIT;
+  return SILC_FSM_WAIT;
 }
 
 /* Resolve the old session information, user mode and joined channels. */
@@ -372,14 +397,14 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
   if (conn->internal->disconnected) {
     /** Disconnected */
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (!conn->local_id) {
     /** Timeout, ID not received */
     conn->internal->registering = FALSE;
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   /** Wait for channels */
@@ -392,12 +417,12 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
   /* Send UMODE command to get our own user mode in the network */
   SILC_LOG_DEBUG(("Resolving user mode"));
   silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
-                          silc_client_register_command_called, NULL,
+                          silc_client_command_called_dummy, NULL,
                           1, 1, silc_buffer_data(conn->internal->local_idp),
                           silc_buffer_len(conn->internal->local_idp));
 
   if (!resume->channel_count)
-    SILC_FSM_YIELD;
+    return SILC_FSM_YIELD;
 
   /* Send IDENTIFY command for all channels we know about.  These are the
      channels we've joined to according our detachment data. */
@@ -428,6 +453,8 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
     channel = silc_client_get_channel_by_id(client, conn, &channel_id);
     if (!channel)
       silc_client_add_channel(client, conn, name, 0, &channel_id);
+    else
+      silc_client_unref_channel(client, conn, channel);
 
     res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
     res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
@@ -453,7 +480,7 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
   silc_free(res_argv_lens);
   silc_free(res_argv_types);
 
-  SILC_FSM_WAIT;
+  return SILC_FSM_WAIT;
 }
 
 /* Resolve joined channel modes, users and topics. */
@@ -471,7 +498,7 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
   if (conn->internal->disconnected) {
     /** Disconnected */
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   SILC_LOG_DEBUG(("Resolving channel details"));
@@ -480,7 +507,7 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
   silc_fsm_next(fsm, silc_client_st_resume_completed);
 
   if (!silc_idcache_get_all(conn->internal->channel_cache, &channels))
-    SILC_FSM_YIELD;
+    return SILC_FSM_YIELD;
 
   /* Resolve channels' mode, users and topic */
   resume->channel_count = silc_list_count(channels) * 3;
@@ -506,7 +533,7 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
     silc_buffer_free(idp);
   }
 
-  SILC_FSM_WAIT;
+  return SILC_FSM_WAIT;
 }
 
 /* Resuming completed */
@@ -523,13 +550,13 @@ SILC_FSM_STATE(silc_client_st_resume_completed)
   if (conn->internal->disconnected) {
     /** Disconnected */
     silc_fsm_next(fsm, silc_client_st_resume_error);
-    SILC_FSM_CONTINUE;
+    return SILC_FSM_CONTINUE;
   }
 
   if (resume->channel_count > 0) {
     resume->channel_count--;
     if (resume->channel_count)
-      SILC_FSM_WAIT;
+      return SILC_FSM_WAIT;
   }
 
   SILC_LOG_DEBUG(("Resuming completed"));
@@ -537,14 +564,14 @@ SILC_FSM_STATE(silc_client_st_resume_completed)
   /* Issue IDENTIFY command for itself to get resolved hostname
      correctly from server. */
   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
-                          silc_client_register_command_called, NULL,
+                          silc_client_command_called_dummy, NULL,
                           1, 5, silc_buffer_data(conn->internal->local_idp),
                           silc_buffer_len(conn->internal->local_idp));
 
   /* Issue INFO command to fetch the real server name and server
      information and other stuff. */
   silc_client_command_send(client, conn, SILC_COMMAND_INFO,
-                          silc_client_register_command_called, NULL,
+                          silc_client_command_called_dummy, NULL,
                           1, 2, silc_buffer_data(conn->internal->remote_idp),
                           silc_buffer_len(conn->internal->remote_idp));
 
@@ -564,25 +591,26 @@ SILC_FSM_STATE(silc_client_st_resume_completed)
                                      &conn->local_entry->id);
 
   /* Call JOIN command replies for all joined channel */
-  silc_idcache_get_all(conn->internal->channel_cache, &channels);
-  silc_list_start(channels);
-  while ((entry = silc_list_get(channels))) {
-    SilcHashTableList htl;
-    const char *cipher, *hmac;
-
-    channel = entry->context;
-    cipher = (channel->internal.channel_key ?
-             silc_cipher_get_name(channel->internal.channel_key) : NULL);
-    hmac = (channel->internal.hmac ?
-           silc_hmac_get_name(channel->internal.hmac) : NULL);
-    silc_hash_table_list(channel->user_list, &htl);
-    silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
-                                       channel->channel_name, channel,
-                                       channel->mode, &htl, channel->topic,
-                                       cipher, hmac, channel->founder_key,
-                                       channel->channel_pubkeys,
-                                       channel->user_limit);
-    silc_hash_table_list_reset(&htl);
+  if (silc_idcache_get_all(conn->internal->channel_cache, &channels)) {
+    silc_list_start(channels);
+    while ((entry = silc_list_get(channels))) {
+      SilcHashTableList htl;
+      const char *cipher, *hmac;
+
+      channel = entry->context;
+      cipher = (channel->internal.send_key ?
+               silc_cipher_get_name(channel->internal.send_key) : NULL);
+      hmac = (channel->internal.hmac ?
+             silc_hmac_get_name(channel->internal.hmac) : NULL);
+      silc_hash_table_list(channel->user_list, &htl);
+      silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
+                                         channel->channel_name, channel,
+                                         channel->mode, &htl, channel->topic,
+                                         cipher, hmac, channel->founder_key,
+                                         channel->channel_pubkeys,
+                                         channel->user_limit);
+      silc_hash_table_list_reset(&htl);
+    }
   }
 
   conn->internal->registering = FALSE;
@@ -590,8 +618,10 @@ SILC_FSM_STATE(silc_client_st_resume_completed)
                                silc_client_connect_timeout, conn);
   silc_free(resume->nickname);
   silc_free(resume);
+  silc_async_free(conn->internal->cop);
+  conn->internal->cop = NULL;
 
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 /* Error resuming to network */
@@ -599,7 +629,6 @@ SILC_FSM_STATE(silc_client_st_resume_completed)
 SILC_FSM_STATE(silc_client_st_resume_error)
 {
   SilcClientConnection conn = fsm_context;
-  SilcClient client = conn->client;
   SilcClientResumeSession resume = state_context;
 
   if (conn->internal->disconnected) {
@@ -607,23 +636,18 @@ SILC_FSM_STATE(silc_client_st_resume_error)
       silc_free(resume->nickname);
       silc_free(resume);
     }
-    SILC_FSM_FINISH;
+    return SILC_FSM_FINISH;
   }
 
   SILC_LOG_DEBUG(("Error resuming to network"));
 
   /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_ERROR;
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   }
 
-  /* Call connect callback */
-  if (conn->internal->callback_called)
-    conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
-                  conn->callback_context);
-  conn->internal->callback_called = TRUE;
-
   silc_schedule_task_del_by_all(conn->internal->schedule, 0,
                                silc_client_connect_timeout, conn);
 
@@ -632,7 +656,7 @@ SILC_FSM_STATE(silc_client_st_resume_error)
     silc_free(resume);
   }
 
-  SILC_FSM_FINISH;
+  return SILC_FSM_FINISH;
 }
 
 /* Generates the session detachment data. This data can be used later