More client library rewrites.
authorPekka Riikonen <priikone@silcnet.org>
Sun, 3 Dec 2006 21:03:06 +0000 (21:03 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sun, 3 Dec 2006 21:03:06 +0000 (21:03 +0000)
13 files changed:
lib/silcclient/client.c
lib/silcclient/client_channel.c
lib/silcclient/client_channel.h
lib/silcclient/client_connect.c
lib/silcclient/client_entry.c
lib/silcclient/client_internal.h
lib/silcclient/client_keyagr.c
lib/silcclient/client_notify.c
lib/silcclient/client_prvmsg.c
lib/silcclient/client_register.c
lib/silcclient/command.c
lib/silcclient/command_reply.c
lib/silcclient/silcclient.h

index 9a64a6c0364c6a2282e85d94b9da706857f4381a..bd354495316759bc9ce72cc3d5b615e6eef483cf 100644 (file)
@@ -293,6 +293,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
   case SILC_PACKET_NEW_ID:
     /** New ID */
     silc_fsm_next(fsm, silc_client_new_id);
+    break;
 
   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
     /* Reply to connection authentication request to resolve authentication
@@ -320,7 +321,9 @@ SILC_FSM_STATE(silc_client_connection_st_close)
 
   SILC_LOG_DEBUG(("Closing remote connection"));
 
-  /* XXX abort any ongoing events (protocols) */
+  /* Abort ongoing events */
+  if (conn->internal->op)
+    silc_async_abort(conn->internal->op, NULL, NULL);
 
   /* Close connection */
   silc_packet_stream_destroy(conn->stream);
@@ -457,6 +460,9 @@ silc_client_add_connection(SilcClient client,
     silc_free(conn);
     return NULL;
   }
+  conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
+  silc_mutex_alloc(&conn->internal->lock);
+  silc_atomic_init16(&conn->internal->cmd_ident, 0);
 
   if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
     silc_free(conn);
@@ -498,96 +504,66 @@ silc_client_add_connection(SilcClient client,
   return conn;
 }
 
-/* Removes connection from client. Frees all memory. */
+/* Deletes connection.  This is always called from the connection machine
+   destructor.  Do not call this directly other places. */
 
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 {
-#if 0
-  SilcClientConnection c;
-  SilcIDCacheList list;
+  SilcList list;
   SilcIDCacheEntry entry;
-  SilcClientCommandPending *r;
-  SilcBool ret;
-
-  silc_dlist_start(client->internal->conns);
-  while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
-    if (c != conn)
-      continue;
-
-    /* Free all cache entries */
-    if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
-      ret = silc_idcache_list_first(list, &entry);
-      while (ret) {
-       silc_client_del_client(client, conn, entry->context);
-       ret = silc_idcache_list_next(list, &entry);
-      }
-      silc_idcache_list_free(list);
-    }
-
-    if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
-      ret = silc_idcache_list_first(list, &entry);
-      while (ret) {
-       silc_client_del_channel(client, conn, entry->context);
-       ret = silc_idcache_list_next(list, &entry);
-      }
-      silc_idcache_list_free(list);
-    }
-
-    if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
-      ret = silc_idcache_list_first(list, &entry);
-      while (ret) {
-       silc_client_del_server(client, conn, entry->context);
-       ret = silc_idcache_list_next(list, &entry);
-      }
-      silc_idcache_list_free(list);
-    }
-
-    /* Clear ID caches */
-    if (conn->internal->client_cache)
-      silc_idcache_free(conn->internal->client_cache);
-    if (conn->internal->channel_cache)
-      silc_idcache_free(conn->internal->channel_cache);
-    if (conn->internal->server_cache)
-      silc_idcache_free(conn->internal->server_cache);
-
-    /* Free data (my ID is freed in above silc_client_del_client).
-       conn->nickname is freed when freeing the local_entry->nickname. */
-    silc_free(conn->remote_host);
-    silc_free(conn->local_id_data);
-    if (conn->internal->send_key)
-      silc_cipher_free(conn->internal->send_key);
-    if (conn->internal->receive_key)
-      silc_cipher_free(conn->internal->receive_key);
-    if (conn->internal->hmac_send)
-      silc_hmac_free(conn->internal->hmac_send);
-    if (conn->internal->hmac_receive)
-      silc_hmac_free(conn->internal->hmac_receive);
-    silc_free(conn->internal->rekey);
-
-    if (conn->internal->active_session) {
-      if (conn->sock)
-       conn->sock->user_data = NULL;
-      silc_client_ftp_session_free(conn->internal->active_session);
-      conn->internal->active_session = NULL;
-    }
-
-    silc_client_ftp_free_sessions(client, conn);
+  SilcFSMThread thread;
+  SilcClientCommandContext cmd;
 
-    if (conn->internal->pending_commands) {
-      silc_dlist_start(conn->internal->pending_commands);
-      while ((r = silc_dlist_get(conn->internal->pending_commands))
-            != SILC_LIST_END)
-       silc_dlist_del(conn->internal->pending_commands, r);
-      silc_dlist_uninit(conn->internal->pending_commands);
-    }
+  SILC_LOG_DEBUG(("Freeing connection %p", conn));
 
-    silc_free(conn->internal);
-    memset(conn, 0, sizeof(*conn));
-    silc_free(conn);
-
-    silc_dlist_del(client->internal->conns, conn);
+  /* Free all cache entries */
+  if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
+    silc_list_start(list);
+    while ((entry = silc_list_get(list)))
+      silc_client_del_client(client, conn, entry->context);
   }
-#endif /* 0 */
+  if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
+    silc_list_start(list);
+    while ((entry = silc_list_get(list)))
+      silc_client_del_channel(client, conn, entry->context);
+  }
+  if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
+    silc_list_start(list);
+    while ((entry = silc_list_get(list)))
+      silc_client_del_server(client, conn, entry->context);
+  }
+
+  /* Free ID caches */
+  if (conn->internal->client_cache)
+    silc_idcache_free(conn->internal->client_cache);
+  if (conn->internal->channel_cache)
+    silc_idcache_free(conn->internal->channel_cache);
+  if (conn->internal->server_cache)
+    silc_idcache_free(conn->internal->server_cache);
+
+  /* Free thread pool */
+  silc_list_start(conn->internal->thread_pool);
+  while ((thread = silc_list_get(conn->internal->thread_pool)))
+    silc_fsm_free(thread);
+
+  /* Free pending commands */
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd = silc_list_get(conn->internal->pending_commands)))
+    silc_client_command_free(cmd);
+
+  silc_free(conn->remote_host);
+  silc_buffer_free(conn->internal->local_idp);
+  silc_buffer_free(conn->internal->remote_idp);
+  silc_mutex_free(conn->internal->lock);
+  if (conn->internal->hash)
+    silc_hash_free(conn->internal->hash);
+  if (conn->internal->sha1hash)
+    silc_hash_free(conn->internal->sha1hash);
+  silc_atomic_uninit16(&conn->internal->cmd_ident);
+
+  silc_free(conn->internal);
+  memset(conn, 'F', sizeof(*conn));
+  silc_free(conn);
 }
 
 
@@ -653,10 +629,6 @@ SilcBool silc_client_connect_to_client(SilcClient client,
     return FALSE;
   }
 
-  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                            "Connecting to port %d of client host %s",
-                            port, remote_host);
-
   /* Signal connection machine to start connecting */
   conn->internal->connect = TRUE;
   return TRUE;
@@ -681,8 +653,11 @@ SilcBool silc_client_key_exchange(SilcClient client,
   if (!client || !stream)
     return FALSE;
 
-  if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port))
+  if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
+    SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
     return FALSE;
