Changed the version string variables to #defines.
[silc.git] / lib / silcclient / client.c
index becb0829d527ae34de7537aa173961e4375c699e..278fa3796644a2fee864081c10097356826552da 100644 (file)
@@ -47,7 +47,7 @@ void silc_client_resolve_auth_method(bool success,
 SilcClient silc_client_alloc(SilcClientOperations *ops, 
                             SilcClientParams *params,
                             void *application,
-                            const char *silc_version)
+                            const char *version_string)
 {
   SilcClient new_client;
 
@@ -58,7 +58,9 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
   new_client->internal->ops = ops;
   new_client->internal->params = 
     silc_calloc(1, sizeof(*new_client->internal->params));
-  new_client->internal->silc_client_version = strdup(silc_version);
+  if (!version_string)
+    version_string = silc_version_string;
+  new_client->internal->silc_client_version = strdup(version_string);
 
   if (params)
     memcpy(new_client->internal->params, params, sizeof(*params));
@@ -102,9 +104,17 @@ int silc_client_init(SilcClient client)
 {
   SILC_LOG_DEBUG(("Initializing client"));
 
+  /* Initialize the crypto library.  If application has done this already
+     this has no effect.  Also, we will not be overriding something
+     application might have registered earlier. */
+  silc_cipher_register_default();
+  silc_pkcs_register_default();
+  silc_hash_register_default();
+  silc_hmac_register_default();
+
   /* Initialize hash functions for client to use */
-  silc_hash_alloc("md5", &client->internal->md5hash);
-  silc_hash_alloc("sha1", &client->internal->sha1hash);
+  silc_hash_alloc("md5", &client->md5hash);
+  silc_hash_alloc("sha1", &client->sha1hash);
 
   /* Initialize none cipher */
   silc_cipher_alloc("none", &client->internal->none_cipher);
@@ -120,7 +130,7 @@ int silc_client_init(SilcClient client)
   /* Initialize the scheduler */
   client->schedule = 
     silc_schedule_init(client->internal->params->task_max ?
-                      client->internal->params->task_max : 200);
+                      client->internal->params->task_max : 200, client);
   if (!client->schedule)
     return FALSE;
 
@@ -194,6 +204,8 @@ silc_client_add_connection(SilcClient client,
   SilcClientConnection conn;
   int i;
 
+  SILC_LOG_DEBUG(("Adding new connection to %s:%d", hostname, port));
+
   conn = silc_calloc(1, sizeof(*conn));
 
   /* Initialize ID caches */
@@ -246,7 +258,8 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
       if (conn->pending_commands)
        silc_dlist_uninit(conn->pending_commands);
       silc_free(conn->remote_host);
-      silc_dlist_uninit(conn->ftp_sessions);
+      if (conn->ftp_sessions)
+        silc_dlist_uninit(conn->ftp_sessions);
       silc_free(conn);
 
       client->internal->conns[i] = NULL;
@@ -322,7 +335,8 @@ silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
                                     (void *)ctx, 0, 0, 
                                     SILC_TASK_FD,
                                     SILC_TASK_PRI_NORMAL);
-  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
+  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
+                             FALSE);
 
   ctx->sock = sock;
 
@@ -383,12 +397,14 @@ static void silc_client_start_key_exchange_cb(SilcSocketConnection sock,
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* 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);
+  if (conn->sock->hostname) {
+    silc_free(conn->remote_host);
+    conn->remote_host = strdup(conn->sock->hostname);
+  } else {
+    conn->sock->hostname = strdup(conn->remote_host);
+  }
   if (!conn->sock->ip)
