+ /* Get channel entry */
+ 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 Client ID */
+ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+ goto out;
+
+ /* 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 */
+ 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, client_entry ?
+ &client_entry->id : &id.u.client_id,
+ NULL, silc_client_notify_resolved,
+ notify));
+ /* 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)) {
+ 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);
+
+ silc_client_unref_client(client, conn, client_entry);
+
+ out:
+ /** Notify processed */
+ silc_client_unref_channel(client, conn, channel);
+ silc_fsm_next(fsm, silc_client_notify_processed);
+ return SILC_FSM_CONTINUE;
+}
+
+/********************************** LEAVE ***********************************/
+
+/* Someone left a channel */
+
+SILC_FSM_STATE(silc_client_notify_leave)
+{
+ 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;
+ SilcID id;
+
+ SILC_LOG_DEBUG(("Notify: LEAVE"));
+
+ /* 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 Client ID */
+ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+ goto out;
+
+ /* Find Client entry */
+ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Remove client from channel */
+ if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
+ goto out;
+
+ /* Notify application. */
+ NOTIFY(client, conn, type, client_entry, channel);
+
+ silc_client_unref_client(client, conn, client_entry);
+
+ out:
+ /** Notify processed */
+ silc_client_unref_channel(client, conn, channel);
+ silc_fsm_next(fsm, silc_client_notify_processed);
+ return SILC_FSM_CONTINUE;
+}
+
+/********************************* SIGNOFF **********************************/
+
+/* Someone quit SILC network */
+
+SILC_FSM_STATE(silc_client_notify_signoff)
+{
+ 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;
+ SilcChannelEntry channel = NULL;
+ unsigned char *tmp;
+ SilcUInt32 tmp_len;
+ SilcID id;
+
+ SILC_LOG_DEBUG(("Notify: SIGNOFF"));
+
+ /* Get Client ID */
+ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+ goto out;
+
+ /* Find Client entry */
+ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get signoff message */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (tmp && tmp_len > 128)
+ tmp[128] = '\0';
+
+ if (packet->dst_id_type == SILC_ID_CHANNEL)
+ if (silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+ &id.u.channel_id, sizeof(id.u.channel_id)))
+ channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+
+ /* Notify application */
+ if (client_entry->internal.valid)
+ NOTIFY(client, conn, type, client_entry, tmp, channel);
+
+ /* Remove from channel */
+ if (channel) {
+ silc_client_remove_from_channel(client, conn, channel, client_entry);
+ silc_client_unref_channel(client, conn, channel);
+ }
+
+ /* Delete client */
+ client_entry->internal.valid = FALSE;
+ silc_client_del_client(client, conn, client_entry);
+ silc_client_unref_client(client, conn, client_entry);
+
+ out:
+ /** Notify processed */
+ silc_fsm_next(fsm, silc_client_notify_processed);
+ return SILC_FSM_CONTINUE;
+}
+
+/******************************** TOPIC_SET *********************************/
+
+/* Someone set topic on a channel */
+
+SILC_FSM_STATE(silc_client_notify_topic_set)
+{
+ 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;
+ SilcID id;
+
+ SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
+
+ /* 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 ID of topic changer */
+ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+ goto out;
+
+ /* Get topic */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ if (id.type == SILC_ID_CLIENT) {
+ /* Find Client entry */
+ client_entry = notify->client_entry;