+  }
 
   /* Add new connection */
   conn = silc_client_add_connection(client, conn_type, params,
@@ -704,7 +679,11 @@ SilcBool silc_client_key_exchange(SilcClient client,
 void silc_client_close_connection(SilcClient client,
                                  SilcClientConnection conn)
 {
+  SILC_LOG_DEBUG(("Closing connection %p", conn));
 
+  /* Signal to close connection */
+  conn->internal->disconnected = TRUE;
+  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
 }
 
 #if 0
@@ -1010,9 +989,6 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
   if (params)
     memcpy(new_client->internal->params, params, sizeof(*params));
 
-  if (!new_client->internal->params->task_max)
-    new_client->internal->params->task_max = 200;
-
   if (!new_client->internal->params->rekey_secs)
     new_client->internal->params->rekey_secs = 3600;
 
@@ -1030,22 +1006,23 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
 
 void silc_client_free(SilcClient client)
 {
-  if (client) {
-    if (client->rng)
-      silc_rng_free(client->rng);
-
-    if (!client->internal->params->dont_register_crypto_library) {
-      silc_cipher_unregister_all();
-      silc_pkcs_unregister_all();
-      silc_hash_unregister_all();
-      silc_hmac_unregister_all();
-    }
+  if (client->rng)
+    silc_rng_free(client->rng);
 
-    silc_free(client->internal->params);
-    silc_free(client->internal->silc_client_version);
-    silc_free(client->internal);
-    silc_free(client);
+  if (!client->internal->params->dont_register_crypto_library) {
+    silc_cipher_unregister_all();
+    silc_pkcs_unregister_all();
+    silc_hash_unregister_all();
+    silc_hmac_unregister_all();
   }
+
+  silc_free(client->username);
+  silc_free(client->hostname);
+  silc_free(client->realname);
+  silc_free(client->internal->params);
+  silc_free(client->internal->silc_client_version);
+  silc_free(client->internal);
+  silc_free(client);
 }
 
 /* Initializes the client. This makes all the necessary steps to make
@@ -1060,11 +1037,13 @@ SilcBool silc_client_init(SilcClient client, const char *username,
   if (!client)
     return FALSE;
 
-  if (!username || !hostname || !realname) {
+  if (!username || !hostname) {
     SILC_LOG_ERROR(("Username, hostname and realname must be given to "
                    "silc_client_init"));
     return FALSE;
   }
+  if (!realname)
+    realname = username;
 
   /* Validate essential strings */
   if (!silc_identifier_verify(username, strlen(username),
@@ -1104,13 +1083,13 @@ SilcBool silc_client_init(SilcClient client, const char *username,
 
   /* Initialize random number generator */
   client->rng = silc_rng_alloc();
+  if (!client->rng)
+    return FALSE;
   silc_rng_init(client->rng);
   silc_rng_global_init(client->rng);
 
   /* Initialize the scheduler */
-  client->schedule =
-    silc_schedule_init(client->internal->params->task_max ?
-                      client->internal->params->task_max : 0, client);
+  client->schedule = silc_schedule_init(0, client);
   if (!client->schedule)
     return FALSE;
 
@@ -1121,19 +1100,15 @@ SilcBool silc_client_init(SilcClient client, const char *username,
   if (!client->internal->packet_engine)
     return FALSE;
 
-  /* Initialize FSM */
-  if (!silc_fsm_init(&client->internal->fsm, client, NULL, NULL,
-                    client->schedule))
-    return FALSE;
-  silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
-
   /* Allocate client lock */
   silc_mutex_alloc(&client->internal->lock);
 
   /* Register commands */
   silc_client_commands_register(client);
 
-  /* Start the client machine */
+  /* Initialize and start the client FSM */
+  silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
+  silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
 
   /* Signal the application when we are running */
index 37cd5a78e38bb499382e9e60c046f9b5c4da676c..28cc6147099f53b100bb654e1ef5af0a11bfb30a 100644 (file)
@@ -673,7 +673,9 @@ SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
 /* Adds client to channel.  Returns TRUE if user was added or is already
    added to the channel, FALSE on error. */
 
-SilcBool silc_client_add_to_channel(SilcChannelEntry channel,
+SilcBool silc_client_add_to_channel(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcChannelEntry channel,
                                    SilcClientEntry client_entry,
                                    SilcUInt32 cumode)
 {
@@ -689,6 +691,10 @@ SilcBool silc_client_add_to_channel(SilcChannelEntry channel,
   chu->client = client_entry;
   chu->channel = channel;
   chu->mode = cumode;
+
+  silc_client_ref_client(client, conn, client_entry);
+  silc_client_ref_channel(client, conn, channel);
+
   silc_hash_table_add(channel->user_list, client_entry, chu);
   silc_hash_table_add(client_entry->channels, channel, chu);
 
@@ -697,7 +703,9 @@ SilcBool silc_client_add_to_channel(SilcChannelEntry channel,
 
 /* Removes client from a channel */
 
-SilcBool silc_client_remove_from_channel(SilcChannelEntry channel,
+SilcBool silc_client_remove_from_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel,
                                         SilcClientEntry client_entry)
 {
   SilcChannelUser chu;
@@ -710,6 +718,9 @@ SilcBool silc_client_remove_from_channel(SilcChannelEntry channel,
   silc_hash_table_del(chu->channel->user_list, chu->client);
   silc_free(chu);
 
+  silc_client_unref_client(client, conn, client_entry);
+  silc_client_unref_channel(client, conn, channel);
+
   return TRUE;
 }
 
@@ -726,8 +737,32 @@ void silc_client_remove_from_channels(SilcClient client,
   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_client_unref_client(client, conn, chu->client);
+    silc_client_unref_channel(client, conn, chu->channel);
     silc_free(chu);
   }
 
   silc_hash_table_list_reset(&htl);
 }
+
+/* Empties channel from users. */
+
+void silc_client_empty_channel(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcChannelEntry channel)
+{
+  SilcHashTableList htl;
+  SilcChannelUser chu;
+
+  silc_hash_table_list(channel->user_list, &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_client_unref_client(client, conn, chu->client);
+    silc_client_unref_channel(client, conn, chu->channel);
+    silc_free(chu);
+  }
+  silc_hash_table_list_reset(&htl);
+
+  silc_hash_table_free(channel->user_list);
+}
index ac6b56a7a45a09ebc3f5d9d48cf965a70ba8f1f3..d72e21897dbed687ef2d3f513fefc2fd5e91b523 100644 (file)
@@ -30,13 +30,20 @@ SilcBool silc_client_save_channel_key(SilcClient client,
                                      SilcChannelEntry channel);
 SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
                                       SilcClientEntry client_entry);
-SilcBool silc_client_add_to_channel(SilcChannelEntry channel,
+SilcBool silc_client_add_to_channel(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcChannelEntry channel,
                                    SilcClientEntry client_entry,
                                    SilcUInt32 cumode);
-SilcBool silc_client_remove_from_channel(SilcChannelEntry channel,
+SilcBool silc_client_remove_from_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel,
                                         SilcClientEntry client_entry);
 void silc_client_remove_from_channels(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcClientEntry client_entry);
+void silc_client_empty_channel(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcChannelEntry channel);
 
 #endif /* CLIENT_CHANNEL_H */
index 3b656229e65fcd605a7212c658314d9e089f304c..4aff0a6b77c0906c9a0aa58e70ad70f2352ed744 100644 (file)
@@ -30,7 +30,6 @@ typedef struct {
   void *completion_context;
 } *VerifyKeyContext;
 
-
 /************************ Static utility functions **************************/
 
 /* Callback called after connected to remote host */
@@ -305,7 +304,8 @@ SILC_FSM_STATE(silc_client_st_connect)
                                               stream, fsm));
   } else {
     /* Connect (TCP) */
-    SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host,
+    SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect(
+                                      NULL, conn->remote_host,
                                       conn->remote_port,
                                       conn->internal->schedule,
                                       silc_client_connect_callback, fsm));
@@ -386,8 +386,9 @@ SILC_FSM_STATE(silc_client_st_connect_key_exchange)
     /** Run key exchange (TCP) */
     silc_fsm_next(fsm, silc_client_st_connect_auth);
 
-  SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream,
-                                  &params, NULL));
+  SILC_FSM_CALL(conn->internal->op = silc_ske_initiator(conn->internal->ske,
+                                                       conn->stream,
+                                                       &params, NULL));
 }
 
 /* For UDP/IP connections, set up the UDP session after successful key
@@ -481,7 +482,8 @@ SILC_FSM_STATE(silc_client_st_connect_auth_start)
 
   /** Start connection authentication */
   silc_fsm_next(fsm, silc_client_st_connected);
-  SILC_FSM_CALL(silc_connauth_initiator(connauth, SILC_CONN_CLIENT,
+  SILC_FSM_CALL(conn->internal->op = silc_connauth_initiator(
+                                       connauth, SILC_CONN_CLIENT,
                                        conn->internal->params.auth_method,
                                        conn->internal->params.auth,
                                        conn->internal->params.auth_len,
@@ -526,9 +528,11 @@ SILC_FSM_STATE(silc_client_st_connected)
 
 SILC_FSM_STATE(silc_client_st_connect_error)
 {
+  SilcClientConnection conn = fsm_context;
 
-  /* XXX */
-  /* Close connection */
+  /* Signal to close connection */
+  conn->internal->disconnected = TRUE;
+  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
 
   return SILC_FSM_FINISH;
 }
index b752743351bbe3aebcbcb0ba24e0c54fcd3dfd2f..43687fb079dc590ce5b148e5aab5fad21e44f58b 100644 (file)
@@ -22,8 +22,6 @@
 #include "silcclient.h"
 #include "client_internal.h"
 
-/* XXX locking */
-
 /************************ Client Searching Locally **************************/
 
 /* Finds entry for client by the client's ID. Returns the entry or NULL
@@ -108,7 +106,7 @@ SilcDList silc_client_get_clients_local(SilcClient client,
     /* Take all without any further checking */
     silc_list_start(list);
     while ((id_cache = silc_list_get(list))) {
-      silc_client_ref_client(client, conn, entry);
+      silc_client_ref_client(client, conn, id_cache->context);
       silc_dlist_add(clients, id_cache->context);
     }
   } else {
@@ -397,8 +395,11 @@ static SilcBool silc_client_get_clients_list_cb(SilcClient client,
  out:
   if (status != SILC_STATUS_OK && i->completion)
     i->completion(client, conn, status, NULL, i->context);
+
   silc_client_list_free(client, conn, clients);
+  silc_buffer_free(i->client_id_list);
   silc_free(i);
+
   return FALSE;
 }
 
@@ -789,11 +790,12 @@ void silc_client_update_client(SilcClient client,
     /* Format nickname */
     silc_client_nickname_format(client, conn, client_entry);
 
-    /* Remove the old cache entry and create a new one */
-    silc_idcache_del_by_context(conn->internal->client_cache, client_entry,
-                               NULL);
-    silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id,
-                    client_entry);
+    /* Update cache entry */
+    silc_mutex_lock(conn->internal->lock);
+    silc_idcache_update_by_context(conn->internal->client_cache,
+                                  client_entry, NULL, nick, TRUE);
+    silc_mutex_unlock(conn->internal->lock);
+    client_entry->nickname_normalized = nick;
   }
   client_entry->mode = mode;
 }
@@ -804,9 +806,9 @@ void silc_client_del_client_entry(SilcClient client,
                                  SilcClientConnection conn,
                                  SilcClientEntry client_entry)
 {
-  SILC_LOG_DEBUG(("Start"));
-
   silc_free(client_entry->realname);
+  silc_free(client_entry->nickname_normalized);
+  silc_free(client_entry->internal.key);
   if (client_entry->public_key)
     silc_pkcs_public_key_free(client_entry->public_key);
   silc_hash_table_free(client_entry->channels);
@@ -814,12 +816,16 @@ void silc_client_del_client_entry(SilcClient client,
     silc_cipher_free(client_entry->internal.send_key);
   if (client_entry->internal.receive_key)
     silc_cipher_free(client_entry->internal.receive_key);
-  silc_free(client_entry->internal.key);
+  if (client_entry->internal.hmac_send)
+    silc_hmac_free(client_entry->internal.hmac_send);
+  if (client_entry->internal.hmac_receive)
+    silc_hmac_free(client_entry->internal.hmac_receive);
 #if 0
   silc_client_ftp_session_free_client(conn, client_entry);
   if (client_entry->internal->ke)
     silc_client_abort_key_agreement(client, conn, client_entry);
 #endif /* 0 */
+  silc_atomic_uninit8(&client_entry->internal.refcnt);
   silc_free(client_entry);
 }
 
@@ -828,9 +834,24 @@ void silc_client_del_client_entry(SilcClient client,
 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
                                SilcClientEntry client_entry)
 {
-  SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache,
-                                            client_entry, NULL);
-#if 0
+  SilcBool ret;
+
+  if (!client_entry)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
+                 silc_atomic_get_int8(&client_entry->internal.refcnt),
+                 silc_atomic_get_int8(&client_entry->internal.refcnt) - 1));
+  if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Deleting client %p"));
+
+  silc_mutex_lock(conn->internal->lock);
+  ret = silc_idcache_del_by_context(conn->internal->client_cache,
+                                   client_entry, NULL);
+  silc_mutex_unlock(conn->internal->lock);
+
   if (ret) {
     /* Remove from channels */
     silc_client_remove_from_channels(client, conn, client_entry);
@@ -838,7 +859,6 @@ SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
     /* Free the client entry data */
     silc_client_del_client_entry(client, conn, client_entry);
   }
-#endif
 
   return ret;
 }
@@ -884,7 +904,6 @@ void silc_client_list_free(SilcClient client, SilcClientConnection conn,
   }
 }
 
-
 /* Formats the nickname of the client specified by the `client_entry'.
    If the format is specified by the application this will format the
    nickname and replace the old nickname in the client entry. If the
@@ -1310,67 +1329,57 @@ SilcChannelEntry silc_client_add_channel(SilcClient client,
   return channel;
 }
 
-/* Foreach callbcak to free all users from the channel when deleting a
-   channel entry. */
-
-static void silc_client_del_channel_foreach(void *key, void *context,
-                                           void *user_context)
-{
-  SilcChannelUser chu = (SilcChannelUser)context;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Remove the context from the client's channel hash table as that
-     table and channel's user_list hash table share this same context. */
-  silc_hash_table_del(chu->client->channels, chu->channel);
-  silc_free(chu);
-}
-
 /* Removes channel from the cache by the channel entry. */
 
 SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
                                 SilcChannelEntry channel)
 {
-  SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
-                                            channel, NULL);
-#if 0
+  SilcBool ret;
+  SilcCipher key;
+  SilcHmac hmac;
 
-  SILC_LOG_DEBUG(("Start"));
+  if (!channel)
+    return FALSE;
+
+  if (silc_atomic_sub_int8(&channel->internal.refcnt, 1) > 0)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Deleting channel %p", channel));
+
+  silc_mutex_lock(conn->internal->lock);
+  ret = silc_idcache_del_by_context(conn->internal->channel_cache,
+                                   channel, NULL);
+  silc_mutex_unlock(conn->internal->lock);
 
-  /* Free all client entrys from the users list. The silc_hash_table_free
-     will free all the entries so they are not freed at the foreach
-     callback. */
-  silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
-                         NULL);
-  silc_hash_table_free(channel->user_list);
+  if (!ret)
+    return FALSE;
 
+  silc_client_empty_channel(client, conn, channel);
   silc_free(channel->channel_name);
   silc_free(channel->topic);
   if (channel->founder_key)
     silc_pkcs_public_key_free(channel->founder_key);
-  silc_free(channel->key);
-  if (channel->channel_key)
-    silc_cipher_free(channel->channel_key);
-  if (channel->hmac)
-    silc_hmac_free(channel->hmac);
-  if (channel->old_channel_keys) {
-    SilcCipher key;
-    silc_dlist_start(channel->old_channel_keys);
-    while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
+  if (channel->internal.channel_key)
+    silc_cipher_free(channel->internal.channel_key);
+  if (channel->internal.hmac)
+    silc_hmac_free(channel->internal.hmac);
+  if (channel->internal.old_channel_keys) {
+    silc_dlist_start(channel->internal.old_channel_keys);
+    while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
       silc_cipher_free(key);
-    silc_dlist_uninit(channel->old_channel_keys);
+    silc_dlist_uninit(channel->internal.old_channel_keys);
   }
-  if (channel->old_hmacs) {
-    SilcHmac hmac;
-    silc_dlist_start(channel->old_hmacs);
-    while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
+  if (channel->internal.old_hmacs) {
+    silc_dlist_start(channel->internal.old_hmacs);
+    while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
       silc_hmac_free(hmac);
-    silc_dlist_uninit(channel->old_hmacs);
+    silc_dlist_uninit(channel->internal.old_hmacs);
   }
-  silc_schedule_task_del_by_context(conn->client->schedule, channel);
   silc_client_del_channel_private_keys(client, conn, channel);
+  silc_atomic_uninit8(&channel->internal.refcnt);
+  silc_schedule_task_del_by_context(conn->client->schedule, channel);
   silc_free(channel);
-#endif
+
   return ret;
 }
 
@@ -1382,7 +1391,6 @@ SilcBool silc_client_replace_channel_id(SilcClient client,
                                        SilcChannelEntry channel,
                                        SilcChannelID *new_id)
 {
-  SilcIDCacheEntry id_cache;
   SilcBool ret = FALSE;
 
   if (!new_id)
@@ -1393,14 +1401,10 @@ SilcBool silc_client_replace_channel_id(SilcClient client,
   SILC_LOG_DEBUG(("New Channel ID id(%s)",
                  silc_id_render(new_id, SILC_ID_CHANNEL)));
 
-  silc_mutex_lock(conn->internal->lock);
-
   /* Update the ID */
-  if (silc_idcache_find_by_id_one(conn->internal->channel_cache,
-                                  &channel->id, &id_cache))
-    ret = silc_idcache_update(conn->internal->channel_cache, id_cache,
-                             &channel->id, new_id, NULL, NULL, FALSE);
-
+  silc_mutex_lock(conn->internal->lock);
+  silc_idcache_update_by_context(conn->internal->channel_cache, channel,
+                                new_id, NULL, FALSE);
   silc_mutex_unlock(conn->internal->lock);
 
   return ret;
@@ -1468,6 +1472,7 @@ SilcServerEntry silc_client_get_server(SilcClient client,
   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
                                     server_name, &id_cache)) {
     silc_free(server_name);
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
   }
 
@@ -1502,8 +1507,10 @@ SilcServerEntry silc_client_get_server_by_id(SilcClient client,
   silc_mutex_lock(conn->internal->lock);
 
   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
-                                  server_id, &id_cache))
+                                  server_id, &id_cache)) {
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
+  }
 
   SILC_LOG_DEBUG(("Found"));
 
@@ -1643,10 +1650,13 @@ SilcServerEntry silc_client_add_server(SilcClient client,
   SilcServerEntry server_entry;
   char *server_namec = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  if (!server_id)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Adding new server %s", server_name));
 
   server_entry = silc_calloc(1, sizeof(*server_entry));
-  if (!server_entry || !server_id)
+  if (!server_entry)
     return NULL;
 
   silc_atomic_init8(&server_entry->internal.refcnt, 0);
@@ -1692,11 +1702,28 @@ SilcServerEntry silc_client_add_server(SilcClient client,
 SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
                                SilcServerEntry server)
 {
-  SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache,
-                                            server, NULL);
+  SilcBool ret;
+
+  if (!server)
+    return FALSE;
+
+  if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Deleting server %p", server));
+
+  silc_mutex_lock(conn->internal->lock);
+  ret = silc_idcache_del_by_context(conn->internal->server_cache,
+                                   server, NULL);
+  silc_mutex_unlock(conn->internal->lock);
+
   silc_free(server->server_name);
   silc_free(server->server_info);
+  if (server->public_key)
+    silc_pkcs_public_key_free(server->public_key);
+  silc_atomic_uninit8(&server->internal.refcnt);
   silc_free(server);
+
   return ret;
 }
 
@@ -1710,27 +1737,27 @@ void silc_client_update_server(SilcClient client,
 {
   char *server_namec = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Updating server %p", server_entry));
 
   if (server_name &&
       (!server_entry->server_name ||
        !silc_utf8_strcasecmp(server_entry->server_name, server_name))) {
 
-    silc_idcache_del_by_context(conn->internal->server_cache,
-                               server_entry, NULL);
+    server_namec = silc_identifier_check(server_name, strlen(server_name),
+                                        SILC_STRING_UTF8, 256, NULL);
+    if (!server_namec)
+      return;
+
     silc_free(server_entry->server_name);
     server_entry->server_name = strdup(server_name);
+    if (!server_entry->server_name)
+      return;
 
-    /* Normalize server name */
-    if (server_name) {
-      server_namec = silc_identifier_check(server_name, strlen(server_name),
-                                          SILC_STRING_UTF8, 256, NULL);
-      if (!server_namec)
-       return;
-
-      silc_idcache_add(conn->internal->server_cache, server_namec,
-                      &server_entry->id, server_entry);
-    }
+    /* Update cache entry */
+    silc_mutex_lock(conn->internal->lock);
+    silc_idcache_update_by_context(conn->internal->server_cache, server_entry,
+                                  NULL, server_namec, TRUE);
+    silc_mutex_unlock(conn->internal->lock);
   }
 
   if (server_info &&
index 51c18227758dd479efd323a1bb78ffbb90a6e497..3dbcd6729439b7193f6bf30bcfeea313d327e759 100644 (file)
 #include "client_channel.h"
 #include "client_notify.h"
 
+/****************************** Definitions *********************************/
+
+/* Packet retry counter and timer defines, for exponential backoff algorithm.
+   Meaningful with UDP transport when packets may get lost. */
+#define SILC_CLIENT_RETRY_COUNT   4      /* Max packet retry count */
+#define SILC_CLIENT_RETRY_MUL     2      /* Retry timer interval growth */
+#define SILC_CLIENT_RETRY_RAND    2      /* Randomizer, timeout += rnd % 2 */
+#define SILC_CLIENT_RETRY_MIN     1      /* Min retry timeout, seconds */
+#define SLIC_CLIENT_RETRY_MAX     16    /* Max retry timeout, seconds */
+
+/********************************** Types ***********************************/
+
 /* Context to hold the connection authentication request callbacks that
    will be called when the server has replied back to our request about
    current authentication method in the session. */
@@ -123,15 +135,8 @@ struct SilcClientInternalStruct {
   SilcClientParams *params;             /* Client parameters */
   SilcPacketEngine packet_engine;        /* Packet engine */
   SilcMutex lock;                       /* Client lock */
-
-  /* List of connections in client. All the connection data is saved here. */
-  SilcDList conns;
-
-  /* Registered commands */
-  SilcList commands;
-
-  /* Client version. Used to compare to remote host's version strings. */
-  char *silc_client_version;
+  SilcList commands;                    /* Registered commands */
+  char *silc_client_version;            /* Version set by application */
 
   /* Events */
   unsigned int run_callback    : 1;     /* Call running callback */
@@ -153,15 +158,17 @@ struct SilcClientConnectionInternalStruct {
   SilcList pending_commands;            /* Pending commands list */
   SilcHash hash;                        /* Negotiated hash function */
   SilcHash sha1hash;                    /* SHA-1 default hash context */
+  SilcBuffer local_idp;                         /* Local ID Payload */
+  SilcBuffer remote_idp;                /* Remote ID Payload */
+  SilcAsyncOperation op;                /* Protocols async operation */
 
   SilcIDCache client_cache;             /* Client entry cache */
   SilcIDCache channel_cache;            /* Channel entry cache */
   SilcIDCache server_cache;             /* Server entry cache */
 
-  SilcBuffer local_idp;                         /* Local ID Payload */
-  SilcBuffer remote_idp;                /* Remote ID Payload */
-
   SilcAtomic16 cmd_ident;               /* Current command identifier */
+  SilcUInt8 retry_count;                /* Packet retry counter */
+  SilcUInt8 retry_timer;                /* Packet retry timer */
 
   /* Events */
   unsigned int connect            : 1;  /* Connect remote host */
@@ -203,6 +210,7 @@ SilcUInt16 silc_client_command_send_argv(SilcClient client,
                                         unsigned char **argv,
                                         SilcUInt32 *argv_lens,
                                         SilcUInt32 *argv_types);
+void silc_client_command_free(SilcClientCommandContext cmd);
 
 void silc_client_ftp(SilcClient client, SilcClientConnection conn,
                     SilcPacket packet);
index f408e4920bac9864009ae1c7965456be9b5772b3..a68d94a0600f364569cbd84d7d7e14b8ad4e602d 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 - 2005 Pekka Riikonen
+  Copyright (C) 2001 - 2006 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
   GNU General Public License for more details.
 
 */
-/* $Id$ */
-/* This file includes the Key Agreement packet processing and actual
-   key agreement routines. This file has nothing to do with the actual
-   connection key exchange protocol, it is implemented in the client.c
-   and in protocol.c. This file implements the client-to-client key
-   agreement as defined by the SILC protocol. */
 
 #include "silc.h"
 #include "silcclient.h"
 #include "client_internal.h"
 
-SILC_TASK_CALLBACK(silc_client_key_agreement_final);
-SILC_TASK_CALLBACK(silc_client_process_key_agreement);
-SILC_TASK_CALLBACK(silc_client_key_agreement_timeout);
-SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start);
+/************************** Types and definitions ***************************/
 
 /* Key agreement context */
 struct SilcClientKeyAgreementStruct {
-  SilcClient client;
-  SilcClientConnection conn;
-  int fd;                                /* Listening/connection socket */
-  SilcSocketConnection sock;             /* Remote socket connection */
-  SilcClientEntry client_entry;                  /* Destination client */
+  SilcClient client;                     /* Client */
+  SilcClientConnection conn;             /* Server connection */
   SilcKeyAgreementCallback completion;   /* Key agreement completion */
   void *context;                         /* User context */
-  SilcTask timeout;                      /* Timeout task */
-  SilcClientKEInternalContext *proto_ctx; /* Key Exchange protocol context */
+
+  /* Responder */
+  SilcNetListener listener;              /* TCP listener */
+  SilcStream stream;                     /* Remote connection (TCP or UDP) */
+
+  /* Initiator */
+  SilcClientConnection client_conn;      /* Connection to remote client */
 };
 
+/************************ Static utility functions **************************/
+
+/* TCP network listener callback.  Accepts new key agreement connection */
+
+static void silc_client_tcp_accept(SilcNetStatus status,
+                                  SilcStream stream,
+                                  void *context)
+{
+  SilcClientEntry client_entry = context;
+  SilcClientKeyAgreement ke = client_entry->ke;
+
+  ke->stream = stream;
+  silc_client_process_key_agreement(ke->client, ke->conn, ke);
+}
+
+/* UDP network callback.  All UDP packets are read from here. */
+
+static void silc_client_udp_accept(SilcStream stream,
+                                  SilcStreamStatus status,
+                                  void *context)
+{
+
+}
+
+
 /* Packet sending function used by the SKE in the key agreement process. */
 
 static void silc_client_key_agreement_send_packet(SilcSKE ske,
@@ -254,62 +272,32 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
   silc_free(ke);
 }
 
-/* Sends key agreement request to the remote client indicated by the
-   `client_entry'. If the caller provides the `hostname' and the `port'
-   arguments then the library will bind the client to that hostname and
-   that port for the key agreement protocol. It also sends the `hostname'
-   and the `port' in the key agreement packet to the remote client. This
-   would indicate that the remote client may initiate the key agreement
-   protocol to the `hostname' on the `port'.  If port is zero then the
-   bound port is undefined (the operating system defines it).
-
-   If the `hostname' and `port' is not provided then empty key agreement
-   packet is sent to the remote client. The remote client may reply with
-   the same packet including its hostname and port. If the library receives
-   the reply from the remote client the `key_agreement' client operation
-   callback will be called to verify whether the user wants to perform the
-   key agreement or not.
-
-   NOTE: If the application provided the `hostname' and the `port' and the
-   remote side initiates the key agreement protocol it is not verified
-   from the user anymore whether the protocol should be executed or not.
-   By setting the `hostname' and `port' the user gives permission to
-   perform the protocol (we are responder in this case).
-
-   NOTE: If the remote side decides not to initiate the key agreement
-   or decides not to reply with the key agreement packet then we cannot
-   perform the key agreement at all. If the key agreement protocol is
-   performed the `completion' callback with the `context' will be called.
-   If remote side decides to ignore the request the `completion' will be
-   called after the specified timeout, `timeout_secs'.
-
-   NOTE: If the `hostname' and the `port' was not provided the `completion'
-   will not be called at all since this does nothing more than sending
-   a packet to the remote host.
-
-   NOTE: There can be only one active key agreement for one client entry.
-   Before setting new one, the old one must be finished (it is finished
-   after calling the completion callback) or the function
-   silc_client_abort_key_agreement must be called. */
+/*************************** Key Agreement API ******************************/
+
+/* Sends key agreement packet to remote client.  If IP addresses are provided
+   creates also listener for Ã­ncoming key agreement connection.  Supports
+   both TCP and UDP transports. */
 
 void silc_client_send_key_agreement(SilcClient client,
                                    SilcClientConnection conn,
                                    SilcClientEntry client_entry,
-                                   const char *hostname,
-                                   const char *bindhost,
+                                   const char *local_ip,
+                                   const char *bind_ip,
                                    int port,
                                    SilcUInt32 timeout_secs,
+                                   SilcBool udp,
                                    SilcKeyAgreementCallback completion,
                                    void *context)
 {
-  SilcSocketConnection sock = conn->sock;
   SilcClientKeyAgreement ke = NULL;
+  SilcAsyncOperation op;
   SilcBuffer buffer;
+  SilcUInt16 ports = NULL;
 
   if (!client_entry)
     return;
 
-  if (client_entry->ke) {
+  if (client_entry->internal->ke) {
     completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED,
               NULL, context);
     return;
@@ -321,111 +309,91 @@ void silc_client_send_key_agreement(SilcClient client,
     return;
   }
 
-  /* Create the listener if hostname and port was provided.
-   * also, use bindhost if it was specified.
-   */
-
-  if (hostname) {
+  /* If local IP is provided, create listener */
+  if (local_ip || bind_ip) {
     ke = silc_calloc(1, sizeof(*ke));
+    if (!ke) {
+      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+                NULL, context);
+      return;
+    }
 
-    if (bindhost)
-      ke->fd = silc_net_create_server(port, bindhost);
-    else
-      ke->fd = silc_net_create_server(port, hostname);
-
-    if (ke->fd < 0) {
-      client->internal->ops->say(
+    /* Create network listener */
+    if (udp) {
+      /* UDP listener */
+      ke->stream =
+       silc_net_udp_connect(bind_ip ? bind_ip : local_ip, port, NULL, 0,
+                            client_entry);
+      if (!ke->stream) {
+       client->internal->ops->say(
+                    client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                    "Cannot create UDP listener on %s on port %d: %s",
+                    bind_ip ? bind_ip : local_ip, port, strerror(errno));
+       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
+                  NULL, context);
+       silc_free(ke);
+       return;
+      }
+      silc_stream_set_notifier(ke->stream, conn->schedule,
+                              silc_client_udp_accept, client_entry);
+    } else {
+      /* TCP listener */
+      ke->listener =
+       silc_net_tcp_create_listener(bind_ip ? &bind_ip :
+                                    &local_ip, 1, port, FALSE,
+                                    FALSE, conn->internal->schedule,
+                                    silc_client_tcp_accept,
+                                    client_entry);
+      if (!ke->listener) {
+       client->internal->ops->say(
                     client, conn, SILC_CLIENT_MESSAGE_ERROR,
                     "Cannot create listener on %s on port %d: %s",
-                    (bindhost) ? bindhost:hostname, port, strerror(errno));
-      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
-                NULL, context);
-      silc_free(ke);
-      return;
+                    bind_ip ? bind_ip : local_ip, port, strerror(errno));
+       completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR,
+                  NULL, context);
+       silc_free(ke);
+       return;
+      }
     }
 
     ke->client = client;
     ke->conn = conn;
-    ke->client_entry = client_entry;
     ke->completion = completion;
     ke->context = context;
-
-    /* Add listener task to the scheduler. This task receives the key
-       negotiations. */
-    silc_schedule_task_add(client->schedule, ke->fd,
-                          silc_client_process_key_agreement,
-                          (void *)ke, 0, 0,
-                          SILC_TASK_FD,
-                          SILC_TASK_PRI_NORMAL);
-
-    /* Register a timeout task that will be executed if the connector
-       will not start the key exchange protocol within the specified
-       timeout. */
-    ke->timeout = silc_schedule_task_add(client->schedule, 0,
-                                        silc_client_key_agreement_timeout,
-                                        (void *)ke, timeout_secs, 0,
-                                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
   }
 
-  /* Encode the key agreement payload */
-  buffer = silc_key_agreement_payload_encode(hostname,
-                                            !ke ? port :
-                                            silc_net_get_local_port(ke->fd));
-
-  /* Send the key agreement packet to the client */
-  silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT,
-                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                         buffer->data, buffer->len, FALSE);
-  silc_buffer_free(buffer);
-}
-
-static int
-silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
-{
-  int sock;
-
-  /* Create connection to server asynchronously */
-  sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
-  if (sock < 0)
-    return -1;
+  /* Add key agreement timeout task */
+  silc_schedule_task_add_timeout(conn->internal->schedule,
+                                silc_client_key_agreement_timeout,
+                                client_entry, timeout_secs, 0);
 
-  /* Register task that will receive the async connect and will
-     read the result. */
-  ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
-                                    silc_client_perform_key_agreement_start,
-                                    (void *)ctx, 0, 0,
-                                    SILC_TASK_FD,
-                                    SILC_TASK_PRI_NORMAL);
-  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
-                             FALSE);
-
-  ctx->sock = sock;
+  /* Encode the key agreement payload */
+  if (ke && ke->listener)
+    ports = silc_net_listener_get_port(ke->listener, NULL);
+  buffer = silc_key_agreement_payload_encode(local_ip, (port ? port :
+                                                       ports ? ports[0] : 0));
+  if (!buffer) {
+    if (ke) {
+      if (ke->listener)
+       silc_net_close_listener(ke->listener);
+      silc_free(ke);
+    }
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+              NULL, context);
+    return;
+  }
+  silc_free(ports);
 
-  return sock;
-}
+  if (ke) {
+    silc_client_ref_client(client, conn, client_entry);
+    client_entry->internal.ke = ke;
+  }
 
-/* Routine used by silc_client_perform_key_agreement to create connection
-   to the remote client on specified port. */
+  /* Send the key agreement packet to the client */
+  silc_packet_send(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0,
+                  silc_buffer_data(buffer), silc_buffer_len(data));
 
-static int
-silc_client_connect_to_client(SilcClient client,
-                             SilcClientConnection conn, int port,
-                             char *host, void *context)
-{
-  SilcClientInternalConnectContext *ctx;
-
-  /* Allocate internal context for connection process. This is
-     needed as we are doing async connecting. */
-  ctx = silc_calloc(1, sizeof(*ctx));
-  ctx->client = client;
-  ctx->conn = conn;
-  ctx->host = strdup(host);
-  ctx->port = port;
-  ctx->tries = 0;
-  ctx->context = context;
-
-  /* Do the actual connecting process */
-  return silc_client_connect_to_client_internal(ctx);
+  silc_buffer_free(buffer);
 }
 
 /* Callback that is called after connection has been created. This actually
index 206425dc9379d212964bc48a5a415d708450f415..e498e9c6c4cc82ba1a29e17f97920f092fa105c4 100644 (file)
@@ -238,6 +238,8 @@ SILC_FSM_STATE(silc_client_notify_none)
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
 
+  SILC_LOG_DEBUG(("Notify: NONE"));
+
   /* Notify application */
   NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL));
 
