Added entry locking using read/write locks.
authorPekka Riikonen <priikone@silcnet.org>
Mon, 29 Jan 2007 16:04:12 +0000 (16:04 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Mon, 29 Jan 2007 16:04:12 +0000 (16:04 +0000)
17 files changed:
lib/silcclient/README
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_channel.c
lib/silcclient/client_channel.h
lib/silcclient/client_connect.c
lib/silcclient/client_entry.c
lib/silcclient/client_entry.h
lib/silcclient/client_internal.h
lib/silcclient/client_keyagr.c
lib/silcclient/client_notify.c
lib/silcclient/client_prvmsg.c
lib/silcclient/command.c
lib/silcclient/command.h
lib/silcclient/command_reply.c
lib/silcclient/silcclient.h
lib/silcclient/silcclient_entry.h

index a1a73f51e3e48dbb0ea91771d69c4c82dc09d0b7..7a475336b636ff7bdb2e546f0b0f04ae10d616e5 100644 (file)
@@ -82,12 +82,12 @@ Finishing threads when closing connection
    When closing SilcClientConnection all threads must first be finished
    before the connection machine is finished.  This is done by finishing
    all running command threads.  That will also finish all waiting packet
-   and notify threads as they are always waiting for a command.  If any 
-   thread is waiting for something else than a command (such as event 
-   threads) they must be explicitly finished.  The threads are finished by 
-   continuing them synchronously.  The threads will detect that we are 
-   disconnected (see below).  SILC_FSM_YIELD must be returned in 
-   st_close() as that gives the FSM scheduler time to finish the threads 
+   and notify threads as they are always waiting for a command.  If any
+   thread is waiting for something else than a command (such as event
+   threads) they must be explicitly finished.  The threads are finished by
+   continuing them synchronously.  The threads will detect that we are
+   disconnected (see below).  SILC_FSM_YIELD must be returned in
+   st_close() as that gives the FSM scheduler time to finish the threads
    first.  After that the machine can be finished.
 
    Also, any thread that runs in SilcClientConnection machine must always
@@ -101,3 +101,13 @@ Finishing threads when closing connection
    If disconnected the thread must finish immediately by returning
    SILC_FSM_FINISH.
 
+Entry locking
+
+   All entires have a read/write lock.  It is used to protect the entries
+   when library updates them at the same time application is reading data
+   from the entries.  An API for application to read-lock the entires
+   exist.  Library only needs to use write-lock.  Using read-lock is not
+   necessary inside library because all connection related stuff, including
+   updating connection related entries are done in one thread.  When library
+   is reading data from the entries it cannot be updating them at the same
+   time.  Hence, using read-lock inside library is not necessary.
index f52ff134d72d7d6223284e02c7480d6aaa6a5f44..72a4800fb1b591338e257251d7a9c00562d3cd5b 100644 (file)
@@ -236,6 +236,7 @@ SILC_FSM_STATE(silc_client_connection_st_run)
   if (conn->internal->connect) {
     SILC_LOG_DEBUG(("Event: connect"));
     conn->internal->connect = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: connect */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
@@ -247,6 +248,7 @@ SILC_FSM_STATE(silc_client_connection_st_run)
   if (conn->internal->key_exchange) {
     SILC_LOG_DEBUG(("Event: key exchange"));
     conn->internal->key_exchange = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: key exchange */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
@@ -258,6 +260,7 @@ SILC_FSM_STATE(silc_client_connection_st_run)
   if (conn->internal->rekeying) {
     SILC_LOG_DEBUG(("Event: rekey"));
     conn->internal->rekeying = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: rekey */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
@@ -377,7 +380,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
 /* Disconnection event to close remote connection.  We close the connection
    and finish the connection machine in this state.  The connection context
    is deleted in the machine destructor.  The connection callback is called
-   in this state if it set. */
+   in this state if it is set. */
 
 SILC_FSM_STATE(silc_client_connection_st_close)
 {
@@ -848,6 +851,7 @@ void silc_client_close_connection(SilcClient client,
   SILC_LOG_DEBUG(("Closing connection %p", conn));
 
   /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
index 1a6da14ebbdd0547ccb7e7ad5364aa1665a663e9..dd4eb8a943170ad8dce9b42b55262f9bf4b2ab43 100644 (file)
@@ -31,7 +31,6 @@ typedef struct SilcClientEntryStruct *SilcClientEntry;
 typedef struct SilcChannelEntryStruct *SilcChannelEntry;
 typedef struct SilcServerEntryStruct *SilcServerEntry;
 
-typedef struct SilcClientAwayStruct SilcClientAway;
 typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement;
 typedef struct SilcClientFtpSessionStruct *SilcClientFtpSession;
 typedef struct SilcClientCommandReplyContextStruct
@@ -45,6 +44,7 @@ typedef struct SilcChannelPrivateKeyStruct *SilcChannelPrivateKey;
 
 /* Internal client entry context */
 typedef struct SilcClientEntryInternalStruct {
+  SilcRwLock lock;             /* Read/write lock */
   SilcCipher send_key;         /* Private message key for sending */
   SilcCipher receive_key;      /* Private message key for receiving */
   SilcHmac hmac_send;          /* Private mesage key HMAC for sending */
@@ -65,6 +65,8 @@ typedef struct SilcClientEntryInternalStruct {
 
 /* Internal channel entry context */
 typedef struct SilcChannelEntryInternalStruct {
+  SilcRwLock lock;                          /* Read/write lock */
+
   /* SilcChannelEntry status information */
   SilcDList old_channel_keys;
   SilcDList old_hmacs;
@@ -90,6 +92,7 @@ typedef struct SilcChannelEntryInternalStruct {
 
 /* Internal server entry context */
 typedef struct SilcServerEntryInternalStruct {
+  SilcRwLock lock;                          /* Read/write lock */
   SilcUInt16 resolve_cmd_ident;                     /* Resolving identifier */
   SilcAtomic8 refcnt;                       /* Reference counter */
 } SilcServerEntryInternal;
index 4d320bfbe10abc364b8c4e685185cc65250acfe0..e8cfa7d1a4c06b07bd583e77b8335bf23eb8447c 100644 (file)
@@ -702,7 +702,8 @@ 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. */
+   added to the channel, FALSE on error.  Must be called with both `channel'
+   and `client_entry' locked. */
 
 SilcBool silc_client_add_to_channel(SilcClient client,
                                    SilcClientConnection conn,
@@ -734,7 +735,7 @@ SilcBool silc_client_add_to_channel(SilcClient client,
   return TRUE;
 }
 
-/* Removes client from a channel */
+/* Removes client from a channel.  This handles entry locking internally. */
 
 SilcBool silc_client_remove_from_channel(SilcClient client,
                                         SilcClientConnection conn,
@@ -749,6 +750,9 @@ SilcBool silc_client_remove_from_channel(SilcClient client,
 
   SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname));
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+  silc_rwlock_wrlock(channel->internal.lock);
+
   silc_hash_table_del(chu->client->channels, chu->channel);
   silc_hash_table_del(chu->channel->user_list, chu->client);
   silc_free(chu);
@@ -757,13 +761,17 @@ SilcBool silc_client_remove_from_channel(SilcClient client,
   if (!silc_hash_table_count(channel->user_list))
     silc_client_del_channel(client, conn, channel);
 
+  silc_rwlock_unlock(client_entry->internal.lock);
+  silc_rwlock_unlock(channel->internal.lock);
+
   silc_client_unref_client(client, conn, client_entry);
   silc_client_unref_channel(client, conn, channel);
 
   return TRUE;
 }
 
-/* Removes a client entry from all channels it has joined. */
+/* Removes a client entry from all channels it has joined.  This handles
+   entry locking internally. */
 
 void silc_client_remove_from_channels(SilcClient client,
                                      SilcClientConnection conn,
@@ -777,8 +785,12 @@ void silc_client_remove_from_channels(SilcClient client,
 
   SILC_LOG_DEBUG(("Remove client from all joined channels"));
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+
   silc_hash_table_list(client_entry->channels, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+    silc_rwlock_wrlock(chu->channel->internal.lock);
+
     silc_hash_table_del(chu->client->channels, chu->channel);
     silc_hash_table_del(chu->channel->user_list, chu->client);
 
@@ -786,15 +798,19 @@ void silc_client_remove_from_channels(SilcClient client,
     if (!silc_hash_table_count(chu->channel->user_list))
       silc_client_del_channel(client, conn, chu->channel);
 
+    silc_rwlock_unlock(chu->channel->internal.lock);
+
     silc_client_unref_client(client, conn, chu->client);
     silc_client_unref_channel(client, conn, chu->channel);
     silc_free(chu);
   }
 
+  silc_rwlock_unlock(client_entry->internal.lock);
+
   silc_hash_table_list_reset(&htl);
 }
 
-/* Empties channel from users. */
+/* Empties channel from users.  This handles entry locking internally. */
 
 void silc_client_empty_channel(SilcClient client,
                               SilcClientConnection conn,
@@ -803,6 +819,8 @@ void silc_client_empty_channel(SilcClient client,
   SilcHashTableList htl;
   SilcChannelUser chu;
 
+  silc_rwlock_wrlock(channel->internal.lock);
+
   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);
@@ -811,5 +829,53 @@ void silc_client_empty_channel(SilcClient client,
     silc_client_unref_channel(client, conn, chu->channel);
     silc_free(chu);
   }
+
+  silc_rwlock_unlock(channel->internal.lock);
+
   silc_hash_table_list_reset(&htl);
 }
+
+/* Save public keys to channel public key list.  Removes keys that are
+   marked to be removed.  Must be called with `channel' locked. */
+
+SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel,
+                                             unsigned char *chpk_list,
+                                             SilcUInt32 chpk_list_len)
+{
+  SilcArgumentDecodedList a, b;
+  SilcDList chpks;
+  SilcBool found;
+
+  chpks = silc_argument_list_parse_decoded(chpk_list, chpk_list_len,
+                                          SILC_ARGUMENT_PUBLIC_KEY);
+  if (!chpks)
+    return FALSE;
+
+  if (!channel->channel_pubkeys) {
+    channel->channel_pubkeys = chpks;
+    return TRUE;
+  }
+
+  silc_dlist_start(chpks);
+  while ((a = silc_dlist_get(chpks))) {
+    found = FALSE;
+    silc_dlist_start(channel->channel_pubkeys);
+    while ((b = silc_dlist_get(channel->channel_pubkeys))) {
+      if (silc_pkcs_public_key_compare(a->argument, b->argument)) {
+       found = TRUE;
+       break;
+      }
+    }
+
+    if ((a->arg_type == 0x00 || a->arg_type == 0x03) && !found) {
+      silc_dlist_add(channel->channel_pubkeys, a);
+      silc_dlist_del(chpks, a);
+    } else if (a->arg_type == 0x01 && found) {
+      silc_dlist_del(channel->channel_pubkeys, b);
+    }
+  }
+
+  silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
+
+  return TRUE;
+}
index ed590e07c20f808dfef7ee05ba38075a2703eb22..6ec49c6c2356f53c34cf1bfa7bd3c1baa6333d17 100644 (file)
@@ -45,5 +45,8 @@ void silc_client_remove_from_channels(SilcClient client,
 void silc_client_empty_channel(SilcClient client,
                               SilcClientConnection conn,
                               SilcChannelEntry channel);
+SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel,
+                                             unsigned char *chpk_list,
+                                             SilcUInt32 chpk_list_len);
 
 #endif /* CLIENT_CHANNEL_H */
index 78e24c60189ce2bbdb3aa0dd4f50c93d6f8e6c14..c40856a354cebc76eb745beae65d2c10f43b5f12 100644 (file)
@@ -95,7 +95,7 @@ static void silc_client_connect_callback(SilcNetStatus status,
 
 static void silc_client_ke_verify_key_cb(SilcBool success, void *context)
 {
-  VerifyKeyContext verify = (VerifyKeyContext)context;
+  SilcVerifyKeyContext verify = context;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -118,7 +118,7 @@ static void silc_client_ke_verify_key(SilcSKE ske,
   SilcFSMThread fsm = context;
   SilcClientConnection conn = silc_fsm_get_context(fsm);
   SilcClient client = conn->client;
-  VerifyKeyContext verify;
+  SilcVerifyKeyContext verify;
 
   /* If we provided repository for SKE and we got here the key was not
      found from the repository. */
index a643597dae9b5c45e5b1d43d2c51ff60f162613a..706a4afecbe1607d83fd4de2b35314e327df692b 100644 (file)
@@ -696,6 +696,7 @@ SilcClientEntry silc_client_add_client(SilcClient client,
   if (!client_entry)
     return NULL;
 
+  silc_rwlock_alloc(&client_entry->internal.lock);
   silc_atomic_init8(&client_entry->internal.refcnt, 0);
   client_entry->id = *id;
   client_entry->internal.valid = TRUE;
@@ -756,7 +757,8 @@ SilcClientEntry silc_client_add_client(SilcClient client,
   return client_entry;
 }
 
-/* Updates the `client_entry' with the new information sent as argument. */
+/* Updates the `client_entry' with the new information sent as argument.
+   This handles entry locking internally. */
 
 void silc_client_update_client(SilcClient client,
                               SilcClientConnection conn,
@@ -770,6 +772,8 @@ void silc_client_update_client(SilcClient client,
 
   SILC_LOG_DEBUG(("Update client entry"));
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+
   if (!client_entry->realname && userinfo)
     client_entry->realname = strdup(userinfo);
   if ((!client_entry->username[0] || !client_entry->hostname[0]) && username)
@@ -787,8 +791,10 @@ void silc_client_update_client(SilcClient client,
     nick = silc_identifier_check(client_entry->nickname,
                                 strlen(client_entry->nickname),
                                 SILC_STRING_UTF8, 128, NULL);
-    if (!nick)
+    if (!nick) {
+      silc_rwlock_unlock(client_entry->internal.lock);
       return;
+    }
 
     /* Format nickname */
     silc_client_nickname_format(client, conn, client_entry,
@@ -802,9 +808,11 @@ void silc_client_update_client(SilcClient client,
     client_entry->nickname_normalized = nick;
   }
   client_entry->mode = mode;
+
+  silc_rwlock_unlock(client_entry->internal.lock);
 }
 
-/* Change a client's nickname */
+/* Change a client's nickname.  Must be called with `client_entry' locked. */
 
 SilcBool silc_client_change_nickname(SilcClient client,
                                     SilcClientConnection conn,
@@ -881,6 +889,7 @@ void silc_client_del_client_entry(SilcClient client,
     silc_client_abort_key_agreement(client, conn, client_entry);
 #endif /* 0 */
   silc_atomic_uninit8(&client_entry->internal.refcnt);
+  silc_rwlock_free(client_entry->internal.lock);
   silc_free(client_entry);
 }
 
@@ -915,15 +924,31 @@ SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
   return ret;
 }
 
+/* Lock client */
+
+void silc_client_lock_client(SilcClientEntry client_entry)
+{
+  silc_rwlock_rdlock(client_entry->internal.lock);
+}
+
+/* Unlock client */
+
+void silc_client_unlock_client(SilcClientEntry client_entry)
+{
+  silc_rwlock_unlock(client_entry->internal.lock);
+}
+
 /* Take reference of client entry */
 
-void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
-                           SilcClientEntry client_entry)
+SilcClientEntry silc_client_ref_client(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry client_entry)
 {
   silc_atomic_add_int8(&client_entry->internal.refcnt, 1);
   SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry,
                  silc_atomic_get_int8(&client_entry->internal.refcnt) - 1,
                  silc_atomic_get_int8(&client_entry->internal.refcnt)));
+  return client_entry;
 }
 
 /* Release reference of client entry */
@@ -1413,6 +1438,7 @@ SilcChannelEntry silc_client_add_channel(SilcClient client,
   if (!channel)
     return NULL;
 
+  silc_rwlock_alloc(&channel->internal.lock);
   silc_atomic_init16(&channel->internal.refcnt, 0);
   channel->id = *channel_id;
   channel->mode = mode;
@@ -1516,6 +1542,7 @@ SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
                            SILC_ARGUMENT_PUBLIC_KEY);
   silc_client_del_channel_private_keys(client, conn, channel);
   silc_atomic_uninit16(&channel->internal.refcnt);
+  silc_rwlock_free(channel->internal.lock);
   silc_schedule_task_del_by_context(conn->client->schedule, channel);
   silc_free(channel);
 
@@ -1523,7 +1550,7 @@ SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
 }
 
 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
-   if the ID could not be changed. */
+   if the ID could not be changed.  This handles entry locking internally. */
 
 SilcBool silc_client_replace_channel_id(SilcClient client,
                                        SilcClientConnection conn,
@@ -1541,23 +1568,41 @@ SilcBool silc_client_replace_channel_id(SilcClient client,
                  silc_id_render(new_id, SILC_ID_CHANNEL)));
 
   /* Update the ID */
+  silc_rwlock_wrlock(channel->internal.lock);
   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);
+  silc_rwlock_unlock(channel->internal.lock);
 
   return ret;
 }
 
+/* Lock channel */
+
+void silc_client_lock_channel(SilcChannelEntry channel_entry)
+{
+  silc_rwlock_rdlock(channel_entry->internal.lock);
+}
+
+/* Unlock client */
+
+void silc_client_unlock_channel(SilcChannelEntry channel_entry)
+{
+  silc_rwlock_unlock(channel_entry->internal.lock);
+}
+
 /* Take reference of channel entry */
 
-void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
-                            SilcChannelEntry channel_entry)
+SilcChannelEntry silc_client_ref_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel_entry)
 {
   silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
   SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
                  silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
                  silc_atomic_get_int16(&channel_entry->internal.refcnt)));
+  return channel_entry;
 }
 
 /* Release reference of channel entry */
@@ -1805,6 +1850,7 @@ SilcServerEntry silc_client_add_server(SilcClient client,
   if (!server_entry)
     return NULL;
 
+  silc_rwlock_alloc(&server_entry->internal.lock);
   silc_atomic_init8(&server_entry->internal.refcnt, 0);
   server_entry->id = *server_id;
   if (server_name)
@@ -1870,6 +1916,7 @@ SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
   if (server->public_key)
     silc_pkcs_public_key_free(server->public_key);
   silc_atomic_uninit8(&server->internal.refcnt);
+  silc_rwlock_free(server->internal.lock);
   silc_free(server);
 
   return ret;
@@ -1916,15 +1963,31 @@ void silc_client_update_server(SilcClient client,
   }
 }
 
+/* Lock server */
+
+void silc_client_lock_server(SilcServerEntry server_entry)
+{
+  silc_rwlock_rdlock(server_entry->internal.lock);
+}
+
+/* Unlock server */
+
+void silc_client_unlock_server(SilcServerEntry server_entry)
+{
+  silc_rwlock_unlock(server_entry->internal.lock);
+}
+
 /* Take reference of server entry */
 
-void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
-                           SilcServerEntry server_entry)
+SilcServerEntry silc_client_ref_server(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcServerEntry server_entry)
 {
   silc_atomic_add_int8(&server_entry->internal.refcnt, 1);
   SILC_LOG_DEBUG(("Server %p refcnt %d->%d", server_entry,
                  silc_atomic_get_int8(&server_entry->internal.refcnt) - 1,
                  silc_atomic_get_int8(&server_entry->internal.refcnt)));
+  return server_entry;
 }
 
 /* Release reference of server entry */
index cc53370e68f799a5b65063b774be090fa1bb1cee..bdd70f26f72d159ebc3d2aeed150ed94041d6887 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2006 Pekka Riikonen
+  Copyright (C) 2006 - 2007 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
@@ -42,6 +42,8 @@ SilcBool silc_client_change_nickname(SilcClient client,
 void silc_client_del_client_entry(SilcClient client,
                                  SilcClientConnection conn,
                                  SilcClientEntry client_entry);
+SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+                               SilcClientEntry client_entry);
 SilcClientEntry silc_idlist_get_client(SilcClient client,
                                       SilcClientConnection conn,
                                       const char *nickname,
@@ -52,6 +54,12 @@ SilcChannelEntry silc_client_add_channel(SilcClient client,
                                         const char *channel_name,
                                         SilcUInt32 mode,
                                         SilcChannelID *channel_id);
+SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
+                                SilcChannelEntry channel);
+bool silc_client_replace_channel_id(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcChannelEntry channel,
+                                   SilcChannelID *new_id);
 SilcServerEntry silc_client_add_server(SilcClient client,
                                       SilcClientConnection conn,
                                       const char *server_name,
@@ -62,10 +70,8 @@ void silc_client_update_server(SilcClient client,
                               SilcServerEntry server_entry,
                               const char *server_name,
                               const char *server_info);
-bool silc_client_replace_channel_id(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcChannelEntry channel,
-                                   SilcChannelID *new_id);
+SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
+                               SilcServerEntry server);
 SilcBool silc_client_nickname_parse(SilcClient client,
                                    SilcClientConnection conn,
                                    char *nickname,
index 91243d8f0564da73ad522eedad4bfbbcf41647a4..414aa4cfb295bef2198f87867334b8f7aca166ae 100644 (file)
@@ -47,16 +47,7 @@ typedef struct {
   SilcSKE ske;
   SilcSKEVerifyCbCompletion completion;
   void *completion_context;
-} *VerifyKeyContext;
-
-/* Structure to hold away messages set by user. This is mainly created
-   for future extensions where away messages could be set according filters
-   such as nickname and hostname. For now only one away message can
-   be set in one connection. */
-struct SilcClientAwayStruct {
-  char *away;
-  struct SilcClientAwayStruct *next;
-};
+} *SilcVerifyKeyContext;
 
 /* Command and command reply context used to hold registered commands
    in the SILC client. */
@@ -105,7 +96,7 @@ typedef struct SilcClientCommandContextStruct {
 /* Internal context for the client->internal pointer in the SilcClient. */
 struct SilcClientInternalStruct {
   SilcFSMStruct fsm;                    /* Client's FSM */
-  SilcFSMEventStruct wait_event;                /* Event signaller */
+  SilcFSMEventStruct wait_event;        /* Event signaller */
   SilcClientOperations *ops;            /* Client operations */
   SilcClientParams *params;             /* Client parameters */
   SilcPacketEngine packet_engine;        /* Packet engine */
@@ -144,6 +135,8 @@ struct SilcClientConnectionInternalStruct {
   SilcAsyncOperation cop;               /* Async operation for application */
   SilcHashTable attrs;                  /* Configured user attributes */
   char *disconnect_message;             /* Disconnection message */
+  char *away_message;                   /* Away message */
+  void *prv_waiter;                     /* Private message packet waiter */
 
   SilcIDCache client_cache;             /* Client entry cache */
   SilcIDCache channel_cache;            /* Channel entry cache */
@@ -166,10 +159,6 @@ struct SilcClientConnectionInternalStruct {
   unsigned int registering        : 1;  /* Set when registering to network */
   unsigned int rekey_responder    : 1;   /* Set when rekeying as responder */
   unsigned int auth_request       : 1;   /* Set when requesting auth method */
-
-  SilcClientAway *away;
-  SilcClientFtpSession active_session;
-  SilcHashTable privmsg_wait;           /* Waited private messages */
 };
 
 SILC_FSM_STATE(silc_client_connection_st_run);
@@ -180,29 +169,8 @@ SILC_FSM_STATE(silc_client_disconnect);
 SILC_FSM_STATE(silc_client_st_stop);
 
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
-SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
-                               SilcClientEntry client_entry);
-SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
-                                SilcChannelEntry channel);
-SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn,
-                               SilcServerEntry server);
-SilcUInt16 silc_client_command_send_argv(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcCommand command,
-                                        SilcClientCommandReply reply,
-                                        void *reply_context,
-                                        SilcUInt32 argc,
-                                        unsigned char **argv,
-                                        SilcUInt32 *argv_lens,
-                                        SilcUInt32 *argv_types);
-void silc_client_command_free(SilcClientCommandContext cmd);
 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
                                void *destructor_context);
+void silc_client_command_free(SilcClientCommandContext cmd);
 
-void silc_client_ftp(SilcClient client, SilcClientConnection conn,
-                    SilcPacket packet);
-void silc_client_connection_auth_request(SilcClient client,
-                                        SilcClientConnection conn,
-                                        SilcPacket packet);
-
-#endif
+#endif /* CLIENT_INTERNAL_H */
index 63aed9d1dc065c0498105ffafeccbba4645c722a..efabe7582285f7b603a1d06c942b87e4c73ead07 100644 (file)
@@ -103,7 +103,7 @@ static void silc_client_keyagr_resolved(SilcClient client,
 
 static void silc_client_keyagr_verify_key_cb(SilcBool success, void *context)
 {
-  VerifyKeyContext verify = context;
+  SilcVerifyKeyContext verify = context;
 
   /* Call the completion callback back to the SKE */
   verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
@@ -125,7 +125,7 @@ static void silc_client_keyagr_verify_key(SilcSKE ske,
   SilcClientKeyAgreement ke = client_entry->internal.ke;
   SilcClientConnection conn = ke->conn;
   SilcClient client = conn->client;
-  VerifyKeyContext verify;
+  SilcVerifyKeyContext verify;
 
   /* If we provided repository for SKE and we got here the key was not
      found from the repository. */
index 740e3e5942e89a92bf9bedec93ce4df60b682231..a492931b26fd131ed58040537c62c6a28a6f2791 100644 (file)
@@ -379,12 +379,21 @@ SILC_FSM_STATE(silc_client_notify_join)
     /* NOT REACHED */
   }
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+  silc_rwlock_wrlock(channel->internal.lock);
+
   if (client_entry != conn->local_entry)
     silc_client_nickname_format(client, conn, client_entry, FALSE);
 
   /* Join the client to channel */
-  if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0))
+  if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0)) {
+    silc_rwlock_unlock(channel->internal.lock);
+    silc_rwlock_unlock(client_entry->internal.lock);
     goto out;
+  }
+
+  silc_rwlock_unlock(channel->internal.lock);
+  silc_rwlock_unlock(client_entry->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
@@ -617,8 +626,10 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
     entry = channel_entry;
   }
 
+  silc_rwlock_wrlock(channel->internal.lock);
   silc_free(channel->topic);
   channel->topic = silc_memdup(tmp, strlen(tmp));
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
@@ -691,6 +702,8 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
   if (!tmp)
     goto out;
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+
   /* Check whether nickname changed at all.  It is possible that nick
      change notify is received but nickname didn't change, only the
      ID changes.  If Client ID hash match, nickname didn't change. */
@@ -702,14 +715,19 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
     silc_idcache_update_by_context(conn->internal->client_cache, client_entry,
                                   &id2.u.client_id, NULL, FALSE);
     silc_mutex_unlock(conn->internal->lock);
+    silc_rwlock_unlock(client_entry->internal.lock);
     goto out;
   }
 
   /* Change the nickname */
   memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
   if (!silc_client_change_nickname(client, conn, client_entry, tmp,
-                                  &id2.u.client_id, NULL, 0))
+                                  &id2.u.client_id, NULL, 0)) {
+    silc_rwlock_unlock(client_entry->internal.lock);
     goto out;
+  }
+
+  silc_rwlock_unlock(client_entry->internal.lock);
 
   /* Notify application */
   NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
@@ -822,11 +840,15 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     entry = channel_entry;
   }
 
+  silc_rwlock_wrlock(channel->internal.lock);
+
   /* Get the channel founder key if it was set */
   tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
   if (tmp) {
-    if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key))
+    if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
+    }
     if (!channel->founder_key) {
       channel->founder_key = founder_key;
       founder_key = NULL;
@@ -842,8 +864,10 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     unsigned char hash[SILC_HASH_MAXLEN];
     SilcHmac newhmac;
 
-    if (!silc_hmac_alloc(hmac, NULL, &newhmac))
+    if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
+    }
 
     /* Get HMAC key from the old HMAC context, and update it to the new one */
     tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