-    conn->sock->ip = strdup(conn->remote_host);
+    conn->sock->ip = strdup(conn->sock->hostname);
   conn->sock->port = conn->remote_port;
 
   /* Allocate internal Key Exchange context. This is sent to the
@@ -410,7 +426,7 @@ static void silc_client_start_key_exchange_cb(SilcSocketConnection sock,
     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
                               "Error: Could not start key exchange protocol");
     silc_net_close_connection(conn->sock->sock);
-    client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_ERROR);
+    client->internal->ops->connected(client, conn, SILC_CLIENT_CONN_ERROR);
     return;
   }
   conn->sock->protocol = protocol;
@@ -456,8 +472,8 @@ void silc_client_start_key_exchange(SilcClient client,
                          conn, client->schedule);
 }
 
-/* Callback called when error has occurred during connecting to the server.
-   The `connect' client operation will be called. */
+/* Callback called when error has occurred during connecting (KE) to
+   the server.  The `connect' client operation will be called. */
 
 SILC_TASK_CALLBACK(silc_client_connect_failure)
 {
@@ -465,13 +481,27 @@ SILC_TASK_CALLBACK(silc_client_connect_failure)
     (SilcClientKEInternalContext *)context;
   SilcClient client = (SilcClient)ctx->client;
 
-  client->internal->ops->connect(client, ctx->sock->user_data, 
-                                SILC_CLIENT_CONN_ERROR);
+  client->internal->ops->connected(client, ctx->sock->user_data, 
+                                  SILC_CLIENT_CONN_ERROR);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
   silc_free(ctx);
 }
 
+/* Callback called when error has occurred during connecting (auth) to
+   the server.  The `connect' client operation will be called. */
+
+SILC_TASK_CALLBACK(silc_client_connect_failure_auth)
+{
+  SilcClientConnAuthInternalContext *ctx =
+    (SilcClientConnAuthInternalContext *)context;
+  SilcClient client = (SilcClient)ctx->client;
+
+  client->internal->ops->connected(client, ctx->sock->user_data, 
+                                  SILC_CLIENT_CONN_ERROR);
+  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. */
 
@@ -516,7 +546,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
       silc_free(ctx);
 
       /* Notify application of failure */
-      client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_ERROR);
+      client->internal->ops->connected(client, conn, SILC_CLIENT_CONN_ERROR);
       silc_client_del_connection(client, conn);
     }
     return;
@@ -588,6 +618,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
+  ctx->packet = NULL;
   silc_free(ctx);
   sock->protocol = NULL;
 
@@ -620,7 +651,21 @@ void silc_client_resolve_auth_method(bool success,
 
   proto_ctx->auth_meth = auth_meth;
 
-  if (auth_data && auth_data_len) {
+  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;
+    }
+
     proto_ctx->auth_data = silc_memdup(auth_data, auth_data_len);
     proto_ctx->auth_data_len = auth_data_len;
   }
@@ -654,21 +699,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     SILC_LOG_DEBUG(("Error during authentication protocol"));
-    silc_protocol_free(protocol);
-    if (ctx->auth_data)
-      silc_free(ctx->auth_data);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
-    conn->sock->protocol = NULL;
-    silc_socket_free(ctx->sock);
-
-    /* Notify application of failure */
-    silc_schedule_task_add(client->schedule, ctx->sock->sock,
-                          silc_client_connect_failure, ctx,
-                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-    return;
+    goto err;
   }
 
   if (conn->params.detach_data) {
@@ -680,12 +711,12 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     SilcUInt16 old_id_len;
 
     if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len))
-      return;
+      goto err;
 
     old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
     if (!old_client_id) {
       silc_free(old_id);
-      return;
+      goto err;
     }
 
     /* Generate authentication data that server will verify */
@@ -696,7 +727,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     if (!auth) {
       silc_free(old_client_id);
       silc_free(old_id);
-      return;
+      goto err;
     }
 
     packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
@@ -751,13 +782,27 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
   silc_protocol_free(protocol);
-  if (ctx->auth_data)
-    silc_free(ctx->auth_data);
+  silc_free(ctx->auth_data);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
   silc_socket_free(ctx->sock);
   silc_free(ctx);
   conn->sock->protocol = NULL;