@@ -380,7 +382,7 @@ SILC_FSM_STATE(silc_client_notify_join)
     silc_client_nickname_format(client, conn, client_entry);
 
   /* Join the client to channel */
-  if (!silc_client_add_to_channel(channel, client_entry, 0))
+  if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0))
     goto out;
 
   /* Notify application. */
@@ -410,7 +412,6 @@ SILC_FSM_STATE(silc_client_notify_leave)
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
   SilcChannelEntry channel = NULL;
-  SilcChannelUser chu;
   SilcID id;
 
   SILC_LOG_DEBUG(("Notify: LEAVE"));
@@ -444,30 +445,7 @@ SILC_FSM_STATE(silc_client_notify_leave)
     goto out;
 
   /* Remove client from channel */
-  chu = silc_client_on_channel(channel, client_entry);
-  if (chu) {
-    silc_hash_table_del(client_entry->channels, channel);
-    silc_hash_table_del(channel->user_list, client_entry);
-    silc_free(chu);
-  }
-
-#if 0 /* Kind of useless, server will return error if client keeps using
-        non-existing client, and the entry is removed then. */
-  /* Some client implementations actually quit network by first doing
-     LEAVE and then immediately SIGNOFF.  We'll check for this by doing
-     check for the client after 5 - 34 seconds.  If it is not valid after
-     that we'll remove the client from cache. */
-  if (!silc_hash_table_count(client_entry->channels)) {
-    SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-    res->context = client;
-    res->sock = silc_socket_dup(conn->sock);
-    res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
-    silc_schedule_task_add(client->schedule, conn->sock->sock,
-                          silc_client_notify_check_client, res,
-                          (5 + (silc_rng_get_rn16(client->rng) % 29)),
-                          0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-  }
-#endif
+  silc_client_remove_from_channel(client, conn, channel, client_entry);
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
@@ -713,9 +691,10 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
       silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
     /* Nickname didn't change.  Update only Client ID.  We don't notify
        application because nickname didn't change. */
-    silc_idcache_update(conn->internal->client_cache, client_entry,
-                       &client_entry->id, &id2.u.client_id, NULL,
-                       NULL, FALSE);
+    silc_mutex_lock(conn->internal->lock);
+    silc_idcache_update_by_context(conn->internal->client_cache, client_entry,
+                                  &id2.u.client_id, NULL, FALSE);
+    silc_mutex_unlock(conn->internal->lock);
     goto out;
   }
 