@@ -874,10 +898,9 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
   /* Get the channel public key that was added or removed */
   tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
   if (tmp)
-    chpks = silc_argument_list_parse_decoded(tmp, tmp_len,
-                                            SILC_ARGUMENT_PUBLIC_KEY);
+    silc_client_channel_save_public_keys(channel, tmp, tmp_len);
 
-  /* XXX add to/remove from channel pubkeys channel->channel_pubkeys */
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
@@ -1017,9 +1040,11 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
   }
 
   /* Save the mode */
+  silc_rwlock_wrlock(channel->internal.lock);
   chu = silc_client_on_channel(channel, client_entry2);
   if (chu)
     chu->mode = mode;
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
index d5a3ceaf15cbe308cec6eef79fe9d7d6536a9ff4..20370cf6647bc828140da993cec051fce555d3b8 100644 (file)
@@ -154,7 +154,7 @@ SILC_FSM_STATE(silc_client_private_message)
 
   /* See if we are away (gone). If we are away we will reply to the
      sender with the set away message. */
-  if (conn->internal->away && conn->internal->away->away &&
+  if (conn->internal->away_message &&
       !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
     /* If it's me, ignore */
     if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
@@ -164,8 +164,8 @@ SILC_FSM_STATE(silc_client_private_message)
     silc_client_send_private_message(client, conn, remote_client,
                                     SILC_MESSAGE_FLAG_AUTOREPLY |
                                     SILC_MESSAGE_FLAG_NOREPLY, NULL,
-                                    conn->internal->away->away,
-                                    strlen(conn->internal->away->away));
+                                    conn->internal->away_message,
+                                    strlen(conn->internal->away_message));
   }
 
  out:
