Fixed entry resolving while processing incoming notify packets,
authorPekka Riikonen <priikone@silcnet.org>
Fri, 2 Feb 2007 20:32:29 +0000 (20:32 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Fri, 2 Feb 2007 20:32:29 +0000 (20:32 +0000)
when the IDs in the notify may become invalid while resolving
(like when remote changes nickname at the same time).  Entry is
also now invalid if it doesn't have nickname.

Serialized JOIN and NICK commands to avoid problems locally with
changing ID while joining channel.

12 files changed:
lib/silcclient/client.h
lib/silcclient/client_channel.c
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_reply.c
lib/silcclient/silcclient_entry.h

index dd4eb8a943170ad8dce9b42b55262f9bf4b2ab43..d84cc162815bd97a9026c50510bf2e2ded31f4b5 100644 (file)
@@ -54,8 +54,8 @@ typedef struct SilcClientEntryInternalStruct {
   SilcClientKeyAgreement ke;   /* Current key agreement context or NULL */
 
   /* Flags */
-  unsigned int valid       : 1;        /* FALSE if this entry is not valid */
-  unsigned int resolving   : 1; /* TRUE when entry is being resolved */
+  unsigned int valid       : 1;        /* FALSE if this entry is not valid.  Entry
+                                  without nickname is not valid. */
   unsigned int generated   : 1; /* TRUE if library generated `key' */
   unsigned int prv_resp    : 1; /* TRUE if we are responder when using
                                   private message keys. */
index e8cfa7d1a4c06b07bd583e77b8335bf23eb8447c..8f8772e1d36f51d530ca9466d88220ee6e97b6c1 100644 (file)
@@ -40,6 +40,7 @@ SilcBool silc_client_send_channel_message(SilcClient client,
   SilcCipher cipher;
   SilcHmac hmac;
   SilcBool ret;
+  SilcID sid, rid;
 
   SILC_LOG_DEBUG(("Sending channel message"));
 
@@ -109,9 +110,14 @@ SilcBool silc_client_send_channel_message(SilcClient client,
   }
 
   /* Encode the message payload. This also encrypts the message payload. */
+  sid.type = SILC_ID_CLIENT;
+  sid.u.client_id = chu->client->id;
+  rid.type = SILC_ID_CHANNEL;
+  rid.u.channel_id = chu->channel->id;
   buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
                                       cipher, hmac, client->rng, NULL,
-                                      conn->private_key, hash, NULL);
+                                      conn->private_key, hash, &sid, &rid,
+                                      NULL);
   if (silc_unlikely(!buffer)) {
     SILC_LOG_ERROR(("Error encoding channel message"));
     return FALSE;
@@ -182,7 +188,7 @@ SILC_FSM_STATE(silc_client_channel_message)
 
   /* Get sender client entry */
   client_entry = silc_client_get_client_by_id(client, conn, &remote_id);
-  if (!client_entry || !client_entry->nickname[0]) {
+  if (!client_entry || !client_entry->internal.valid) {
     /** Resolve client info */
     silc_client_unref_client(client, conn, client_entry);
     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
@@ -225,8 +231,10 @@ SILC_FSM_STATE(silc_client_channel_message)
     payload = silc_message_payload_parse(silc_buffer_data(buffer),
                                         silc_buffer_len(buffer), FALSE,
                                         FALSE, channel->internal.receive_key,
-                                        channel->internal.hmac, NULL,
-                                        FALSE, NULL);
+                                        channel->internal.hmac,
+                                        packet->src_id, packet->src_id_len,
+                                        packet->dst_id, packet->dst_id_len,
+                                        NULL, FALSE, NULL);
 
     /* If decryption failed and we have just performed channel key rekey
        we will use the old key in decryption. If that fails too then we
@@ -251,6 +259,10 @@ SILC_FSM_STATE(silc_client_channel_message)
        payload = silc_message_payload_parse(silc_buffer_data(buffer),
                                             silc_buffer_len(buffer),
                                             FALSE, FALSE, cipher, hmac,
+                                            packet->src_id,
+                                            packet->src_id_len,
+                                            packet->dst_id,
+                                            packet->dst_id_len,
                                             NULL, FALSE, NULL);
        if (payload)
          break;
@@ -266,8 +278,12 @@ SILC_FSM_STATE(silc_client_channel_message)
                                           silc_buffer_len(buffer),
                                           FALSE, FALSE,
                                           channel->internal.receive_key,
-                                          channel->internal.hmac, NULL,
-                                          FALSE, NULL);
+                                          channel->internal.hmac,
+                                          packet->src_id,
+                                          packet->src_id_len,
+                                          packet->dst_id,
+                                          packet->dst_id_len,
+                                          NULL, FALSE, NULL);
 
     if (!payload) {
       silc_dlist_start(channel->internal.private_keys);
@@ -276,7 +292,11 @@ SILC_FSM_STATE(silc_client_channel_message)
        payload = silc_message_payload_parse(silc_buffer_data(buffer),
                                             silc_buffer_len(buffer),
                                             FALSE, FALSE, key->cipher,
-                                            key->hmac, NULL, FALSE, NULL);
+                                            key->hmac, packet->src_id,
+                                            packet->src_id_len,
+                                            packet->dst_id,
+                                            packet->dst_id_len,
+                                            NULL, FALSE, NULL);
        if (payload)
          break;
       }
@@ -735,7 +755,8 @@ SilcBool silc_client_add_to_channel(SilcClient client,
   return TRUE;
 }
 
-/* Removes client from a channel.  This handles entry locking internally. */
+/* Removes client from a channel.  Returns FALSE if user is not on channel.
+   This handles entry locking internally. */
 
 SilcBool silc_client_remove_from_channel(SilcClient client,
                                         SilcClientConnection conn,
index c40856a354cebc76eb745beae65d2c10f43b5f12..ec7e81e50ed3aed892a4859256f737108fc5971a 100644 (file)
@@ -658,6 +658,10 @@ SILC_FSM_STATE(silc_client_st_connected)
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
 
+  /* Get SILC protocol version remote supports */
+  silc_ske_parse_version(conn->internal->ske, &conn->internal->remote_version,
+                        NULL, NULL, NULL, NULL);
+
   silc_ske_free(conn->internal->ske);
   conn->internal->ske = NULL;
 
@@ -733,9 +737,11 @@ SILC_TASK_CALLBACK(silc_client_rekey_timer)
   SilcClientConnection conn = context;
 
   /* Signal to start rekey */
-  conn->internal->rekey_responder = FALSE;
-  conn->internal->rekeying = TRUE;
-  SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+  if (!silc_fsm_is_started(&conn->internal->event_thread)) {
+    conn->internal->rekey_responder = FALSE;
+    conn->internal->rekeying = TRUE;
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+  }
 
   /* Reinstall rekey timer */
   silc_schedule_task_add_timeout(conn->internal->schedule,
index 706a4afecbe1607d83fd4de2b35314e327df692b..1cc1833709b635c20ad0e7bf1d9bbbbe4d158fbb 100644 (file)
@@ -136,6 +136,7 @@ typedef struct {
   SilcDList clients;
   SilcGetClientCallback completion;
   void *context;
+  SilcClientEntry client_entry;
 } *SilcClientGetClientInternal;
 
 /* Resolving command callback */
@@ -153,6 +154,12 @@ static SilcBool silc_client_get_clients_cb(SilcClient client,
 
   if (error != SILC_STATUS_OK) {
     SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
+
+    if (i->client_entry) {
+      i->client_entry->internal.resolve_cmd_ident = 0;
+      silc_client_unref_client(client, conn, i->client_entry);
+    }
+
     if (i->completion)
       i->completion(client, conn, error, NULL, i->context);
     goto out;
@@ -170,6 +177,12 @@ static SilcBool silc_client_get_clients_cb(SilcClient client,
     /* Deliver the clients to the caller */
     if (i->completion) {
       SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
+
+      if (i->client_entry) {
+       i->client_entry->internal.resolve_cmd_ident = 0;
+       silc_client_unref_client(client, conn, i->client_entry);
+      }
+
       silc_dlist_start(i->clients);
       i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
     }
@@ -236,10 +249,13 @@ silc_client_get_client_by_id_resolve(SilcClient client,
   if (!cmd_ident && completion)
     completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
 
-  if (client_entry && cmd_ident)
+  if (client_entry && cmd_ident) {
     client_entry->internal.resolve_cmd_ident = cmd_ident;
+    i->client_entry = client_entry;
+  } else {
+    silc_client_unref_client(client, conn, client_entry);
+  }
 
-  silc_client_unref_client(client, conn, client_entry);
   silc_buffer_free(idp);
 
   return cmd_ident;
@@ -699,7 +715,6 @@ SilcClientEntry silc_client_add_client(SilcClient client,
   silc_rwlock_alloc(&client_entry->internal.lock);
   silc_atomic_init8(&client_entry->internal.refcnt, 0);
   client_entry->id = *id;
-  client_entry->internal.valid = TRUE;
   client_entry->mode = mode;
   client_entry->realname = userinfo ? strdup(userinfo) : NULL;
   silc_parse_userfqdn(nickname, client_entry->nickname,
@@ -752,6 +767,9 @@ SilcClientEntry silc_client_add_client(SilcClient client,
   /* Format the nickname */
   silc_client_nickname_format(client, conn, client_entry, FALSE);
 
+  if (client_entry->nickname[0])
+    client_entry->internal.valid = TRUE;
+
   SILC_LOG_DEBUG(("Added %p", client_entry));
 
   return client_entry;
@@ -806,6 +824,7 @@ void silc_client_update_client(SilcClient client,
                                   client_entry, NULL, nick, TRUE);
     silc_mutex_unlock(conn->internal->lock);
     client_entry->nickname_normalized = nick;
+    client_entry->internal.valid = TRUE;
   }
   client_entry->mode = mode;
 
@@ -860,6 +879,7 @@ SilcBool silc_client_change_nickname(SilcClient client,
                          0, NULL);
   }
 
+  client_entry->internal.valid = TRUE;
   return TRUE;
 }
 
@@ -924,6 +944,27 @@ SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
   return ret;
 }
 
+/* Internal routine used to find client by ID and if not found this creates
+   new client entry and returns it. */
+
+SilcClientEntry silc_client_get_client(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientID *client_id)
+{
+  SilcClientEntry client_entry;
+
+  client_entry = silc_client_get_client_by_id(client, conn, client_id);
+  if (!client_entry) {
+    client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
+                                         client_id, 0);
+    if (!client_entry)
+      return NULL;
+    silc_client_ref_client(client, conn, client_entry);
+  }
+
+  return client_entry;
+}
+
 /* Lock client */
 
 void silc_client_lock_client(SilcClientEntry client_entry)
@@ -998,13 +1039,13 @@ SilcClientEntry silc_client_nickname_format(SilcClient client,
   SilcDList clients;
   SilcClientEntry entry, unformatted = NULL;
 
-  SILC_LOG_DEBUG(("Format nickname"));
-
   if (!client->internal->params->nickname_format[0])
     return client_entry;
   if (!client_entry->nickname[0])
     return NULL;
 
+  SILC_LOG_DEBUG(("Format nickname"));
+
   /* Get all clients with same nickname. Do not perform the formatting
      if there aren't any clients with same nickname unless the application
      is forcing us to do so. */
index bdd70f26f72d159ebc3d2aeed150ed94041d6887..ee785f2d155b1f138e7ebb010e919199d7583ac2 100644 (file)
@@ -44,11 +44,9 @@ void silc_client_del_client_entry(SilcClient client,
                                  SilcClientEntry client_entry);
 SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
                                SilcClientEntry client_entry);
-SilcClientEntry silc_idlist_get_client(SilcClient client,
+SilcClientEntry silc_client_get_client(SilcClient client,
                                       SilcClientConnection conn,
-                                      const char *nickname,
-                                      const char *format,
-                                      bool query);
+                                      SilcClientID *client_id);
 SilcChannelEntry silc_client_add_channel(SilcClient client,
                                         SilcClientConnection conn,
                                         const char *channel_name,
index 414aa4cfb295bef2198f87867334b8f7aca166ae..17d1fad0ede54b7451c2c028f536cf66eb01aa2c 100644 (file)
@@ -142,6 +142,7 @@ struct SilcClientConnectionInternalStruct {
   SilcIDCache channel_cache;            /* Channel entry cache */
   SilcIDCache server_cache;             /* Server entry cache */
 
+  SilcUInt32 remote_version;            /* Remote SILC protocol version */
   SilcAtomic16 cmd_ident;               /* Current command identifier */
   SilcUInt8 retry_count;                /* Packet retry counter */
   SilcUInt8 retry_timer;                /* Packet retry timer */
index efabe7582285f7b603a1d06c942b87e4c73ead07..15d0f13048e55ea484e9f81f0e30b7db5e9020ee 100644 (file)
@@ -661,7 +661,7 @@ SILC_FSM_STATE(silc_client_key_agreement)
 
   /* Check whether we know this client already */
   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
-  if (!remote_client || !remote_client->nickname[0]) {
+  if (!remote_client || !remote_client->internal.valid) {
     /** Resolve client info */
     silc_client_unref_client(client, conn, remote_client);
     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
index a492931b26fd131ed58040537c62c6a28a6f2791..40a66d3ee9006afe9f7cfaffa20cb76c9df09451 100644 (file)
 
 /* Notify processing context */
 typedef struct {
-  SilcPacket packet;
-  SilcNotifyPayload payload;
-  SilcFSMThread fsm;
-  SilcChannelEntry channel;
+  SilcPacket packet;                 /* Notify packet */
+  SilcNotifyPayload payload;         /* Parsed notify payload */
+  SilcFSMThread fsm;                 /* Notify FSM thread */
+  SilcChannelEntry channel;          /* Channel entry being resolved */
+  SilcClientEntry client_entry;              /* Client entry being resolved */
 } *SilcClientNotify;
 
 /************************ Static utility functions **************************/
 
-/* Entry resolving callback.  This will continue processing the notify. */
+/* The client entires in notify processing are resolved if they do not exist,
+   or they are not valid.  We go to this callback after resolving where we
+   check if the client entry has become valid.  If resolving succeeded the
+   entry is valid but remains invalid if resolving failed.  This callback
+   will continue processing the notify.  We use this callback also with other
+   entry resolving. */
 
 static void silc_client_notify_resolved(SilcClient client,
                                        SilcClientConnection conn,
@@ -46,8 +52,14 @@ static void silc_client_notify_resolved(SilcClient client,
 {
   SilcClientNotify notify = context;
 
-  /* If no entries found, just finish the notify processing, a silent error */
-  if (!entries)
+  /* If entry is still invalid, resolving failed.  Finish notify processing. */
+  if (notify->client_entry && !notify->client_entry->internal.valid) {
+    silc_fsm_next(notify->fsm, silc_client_notify_processed);
+    silc_client_unref_client(client, conn, notify->client_entry);
+  }
+
+  /* If no entries found, just finish the notify processing */
+  if (!entries && !notify->client_entry)
     silc_fsm_next(notify->fsm, silc_client_notify_processed);
 
   if (notify->channel) {
@@ -209,7 +221,7 @@ SILC_FSM_STATE(silc_client_notify)
     break;
   }
 
-  return SILC_FSM_YIELD;
+  return SILC_FSM_CONTINUE;
 }
 
 /* Notify processed, finish the packet processing thread */
@@ -298,7 +310,7 @@ SILC_FSM_STATE(silc_client_notify_invite)
 
   /* Find Client entry and if not found query it */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0]) {
+  if (!client_entry || !client_entry->internal.valid) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     notify->channel = channel;
@@ -364,17 +376,21 @@ SILC_FSM_STATE(silc_client_notify_join)
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  /* Find Client entry and if not found query it */
-  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0] ||
+  /* Find client entry and if not found query it.  If we just queried it
+     don't do it again, unless some data (like username) is missing. */
+  client_entry = notify->client_entry;
+  if (!client_entry)
+    client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+  if (!client_entry || !client_entry->internal.valid ||
       !client_entry->username[0]) {
     /** Resolve client */
-    silc_client_unref_client(client, conn, client_entry);
     notify->channel = channel;
+    notify->client_entry = client_entry;
     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
                  silc_client_get_client_by_id_resolve(
-                                        client, conn, &id.u.client_id, NULL,
-                                        silc_client_notify_resolved,
+                                        client, conn, client_entry ?
+                                        &client_entry->id : &id.u.client_id,
+                                        NULL, silc_client_notify_resolved,
                                         notify));
     /* NOT REACHED */
   }
@@ -455,7 +471,8 @@ SILC_FSM_STATE(silc_client_notify_leave)
     goto out;
 
   /* Remove client from channel */
-  silc_client_remove_from_channel(client, conn, channel, client_entry);
+  if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
+    goto out;
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
@@ -505,7 +522,8 @@ SILC_FSM_STATE(silc_client_notify_signoff)
     tmp[128] = '\0';
 
   /* Notify application */
-  NOTIFY(client, conn, type, client_entry, tmp);
+  if (client_entry->internal.valid)
+    NOTIFY(client, conn, type, client_entry, tmp);
 
   /* Remove from channel */
   if (packet->dst_id_type == SILC_ID_CHANNEL) {
@@ -520,6 +538,7 @@ SILC_FSM_STATE(silc_client_notify_signoff)
   }
 
   /* Delete client */
+  client_entry->internal.valid = FALSE;
   silc_client_del_client(client, conn, client_entry);
   silc_client_unref_client(client, conn, client_entry);
 
@@ -571,7 +590,7 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
     /* NOT REACHED */
   }
 
-  /* Get ID */
+  /* Get ID of topic changer */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
@@ -582,18 +601,26 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry || !client_entry->nickname[0]) {
-      /** Resolve client */
-      silc_client_unref_client(client, conn, client_entry);
-      notify->channel = channel;
-      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
-                   silc_client_get_client_by_id_resolve(
+    client_entry = notify->client_entry;
+    if (!client_entry) {
+      client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+      if (!client_entry || !client_entry->internal.valid) {
+       /** Resolve client */
+       notify->channel = channel;
+       notify->client_entry = client_entry;
+       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                     silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
                                           notify));
-      /* NOT REACHED */
+       /* NOT REACHED */
+      }
     }
+
+    /* If client is not on channel, ignore this notify */
+    if (!silc_client_on_channel(channel, client_entry))
+      goto out;
+
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
@@ -664,6 +691,7 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
   unsigned char *tmp, oldnick[128 + 1];
   SilcUInt32 tmp_len;
   SilcID id, id2;
+  SilcBool valid;
 
   SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
 
@@ -685,17 +713,12 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
       SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
     goto out;
 
-  /* Find old Client entry */
+  /* Find old client entry.  If we don't have the entry, we ignore this
+     notify. */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0]) {
-    /** Resolve client */
-    silc_client_unref_client(client, conn, client_entry);
-    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
-                                        client, conn, &id.u.client_id, NULL,
-                                        silc_client_notify_resolved,
-                                        notify));
-    /* NOT REACHED */
-  }
+  if (!client_entry)
+    goto out;
+  valid = client_entry->internal.valid;
 
   /* Take the new nickname */
   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
@@ -729,8 +752,10 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
 
   silc_rwlock_unlock(client_entry->internal.lock);
 
-  /* Notify application */
-  NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
+  /* Notify application, if client entry is valid.  We do not send nick change
+     notify for entries that were invalid (application doesn't know them). */
+  if (valid)
+    NOTIFY(client, conn, type, client_entry, oldnick, client_entry->nickname);
 
  out:
   /** Notify processed */
@@ -790,24 +815,32 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     goto out;
   SILC_GET32_MSB(mode, tmp);
 
-  /* Get ID */
+  /* Get ID of who changed the mode */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry || !client_entry->nickname[0]) {
-      /** Resolve client */
-      silc_client_unref_client(client, conn, client_entry);
-      notify->channel = channel;
-      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
-                   silc_client_get_client_by_id_resolve(
+    client_entry = notify->client_entry;
+    if (!client_entry) {
+      client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+      if (!client_entry || !client_entry->internal.valid) {
+       /** Resolve client */
+       notify->channel = channel;
+       notify->client_entry = client_entry;
+       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                     silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
                                           notify));
-      /* NOT REACHED */
+       /* NOT REACHED */
+      }
     }
+
+    /* If client is not on channel, ignore this notify */
+    if (!silc_client_on_channel(channel, client_entry))
+      goto out;
+
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
@@ -973,7 +1006,7 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
 
   /* 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]) {
+  if (!client_entry2 || !client_entry2->internal.valid) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry2);
     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
@@ -983,30 +1016,42 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
     /* NOT REACHED */
   }
 
+  /* If target client is not on channel, ignore this notify */
+  if (!silc_client_on_channel(channel, client_entry2))
+    goto out;
+
   /* Get the mode */
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
   if (!tmp)
     goto out;
   SILC_GET32_MSB(mode, tmp);
 
-  /* Get ID */
+  /* Get ID of mode changer */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry || !client_entry->nickname[0]) {
-      /** Resolve client */
-      silc_client_unref_client(client, conn, client_entry);
-      notify->channel = channel;
-      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
-                   silc_client_get_client_by_id_resolve(
+    client_entry = notify->client_entry;
+    if (!client_entry) {
+      client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+      if (!client_entry || !client_entry->internal.valid) {
+       /** Resolve client */
+       notify->channel = channel;
+       notify->client_entry = client_entry;
+       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                     silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
                                           notify));
-      /* NOT REACHED */
+       /* NOT REACHED */
+      }
     }
+
+    /* If client is not on channel, ignore this notify */
+    if (!silc_client_on_channel(channel, client_entry))
+      goto out;
+
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
@@ -1190,11 +1235,11 @@ SILC_FSM_STATE(silc_client_notify_kicked)
     /* NOT REACHED */
   }
 