@@ -725,12 +704,14 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
     goto out;
 
   /* Update nickname */
-  if (!silc_idcache_update(conn->internal->client_cache, client_entry,
-                          NULL, NULL, client_entry->nickname_normalized,
-                          nick, TRUE)) {
+  silc_mutex_lock(conn->internal->lock);
+  if (!silc_idcache_update_by_context(conn->internal->client_cache,
+                                     client_entry, NULL, nick, TRUE)) {
     silc_free(nick);
+    silc_mutex_unlock(conn->internal->lock);
     goto out;
   }
+  silc_mutex_unlock(conn->internal->lock);
   memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
   memcpy(client_entry->nickname, tmp, tmp_len);
   client_entry->nickname_normalized = nick;
@@ -967,6 +948,22 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
     /* NOT REACHED */
   }
 
+  /* Get target Client ID */
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
+    goto out;
+
+  /* Find target Client entry */
+  client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
+  if (!client_entry2 || !client_entry2->nickname[0]) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry2);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id2.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        notify));
+    /* NOT REACHED */
+  }
+
   /* Get the mode */
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
   if (!tmp)
@@ -1023,23 +1020,6 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
     entry = channel_entry;
   }
 
-  /* Get target Client ID */
-  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
-    goto out;
-
-  /* Find target Client entry */
-  client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
-  if (!client_entry2 || !client_entry2->nickname[0]) {
-    /** Resolve client */
-    silc_client_unref_client(client, conn, client_entry);
-    silc_client_unref_client(client, conn, client_entry2);
-    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
-                                        client, conn, &id2.u.client_id, NULL,
-                                        silc_client_notify_resolved,
-                                        notify));
-    /* NOT REACHED */
-  }
-
   /* Save the mode */
   chu = silc_client_on_channel(channel, client_entry2);
   if (chu)