@@ -186,21 +186,51 @@ SILC_FSM_STATE(silc_client_private_message_error)
   return SILC_FSM_FINISH;
 }
 
+/* Initialize private message waiter for the `conn' connection. */
+
+SilcBool silc_client_private_message_wait_init(SilcClient client,
+                                              SilcClientConnection conn)
+{
+  if (conn->internal->prv_waiter)
+    return TRUE;
+
+  conn->internal->prv_waiter =
+    silc_packet_wait_init(conn->stream, SILC_PACKET_PRIVATE_MESSAGE, -1);
+  if (!conn->internal->prv_waiter)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Uninitializes private message waiter. */
+
+void silc_client_private_message_wait_uninit(SilcClient client,
+                                            SilcClientConnection conn)
+{
+  if (!conn->internal->prv_waiter)
+    return;
+  silc_packet_wait_uninit(conn->internal->prv_waiter, conn->stream);
+  conn->internal->prv_waiter = NULL;
+}
+
 /* Blocks the calling process or thread until private message has been
    received from the specified client. */
 
-SilcBool silc_client_private_message_wait(SilcClientConnection conn,
+SilcBool silc_client_private_message_wait(SilcClient client,
+                                         SilcClientConnection conn,
                                          SilcClientEntry client_entry,
-                                         void *waiter,
                                          SilcMessagePayload *payload)
 {
   SilcPacket packet;
   SilcClientID remote_id;
   SilcFSMThread thread;
 
+  if (!conn->internal->prv_waiter)
+    return FALSE;
+
   /* Block until private message arrives */
   do {
-    if ((silc_packet_wait(waiter, 0, &packet)) < 0)
+    if ((silc_packet_wait(conn->internal->prv_waiter, 0, &packet)) < 0)
       return FALSE;
 
     /* Parse sender ID */
@@ -589,23 +619,25 @@ void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
    new one.  If `message' is NULL the old away message is removed.
    The sender may freely free the memory of the `message'. */
 
-void silc_client_set_away_message(SilcClient client,
-                                 SilcClientConnection conn,
-                                 char *message)
+SilcBool silc_client_set_away_message(SilcClient client,
+                                     SilcClientConnection conn,
+                                     char *message)
 {
-  assert(client && conn);
+  if (!client || !conn)
+    return FALSE;
 
-  if (!message && conn->internal->away) {
-    silc_free(conn->internal->away->away);
-    silc_free(conn->internal->away);
-    conn->internal->away = NULL;
+  if (!message) {
+    silc_free(conn->internal->away_message);
+    conn->internal->away_message = NULL;
+    return TRUE;
   }
 
-  if (message) {
-    if (!conn->internal->away)
-      conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
-    if (conn->internal->away->away)
-      silc_free(conn->internal->away->away);
-    conn->internal->away->away = strdup(message);
-  }
+  if (conn->internal->away_message)
+    silc_free(conn->internal->away_message);
+
+  conn->internal->away_message = strdup(message);
+  if (!conn->internal->away_message)
+    return FALSE;
+
+  return TRUE;
 }
index ade3e1b82702ff68d9ee4438378ac3c02d933893..99fe98a974ba7fbccaaee1279782cfaf31944ad7 100644 (file)
@@ -1121,7 +1121,6 @@ SILC_FSM_STATE(silc_client_command_quit)
 
 /********************************** KILL ************************************/
 
-
 /* Command KILL. Router operator can use this command to remove an client
    fromthe SILC Network. */
 
index dfcb60c20bb0827ae74454a5adeee50acd37354d..ef535b9264cef2f80d5682dce66a8f89c01a6889 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2006 Pekka Riikonen
+  Copyright (C) 2006 - 2007 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
@@ -48,6 +48,15 @@ SILC_FSM_STATE(silc_client_command_users);
 SILC_FSM_STATE(silc_client_command_getkey);
 SILC_FSM_STATE(silc_client_command_service);
 
+SilcUInt16 silc_client_command_send_argv(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcCommand command,
+                                        SilcClientCommandReply reply,
+                                        void *reply_context,
+                                        SilcUInt32 argc,
+                                        unsigned char **argv,
+                                        SilcUInt32 *argv_lens,
+                                        SilcUInt32 *argv_types);
 void silc_client_commands_register(SilcClient client);
 void silc_client_commands_unregister(SilcClient client);
 
index 7d5a01e2f6f23d852c321228f428c61a82fd732d..cf4e151bbff63356ebd77a56c6983638adb67417 100644 (file)
@@ -480,6 +480,8 @@ SILC_FSM_STATE(silc_client_command_reply_whois)
                              nickname, username, realname, mode);
   }
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+
   if (fingerprint && fingerprint_len == sizeof(client_entry->fingerprint))
     memcpy(client_entry->fingerprint, fingerprint, fingerprint_len);
 
@@ -491,6 +493,8 @@ SILC_FSM_STATE(silc_client_command_reply_whois)
     client_entry->attrs = silc_attribute_payload_parse(tmp, len);
   }
 
+  silc_rwlock_unlock(client_entry->internal.lock);
+
   /* Parse channel and channel user mode list */
   if (has_channels) {
     channel_list = silc_channel_payload_parse_list(silc_buffer_data(&channels),
@@ -718,6 +722,8 @@ SILC_FSM_STATE(silc_client_command_reply_nick)
     goto out;
   }
 
+  silc_rwlock_wrlock(conn->local_entry->internal.lock);
+
   /* Change the nickname */
   old_client_id = *conn->local_id;
   if (!silc_client_change_nickname(client, conn, conn->local_entry,
@@ -726,6 +732,8 @@ SILC_FSM_STATE(silc_client_command_reply_nick)
     goto out;
   }
 
+  silc_rwlock_unlock(conn->local_entry->internal.lock);
+
   /* Notify application */
   silc_client_command_callback(cmd, conn->local_entry,
                               conn->local_entry->nickname, &old_client_id);
@@ -830,6 +838,8 @@ SILC_FSM_STATE(silc_client_command_reply_topic)
     goto out;
   }
 
+  silc_rwlock_wrlock(channel->internal.lock);
+
   /* Take topic */
   topic = silc_argument_get_arg_type(args, 3, &len);
   if (topic) {
@@ -837,6 +847,8 @@ SILC_FSM_STATE(silc_client_command_reply_topic)
     channel->topic = silc_memdup(topic, len);
   }
 
+  silc_rwlock_unlock(channel->internal.lock);
+
   /* Notify application */
   silc_client_command_callback(cmd, channel, channel->topic);
 
@@ -1185,6 +1197,8 @@ SILC_FSM_STATE(silc_client_command_reply_join)
   }
   silc_buffer_set(&client_mode_list, tmp, len);
 
+  silc_rwlock_wrlock(channel->internal.lock);
+
   /* Add clients we received in the reply to the channel */
   for (i = 0; i < list_count; i++) {
     SilcUInt16 idp_len;
@@ -1207,13 +1221,19 @@ SILC_FSM_STATE(silc_client_command_reply_join)
       continue;
 
     /* Join client to the channel */
+    silc_rwlock_wrlock(client_entry->internal.lock);
     silc_client_add_to_channel(client, conn, channel, client_entry, mode);
+    silc_rwlock_unlock(client_entry->internal.lock);
     silc_client_unref_client(client, conn, client_entry);
 
-    if (!silc_buffer_pull(&client_id_list, idp_len))
+    if (!silc_buffer_pull(&client_id_list, idp_len)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
-    if (!silc_buffer_pull(&client_mode_list, 4))
+    }
+    if (!silc_buffer_pull(&client_mode_list, 4)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
+    }
   }
 
   /* Get hmac */
@@ -1224,6 +1244,7 @@ SILC_FSM_STATE(silc_client_command_reply_join)
        SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
            "Cannot join channel: Unsupported HMAC `%s'", hmac);
       ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
     }
   }
