+ /* Get new Client ID */
+ if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id2, NULL))
+ goto out;
+
+ /* Ignore my ID */
+ if (conn->local_id &&
+ SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
+ goto out;
+
+ /* 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)
+ goto out;
+ valid = client_entry->internal.valid;
+
+ /* Take the new nickname */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ 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. */
+ if (SILC_ID_COMPARE_HASH(&client_entry->id, &id2.u.client_id) &&
+ silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
+ /* Nickname didn't change. Update only Client ID. We don't notify
+ application because nickname didn't change. */
+ silc_mutex_lock(conn->internal->lock);
+ silc_idcache_update_by_context(conn->internal->client_cache, client_entry,
+ &id2.u.client_id, NULL, FALSE);
+ silc_mutex_unlock(conn->internal->lock);
+ 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)) {
+ silc_rwlock_unlock(client_entry->internal.lock);
+ goto out;
+ }
+
+ silc_rwlock_unlock(client_entry->internal.lock);
+
+ /* 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 */
+ silc_client_unref_client(client, conn, client_entry);
+ silc_fsm_next(fsm, silc_client_notify_processed);
+ return SILC_FSM_CONTINUE;
+}
+
+/****************************** CMODE_CHANGE ********************************/
+
+/* Someone changed channel mode */
+
+SILC_FSM_STATE(silc_client_notify_cmode_change)
+{
+ SilcClientConnection conn = fsm_context;
+ SilcClient client = conn->client;
+ SilcClientNotify notify = state_context;
+ SilcNotifyPayload payload = notify->payload;
+ SilcPacket packet = notify->packet;
+ SilcNotifyType type = silc_notify_get_type(payload);
+ SilcArgumentPayload args = silc_notify_get_args(payload);
+ SilcClientEntry client_entry = NULL;
+ SilcChannelEntry channel = NULL, channel_entry = NULL;
+ SilcServerEntry server = NULL;
+ void *entry;
+ unsigned char *tmp;
+ SilcUInt32 tmp_len, mode;
+ SilcID id;
+ char *passphrase, *cipher, *hmac;
+ SilcPublicKey founder_key = NULL;
+ SilcDList chpks = NULL;
+
+ SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
+
+ /* Get channel entry */
+ if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+ &id.u.channel_id, sizeof(id.u.channel_id)))
+ goto out;
+ channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+ if (!channel)
+ goto out;
+
+ /* If channel is being resolved handle notify after resolving */
+ if (channel->internal.resolve_cmd_ident) {
+ silc_client_unref_channel(client, conn, channel);
+ SILC_FSM_CALL(silc_client_command_pending(
+ conn, SILC_COMMAND_NONE,
+ channel->internal.resolve_cmd_ident,
+ silc_client_notify_wait_continue,
+ notify));
+ /* NOT REACHED */
+ }
+
+ /* Get the mode */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+ SILC_GET32_MSB(mode, tmp);
+
+ /* 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 = 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 */