index 61c43b621aabf0e2d247bfc72a93e19121038e5c..a4addb359edd0ccf8b8d621a8ff5e0133b060a63 100644 (file)
@@ -669,12 +669,17 @@ silc_client_list_private_message_keys(SilcClient client,
   if (!client || !conn)
     return NULL;
 
-  if (!silc_idcache_get_all(conn->internal->client_cache, &list))
+  silc_mutex_lock(conn->internal->lock);
+  if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
+  }
 
   keys = silc_calloc(silc_list_count(list), sizeof(*keys));
-  if (!keys)
+  if (!keys) {
+    silc_mutex_unlock(conn->internal->lock);
     return NULL;
+  }
 
   silc_list_start(list);
   while ((id_cache = silc_list_get(list))) {
@@ -691,6 +696,8 @@ silc_client_list_private_message_keys(SilcClient client,
     }
   }
 
+  silc_mutex_unlock(conn->internal->lock);
+
   if (key_count)
     *key_count = count;
 
index 8ff3fb4c54d9d58fe369df488a239daf0d585653..84f35f72cb6c9973311cf4ca56b0d1065732edd9 100644 (file)
@@ -36,9 +36,21 @@ typedef struct {
   SilcBool success;
 } *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 TRUE;
+}
 
 /****************************** NEW_ID packet *******************************/
 