@@ -1267,12 +1288,13 @@ SILC_FSM_STATE(silc_client_command_reply_join)
   /* Get channel public key list */
   tmp = silc_argument_get_arg_type(args, 16, &len);
   if (tmp)
-    channel->channel_pubkeys =
-      silc_argument_list_parse_decoded(tmp, len, SILC_ARGUMENT_PUBLIC_KEY);
+    silc_client_channel_save_public_keys(channel, tmp, len);
 
   /* Set current channel */
   conn->current_channel = channel;
 
+  silc_rwlock_unlock(channel->internal.lock);
+
   cipher = (channel->internal.send_key ?
            silc_cipher_get_name(channel->internal.send_key) : NULL);
   silc_hash_table_list(channel->user_list, &htl);
@@ -1368,7 +1390,9 @@ SILC_FSM_STATE(silc_client_command_reply_umode)
   }
 
   SILC_GET32_MSB(mode, tmp);
+  silc_rwlock_wrlock(conn->local_entry->internal.lock);
   conn->local_entry->mode = mode;
+  silc_rwlock_unlock(conn->local_entry->internal.lock);
 
   /* Notify application */
   silc_client_command_callback(cmd, mode);
@@ -1421,15 +1445,17 @@ SILC_FSM_STATE(silc_client_command_reply_cmode)
     goto out;
   }
 
