-/* Called when notify is received and some async operation (such as command)
- is required before processing the notify message. This calls again the
- silc_client_notify_by_server and reprocesses the original notify packet. */
-
-static void silc_client_notify_by_server_pending(void *context)
-{
- SilcPacketContext *p = (SilcPacketContext *)context;
- silc_client_notify_by_server(p->context, p->sock, p);
- silc_packet_context_free(p);
-}
-
-/* Received notify message from server */
-
-void silc_client_notify_by_server(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcBuffer buffer = packet->buffer;
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcNotifyPayload payload;
- SilcNotifyType type;
- SilcArgumentPayload args;
-
- SilcClientID *client_id = NULL;
- SilcChannelID *channel_id = NULL;
- SilcClientEntry client_entry;
- SilcClientEntry client_entry2;
- SilcChannelEntry channel;
- SilcChannelUser chu;
- SilcIDCacheEntry id_cache = NULL;
- unsigned char *tmp;
- unsigned int tmp_len, mode;
-
- payload = silc_notify_payload_parse(buffer);
- type = silc_notify_get_type(payload);
- args = silc_notify_get_args(payload);
- if (!args)
- goto out;
-
- switch(type) {
- case SILC_NOTIFY_TYPE_NONE:
- /* Notify application */
- client->ops->notify(client, conn, type,
- silc_argument_get_arg_type(args, 1, NULL));
- break;
-
- case SILC_NOTIFY_TYPE_INVITE:
- /*
- * Someone invited me to a channel. Find Client and Channel entries
- * for the application.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find Client entry and if not found query it */
- client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
- if (!client_entry) {
- SilcPacketContext *p = silc_packet_context_dup(packet);
- p->context = (void *)client;
- p->sock = sock;
- silc_client_command_pending(conn, SILC_COMMAND_WHOIS, SILC_IDLIST_IDENT,
- silc_client_notify_by_server_pending, p);
- goto out;
- }
-
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* XXX Will ALWAYS fail because currently we don't have way to resolve
- channel information for channel that we're not joined to. */
- /* XXX ways to fix: use (extended) LIST command, or define the channel
- name to the notfy type when name resolving is not mandatory. */
- /* Find channel entry */
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- goto out;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Notify application */
- client->ops->notify(client, conn, type, client_entry, channel);
- break;
-
- case SILC_NOTIFY_TYPE_JOIN:
- /*
- * Someone has joined to a channel. Get their ID and nickname and
- * cache them for later use.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find Client entry and if not found query it */
- client_entry = silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
- if (!client_entry) {
- SilcPacketContext *p = silc_packet_context_dup(packet);
- p->context = (void *)client;
- p->sock = sock;
- silc_client_command_pending(conn, SILC_COMMAND_WHOIS, SILC_IDLIST_IDENT,
- silc_client_notify_by_server_pending, p);
- goto out;
- }
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Add client to channel */
- chu = silc_calloc(1, sizeof(*chu));
- chu->client = client_entry;
- silc_list_add(channel->clients, chu);
-
- /* XXX add support for multiple same nicks on same channel. Check
- for them here */
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, channel);
- break;
-
- case SILC_NOTIFY_TYPE_LEAVE:
- /*
- * Someone has left a channel. We will remove it from the channel but
- * we'll keep it in the cache in case we'll need it later.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find Client entry */
- client_entry =
- silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
- if (!client_entry)
- goto out;
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Remove client from channel */
- silc_list_start(channel->clients);
- while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
- if (chu->client == client_entry) {
- silc_list_del(channel->clients, chu);
- silc_free(chu);
- break;
- }
- }
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, channel);
- break;
-
- case SILC_NOTIFY_TYPE_SIGNOFF:
- /*
- * Someone left SILC. We'll remove it from all channels and from cache.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find Client entry */
- client_entry =
- silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
- if (!client_entry)
- goto out;
-
- /* Remove from all channels */
- silc_client_remove_from_channels(client, conn, client_entry);
-
- /* Remove from cache */
- silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
- client_entry->id);
-
- /* Notify application */
- client->ops->notify(client, conn, type, client_entry);
-
- /* Free data */
- if (client_entry->nickname)
- silc_free(client_entry->nickname);
- if (client_entry->server)
- silc_free(client_entry->server);
- if (client_entry->id)
- silc_free(client_entry->id);
- if (client_entry->send_key)
- silc_cipher_free(client_entry->send_key);
- if (client_entry->receive_key)
- silc_cipher_free(client_entry->receive_key);
- break;
-
- case SILC_NOTIFY_TYPE_TOPIC_SET:
- /*
- * Someone set the topic on a channel.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find Client entry */
- client_entry =
- silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
- if (!client_entry)
- goto out;
-
- /* Get topic */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, tmp, channel);
- break;
-
- case SILC_NOTIFY_TYPE_NICK_CHANGE:
- /*
- * Someone changed their nickname. If we don't have entry for the new
- * ID we will query it and return here after it's done. After we've
- * returned we fetch the old entry and free it and notify the
- * application.
- */
-
- /* Get new Client ID */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Ignore my ID */
- if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
- break;
-
- /* Find Client entry and if not found query it */
- client_entry2 =
- silc_idlist_get_client_by_id(client, conn, client_id, TRUE);
- if (!client_entry2) {
- SilcPacketContext *p = silc_packet_context_dup(packet);
- p->context = (void *)client;
- p->sock = sock;
- silc_client_command_pending(conn, SILC_COMMAND_WHOIS, SILC_IDLIST_IDENT,
- silc_client_notify_by_server_pending, p);
- goto out;
- }
-
- /* Get old Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find old Client entry */
- client_entry =
- silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
- if (!client_entry)
- goto out;
-
- /* Remove the old from cache */
- silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
- client_entry->id);
-
- /* Replace old ID entry with new one on all channels. */
- silc_client_replace_from_channels(client, conn, client_entry,
- client_entry2);
-
- /* Notify application */
- client->ops->notify(client, conn, type, client_entry, client_entry2);
-
- /* Free data */
- if (client_entry->nickname)
- silc_free(client_entry->nickname);
- if (client_entry->server)
- silc_free(client_entry->server);
- if (client_entry->id)
- silc_free(client_entry->id);
- if (client_entry->send_key)
- silc_cipher_free(client_entry->send_key);
- if (client_entry->receive_key)
- silc_cipher_free(client_entry->receive_key);
- break;
-
- case SILC_NOTIFY_TYPE_CMODE_CHANGE:
- /*
- * Someone changed a channel mode
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find Client entry */
- client_entry =
- silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
- if (!client_entry)
- 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 channel entry */
- channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Save the new mode */
- channel->mode = mode;
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, mode, channel);
- break;
-
- case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
- /*
- * Someone changed user's mode on a channel
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find Client entry */
- client_entry =
- silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
- if (!client_entry)
- 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 target Client ID */
- tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
- if (!tmp)
- goto out;
-
- silc_free(client_id);
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
-
- /* Find target Client entry */
- client_entry2 =
- silc_idlist_get_client_by_id(client, conn, client_id, FALSE);
- if (!client_entry2)
- goto out;
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Save the mode */
- silc_list_start(channel->clients);
- while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
- if (chu->client == client_entry) {
- chu->mode = mode;
- break;
- }
- }
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, mode,
- client_entry2, channel);
- break;
-
- case SILC_NOTIFY_TYPE_MOTD:
- /*
- * Received Message of the day
- */
-
- /* Get motd */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- /* Notify application */
- client->ops->notify(client, conn, type, tmp);
- break;
-
- default:
- break;
- }
-
- out:
- silc_notify_payload_free(payload);
- if (client_id)
- silc_free(client_id);
- if (channel_id)
- silc_free(channel_id);
-}
-