@@ -77,10 +89,14 @@ SILC_FSM_STATE(silc_client_new_id)
   conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
 
   /* Save cache entry */
+  silc_mutex_lock(conn->internal->lock);
   if (!silc_idcache_find_by_id_one(conn->internal->client_cache,
                                   conn->local_id,
-                                  &conn->internal->local_entry))
+                                  &conn->internal->local_entry)) {
+    silc_mutex_unlock(conn->internal->lock);
     goto out;
+  }
+  silc_mutex_unlock(conn->internal->lock);
 
   /* Save remote ID */
   if (packet->src_id_len) {
@@ -138,7 +154,8 @@ SILC_FSM_STATE(silc_client_st_register)
 
   /** Wait for new ID */
   conn->internal->registering = TRUE;
-  silc_fsm_next_later(fsm, silc_client_st_register_complete, 15, 0);
+  silc_fsm_next_later(fsm, silc_client_st_register_complete,
+                     conn->internal->retry_timer, 0);
   return SILC_FSM_WAIT;
 }
 
@@ -150,9 +167,21 @@ SILC_FSM_STATE(silc_client_st_register_complete)
   SilcClient client = conn->client;
 
   if (!conn->local_id) {
-    /** Timeout, ID not received */
-    conn->internal->registering = FALSE;
-    silc_fsm_next(fsm, silc_client_st_register_error);
+    if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
+      /** Timeout, ID not received */
+      conn->internal->registering = FALSE;
+      conn->internal->retry_count = 0;
+      conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
+      silc_fsm_next(fsm, silc_client_st_register_error);
+      return SILC_FSM_CONTINUE;
+    }
+
+    /** Resend registering packet */
+    silc_fsm_next(fsm, silc_client_st_register);
+    conn->internal->retry_timer = ((conn->internal->retry_timer *
+                                  SILC_CLIENT_RETRY_MUL) +
+                                  (silc_rng_get_rn16(client->rng) %
+                                   SILC_CLIENT_RETRY_RAND));
     return SILC_FSM_CONTINUE;
   }
 
