updates.
[silc.git] / lib / silcclient / client.c
index 1cd84f2f6b9d34cdce3173a63226e475a107a46f..4dd828f89bce4d088871d93af1755568c0b59c2b 100644 (file)
@@ -54,6 +54,9 @@ SilcClient silc_client_alloc(SilcClientOperations *ops, void *application)
 void silc_client_free(SilcClient client)
 {
   if (client) {
+    if (client->rng)
+      silc_rng_free(client->rng);
+
     silc_free(client);
   }
 }
@@ -76,7 +79,7 @@ int silc_client_init(SilcClient client)
   /* Initialize random number generator */
   client->rng = silc_rng_alloc();
   silc_rng_init(client->rng);
-  silc_math_primegen_init(); /* XXX */
+  silc_rng_global_init(client->rng);
 
   /* Register protocols */
   silc_client_protocols_register();
@@ -271,6 +274,7 @@ int silc_client_start_key_exchange(SilcClient client,
 
   conn->nickname = strdup(client->username);
   conn->sock->hostname = conn->remote_host;
+  conn->sock->ip = strdup(conn->remote_host);
   conn->sock->port = conn->remote_port;
 
   /* Allocate internal Key Exchange context. This is sent to the
@@ -412,7 +416,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
                                    &proto_ctx->auth_data_len))
     {
       /* XXX do AUTH_REQUEST resolcing with server */
-      proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
+      proto_ctx->auth_meth = SILC_AUTH_NONE;
     }
 
   /* Free old protocol as it is finished now */
@@ -496,9 +500,6 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
   conn->remote_id_data_len = SILC_ID_SERVER_LEN;
 
-  /* Notify application of successful connection */
-  client->ops->connect(client, conn, TRUE);
-
   silc_protocol_free(protocol);
   if (ctx->auth_data)
     silc_free(ctx->auth_data);
@@ -602,8 +603,7 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
        return;
       }
       
-      client->ops->say(client, conn, "Connection closed: premature EOF");
-      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+      SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
       client->ops->disconnect(client, conn);
       silc_client_close_connection(client, sock);
       return;
@@ -850,6 +850,13 @@ void silc_client_packet_parse_type(SilcClient client,
       break;
     }
 
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat packet
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    break;
+
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1176,8 +1183,7 @@ void silc_client_close_connection(SilcClient client,
   silc_net_close_connection(sock->sock);
 
   client->ops->say(client, sock->user_data,
-                  "Closed connection to host %s", sock->hostname ?
-                  sock->hostname : sock->ip);
+                  "Closed connection to host %s", sock->hostname);
 
   /* Free everything */
   if (sock->user_data) {
@@ -1277,7 +1283,34 @@ static void silc_client_notify_by_server_pending(void *context)
 {
   SilcPacketContext *p = (SilcPacketContext *)context;
   silc_client_notify_by_server(p->context, p->sock, p);
-  silc_packet_context_free(p);
+}
+
+/* Destructor for the pending command callback */
+
+static void silc_client_notify_by_server_destructor(void *context)
+{
+  silc_packet_context_free((SilcPacketContext *)context);
+}
+
+/* Resolve client information from server by Client ID. */
+
+static void silc_client_notify_by_server_resolve(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcPacketContext *packet,
+                                                SilcClientID *client_id)
+{
+  SilcPacketContext *p = silc_packet_context_dup(packet);
+  SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+
+  p->context = (void *)client;
+  p->sock = conn->sock;
+
+  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                             silc_client_notify_by_server_destructor,
+                             silc_client_notify_by_server_pending, p);
+  silc_buffer_free(idp);
 }
 
 /* Received notify message from server */
@@ -1334,13 +1367,9 @@ void silc_client_notify_by_server(SilcClient client,
       goto out;
 
     /* Find Client entry and if not found query it */
-    client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
+    client_entry = silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry) {
-      SilcPacketContext *p = silc_packet_context_dup(packet);
-      p->context = (void *)client;
-      p->sock = sock;
-      silc_client_command_pending(conn, SILC_COMMAND_WHOIS, SILC_IDLIST_IDENT,
-                                 silc_client_notify_by_server_pending, p);
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
       goto out;
     }
 