+  return;
+
+ err:
+  silc_protocol_free(protocol);
+  silc_free(ctx->auth_data);
+  silc_free(ctx->dest_id);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  conn->sock->protocol = NULL;
+  silc_socket_free(ctx->sock);
+
+  /* Notify application of failure */
+  silc_schedule_task_add(client->schedule, ctx->sock->sock,
+                        silc_client_connect_failure_auth, ctx,
+                        0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* Internal routine that sends packet or marks packet to be sent. This
@@ -859,14 +904,14 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
         close the connection */
       if (SILC_IS_DISCONNECTING(sock)) {
        if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
-         client->internal->ops->disconnect(client, conn);
+         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->disconnect(client, conn);
+       client->internal->ops->disconnected(client, conn, 0, NULL);
       silc_client_close_connection_real(client, sock, conn);
       return;
     }
@@ -955,13 +1000,15 @@ void silc_client_packet_parse_type(SilcClient client,
   SilcBuffer buffer = packet->buffer;
   SilcPacketType type = packet->type;
 
-  SILC_LOG_DEBUG(("Parsing packet type %d", type));
+  SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(type)));
 
   /* Parse the packet type */
   switch(type) {
+
   case SILC_PACKET_DISCONNECT:
     silc_client_disconnected_by_server(client, sock, buffer);
     break;
+
   case SILC_PACKET_SUCCESS:
     /*
      * Success received for something. For now we can have only
@@ -971,6 +1018,7 @@ void silc_client_packet_parse_type(SilcClient client,
     if (sock->protocol)
       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
     break;
+
   case SILC_PACKET_FAILURE:
     /*
      * Failure received for some protocol. Set the protocol state to 
@@ -979,6 +1027,7 @@ void silc_client_packet_parse_type(SilcClient client,
      */
     silc_client_process_failure(client, sock, packet);
     break;
+
   case SILC_PACKET_REJECT:
     break;
 
@@ -1002,6 +1051,7 @@ void silc_client_packet_parse_type(SilcClient client,
      */
     silc_client_channel_message(client, sock, packet);
     break;
+
   case SILC_PACKET_CHANNEL_KEY:
     /*
      * Received key for a channel. By receiving this key the client will be
@@ -1017,12 +1067,21 @@ void silc_client_packet_parse_type(SilcClient client,
      */
     silc_client_private_message(client, sock, packet);
     break;
+
   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
     /*
      * Received private message key
      */
     break;
 
+  case SILC_PACKET_COMMAND:
+    /*
+     * Received command packet, a special case since normally client
+     * does not receive commands.
+     */
+    silc_client_command_process(client, sock, packet);
+    break;
+
   case SILC_PACKET_COMMAND_REPLY:
     /*
      * Recived reply for a command
@@ -1089,6 +1148,7 @@ void silc_client_packet_parse_type(SilcClient client,
                      "protocol active, packet dropped."));
     }
     break;
+
   case SILC_PACKET_KEY_EXCHANGE_2:
     if (sock->protocol && sock->protocol->protocol && 
        (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
@@ -1232,7 +1292,7 @@ void silc_client_packet_send(SilcClient client,
                             SilcHmac hmac,
                             unsigned char *data, 
                             SilcUInt32 data_len, 
-                            int force_send)
+                            bool force_send)
 {
   SilcPacketContext packetdata;
   const SilcBufferStruct packet;
@@ -1340,6 +1400,9 @@ void silc_client_close_connection_real(SilcClient client,
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (!sock && !conn)
+    return;
+
   if (!sock || (sock && conn->sock == sock))
     del = TRUE;
   if (!sock)
@@ -1350,7 +1413,6 @@ void silc_client_close_connection_real(SilcClient client,
 
   /* Unregister all tasks */
   silc_schedule_task_del_by_fd(client->schedule, sock->sock);
-  silc_schedule_task_del_by_fd(client->schedule, sock->sock);
 
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
@@ -1364,7 +1426,7 @@ void silc_client_close_connection_real(SilcClient client,
       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->connect client operation). */
+        (the ops->connected client operation). */
       return;
     } else {
       sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -1378,6 +1440,7 @@ void silc_client_close_connection_real(SilcClient client,
     /* Free all cache entries */
     SilcIDCacheList list;
     SilcIDCacheEntry entry;
+    SilcClientCommandPending *r;
     bool ret;
 
     if (silc_idcache_get_all(conn->client_cache, &list)) {
@@ -1429,8 +1492,6 @@ void silc_client_close_connection_real(SilcClient client,
       silc_hmac_free(conn->hmac_send);
     if (conn->hmac_receive)
       silc_hmac_free(conn->hmac_receive);
-    if (conn->pending_commands)
-      silc_dlist_uninit(conn->pending_commands);
     if (conn->rekey)
       silc_free(conn->rekey);
 
@@ -1442,6 +1503,12 @@ void silc_client_close_connection_real(SilcClient client,
 
     silc_client_ftp_free_sessions(client, conn);
 
+    silc_dlist_start(conn->pending_commands);
+    while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END)
+      silc_dlist_del(conn->pending_commands, r);
+    if (conn->pending_commands)
+      silc_dlist_uninit(conn->pending_commands);
+
     memset(conn, 0, sizeof(*conn));
     silc_client_del_connection(client, conn);
   }
@@ -1479,16 +1546,28 @@ SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later)
 
 void silc_client_disconnected_by_server(SilcClient client,
                                        SilcSocketConnection sock,
-                                       SilcBuffer message)
+                                       SilcBuffer packet)
 {
-  char *msg;
+  SilcClientConnection conn;
+  SilcStatus status;
+  char *message = NULL;
 
   SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
 
-  msg = silc_memdup(message->data, message->len);
-  client->internal->ops->say(client, sock->user_data, 
-                            SILC_CLIENT_MESSAGE_AUDIT, msg);
-  silc_free(msg);
+  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);
 
@@ -1539,9 +1618,9 @@ static void silc_client_resume_session_cb(SilcClient client,
   SilcBuffer sidp;
 
   /* Notify application that connection is created to server */
-  client->internal->ops->connect(client, conn, success ?
-                                SILC_CLIENT_CONN_SUCCESS_RESUME :
-                                SILC_CLIENT_CONN_ERROR);
+  client->internal->ops->connected(client, conn, success ?
+                                  SILC_CLIENT_CONN_SUCCESS_RESUME :
+                                  SILC_CLIENT_CONN_ERROR);
 
   if (success) {
     /* Issue INFO command to fetch the real server name and server
@@ -1597,8 +1676,6 @@ void silc_client_receive_new_id(SilcClient client,
   conn->local_entry->nickname = conn->nickname;
   if (!conn->local_entry->username)
     conn->local_entry->username = strdup(client->username);
-  if (!conn->local_entry->hostname)
-    conn->local_entry->hostname = strdup(client->hostname);
   if (!conn->local_entry->server)
     conn->local_entry->server = strdup(conn->remote_host);
   conn->local_entry->id = conn->local_id;
@@ -1614,9 +1691,19 @@ void silc_client_receive_new_id(SilcClient client,
                   (void *)conn->local_entry, 0, NULL);
 
   if (connecting) {
-    if (!conn->params.detach_data) {
-      SilcBuffer sidp;
+    SilcBuffer sidp;
 
+    /* Issue IDENTIFY command for itself to get resolved hostname
+       correctly from server. */
+    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                silc_client_command_reply_identify_i, 0, 
+                                ++conn->cmd_ident);
+    sidp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
+    silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                            conn->cmd_ident, 1, 5, sidp->data, sidp->len);
+    silc_buffer_free(sidp);
+
+    if (!conn->params.detach_data) {
       /* Send NICK command if the nickname was set by the application (and is
         not same as the username). Send this with little timeout. */
       if (client->nickname && strcmp(client->nickname, client->username))
@@ -1626,7 +1713,7 @@ void silc_client_receive_new_id(SilcClient client,
 
       /* Notify application of successful connection. We do it here now that
         we've received the Client ID and are allowed to send traffic. */
-      client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_SUCCESS);
+      client->internal->ops->connected(client, conn, SILC_CLIENT_CONN_SUCCESS);
 
       /* Issue INFO command to fetch the real server name and server
         information and other stuff. */