@@ -160,21 +189,22 @@ 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, NULL, NULL,
+  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                          silc_client_register_command_called, NULL,
                           1, 5, silc_buffer_data(conn->internal->local_idp),
                           silc_buffer_len(conn->internal->local_idp));
 
-  /* Send NICK command if the nickname was set by the application (and is
-     not same as the username).  Send this with little timeout. */
+  /* Call NICK command if the nickname was set by the application (and is
+     not same as the username). */
   if (conn->internal->params.nickname &&
       !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
-    silc_client_command_send(client, conn, SILC_COMMAND_NICK, NULL, NULL,
-                            1, 1, conn->internal->params.nickname,
-                            strlen(conn->internal->params.nickname));
+    silc_client_command_call(client, conn, NULL,
+                            "NICK", conn->internal->params.nickname, NULL);
 
   /* Issue INFO command to fetch the real server name and server
      information and other stuff. */
-  silc_client_command_send(client, conn, SILC_COMMAND_INFO, NULL, NULL,
+  silc_client_command_send(client, conn, SILC_COMMAND_INFO,
+                          silc_client_register_command_called, NULL,
                           1, 2, silc_buffer_data(conn->internal->remote_idp),
                           silc_buffer_len(conn->internal->remote_idp));
 
@@ -186,14 +216,20 @@ SILC_FSM_STATE(silc_client_st_register_complete)
   return SILC_FSM_FINISH;
 }
 
+/* Error registering to network */
+
 SILC_FSM_STATE(silc_client_st_register_error)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
 
-  /* XXX */
-  /* Close connection */
+  SILC_LOG_DEBUG(("Error registering to network"));
+
+  /* Signal to close connection */
+  conn->internal->disconnected = TRUE;
+  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
 
+  /* Call connect callback */
   conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
                 conn->callback_context);
 