-  /* Save the mode */
-  SILC_GET32_MSB(mode, tmp);
-  channel->mode = mode;
-
   /* Get founder public key */
   tmp = silc_argument_get_arg_type(args, 4, &len);
   if (tmp)
     silc_public_key_payload_decode(tmp, len, &public_key);
 
+  silc_rwlock_wrlock(channel->internal.lock);
+
+  /* Save the mode */
+  SILC_GET32_MSB(mode, tmp);
+  channel->mode = mode;
+
   /* Get user limit */
   tmp = silc_argument_get_arg_type(args, 6, &len);
   if (tmp && len == 4)
@@ -1440,8 +1466,9 @@ SILC_FSM_STATE(silc_client_command_reply_cmode)
   /* Get channel public key(s) */
   tmp = silc_argument_get_arg_type(args, 5, &len);
   if (tmp)
-    channel_pubkeys =
-      silc_argument_list_parse_decoded(tmp, len, SILC_ARGUMENT_PUBLIC_KEY);
+    silc_client_channel_save_public_keys(channel, tmp, len);
+
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application */
   silc_client_command_callback(cmd, channel, mode, public_key,
@@ -1513,9 +1540,11 @@ SILC_FSM_STATE(silc_client_command_reply_cumode)
   }
 
   /* Save the mode */
