+ /* 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 */
+ server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
+ if (!server) {
+ /** Resolve server */
+ notify->channel = channel;
+ SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+ silc_client_get_server_by_id_resolve(
+ client, conn, &id.u.server_id,
+ silc_client_notify_resolved,
+ notify));
+ /* NOT REACHED */
+ }
+ entry = server;
+ } else {
+ /* Find Channel entry */
+ channel_entry = silc_client_get_channel_by_id(client, conn,
+ &id.u.channel_id);
+ if (!channel_entry) {
+ /** Resolve channel */
+ notify->channel = channel;
+ SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+ silc_client_get_channel_by_id_resolve(
+ client, conn, &id.u.channel_id,
+ silc_client_notify_resolved,
+ notify));
+ /* NOT REACHED */
+ }
+ 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);
+
+ if (client_entry)
+ silc_client_unref_client(client, conn, client_entry);
+ if (server)
+ silc_client_unref_server(client, conn, server);
+ if (channel_entry)
+ silc_client_unref_channel(client, conn, channel_entry);
+
+ out:
+ /** Notify processed */
+ silc_client_unref_channel(client, conn, channel);
+ silc_fsm_next(fsm, silc_client_notify_processed);
+ return SILC_FSM_CONTINUE;
+}
+
+/****************************** NICK_CHANGE *********************************/
+
+/* Someone changed their nickname on a channel */
+
+SILC_FSM_STATE(silc_client_notify_nick_change)
+{
+ SilcClientConnection conn = fsm_context;
+ SilcClient client = conn->client;
+ SilcClientNotify notify = state_context;
+ SilcNotifyPayload payload = notify->payload;
+ SilcNotifyType type = silc_notify_get_type(payload);
+ SilcArgumentPayload args = silc_notify_get_args(payload);
+ SilcClientEntry client_entry = NULL;
+ unsigned char *tmp, oldnick[256 + 1];
+ SilcUInt32 tmp_len;
+ SilcID id, id2;
+ SilcBool valid;
+
+ SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
+
+ /* Get ID */
+ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+ goto out;
+
+ /* Ignore my ID */
+ if (conn->local_id &&
+ SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
+ goto out;
+
+ /* 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 */