-  /* Get Client ID */
+  /* Get the kicked Client ID */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  /* Find Client entry */
+  /* Find client entry */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
   if (!client_entry)
     goto out;
@@ -1205,7 +1250,7 @@ SILC_FSM_STATE(silc_client_notify_kicked)
 
   /* Find kicker's client entry and if not found resolve it */
   client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry2 || !client_entry2->nickname[0]) {
+  if (!client_entry2 || !client_entry2->internal.valid) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     silc_client_unref_client(client, conn, client_entry2);
@@ -1222,8 +1267,10 @@ SILC_FSM_STATE(silc_client_notify_kicked)
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 
   /* Remove kicked client from channel */
-  if (client_entry != conn->local_entry)
-    silc_client_remove_from_channel(client, conn, channel, client_entry);
+  if (client_entry != conn->local_entry) {
+    if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
+      goto out;
+  }
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
@@ -1288,7 +1335,7 @@ SILC_FSM_STATE(silc_client_notify_killed)
     /* Find Client entry */
     client_entry2 = silc_client_get_client_by_id(client, conn,
                                                 &id.u.client_id);
-    if (!client_entry2 || !client_entry2->nickname[0]) {
+    if (!client_entry2 || !client_entry2->internal.valid) {
       /** Resolve client */
       silc_client_unref_client(client, conn, client_entry);
       silc_client_unref_client(client, conn, client_entry2);
@@ -1332,6 +1379,7 @@ SILC_FSM_STATE(silc_client_notify_killed)
   /* Delete the killed client */
   if (client_entry != conn->local_entry) {
     silc_client_remove_from_channels(client, conn, client_entry);
+    client_entry->internal.valid = FALSE;
     silc_client_del_client(client, conn, client_entry);
   }
 
@@ -1379,7 +1427,7 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
 
     /* Get the client entry */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (client_entry)
+    if (client_entry && client_entry->internal.valid)
       silc_dlist_add(clients, client_entry);
   }
 
@@ -1391,6 +1439,7 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
   silc_dlist_start(clients);
   while ((client_entry = silc_dlist_get(clients))) {
     silc_client_remove_from_channels(client, conn, client_entry);
+    client_entry->internal.valid = FALSE;
     silc_client_del_client(client, conn, client_entry);
   }
 
@@ -1474,9 +1523,9 @@ SILC_FSM_STATE(silc_client_notify_watch)
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  /* Find Client entry and if not found resolve it */
+  /* Find client entry and if not found resolve it */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0]) {
+  if (!client_entry || !client_entry->internal.valid) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
index 20370cf6647bc828140da993cec051fce555d3b8..9b6002e5dac80f52a182e78c427ca85e5253df6a 100644 (file)
@@ -36,6 +36,7 @@ SilcBool silc_client_send_private_message(SilcClient client,
 {
   SilcBuffer buffer;
   SilcBool ret;
+  SilcID sid, rid;
 
   if (silc_unlikely(!client || !conn || !client_entry))
     return FALSE;
@@ -46,6 +47,11 @@ SilcBool silc_client_send_private_message(SilcClient client,
 
   SILC_LOG_DEBUG(("Sending private message"));
 
+  sid.type = SILC_ID_CLIENT;
+  sid.u.client_id = conn->local_entry->id;
+  rid.type = SILC_ID_CLIENT;
+  rid.u.client_id = client_entry->id;
+
   /* Encode private message payload */
   buffer =
     silc_message_payload_encode(flags, data, data_len,
@@ -54,7 +60,7 @@ SilcBool silc_client_send_private_message(SilcClient client,
                                TRUE, client_entry->internal.send_key,
                                client_entry->internal.hmac_send,
                                client->rng, NULL, conn->private_key,
-                               hash, NULL);
+                               hash, &sid, &rid, NULL);
   if (silc_unlikely(!buffer)) {
     SILC_LOG_ERROR(("Error encoding private message"));
     return FALSE;
@@ -142,6 +148,8 @@ SILC_FSM_STATE(silc_client_private_message)
                               TRUE, !remote_client->internal.generated,
                               remote_client->internal.receive_key,
                               remote_client->internal.hmac_receive,
+                              packet->src_id, packet->src_id_len,
+                              packet->dst_id, packet->dst_id_len,
                               NULL, FALSE, NULL);
   if (silc_unlikely(!payload))
     goto out;
@@ -265,6 +273,8 @@ SilcBool silc_client_private_message_wait(SilcClient client,
                                 TRUE, !client_entry->internal.generated,
                                 client_entry->internal.receive_key,
                                 client_entry->internal.hmac_receive,
+                                packet->src_id, packet->src_id_len,
+                                packet->dst_id, packet->dst_id_len,
                                 NULL, FALSE, NULL);
     if (!(*payload)) {
       silc_packet_free(packet);
@@ -316,13 +326,15 @@ static void silc_client_private_message_key_cb(SilcClient client,
                                               SilcDList clients,
                                               void *context)
 {
-  SilcPacket packet = context;
+  SilcFSMThread thread = context;
+  SilcPacket packet = silc_fsm_get_state_context(thread);
   unsigned char *cipher = NULL, *hmac = NULL;
   SilcClientEntry client_entry;
   int ret;
 
   if (!clients) {
     silc_packet_free(packet);
+    silc_fsm_finish(thread);
     return;
   }
 
@@ -347,6 +359,7 @@ static void silc_client_private_message_key_cb(SilcClient client,
   silc_free(cipher);
   silc_free(hmac);
   silc_packet_free(packet);
+  silc_fsm_finish(thread);
 }
 
 /* Processes incoming Private Message Key payload to indicate that the
index 99fe98a974ba7fbccaaee1279782cfaf31944ad7..72a9c38fc08e1d6a5e9107d9392faab905fa3b4c 100644 (file)
@@ -797,7 +797,7 @@ SILC_FSM_STATE(silc_client_command_identify)
 
 SILC_FSM_STATE(silc_client_command_nick)
 {
-  SilcClientCommandContext cmd = fsm_context;
+  SilcClientCommandContext cmd2, cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
 
   if (cmd->argc < 2) {
@@ -825,6 +825,19 @@ SILC_FSM_STATE(silc_client_command_nick)
     goto out;
   }
 
+  /* If JOIN command is active, wait for it to finish before sending NICK.
+     To avoid problems locally with changing IDs while joining, we do this. */
+  silc_mutex_lock(conn->internal->lock);
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
+    if (cmd2->cmd == SILC_COMMAND_JOIN) {
+      silc_mutex_unlock(conn->internal->lock);
+      silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
+      return SILC_FSM_WAIT;
+    }
+  }
+  silc_mutex_unlock(conn->internal->lock);
+
   if (cmd->argv_lens[1] > 128)
     cmd->argv_lens[1] = 128;
 
@@ -1274,7 +1287,7 @@ SILC_FSM_STATE(silc_client_command_ping)
 
 SILC_FSM_STATE(silc_client_command_join)
 {
-  SilcClientCommandContext cmd = fsm_context;
+  SilcClientCommandContext cmd2, cmd = fsm_context;
   SilcClientConnection conn = cmd->conn;
   SilcClient client = conn->client;
   SilcChannelEntry channel = NULL;
@@ -1292,6 +1305,19 @@ SILC_FSM_STATE(silc_client_command_join)
   if (channel && silc_client_on_channel(channel, conn->local_entry))
     goto out;
 
+  /* If NICK command is active, wait for it to finish before sending JOIN.
+     To avoid problems locally with changing IDs while joining, we do this. */
+  silc_mutex_lock(conn->internal->lock);
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
+    if (cmd2->cmd == SILC_COMMAND_NICK) {
+      silc_mutex_unlock(conn->internal->lock);
+      silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
+      return SILC_FSM_WAIT;
+    }
+  }
+  silc_mutex_unlock(conn->internal->lock);
+
   if (cmd->argv_lens[1] > 256)
     cmd->argv_lens[1] = 256;
 
index cf4e151bbff63356ebd77a56c6983638adb67417..80e85186e2b5988dedfe1368889eb4a6b96fa791 100644 (file)
@@ -1217,7 +1217,7 @@ SILC_FSM_STATE(silc_client_command_reply_join)
 
     /* Get client entry */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry)
+    if (!client_entry || !client_entry->internal.valid)
       continue;
 
     /* Join client to the channel */
@@ -1921,7 +1921,7 @@ 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 && client_entry->internal.valid) {
       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);
index 56cbaf7d6fe5c1c2b82eb0e97e15d257301310ec..0f4e996a8572955a0576ef8bd308862a782459da 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
@@ -208,10 +208,17 @@ typedef void (*SilcGetClientCallback)(SilcClient client,
  *    before reading any data from the `client_entry'.  The lock must be
  *    unlocked with silc_client_unlock_client.
  *
+ * NOTES
+ *
  *    The entry must be unlocked before calling any Client Library API
- *    functions where the entry is given as argument.
+ *    functions where the entry is given as argument, unless otherwise stated.
+ *
+ *    The entry should not be locked for long periods of time.  For example,
+ *    it is not appropriate to hold the lock while waiting user interface to
+ *    be drawn.  The appropriate way is to read the data and duplicate it if
+ *    necessary, unlock the entry, then draw on the user interface.
  *
- *    This function is not needed if application is not multithreaded
+ *    This function is not needed if application is not multithreaded.
  *
  ***/
 void silc_client_lock_client(SilcClientEntry client_entry);
@@ -245,7 +252,7 @@ void silc_client_unlock_client(SilcClientEntry client_entry);
  *    after it is not needed anymore.  Returns `client_entry'.
  *
  ***/
-SilcClientEntry silc_client_ref_client(SilcClient client, 
+SilcClientEntry silc_client_ref_client(SilcClient client,
                                       SilcClientConnection conn,
                                       SilcClientEntry client_entry);
 
@@ -595,10 +602,17 @@ typedef void (*SilcGetChannelCallback)(SilcClient client,
  *    before reading any data from the `channel_entry'.  The lock must be
  *    unlocked with silc_client_unlock_channel.
  *
+ * NOTES
+ *
  *    The entry must be unlocked before calling any Client Library API
- *    functions where the entry is given as argument.
+ *    functions where the entry is given as argument, unless otherwise stated.
  *
- *    This function is not needed if application is not multithreaded
+ *    The entry should not be locked for long periods of time.  For example,
+ *    it is not appropriate to hold the lock while waiting user interface to
+ *    be drawn.  The appropriate way is to read the data and duplicate it if
+ *    necessary, unlock the entry, then draw on the user interface.
+ *
+ *    This function is not needed if application is not multithreaded.
  *
  ***/
 void silc_client_lock_channel(SilcChannelEntry channel_entry);
@@ -632,7 +646,7 @@ void silc_client_unlock_channel(SilcChannelEntry channel_entry);
  *    after it is not needed anymore.  Returns `channel_entry'.
  *
  ***/
-SilcChannelEntry silc_client_ref_channel(SilcClient client, 
+SilcChannelEntry silc_client_ref_channel(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcChannelEntry channel_entry);
 
@@ -826,10 +840,17 @@ typedef void (*SilcGetServerCallback)(SilcClient client,
  *    before reading any data from the `server_entry'.  The lock must be
  *    unlocked with silc_client_unlock_server.
  *
+ * NOTES
+ *
  *    The entry must be unlocked before calling any Client Library API
- *    functions where the entry is given as argument.
+ *    functions where the entry is given as argument, unless otherwise stated.
+ *
+ *    The entry should not be locked for long periods of time.  For example,
+ *    it is not appropriate to hold the lock while waiting user interface to
+ *    be drawn.  The appropriate way is to read the data and duplicate it if
+ *    necessary, unlock the entry, then draw on the user interface.
  *
- *    This function is not needed if application is not multithreaded
+ *    This function is not needed if application is not multithreaded.
  *
  ***/
 void silc_client_lock_server(SilcServerEntry server_entry);
@@ -863,7 +884,7 @@ void silc_client_unlock_server(SilcServerEntry server_entry);
  *    after it is not needed anymore.  Returns `server_entry'.
  *
  ***/
-SilcServerEntry silc_client_ref_server(SilcClient client, 
+SilcServerEntry silc_client_ref_server(SilcClient client,
                                       SilcClientConnection conn,
                                       SilcServerEntry server_entry);