+  silc_rwlock_wrlock(channel->internal.lock);
   chu = silc_client_on_channel(channel, client_entry);
   if (chu)
     chu->mode = mode;
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application */
   silc_client_command_callback(cmd, mode, channel, client_entry);
@@ -1877,6 +1906,8 @@ SILC_FSM_STATE(silc_client_command_reply_users)
 
   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
 
+  silc_rwlock_wrlock(channel->internal.lock);
+
   /* Cache the received Client ID's and modes. */
   for (i = 0; i < list_count; i++) {
     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
@@ -1890,14 +1921,21 @@ SILC_FSM_STATE(silc_client_command_reply_users)
     /* Save the client on this channel.  Unknown clients are ignored as they
        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)
+    if (client_entry) {
+      silc_rwlock_wrlock(client_entry->internal.lock);
       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
+      silc_rwlock_unlock(client_entry->internal.lock);
+    }
     silc_client_unref_client(client, conn, client_entry);
 
-    if (!silc_buffer_pull(&client_id_list, idp_len))
+    if (!silc_buffer_pull(&client_id_list, idp_len)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
-    if (!silc_buffer_pull(&client_mode_list, 4))
+    }
+    if (!silc_buffer_pull(&client_mode_list, 4)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
+    }
   }
 
   /* Notify application */
@@ -1959,6 +1997,8 @@ SILC_FSM_STATE(silc_client_command_reply_getkey)
       goto out;
     }
 
+    silc_rwlock_wrlock(client_entry->internal.lock);
+
     /* Save fingerprint */
     if (!client_entry->fingerprint)
       silc_hash_make(conn->internal->sha1hash, tmp + 4, len - 4,
@@ -1968,6 +2008,8 @@ SILC_FSM_STATE(silc_client_command_reply_getkey)
       public_key = NULL;
     }
 
+    silc_rwlock_unlock(client_entry->internal.lock);
+
     /* Notify application */
     silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry,
                                 client_entry->public_key);
@@ -1980,11 +2022,15 @@ SILC_FSM_STATE(silc_client_command_reply_getkey)
       goto out;
     }
 