@@ -1384,21 +1413,28 @@ void silc_client_notify_by_server(SilcClient client,
       goto out;
 
     /* Find Client entry and if not found query it */
-    client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
+    client_entry = silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry) {
-      SilcPacketContext *p = silc_packet_context_dup(packet);
-      p->context = (void *)client;
-      p->sock = sock;
-      silc_client_command_pending(conn, SILC_COMMAND_WHOIS, SILC_IDLIST_IDENT, 
-                                 silc_client_notify_by_server_pending, p);
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
       goto out;
     }
 
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
+    /* If nickname or username hasn't been resolved, do so */
+    if (!client_entry->nickname || !client_entry->username) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* Get Channel 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);
     if (!channel_id)
       goto out;
+
+    /* Get channel entry */
     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
                                     SILC_ID_CHANNEL, &id_cache))
       break;
@@ -1436,7 +1472,7 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find Client entry */
     client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry)
       goto out;
 
@@ -1483,7 +1519,7 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find Client entry */
     client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry)
       goto out;
 
@@ -1494,8 +1530,13 @@ void silc_client_notify_by_server(SilcClient client,
     silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
                           client_entry->id);
 
+    /* Get signoff message */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp_len > 128)
+      tmp = NULL;
+
     /* Notify application */
-    client->ops->notify(client, conn, type, client_entry);
+    client->ops->notify(client, conn, type, client_entry, tmp);
 
     /* Free data */
     if (client_entry->nickname)
@@ -1526,7 +1567,7 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find Client entry */
     client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry)
       goto out;
 
@@ -1575,15 +1616,12 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find Client entry and if not found query it */
     client_entry2 = 
-      silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry2) {
-      SilcPacketContext *p = silc_packet_context_dup(packet);
-      p->context = (void *)client;
-      p->sock = sock;
-      silc_client_command_pending(conn, SILC_COMMAND_WHOIS, SILC_IDLIST_IDENT,
-                                 silc_client_notify_by_server_pending, p);
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
       goto out;
     }
+    silc_free(client_id);
 
     /* Get old Client ID */
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
@@ -1596,7 +1634,7 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find old Client entry */
     client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry)
       goto out;
 
@@ -1640,7 +1678,7 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find Client entry */
     client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry)
       goto out;
 
@@ -1687,7 +1725,7 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find Client entry */
     client_entry = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry)
       goto out;
 
@@ -1710,7 +1748,7 @@ void silc_client_notify_by_server(SilcClient client,
 
     /* Find target Client entry */
     client_entry2 = 
-      silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
+      silc_idlist_get_client_by_id(client, conn, client_id);
     if (!client_entry2)
       goto out;
 
@@ -1754,6 +1792,45 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application */
     client->ops->notify(client, conn, type, tmp);
     break;
+
+  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.
+     */
+
+    /* 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);
+    if (!channel_id)
+      goto out;
+    
+    /* Get the channel entry */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Free the old ID */
+    silc_free(channel_id);
+    silc_free(channel->id);
+
+    /* 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);
+    if (!channel->id)
+      goto out;
+
+    id_cache->id = (void *)channel->id;
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, channel, channel);
+    break;
     
   default:
     break;
@@ -1775,6 +1852,10 @@ void silc_client_receive_new_id(SilcClient client,
                                SilcIDPayload idp)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  int connecting = FALSE;
+
+  if (!conn->local_entry)
+    connecting = TRUE;
 
   /* Delete old ID from ID cache */
   silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
@@ -1806,6 +1887,11 @@ void silc_client_receive_new_id(SilcClient client,
   /* Put it to the ID cache */
   silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
                   conn->local_id, (void *)conn->local_entry, TRUE);
+
+  /* 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)
+    client->ops->connect(client, conn, TRUE);
 }
 
 /* Processed received Channel ID for a channel. This is called when client
@@ -1929,7 +2015,7 @@ void silc_client_channel_message(SilcClient client,
   SilcChannelUser chu;
   SilcIDCacheEntry id_cache = NULL;
   SilcClientID *client_id = NULL;
-  char *nickname;
+  int found = FALSE;
 
   /* Sanity checks */
   if (packet->dst_id_type != SILC_ID_CHANNEL)
@@ -1963,20 +2049,18 @@ void silc_client_channel_message(SilcClient client,
   if (!payload)
     goto out;
 
-  /* Find nickname */
-  nickname = "[unknown]";
+  /* Find client entry */
   silc_list_start(channel->clients);
   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
     if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
-      nickname = chu->client->nickname;
+      found = TRUE;
       break;
     }
   }
 
   /* Pass the message to application */
