updates.
[silc.git] / lib / silcclient / client.c
index ba3c70e9b92d1c12b2d6d2f05d0d647fc7c9a7f7..620e3de07318002e6e9c3306e56d27dedf613d00 100644 (file)
@@ -360,34 +360,26 @@ int silc_client_connect_to_server(SilcClient client, int port,
   return sock;
 }
 
-/* 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. */
+/* Socket hostname and IP lookup callback that is called before actually
+   starting the key exchange.  The lookup is called from the function
+   silc_client_start_key_exchange. */
 
-bool silc_client_start_key_exchange(SilcClient client,
-                                   SilcClientConnection conn,
-                                   int fd)
+static void silc_client_start_key_exchange_cb(SilcSocketConnection sock,
+                                             void *context)
 {
+  SilcClientConnection conn = (SilcClientConnection)context;
+  SilcClient client = conn->client;
   SilcProtocol protocol;
   SilcClientKEInternalContext *proto_ctx;
-  void *context;
-
-  /* Allocate new socket connection object */
-  silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
 
-  /* Sometimes when doing quick reconnects the new socket may be same as
-     the old one and there might be pending stuff for the old socket. 
-     If new one is same then those pending sutff might cause problems.
-     Make sure they do not do that. */
-  silc_schedule_task_del_by_fd(client->schedule, fd);
+  SILC_LOG_DEBUG(("Start"));
 
-  conn->nickname = (client->nickname ? strdup(client->nickname) :
-                   strdup(client->username));
+  /* XXX We should most likely use the resolved host name instead of the
+     one user provided for us. */
+  silc_free(conn->sock->hostname);
   conn->sock->hostname = strdup(conn->remote_host);
-  conn->sock->ip = strdup(conn->remote_host);
+  if (!conn->sock->ip)
+    conn->sock->ip = strdup(conn->remote_host);
   conn->sock->port = conn->remote_port;
 
   /* Allocate internal Key Exchange context. This is sent to the
@@ -408,7 +400,9 @@ bool silc_client_start_key_exchange(SilcClient client,
   if (!protocol) {
     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
                               "Error: Could not start key exchange protocol");
-    return FALSE;
+    silc_net_close_connection(conn->sock->sock);
+    client->internal->ops->connect(client, conn, FALSE);
+    return;
   }
   conn->sock->protocol = protocol;
 
@@ -419,11 +413,38 @@ bool silc_client_start_key_exchange(SilcClient client,
      be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
      later when outgoing data is available. */
   context = (void *)client;
-  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(conn->sock->sock);
 
   /* Execute the protocol */
   silc_protocol_execute(protocol, client->schedule, 0, 0);
-  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. */
+
+void silc_client_start_key_exchange(SilcClient client,
+                                   SilcClientConnection conn,
+                                   int fd)
+{
+  /* Allocate new socket connection object */
+  silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
+
+  /* Sometimes when doing quick reconnects the new socket may be same as
+     the old one and there might be pending stuff for the old socket. 
+     If new one is same then those pending sutff might cause problems.
+     Make sure they do not do that. */
+  silc_schedule_task_del_by_fd(client->schedule, fd);
+
+  conn->nickname = (client->nickname ? strdup(client->nickname) :
+                   strdup(client->username));
+
+  /* Resolve the remote hostname and IP address for our socket connection */
+  silc_socket_host_lookup(conn->sock, FALSE, silc_client_start_key_exchange_cb,
+                         conn, client->schedule);
 }
 
 /* Callback called when error has occurred during connecting to the server.
@@ -436,6 +457,8 @@ SILC_TASK_CALLBACK(silc_client_connect_failure)
   SilcClient client = (SilcClient)ctx->client;
 
   client->internal->ops->connect(client, ctx->sock->user_data, FALSE);
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
 }
 
@@ -493,10 +516,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
   silc_schedule_task_del(client->schedule, ctx->task);
   silc_free(ctx);
 
-  if (!silc_client_start_key_exchange(client, conn, fd)) {
-    silc_net_close_connection(fd);
-    client->internal->ops->connect(client, conn, FALSE);
-  }
+  silc_client_start_key_exchange(client, conn, fd);
 }
 
 /* Second part of the connecting to the server. This executed 
@@ -591,8 +611,7 @@ void silc_client_resolve_auth_method(bool success,
   proto_ctx->auth_meth = auth_meth;
 
   if (auth_data && auth_data_len) {
-    proto_ctx->auth_data = silc_calloc(auth_data_len, sizeof(*auth_data));
-    memcpy(proto_ctx->auth_data, auth_data, auth_data_len);
+    proto_ctx->auth_data = silc_memdup(auth_data, auth_data_len);
     proto_ctx->auth_data_len = auth_data_len;
   }
 
@@ -1040,7 +1059,8 @@ void silc_client_packet_parse_type(SilcClient client,
        
        if (proto_ctx->packet)
          silc_packet_context_free(proto_ctx->packet);
-       
+        if (proto_ctx->dest_id)
+          silc_free(proto_ctx->dest_id);
        proto_ctx->packet = silc_packet_context_dup(packet);
        proto_ctx->dest_id_type = packet->src_id_type;
        proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
@@ -1349,13 +1369,14 @@ void silc_client_close_connection(SilcClient client,
 
     /* Clear ID caches */
     if (conn->client_cache)
-      silc_idcache_del_all(conn->client_cache);
+      silc_idcache_free(conn->client_cache);
     if (conn->channel_cache)
-      silc_idcache_del_all(conn->channel_cache);
+      silc_idcache_free(conn->channel_cache);
     if (conn->server_cache)
-      silc_idcache_del_all(conn->server_cache);
+      silc_idcache_free(conn->server_cache);
 
-    /* Free data (my ID is freed in above silc_client_del_client) */
+    /* Free data (my ID is freed in above silc_client_del_client).
+       conn->nickname is freed when freeing the local_entry->nickname. */
     if (conn->remote_host)
       silc_free(conn->remote_host);
     if (conn->local_id_data)
@@ -1416,8 +1437,7 @@ void silc_client_disconnected_by_server(SilcClient client,
 
   SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
 
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
+  msg = silc_memdup(message->data, message->len);
   client->internal->ops->say(client, sock->user_data, 
                             SILC_CLIENT_MESSAGE_AUDIT, msg);
   silc_free(msg);
@@ -1440,13 +1460,24 @@ void silc_client_error_by_server(SilcClient client,
 {
   char *msg;
 
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
+  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;
+
+  silc_client_command_send(client, conn, SILC_COMMAND_NICK, 
+                          ++conn->cmd_ident, 1, 1, 
+                          client->nickname, strlen(client->nickname));
+}
+
 /* Processes the received new Client ID from server. Old Client ID is
    deleted from cache and new one is added. */
 
@@ -1491,21 +1522,27 @@ void silc_client_receive_new_id(SilcClient client,
     conn->local_entry->username = strdup(client->username);
   if (!conn->local_entry->hostname)
     conn->local_entry->hostname = strdup(client->hostname);
-  conn->local_entry->server = strdup(conn->remote_host);
+  if (!conn->local_entry->server)
+    conn->local_entry->server = strdup(conn->remote_host);
   conn->local_entry->id = conn->local_id;
   conn->local_entry->valid = TRUE;
-  
+  if (!conn->local_entry->channels)
+    conn->local_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, 
+                                                       NULL, NULL,
+                                                       NULL, NULL, NULL, 
+                                                       TRUE);
+
   /* Put it to the ID cache */
   silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id, 
                   (void *)conn->local_entry, 0, NULL);
 
   if (connecting) {
     /* Send NICK command if the nickname was set by the application (and is
-       not same as the username). */
+       not same as the username). Send this with little timeout. */
     if (client->nickname && strcmp(client->nickname, client->username))
-      silc_client_command_send(client, conn, SILC_COMMAND_NICK, 
-                              ++conn->cmd_ident, 1, 1, 
-                              client->nickname, strlen(client->nickname));
+      silc_schedule_task_add(client->schedule, 0,
+                            silc_client_send_auto_nick, conn,
+                            1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
     /* Issue INFO command to fetch the real server name and server information
        and other stuff. */
@@ -1523,73 +1560,23 @@ void silc_client_receive_new_id(SilcClient client,
   }
 }
 
-/* Processed received Channel ID for a channel. This is called when client
-   joins to channel and server replies with channel ID. The ID is cached. 
-   Returns the created channel entry. This is also called when received
-   channel ID in for example USERS command reply that we do not have. */
-
-SilcChannelEntry silc_client_new_channel_id(SilcClient client,
-                                           SilcSocketConnection sock,
-                                           char *channel_name,
-                                           uint32 mode, 
-                                           SilcIDPayload idp)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcChannelEntry channel;
-
-  SILC_LOG_DEBUG(("New channel ID"));
-
-  channel = silc_calloc(1, sizeof(*channel));
-  channel->channel_name = channel_name;
-  channel->id = silc_id_payload_get_id(idp);
-  channel->mode = mode;
-  silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
-
-  /* Put it to the ID cache */
-  silc_idcache_add(conn->channel_cache, channel->channel_name, 
-                  (void *)channel->id, (void *)channel, 0, NULL);
-
-  return channel;
-}
-
-/* Removes a client entry from all channel it has joined. This really is
-   a performance killer (client_entry should have pointers to channel 
-   entry list). */
+/* Removes a client entry from all channels it has joined. */
 
 void silc_client_remove_from_channels(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcClientEntry client_entry)
 {
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
-  SilcChannelEntry channel;
+  SilcHashTableList htl;
   SilcChannelUser chu;
 
-  if (!silc_idcache_get_all(conn->channel_cache, &list))
-    return;
-
-  silc_idcache_list_first(list, &id_cache);
-  channel = (SilcChannelEntry)id_cache->context;
-  
-  while (channel) {
-    
-    /* Remove client from channel */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == client_entry) {
-       silc_list_del(channel->clients, chu);
-       silc_free(chu);
-       break;
-      }
-    }
-
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
-    
-    channel = (SilcChannelEntry)id_cache->context;
+  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_idcache_list_free(list);
+  silc_hash_table_list_reset(&htl);
 }
 
 /* Replaces `old' client entries from all channels to `new' client entry.
@@ -1602,35 +1589,20 @@ void silc_client_replace_from_channels(SilcClient client,
                                       SilcClientEntry old,
                                       SilcClientEntry new)
 {
-  SilcIDCacheEntry id_cache;
-  SilcIDCacheList list;
-  SilcChannelEntry channel;
+  SilcHashTableList htl;
   SilcChannelUser chu;
 
-  if (!silc_idcache_get_all(conn->channel_cache, &list))
-    return;
-
-  silc_idcache_list_first(list, &id_cache);
-  channel = (SilcChannelEntry)id_cache->context;
-  
-  while (channel) {
-    
+  silc_hash_table_list(old->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
     /* Replace client entry */
-    silc_list_start(channel->clients);
-    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-      if (chu->client == old) {
-       chu->client = new;
-       break;
-      }
-    }
-
-    if (!silc_idcache_list_next(list, &id_cache))
-      break;
+    silc_hash_table_del(chu->client->channels, chu->channel);
+    silc_hash_table_del(chu->channel->user_list, chu->client);
     
-    channel = (SilcChannelEntry)id_cache->context;
+    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_idcache_list_free(list);
+  silc_hash_table_list_reset(&htl);
 }
 
 /* Registers failure timeout to process the received failure packet