+    silc_rwlock_wrlock(server_entry->internal.lock);
+
     if (!server_entry->public_key) {
       server_entry->public_key = public_key;
       public_key = NULL;
     }
 
+    silc_rwlock_unlock(server_entry->internal.lock);
+
     /* Notify application */
     silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
                                 server_entry->public_key);
index dc4af8b4fca47c46c5aaef5542bc5b80c289628b..346a166a57ee7214046586ac471ecc78c30e581f 100644 (file)
  * DESCRIPTION
  *
  * This interface defines the SILC Client Library API for the application.
- * The client operations are defined first.  These are callback functions that
- * the application MUST implement since the library may call the functions
- * at any time.  At the end of file is the API for the application that
- * it can use from the library.  This is the only file that the application
- * may include from the SIlC Client Library.
- *
- * o SILC Client Operations
- *
- *   These functions must be implemented by the application calling the SILC
- *   client library. The client library can call these functions at any time.
- *
- *   To use this structure: define a static SilcClientOperations variable,
- *   fill it and pass its pointer to silc_client_alloc function.
- *
- * o SILC Client Library API
- *
- *   This is the API that is published by the SILC Client Library for the
- *   applications.  These functions are implemented in the SILC Client Library.
- *   Application may freely call these functions from the library.
+ * The Client Library is a full features SILC client without a user interface.
+ * A simple interface called SILC Client Operations (SilcClientOperations)
+ * is provided for applications to implmeent the necessary functions to use
+ * the client library.  The silcclient.h header file includes client library
+ * API, such as command handling and message sending.  The silcclient_entry.h
+ * header file includes entry handling, such as channel and user entry
+ * handling.
+ *
+ * Practically all functions in the Client Library API accepts SilcClient
+ * and SilcClientConnection as their first two argument.  The first argument
+ * is the actual SilcClient context and the second is the SilcClientConnection
+ * context of the connection in question.  Application may create and handle
+ * multiple connections in one SilcClient.  Connections can be created to
+ * servers and other clients.
+ *
+ * The Client Library support multiple threads and is threads safe is used
+ * correctly.  Messages can be sent from multiple threads without any
+ * locking.  Messages however are always received only in one thread unless
+ * message waiting (see silc_client_private_message_wait as an example) is
+ * used.  The threads can be turned on and off by giving a parameter to the
+ * SilcClient.  When turned on, each new connection to remote host is always
+ * executed in an own thread.  All tasks related to that connection are then
+ * executed in that thread.  This means that client operation callbacks for
+ * that connections may be called from threads and application will need to
+ * employ concurrency control if the callbacks need to access shared data
+ * in the application.  Messages are also received in that thread.
+ *
+ * All entries (SilcClientEntry, SilcChannelEntry and SilcServerEntry) are
+ * reference counted.  If application wishes to save an entry pointer it must
+ * always first acquire a reference.  The reference must be released once the
+ * entry is not needed anymore.  If application wants to read any data from
+ * the entry structure it must first lock the entry.  This protects access to
+ * the entries in multithreaded environment.  If threads are not used, locking
+ * the entries is not needed.  They however still must be referenced even
+ * when threads are not used.
  *
  ***/
 
@@ -1156,16 +1172,6 @@ SilcBool silc_client_send_channel_message(SilcClient client,
                                          unsigned char *data,
                                          SilcUInt32 data_len);
 
-/* Block process until channel message from `channel' is received */
-SilcBool
-silc_client_receive_channel_message(SilcClient client,
-                                   SilcClientConnection conn,
-                                   SilcChannelEntry channel,
-                                   SilcClientEntry *return_sender,
-                                   SilcMessageFlags *return_flags,
-                                   const unsigned char **return_message,
-                                   SilcUInt32 *return_message_len);
-
 /****f* silcclient/SilcClientAPI/silc_client_send_private_message
  *
  * SYNOPSIS
@@ -1199,6 +1205,83 @@ SilcBool silc_client_send_private_message(SilcClient client,
                                          unsigned char *data,
                                          SilcUInt32 data_len);
 
+/****f* silcclient/SilcClientAPI/silc_client_private_message_wait_init
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool
+ *    silc_client_private_message_wait_init(SilcClient client,
+ *                                          SilcClientConnection conn);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes private message waiting functionality for the connection
+ *    indicated by `conn'.  Once this is called private message from remote
+ *    connection indicated by `conn' for any client entry beloning to that
+ *    connection may be waited for, for example in an thread.  The function
+ *    silc_client_private_message_wait is used to block the current thread
+ *    until a private message is received from a specified client entry.
+ *    Return FALSE on error.
+ *
+ ***/
+SilcBool silc_client_private_message_wait_init(SilcClient client,
+                                              SilcClientConnection conn);
+
+/****f* silcclient/SilcClientAPI/silc_client_private_message_wait_uninit
+ *
+ * SYNOPSIS
+ *
+ *    void
+ *    silc_client_private_message_wait_uninit(SilcClient client,
+ *                                            SilcClientConnection conn);
+ *
+ * DESCRIPTION
+ *
+ *    Unintializes private message waiting for connection indicated by
+ *    `conn'.  After this call private message cannot be waited anymore.
+ *    This call may be called from any thread.  This call will signal all
+ *    private message waiting threads to stop waiting.
+ *
+ ***/
+void silc_client_private_message_wait_uninit(SilcClient client,
+                                            SilcClientConnection conn);
+
+/****f* silcclient/SilcClientAPI/silc_client_private_message_wait
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool
+ *    silc_client_private_message_wait(SilcClient client,
+ *                                     SilcClientConnection conn,
+ *                                     SilcClientEntry client_entry,
+ *                                     SilcMessagePayload *payload);
+ *
+ * DESCRIPTION
+ *
+ *    Blocks current thread or process until a private message has been
+ *    received from the remote client indicated by `client_entry'.  Before
+ *    private messages can be waited the silc_client_private_message_wait_init
+ *    must be called.  This function can be used from a thread to wait for
+ *    private message from the specified client.  Multiple threads can be
+ *    created to wait messages from multiple clients.  Any other private
+ *    message received from the connection indicated by `conn' will be
+ *    forwarded to the normal `private_message' client operation.  The
+ *    private messages from `client_entry' will not be delivered to the
+ *    `private_message' client operation.
+ *
+ *    Returns TRUE and the received private message into `payload'.  The caller
+ *    must free the returned SilcMessagePayload.  If this function returns
+ *    FALSE the private messages cannot be waited anymore.  This happens
+ *    when some other thread calls silc_client_private_message_wait_uninit.
+ *    This returns FALSE also if silc_client_private_message_wait_init has
+ *    not been called.
+ *
+ ***/
+SilcBool silc_client_private_message_wait(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         SilcMessagePayload *payload);
+
 /****f* silcclient/SilcClientAPI/silc_client_on_channel
  *
  * SYNOPSIS
@@ -1871,9 +1954,9 @@ void silc_client_abort_key_agreement(SilcClient client,
  *
  * SYNOPSIS
  *
- *    void silc_client_set_away_message(SilcClient client,
- *                                      SilcClientConnection conn,
- *                                      char *message);
+ *    SilcBool silc_client_set_away_message(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          char *message);
  *
  * DESCRIPTION
  *
@@ -1883,12 +1966,13 @@ void silc_client_abort_key_agreement(SilcClient client,
  *    automatically back to the the client who send private message.  If
  *    away message is already set this replaces the old message with the
  *    new one.  If `message' is NULL the old away message is removed.
- *    The sender may freely free the memory of the `message'.
+ *    The sender may freely free the memory of the `message'.  Returns
+ *    FALSE on error.
  *
  ***/