-  client->ops->channel_message(client, conn, nickname,
-                              channel->channel_name,
-                              silc_channel_get_data(payload, NULL));
+  client->ops->channel_message(client, conn, found ? chu->client : NULL,
+                              channel, silc_channel_get_data(payload, NULL));
 
  out:
   if (id)
@@ -1996,10 +2080,16 @@ void silc_client_private_message(SilcClient client,
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcBuffer buffer = packet->buffer;
+  SilcIDCacheEntry id_cache;
+  SilcClientID *remote_id = NULL;
+  SilcClientEntry remote_client;
   unsigned short nick_len;
-  unsigned char *nickname, *message;
+  unsigned char *nickname, *message = NULL;
   int ret;
 
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    goto out;
+
   /* Get nickname */
   ret = silc_buffer_unformat(buffer, 
                             SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
@@ -2012,47 +2102,39 @@ void silc_client_private_message(SilcClient client,
   message = silc_calloc(buffer->len + 1, sizeof(char));
   memcpy(message, buffer->data, buffer->len);
 
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    goto out;
+
+  /* Check whether we know this client already */
+  if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
+                                  SILC_ID_CLIENT, &id_cache))
+    {
+      /* Allocate client entry */
+      remote_client = silc_calloc(1, sizeof(*remote_client));
+      remote_client->id = remote_id;
+      silc_parse_nickname(nickname, &remote_client->nickname, 
+                         &remote_client->server, &remote_client->num);
+      
+      /* Save the client to cache */
+      silc_idcache_add(conn->client_cache, remote_client->nickname,
+                      SILC_ID_CLIENT, remote_client->id, remote_client, 
+                      TRUE);
+    } else {
+      remote_client = (SilcClientEntry)id_cache->context;
+    }
+
   /* Pass the private message to application */
-  client->ops->private_message(client, conn, nickname, message);
+  client->ops->private_message(client, conn, remote_client, message);
 
   /* See if we are away (gone). If we are away we will reply to the
      sender with the set away message. */
   if (conn->away && conn->away->away) {
-    SilcClientID *remote_id;
-    SilcClientEntry remote_client;
-    SilcIDCacheEntry id_cache;
-
-    if (packet->src_id_type != SILC_ID_CLIENT)
-      goto out;
-
-    remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
-                              SILC_ID_CLIENT);
-    if (!remote_id)
-      goto out;
-
     /* If it's me, ignore */
     if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
       goto out;
 
-    /* Check whether we know this client already */
-    if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
-                                    SILC_ID_CLIENT, &id_cache))
-      {
-       /* Allocate client entry */
-       remote_client = silc_calloc(1, sizeof(*remote_client));
-       remote_client->id = remote_id;
-       silc_parse_nickname(nickname, &remote_client->nickname, 
-                           &remote_client->server, &remote_client->num);
-
-       /* Save the client to cache */
-       silc_idcache_add(conn->client_cache, remote_client->nickname,
-                        SILC_ID_CLIENT, remote_client->id, remote_client, 
-                        TRUE);
-      } else {
-       silc_free(remote_id);
-       remote_client = (SilcClientEntry)id_cache->context;
-      }
-
     /* Send the away message */
     silc_client_packet_send_private_message(client, sock, remote_client,
                                            conn->away->away,
@@ -2060,8 +2142,13 @@ void silc_client_private_message(SilcClient client,
   }
 
  out:
-  memset(message, 0, buffer->len);
-  silc_free(message);
+  if (remote_id)
+    silc_free(remote_id);
+
+  if (message) {
+    memset(message, 0, buffer->len);
+    silc_free(message);
+  }
   silc_free(nickname);
 }