index 4accee1a8ffa903f81bf9ae95278847dc99bff3a..468ac1bb243e168c552f849ac5980b74bf45c96a 100644 (file)
@@ -139,11 +139,17 @@ silc_client_command_register(SilcClient client,
   SilcClientCommand cmd;
 
   cmd = silc_calloc(1, sizeof(*cmd));
+  if (!cmd)
+    return FALSE;
   cmd->cmd = command;
   cmd->command = command_func;
   cmd->reply = command_reply_func;
-  cmd->name = name ? strdup(name) : NULL;
   cmd->max_args = max_args;
+  cmd->name = name ? strdup(name) : NULL;
+  if (!cmd->name) {
+    silc_free(cmd);
+    return FALSE;
+  }
 
   silc_list_add(client->internal->commands, cmd);
 
@@ -191,20 +197,6 @@ static SilcClientCommand silc_client_command_find(SilcClient client,
   return NULL;
 }
 
-/* Free command context and its internals */
-
-static void silc_client_command_free(SilcClientCommandContext cmd)
-{
-  int i;
-
-  for (i = 0; i < cmd->argc; i++)
-    silc_free(cmd->argv[i]);
-  silc_free(cmd->argv);
-  silc_free(cmd->argv_lens);
-  silc_free(cmd->argv_types);
-  silc_free(cmd);
-}
-
 /* Command thread destructor */
 
 static void silc_client_command_destructor(SilcFSMThread thread,
@@ -353,6 +345,26 @@ static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
 
 /****************************** Command API *********************************/
 
+/* Free command context and its internals */
+
+void silc_client_command_free(SilcClientCommandContext cmd)
+{
+  SilcClientCommandReplyCallback cb;
+  int i;
+
+  for (i = 0; i < cmd->argc; i++)
+    silc_free(cmd->argv[i]);
+  silc_free(cmd->argv);
+  silc_free(cmd->argv_lens);
+  silc_free(cmd->argv_types);
+
+  silc_list_start(cmd->reply_callbacks);
+  while ((cb = silc_list_get(cmd->reply_callbacks)))
+    silc_free(cb);
+
+  silc_free(cmd);
+}
+
 /* Executes a command */
 
 SilcUInt16 silc_client_command_call(SilcClient client,
@@ -435,6 +447,8 @@ SilcUInt16 silc_client_command_call(SilcClient client,
   cmd->cmd_ident = silc_client_cmd_ident(conn);
   cmd->called = TRUE;
   cmd->verbose = TRUE;
+  silc_list_init(cmd->reply_callbacks,
+                struct SilcClientCommandReplyCallbackStruct, next);
 
   /*** Call command */
   SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
@@ -467,6 +481,8 @@ SilcUInt16 silc_client_command_send(SilcClient client,
     return 0;
   cmd->conn = conn;
   cmd->cmd = command;
+  silc_list_init(cmd->reply_callbacks,
+                struct SilcClientCommandReplyCallbackStruct, next);
 
   /* Send the command */
   va_start(ap, argc);
@@ -2577,6 +2593,7 @@ SILC_FSM_STATE(silc_client_command_getkey)
       /* NOT REACHED */
     }
     idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
+    silc_client_unref_server(client, conn, server_entry);
   } else {
     client_entry = silc_dlist_get(clients);
     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
index a4d51b0db9c868ebffb4047a0254d4cde0d770fa..f5adc9c231b95e6ed856c9d0e55a6fc7033ce092 100644 (file)
@@ -188,9 +188,13 @@ SILC_FSM_STATE(silc_client_command_reply_wait)
 SILC_FSM_STATE(silc_client_command_reply_timeout)
 {
   SilcClientCommandContext cmd = fsm_context;
+  SilcClientConnection conn = cmd->conn;
   SilcArgumentPayload args = NULL;
 
+  SILC_LOG_DEBUG(("Command %s timeout", silc_get_command_name(cmd->cmd)));
+
   /* Timeout, reply not received in timely fashion */
+  silc_list_del(conn->internal->pending_commands, cmd);
   ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT);
   return SILC_FSM_FINISH;
 }
@@ -680,10 +684,7 @@ SILC_FSM_STATE(silc_client_command_reply_nick)
   silc_mutex_lock(conn->internal->lock);
   if (!silc_idcache_update(conn->internal->client_cache,
                           conn->internal->local_entry,
-                          &conn->local_entry->id,
-                          &id.u.client_id,
-                          conn->local_entry->nickname_normalized,
-                          tmp, TRUE)) {
+                          &id.u.client_id, tmp, TRUE)) {
     silc_free(tmp);
     silc_mutex_unlock(conn->internal->lock);
     ERROR_CALLBACK(SILC_STATUS_ERR_BAD_NICKNAME);
@@ -950,16 +951,18 @@ SILC_FSM_STATE(silc_client_command_reply_info)
   /* See whether we have this server cached. If not create it. */
   server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
   if (!server) {
-    SILC_LOG_DEBUG(("New server entry"));
+    SILC_LOG_DEBUG(("Add new server entry (INFO)"));
     server = silc_client_add_server(client, conn, server_name,
                                    server_info, &id.u.server_id);
     if (!server)
       goto out;
+    silc_client_ref_server(client, conn, server);
   }
 
   /* Notify application */
   silc_client_command_callback(cmd, server, server->server_name,
                               server->server_info);
+  silc_client_unref_server(client, conn, server);
 
  out:
   silc_fsm_next(fsm, silc_client_command_reply_processed);
@@ -1171,15 +1174,13 @@ SILC_FSM_STATE(silc_client_command_reply_join)
     /* Mode */
     SILC_GET32_MSB(mode, client_mode_list.data);
 
-    SILC_LOG_DEBUG(("id %s", silc_id_render(&id.u.client_id, SILC_ID_CLIENT)));
-
     /* Get client entry */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
     if (!client_entry)
       continue;
 
     /* Join client to the channel */
-    silc_client_add_to_channel(channel, client_entry, mode);
+    silc_client_add_to_channel(client, conn, channel, client_entry, mode);
     silc_client_unref_client(client, conn, client_entry);
 
     if (!silc_buffer_pull(&client_id_list, idp_len))
@@ -1724,7 +1725,7 @@ SILC_FSM_STATE(silc_client_command_reply_leave)
   }
 
   /* Remove us from this channel. */
-  silc_client_remove_from_channel(channel, conn->local_entry);
+  silc_client_remove_from_channel(client, conn, channel, conn->local_entry);
 
   /* Notify application */
   silc_client_command_callback(cmd, channel);
@@ -1865,7 +1866,7 @@ SILC_FSM_STATE(silc_client_command_reply_users)
        clearly do not exist since the resolving didn't find them. */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
     if (client_entry)
-      silc_client_add_to_channel(channel, client_entry, mode);
+      silc_client_add_to_channel(client, conn, channel, client_entry, mode);
     silc_client_unref_client(client, conn, client_entry);
 
     if (!silc_buffer_pull(&client_id_list, idp_len))
@@ -1961,6 +1962,7 @@ SILC_FSM_STATE(silc_client_command_reply_getkey)
     /* Notify application */
     silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
                                 server_entry->public_key);
+    silc_client_unref_server(client, conn, server_entry);
   }
 
  out:
index bb7bc9aa4e1badd67bba58e1e18d20d2e83495ac..b44b09ff4a471a103af3858bb191582ae16a374d 100644 (file)
@@ -269,6 +269,7 @@ typedef enum {
   SILC_KEY_AGREEMENT_ABORTED,         /* The protocol aborted */
   SILC_KEY_AGREEMENT_ALREADY_STARTED,  /* Already started */
   SILC_KEY_AGREEMENT_SELF_DENIED,      /* Negotiationg with itself denied */
+  SILC_KEY_AGREEMENT_NO_MEMORY,        /* System out of memory */
 } SilcKeyAgreementStatus;
 /***/
 
@@ -643,11 +644,6 @@ typedef struct {
      in the callbacks to protect application specific data. */
   SilcBool threads;
 
-  /* Number of maximum tasks the client library's scheduler can handle.
-     If set to zero default value will be used.  For WIN32 systems this
-     should be set to 64 as it is the hard limit dictated  by the WIN32. */
-  int task_max;
-
   /* Rekey timeout in seconds. The client will perform rekey in this
      time interval. If set to zero, the default value will be used. */
   unsigned int rekey_secs;