-void silc_client_set_away_message(SilcClient client,
-                                 SilcClientConnection conn,
-                                 char *message);
+SilcBool silc_client_set_away_message(SilcClient client,
+                                     SilcClientConnection conn,
+                                     char *message);
 
 /****d* silcclient/SilcClientAPI/SilcClientMonitorStatus
  *
index 8b6cca255d4e4d98a558a62074fb6a7d2752a7de..56cbaf7d6fe5c1c2b82eb0e97e15d257301310ec 100644 (file)
@@ -195,23 +195,59 @@ typedef void (*SilcGetClientCallback)(SilcClient client,
                                      SilcDList clients,
                                      void *context);
 
+/****f* silcclient/SilcClientAPI/silc_client_lock_client
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_lock_client(SilcClientEntry client_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Acquires lock for the client entry indicate by `client_entry'.  When
+ *    application wants to access `client_entry' it must lock the entry
+ *    before reading any data from the `client_entry'.  The lock must be
+ *    unlocked with silc_client_unlock_client.
+ *
+ *    The entry must be unlocked before calling any Client Library API
+ *    functions where the entry is given as argument.
+ *
+ *    This function is not needed if application is not multithreaded
+ *
+ ***/
+void silc_client_lock_client(SilcClientEntry client_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_unlock_client
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_unlock_client(SilcClientEntry client_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Releases the lock acquired with silc_client_lock_client.
+ *
+ ***/
+void silc_client_unlock_client(SilcClientEntry client_entry);
+
 /****f* silcclient/SilcClientAPI/silc_client_ref_client
  *
  * SYNOPSIS
  *
- *    void silc_client_ref_client(SilcClient client,
- *                                SilcClientConnection conn,
- *                                SilcClientEntry client_entry);
+ *    SilcClientEntry
+ *    silc_client_ref_client(SilcClient client,
+ *                           SilcClientConnection conn,
+ *                           SilcClientEntry client_entry);
  *
  * DESCRIPTION
  *
  *    Takes a reference of the client entry indicated by `client_entry'
  *    The reference must be released by calling silc_client_unref_client
- *    after it is not needed anymore.
+ *    after it is not needed anymore.  Returns `client_entry'.
  *
  ***/
-void silc_client_ref_client(SilcClient client, SilcClientConnection conn,
-                           SilcClientEntry client_entry);
+SilcClientEntry silc_client_ref_client(SilcClient client, 
+                                      SilcClientConnection conn,
+                                      SilcClientEntry client_entry);
 
 /****f* silcclient/SilcClientAPI/silc_client_unref_client
  *
@@ -546,23 +582,59 @@ typedef void (*SilcGetChannelCallback)(SilcClient client,
                                       SilcDList channels,
                                       void *context);
 
+/****f* silcclient/SilcClientAPI/silc_client_lock_channel
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_lock_channel(SilcChannelEntry channel_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Acquires lock for the channel entry indicate by `channel_entry'.  When
+ *    application wants to access `channel_entry' it must lock the entry
+ *    before reading any data from the `channel_entry'.  The lock must be
+ *    unlocked with silc_client_unlock_channel.
+ *
+ *    The entry must be unlocked before calling any Client Library API
+ *    functions where the entry is given as argument.
+ *
+ *    This function is not needed if application is not multithreaded
+ *
+ ***/
+void silc_client_lock_channel(SilcChannelEntry channel_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_unlock_channel
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_unlock_channel(SilcChannelEntry channel_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Releases the lock acquired with silc_client_lock_channel.
+ *
+ ***/
+void silc_client_unlock_channel(SilcChannelEntry channel_entry);
+
 /****f* silcclient/SilcClientAPI/silc_client_ref_channel
  *
  * SYNOPSIS
  *
- *    void silc_client_ref_channel(SilcClient client,
- *                                 SilcClientConnection conn,
- *                                 SilcChannelEntry channel_entry);
+ *    SilcChannelEntry
+ *    silc_client_ref_channel(SilcClient client,
+ *                            SilcClientConnection conn,
+ *                            SilcChannelEntry channel_entry);
  *
  * DESCRIPTION
  *
  *    Takes a reference of the channel entry indicated by `channel_entry'
  *    The reference must be released by calling silc_client_unref_channel
- *    after it is not needed anymore.
+ *    after it is not needed anymore.  Returns `channel_entry'.
  *
  ***/
-void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
-                            SilcChannelEntry channel_entry);
+SilcChannelEntry silc_client_ref_channel(SilcClient client, 
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel_entry);
 
 /****f* silcclient/SilcClientAPI/silc_client_unref_channel
  *
@@ -741,23 +813,59 @@ typedef void (*SilcGetServerCallback)(SilcClient client,
                                      SilcDList servers,
                                      void *context);
 
+/****f* silcclient/SilcClientAPI/silc_client_lock_server
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_lock_server(SilcServerEntry server_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Acquires lock for the server entry indicate by `server_entry'.  When
+ *    application wants to access `server_entry' it must lock the entry
+ *    before reading any data from the `server_entry'.  The lock must be
+ *    unlocked with silc_client_unlock_server.
+ *
+ *    The entry must be unlocked before calling any Client Library API
+ *    functions where the entry is given as argument.
+ *
+ *    This function is not needed if application is not multithreaded
+ *
+ ***/
+void silc_client_lock_server(SilcServerEntry server_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_unlock_server
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_unlock_server(SilcServerEntry server_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Releases the lock acquired with silc_client_lock_server.
+ *
+ ***/
+void silc_client_unlock_server(SilcServerEntry server_entry);
+
 /****f* silcclient/SilcClientAPI/silc_client_ref_server
  *
  * SYNOPSIS
  *
- *    void silc_client_ref_server(SilcClient client,
- *                                SilcClientConnection conn,
- *                                SilcServerEntry server_entry);
+ *    SilcServerEntry
+ *    silc_client_ref_server(SilcClient client,
+ *                           SilcClientConnection conn,
+ *                           SilcServerEntry server_entry);
  *
  * DESCRIPTION
  *
  *    Takes a reference of the server entry indicated by `server_entry'
  *    The reference must be released by calling silc_client_unref_server
- *    after it is not needed anymore.
+ *    after it is not needed anymore.  Returns `server_entry'.
  *
  ***/
-void silc_client_ref_server(SilcClient client, SilcClientConnection conn,
-                            SilcServerEntry server_entry);
+SilcServerEntry silc_client_ref_server(SilcClient client, 
+                                      SilcClientConnection conn,
+                                      SilcServerEntry server_entry);
 
 /****f* silcclient/SilcClientAPI/silc_client_unref_server
  *