updates.
[silc.git] / lib / silcclient / client.c
index 8c950acb0f8baa70ea47720479f0bf730aef3145..4676dc514305f4d82f77f471497965fa4d8c95e6 100644 (file)
@@ -148,6 +148,20 @@ void silc_client_run(SilcClient client)
   silc_schedule(client->schedule);
 }
 
+/* Runs the client and returns immeadiately. This function is used when
+   the SILC Client object indicated by the `client' is run under some
+   other scheduler, or event loop or main loop.  On GUI applications,
+   for example this may be desired to use to run the client under the
+   GUI application's main loop.  Typically the GUI application would
+   register an idle task that calls this function multiple times in
+   a second to quickly process the SILC specific data. */
+
+void silc_client_run_one(SilcClient client)
+{
+  /* Run the scheduler once. */
+  silc_schedule_one(client->schedule, 0);
+}
+
 static void silc_client_entry_destructor(SilcIDCache cache,
                                         SilcIDCacheEntry entry)
 {
@@ -357,7 +371,7 @@ bool silc_client_start_key_exchange(SilcClient client,
   silc_schedule_task_del_by_fd(client->schedule, fd);
 
   conn->nickname = strdup(client->username);
-  conn->sock->hostname = conn->remote_host;
+  conn->sock->hostname = strdup(conn->remote_host);
   conn->sock->ip = strdup(conn->remote_host);
   conn->sock->port = conn->remote_port;
 
@@ -397,6 +411,19 @@ bool silc_client_start_key_exchange(SilcClient client,
   return TRUE;
 }
 
+/* Callback called when error has occurred during connecting to the server.
+   The `connect' client operation will be called. */
+
+SILC_TASK_CALLBACK(silc_client_connect_failure)
+{
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)context;
+  SilcClient client = (SilcClient)ctx->client;
+
+  client->ops->connect(client, ctx->sock->user_data, FALSE);
+  silc_free(ctx);
+}
+
 /* Start of the connection to the remote server. This is called after
    succesful TCP/IP connection has been established to the remote host. */
 
@@ -485,8 +512,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
     silc_socket_free(ctx->sock);
 
     /* Notify application of failure */
-    client->ops->connect(client, ctx->sock->user_data, FALSE);
-    silc_free(ctx);
+    silc_schedule_task_add(client->schedule, ctx->sock->sock,
+                          silc_client_connect_failure, ctx,
+                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -591,8 +619,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     silc_socket_free(ctx->sock);
 
     /* Notify application of failure */
-    client->ops->connect(client, ctx->sock->user_data, FALSE);
-    silc_free(ctx);
+    silc_schedule_task_add(client->schedule, ctx->sock->sock,
+                          silc_client_connect_failure, ctx,
+                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -778,7 +807,7 @@ static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
   SilcPacketContext *packet = parser_context->packet;
   SilcPacketType ret;
 
-  if (conn && conn->hmac_receive)
+  if (conn && conn->hmac_receive && conn->sock == sock)
     conn->psn_receive = parser_context->packet->sequence + 1;
 
   /* Parse the packet immediately */
@@ -797,9 +826,10 @@ static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
      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)) {
+  if ((ret == SILC_PACKET_REKEY || ret == SILC_PACKET_REKEY_DONE) ||
+      (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);
@@ -1020,7 +1050,7 @@ void silc_client_packet_parse_type(SilcClient client,
        */
       SilcIDPayload idp;
 
-      idp = silc_id_payload_parse(buffer);
+      idp = silc_id_payload_parse(buffer->data, buffer->len);
       if (!idp)
        break;
       if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
@@ -1231,6 +1261,8 @@ void silc_client_close_connection(SilcClient client,
 {
   int del = FALSE;
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (!sock || (sock && conn->sock == sock))
     del = TRUE;
   if (!sock)
@@ -1339,6 +1371,22 @@ void silc_client_close_connection(SilcClient client,
   silc_socket_free(sock);
 }
 
+/* Called when we receive disconnection packet from server. This 
+   closes our end properly and displays the reason of the disconnection
+   on the screen. */
+
+SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later)
+{
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock;
+
+  SILC_CLIENT_GET_SOCK(client, fd, sock);
+  if (sock == NULL)
+    return;
+
+  silc_client_close_connection(client, sock, sock->user_data);
+}
+
 /* Called when we receive disconnection packet from server. This 
    closes our end properly and displays the reason of the disconnection
    on the screen. */
@@ -1357,7 +1405,12 @@ void silc_client_disconnected_by_server(SilcClient client,
   silc_free(msg);
 
   SILC_SET_DISCONNECTED(sock);
-  silc_client_close_connection(client, sock, sock->user_data);
+
+  /* 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. 
@@ -1384,6 +1437,7 @@ void silc_client_receive_new_id(SilcClient client,
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   int connecting = FALSE;
+  SilcClientID *client_id = silc_id_payload_get_id(idp);
   SilcBuffer sidp;
 
   if (!conn->local_entry)
@@ -1391,6 +1445,12 @@ void silc_client_receive_new_id(SilcClient client,
 
   /* Delete old ID from ID cache */
   if (conn->local_id) {
+    /* Check whether they are different */
+    if (SILC_ID_CLIENT_COMPARE(conn->local_id, client_id)) {
+      silc_free(client_id);
+      return;
+    }
+
     silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
     silc_free(conn->local_id);
   }
@@ -1400,7 +1460,7 @@ void silc_client_receive_new_id(SilcClient client,
   if (conn->local_id_data)
     silc_free(conn->local_id_data);
 
-  conn->local_id = silc_id_payload_get_id(idp);
+  conn->local_id = client_id;
   conn->local_id_data = silc_id_payload_get_data(idp);
   conn->local_id_data_len = silc_id_payload_get_len(idp);;
 
@@ -1418,19 +1478,20 @@ void silc_client_receive_new_id(SilcClient client,
   
   /* Put it to the ID cache */
   silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id, 
-                  (void *)conn->local_entry, FALSE);
-
-  /* Issue INFO command to fetch the real server name and server information
-     and other stuff. */
-  sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
-  silc_client_send_command(client, conn, SILC_COMMAND_INFO,
-                          ++conn->cmd_ident, 1, 2, sidp->data, sidp->len);
-  silc_buffer_free(sidp);
-
-  /* Notify application of successful connection. We do it here now that
-     we've received the Client ID and are allowed to send traffic. */
-  if (connecting)
+                  (void *)conn->local_entry, 0, NULL);
+
+  if (connecting) {
+    /* Issue INFO comqmand to fetch the real server name and server information
+       and other stuff. */
+    sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
+    silc_client_send_command(client, conn, SILC_COMMAND_INFO,
+                            ++conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+    silc_buffer_free(sidp);
+
+    /* Notify application of successful connection. We do it here now that
+       we've received the Client ID and are allowed to send traffic. */
     client->ops->connect(client, conn, TRUE);
+  }
 }
 
 /* Processed received Channel ID for a channel. This is called when client
@@ -1457,7 +1518,7 @@ SilcChannelEntry silc_client_new_channel_id(SilcClient client,
 
   /* Put it to the ID cache */
   silc_idcache_add(conn->channel_cache, channel->channel_name, 
-                  (void *)channel->id, (void *)channel, FALSE);
+                  (void *)channel->id, (void *)channel, 0, NULL);
 
   return channel;
 }