5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2006 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
27 #define NOTIFY conn->client->internal->ops->notify
29 /* Notify processing context */
32 SilcNotifyPayload payload;
34 SilcChannelEntry channel;
37 /************************ Static utility functions **************************/
39 /* Entry resolving callback. This will continue processing the notify. */
41 static void silc_client_notify_resolved(SilcClient client,
42 SilcClientConnection conn,
47 SilcClientNotify notify = context;
49 /* If no entries found, just finish the notify processing, a silent error */
51 silc_fsm_next(notify->fsm, silc_client_notify_processed);
53 if (notify->channel) {
54 notify->channel->internal.resolve_cmd_ident = 0;
55 silc_client_unref_channel(client, conn, notify->channel);
58 /* Continue processing the notify */
59 SILC_FSM_CALL_CONTINUE_SYNC(notify->fsm);
62 /* Continue notify processing after it was suspended while waiting for
63 channel information being resolved. */
65 static SilcBool silc_client_notify_wait_continue(SilcClient client,
66 SilcClientConnection conn,
73 SilcClientNotify notify = context;
75 /* Continue after last command reply received */
76 if (SILC_STATUS_IS_ERROR(status) || status == SILC_STATUS_OK ||
77 status == SILC_STATUS_LIST_END)
78 SILC_FSM_CALL_CONTINUE(notify->fsm);
83 /********************************* Notify ***********************************/
85 /* Process received notify packet */
87 SILC_FSM_STATE(silc_client_notify)
89 SilcPacket packet = state_context;
90 SilcClientNotify notify;
91 SilcNotifyPayload payload;
93 payload = silc_notify_payload_parse(silc_buffer_data(&packet->buffer),
94 silc_buffer_len(&packet->buffer));
96 SILC_LOG_DEBUG(("Malformed notify payload"));
97 silc_packet_free(packet);
98 return SILC_FSM_FINISH;
101 if (!silc_notify_get_args(payload)) {
102 SILC_LOG_DEBUG(("Malformed notify"));
103 silc_notify_payload_free(payload);
104 silc_packet_free(packet);
105 return SILC_FSM_FINISH;
108 notify = silc_calloc(1, sizeof(*notify));
110 silc_notify_payload_free(payload);
111 silc_packet_free(packet);
112 return SILC_FSM_FINISH;
115 /* Save notify payload to packet context during processing */
116 notify->packet = packet;
117 notify->payload = payload;
119 silc_fsm_set_state_context(fsm, notify);
121 /* Process the notify */
122 switch (silc_notify_get_type(payload)) {
124 case SILC_NOTIFY_TYPE_NONE:
126 silc_fsm_next(fsm, silc_client_notify_none);
129 case SILC_NOTIFY_TYPE_INVITE:
131 silc_fsm_next(fsm, silc_client_notify_invite);
134 case SILC_NOTIFY_TYPE_JOIN:
136 silc_fsm_next(fsm, silc_client_notify_join);
139 case SILC_NOTIFY_TYPE_LEAVE:
141 silc_fsm_next(fsm, silc_client_notify_leave);
144 case SILC_NOTIFY_TYPE_SIGNOFF:
146 silc_fsm_next(fsm, silc_client_notify_signoff);
149 case SILC_NOTIFY_TYPE_TOPIC_SET:
151 silc_fsm_next(fsm, silc_client_notify_topic_set);
154 case SILC_NOTIFY_TYPE_NICK_CHANGE:
156 silc_fsm_next(fsm, silc_client_notify_nick_change);
159 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
161 silc_fsm_next(fsm, silc_client_notify_cmode_change);
164 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
166 silc_fsm_next(fsm, silc_client_notify_cumode_change);
169 case SILC_NOTIFY_TYPE_MOTD:
171 silc_fsm_next(fsm, silc_client_notify_motd);
174 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
175 /** CHANNEL_CHANGE */
176 silc_fsm_next(fsm, silc_client_notify_channel_change);
179 case SILC_NOTIFY_TYPE_KICKED:
181 silc_fsm_next(fsm, silc_client_notify_kicked);
184 case SILC_NOTIFY_TYPE_KILLED:
186 silc_fsm_next(fsm, silc_client_notify_killed);
189 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
190 /** SERVER_SIGNOFF */
191 silc_fsm_next(fsm, silc_client_notify_server_signoff);
194 case SILC_NOTIFY_TYPE_ERROR:
196 silc_fsm_next(fsm, silc_client_notify_error);
199 case SILC_NOTIFY_TYPE_WATCH:
201 silc_fsm_next(fsm, silc_client_notify_watch);
205 /** Unknown notify */
206 silc_notify_payload_free(payload);
207 silc_packet_free(packet);
209 return SILC_FSM_FINISH;
213 return SILC_FSM_YIELD;
216 /* Notify processed, finish the packet processing thread */
218 SILC_FSM_STATE(silc_client_notify_processed)
220 SilcClientNotify notify = state_context;
221 SilcPacket packet = notify->packet;
222 SilcNotifyPayload payload = notify->payload;
224 silc_notify_payload_free(payload);
225 silc_packet_free(packet);
227 return SILC_FSM_FINISH;
230 /********************************** NONE ************************************/
232 SILC_FSM_STATE(silc_client_notify_none)
234 SilcClientConnection conn = fsm_context;
235 SilcClient client = conn->client;
236 SilcClientNotify notify = state_context;
237 SilcNotifyPayload payload = notify->payload;
238 SilcNotifyType type = silc_notify_get_type(payload);
239 SilcArgumentPayload args = silc_notify_get_args(payload);
241 /* Notify application */
242 NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL));
244 /** Notify processed */
245 silc_fsm_next(fsm, silc_client_notify_processed);
246 return SILC_FSM_CONTINUE;
249 /********************************* INVITE ***********************************/
251 /* Someone invite me to a channel */
253 SILC_FSM_STATE(silc_client_notify_invite)
255 SilcClientConnection conn = fsm_context;
256 SilcClient client = conn->client;
257 SilcClientNotify notify = state_context;
258 SilcNotifyPayload payload = notify->payload;
259 SilcNotifyType type = silc_notify_get_type(payload);
260 SilcArgumentPayload args = silc_notify_get_args(payload);
261 SilcClientEntry client_entry;
262 SilcChannelEntry channel = NULL;
267 SILC_LOG_DEBUG(("Notify: INVITE"));
270 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
273 /* Get the channel name */
274 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
278 /* Get the channel entry */
279 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
281 /* If channel is being resolved handle notify after resolving */
282 if (channel->internal.resolve_cmd_ident) {
283 silc_client_unref_channel(client, conn, channel);
284 SILC_FSM_CALL(silc_client_command_pending(
285 conn, SILC_COMMAND_NONE,
286 channel->internal.resolve_cmd_ident,
287 silc_client_notify_wait_continue,
292 /* Get sender Client ID */
293 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
296 /* Find Client entry and if not found query it */
297 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
298 if (!client_entry || !client_entry->nickname[0]) {
299 /** Resolve client */
300 silc_client_unref_client(client, conn, client_entry);
301 notify->channel = channel;
302 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
303 silc_client_get_client_by_id_resolve(
304 client, conn, &id.u.client_id, NULL,
305 silc_client_notify_resolved,
310 /* Notify application */
311 NOTIFY(client, conn, type, channel, tmp, client_entry);
313 silc_client_unref_client(client, conn, client_entry);
316 /** Notify processed */
317 silc_client_unref_channel(client, conn, channel);
318 silc_fsm_next(fsm, silc_client_notify_processed);
319 return SILC_FSM_CONTINUE;
322 /********************************** JOIN ************************************/
324 /* Someone joined a channel */
326 SILC_FSM_STATE(silc_client_notify_join)
328 SilcClientConnection conn = fsm_context;
329 SilcClient client = conn->client;
330 SilcClientNotify notify = state_context;
331 SilcNotifyPayload payload = notify->payload;
332 SilcNotifyType type = silc_notify_get_type(payload);
333 SilcArgumentPayload args = silc_notify_get_args(payload);
334 SilcClientEntry client_entry;
335 SilcChannelEntry channel = NULL;
338 SILC_LOG_DEBUG(("Notify: JOIN"));
341 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
344 /* Get channel entry */
345 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
349 /* If channel is being resolved handle notify after resolving */
350 if (channel->internal.resolve_cmd_ident) {
351 silc_client_unref_channel(client, conn, channel);
352 SILC_FSM_CALL(silc_client_command_pending(
353 conn, SILC_COMMAND_NONE,
354 channel->internal.resolve_cmd_ident,
355 silc_client_notify_wait_continue,
361 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
364 /* Find Client entry and if not found query it */
365 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
366 if (!client_entry || !client_entry->nickname[0] ||
367 !client_entry->username[0]) {
368 /** Resolve client */
369 silc_client_unref_client(client, conn, client_entry);
370 notify->channel = channel;
371 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
372 silc_client_get_client_by_id_resolve(
373 client, conn, &id.u.client_id, NULL,
374 silc_client_notify_resolved,
379 if (client_entry != conn->local_entry)
380 silc_client_nickname_format(client, conn, client_entry);
382 /* Join the client to channel */
383 if (!silc_client_add_to_channel(channel, client_entry, 0))
386 /* Notify application. */
387 NOTIFY(client, conn, type, client_entry, channel);
389 silc_client_unref_client(client, conn, client_entry);
392 /** Notify processed */
393 silc_client_unref_channel(client, conn, channel);
394 silc_fsm_next(fsm, silc_client_notify_processed);
395 return SILC_FSM_CONTINUE;
398 /********************************** LEAVE ***********************************/
400 /* Someone left a channel */
402 SILC_FSM_STATE(silc_client_notify_leave)
404 SilcClientConnection conn = fsm_context;
405 SilcClient client = conn->client;
406 SilcClientNotify notify = state_context;
407 SilcNotifyPayload payload = notify->payload;
408 SilcPacket packet = notify->packet;
409 SilcNotifyType type = silc_notify_get_type(payload);
410 SilcArgumentPayload args = silc_notify_get_args(payload);
411 SilcClientEntry client_entry = NULL;
412 SilcChannelEntry channel = NULL;
416 SILC_LOG_DEBUG(("Notify: LEAVE"));
418 /* Get channel entry */
419 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
420 &id.u.channel_id, sizeof(id.u.channel_id)))
422 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
426 /* If channel is being resolved handle notify after resolving */
427 if (channel->internal.resolve_cmd_ident) {
428 silc_client_unref_channel(client, conn, channel);
429 SILC_FSM_CALL(silc_client_command_pending(
430 conn, SILC_COMMAND_NONE,
431 channel->internal.resolve_cmd_ident,
432 silc_client_notify_wait_continue,
438 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
441 /* Find Client entry */
442 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
446 /* Remove client from channel */
447 chu = silc_client_on_channel(channel, client_entry);
449 silc_hash_table_del(client_entry->channels, channel);
450 silc_hash_table_del(channel->user_list, client_entry);
454 #if 0 /* Kind of useless, server will return error if client keeps using
455 non-existing client, and the entry is removed then. */
456 /* Some client implementations actually quit network by first doing
457 LEAVE and then immediately SIGNOFF. We'll check for this by doing
458 check for the client after 5 - 34 seconds. If it is not valid after
459 that we'll remove the client from cache. */
460 if (!silc_hash_table_count(client_entry->channels)) {
461 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
462 res->context = client;
463 res->sock = silc_socket_dup(conn->sock);
464 res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
465 silc_schedule_task_add(client->schedule, conn->sock->sock,
466 silc_client_notify_check_client, res,
467 (5 + (silc_rng_get_rn16(client->rng) % 29)),
468 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
472 /* Notify application. */
473 NOTIFY(client, conn, type, client_entry, channel);
475 silc_client_unref_client(client, conn, client_entry);
478 /** Notify processed */
479 silc_client_unref_channel(client, conn, channel);
480 silc_fsm_next(fsm, silc_client_notify_processed);
481 return SILC_FSM_CONTINUE;
484 /********************************* SIGNOFF **********************************/
486 /* Someone quit SILC network */
488 SILC_FSM_STATE(silc_client_notify_signoff)
490 SilcClientConnection conn = fsm_context;
491 SilcClient client = conn->client;
492 SilcClientNotify notify = state_context;
493 SilcNotifyPayload payload = notify->payload;
494 SilcNotifyType type = silc_notify_get_type(payload);
495 SilcArgumentPayload args = silc_notify_get_args(payload);
496 SilcClientEntry client_entry;
501 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
504 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
507 /* Find Client entry */
508 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
512 /* Get signoff message */
513 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
517 /* Notify application */
518 NOTIFY(client, conn, type, client_entry, tmp);
520 /* Remove from all channels */
521 silc_client_remove_from_channels(client, conn, client_entry);
524 /* Remove from cache */
525 silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
529 silc_client_unref_client(client, conn, client_entry);
530 silc_client_del_client_entry(client, conn, client_entry);
533 /** Notify processed */
534 silc_fsm_next(fsm, silc_client_notify_processed);
535 return SILC_FSM_CONTINUE;
538 /******************************** TOPIC_SET *********************************/
540 /* Someone set topic on a channel */
542 SILC_FSM_STATE(silc_client_notify_topic_set)
544 SilcClientConnection conn = fsm_context;
545 SilcClient client = conn->client;
546 SilcClientNotify notify = state_context;
547 SilcNotifyPayload payload = notify->payload;
548 SilcPacket packet = notify->packet;
549 SilcNotifyType type = silc_notify_get_type(payload);
550 SilcArgumentPayload args = silc_notify_get_args(payload);
551 SilcClientEntry client_entry = NULL;
552 SilcChannelEntry channel = NULL, channel_entry = NULL;
553 SilcServerEntry server = NULL;
559 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
561 /* Get channel entry */
562 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
563 &id.u.channel_id, sizeof(id.u.channel_id)))
565 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
569 /* If channel is being resolved handle notify after resolving */
570 if (channel->internal.resolve_cmd_ident) {
571 silc_client_unref_channel(client, conn, channel);
572 SILC_FSM_CALL(silc_client_command_pending(
573 conn, SILC_COMMAND_NONE,
574 channel->internal.resolve_cmd_ident,
575 silc_client_notify_wait_continue,
581 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
585 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
589 if (id.type == SILC_ID_CLIENT) {
590 /* Find Client entry */
591 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
592 if (!client_entry || !client_entry->nickname[0]) {
593 /** Resolve client */
594 silc_client_unref_client(client, conn, client_entry);
595 notify->channel = channel;
596 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
597 silc_client_get_client_by_id_resolve(
598 client, conn, &id.u.client_id, NULL,
599 silc_client_notify_resolved,
603 entry = client_entry;
604 } else if (id.type == SILC_ID_SERVER) {
605 /* Find Server entry */
606 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
608 /** Resolve server */
609 notify->channel = channel;
610 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
611 silc_client_get_server_by_id_resolve(
612 client, conn, &id.u.server_id,
613 silc_client_notify_resolved,
619 /* Find Channel entry */
620 channel_entry = silc_client_get_channel_by_id(client, conn,
622 if (!channel_entry) {
623 /** Resolve channel */
624 notify->channel = channel;
625 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
626 silc_client_get_channel_by_id_resolve(
627 client, conn, &id.u.channel_id,
628 silc_client_notify_resolved,
632 entry = channel_entry;
635 silc_free(channel->topic);
636 channel->topic = silc_memdup(tmp, strlen(tmp));
638 /* Notify application. */
639 NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
642 silc_client_unref_client(client, conn, client_entry);
644 silc_client_unref_server(client, conn, server);
646 silc_client_unref_channel(client, conn, channel_entry);
649 /** Notify processed */
650 silc_client_unref_channel(client, conn, channel);
651 silc_fsm_next(fsm, silc_client_notify_processed);
652 return SILC_FSM_CONTINUE;
655 /****************************** NICK_CHANGE *********************************/
657 /* Someone changed their nickname on a channel */
659 SILC_FSM_STATE(silc_client_notify_nick_change)
661 SilcClientConnection conn = fsm_context;
662 SilcClient client = conn->client;
663 SilcClientNotify notify = state_context;
664 SilcNotifyPayload payload = notify->payload;
665 SilcNotifyType type = silc_notify_get_type(payload);
666 SilcArgumentPayload args = silc_notify_get_args(payload);
667 SilcClientEntry client_entry = NULL;
668 unsigned char *tmp, *nick, oldnick[128 + 1];
672 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
675 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
679 if (conn->local_id &&
680 SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
683 /* Find old Client entry */
684 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
685 if (!client_entry || !client_entry->nickname[0]) {
686 /** Resolve client */
687 silc_client_unref_client(client, conn, client_entry);
688 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
689 client, conn, &id.u.client_id, NULL,
690 silc_client_notify_resolved,
695 /* Get new Client ID */
696 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
699 /* Take the new nickname */
700 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
704 /* Check whether nickname changed at all. It is possible that nick
705 change notify is received but nickname didn't change, only the
706 ID changes. If Client ID hash match, nickname didn't change. */
707 if (SILC_ID_COMPARE_HASH(&client_entry->id, &id.u.client_id) &&
708 silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
709 /* Nickname didn't change. Update only Client ID. We don't notify
710 application because nickname didn't change. */
711 silc_idcache_update(conn->internal->client_cache, client_entry,
712 &client_entry->id, &id.u.client_id, NULL, NULL, FALSE);
716 /* Normalize nickname */
717 nick = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, 128, NULL);
721 /* Update nickname */
722 if (!silc_idcache_update(conn->internal->client_cache, client_entry,
723 NULL, NULL, client_entry->nickname_normalized,
728 memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
729 memcpy(client_entry->nickname, tmp, tmp_len);
730 client_entry->nickname_normalized = nick;
731 silc_client_nickname_format(client, conn, client_entry);
733 /* Notify application */
734 NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
737 /** Notify processed */
738 silc_client_unref_client(client, conn, client_entry);
739 silc_fsm_next(fsm, silc_client_notify_processed);
740 return SILC_FSM_CONTINUE;
743 /****************************** CMODE_CHANGE ********************************/
745 /* Someone changed channel mode */
747 SILC_FSM_STATE(silc_client_notify_cmode_change)
749 SilcClientConnection conn = fsm_context;
750 SilcClient client = conn->client;
751 SilcClientNotify notify = state_context;
752 SilcNotifyPayload payload = notify->payload;
753 SilcPacket packet = notify->packet;
754 SilcNotifyType type = silc_notify_get_type(payload);
755 SilcArgumentPayload args = silc_notify_get_args(payload);
756 SilcClientEntry client_entry = NULL;
757 SilcChannelEntry channel = NULL, channel_entry = NULL;
758 SilcServerEntry server = NULL;
761 SilcUInt32 tmp_len, mode;
763 char *passphrase, *cipher, *hmac;
764 SilcPublicKey founder_key = NULL;
765 SilcDList chpks = NULL;
767 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
769 /* Get channel entry */
770 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
771 &id.u.channel_id, sizeof(id.u.channel_id)))
773 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
777 /* If channel is being resolved handle notify after resolving */
778 if (channel->internal.resolve_cmd_ident) {
779 silc_client_unref_channel(client, conn, channel);
780 SILC_FSM_CALL(silc_client_command_pending(
781 conn, SILC_COMMAND_NONE,
782 channel->internal.resolve_cmd_ident,
783 silc_client_notify_wait_continue,
789 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
792 SILC_GET32_MSB(mode, tmp);
795 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
798 if (id.type == SILC_ID_CLIENT) {
799 /* Find Client entry */
800 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
801 if (!client_entry || !client_entry->nickname[0]) {
802 /** Resolve client */
803 silc_client_unref_client(client, conn, client_entry);
804 notify->channel = channel;
805 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
806 silc_client_get_client_by_id_resolve(
807 client, conn, &id.u.client_id, NULL,
808 silc_client_notify_resolved,
812 entry = client_entry;
813 } else if (id.type == SILC_ID_SERVER) {
814 /* Find Server entry */
815 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
817 /** Resolve server */
818 notify->channel = channel;
819 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
820 silc_client_get_server_by_id_resolve(
821 client, conn, &id.u.server_id,
822 silc_client_notify_resolved,
828 /* Find Channel entry */
829 channel_entry = silc_client_get_channel_by_id(client, conn,
831 if (!channel_entry) {
832 /** Resolve channel */
833 notify->channel = channel;
834 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
835 silc_client_get_channel_by_id_resolve(
836 client, conn, &id.u.channel_id,
837 silc_client_notify_resolved,
841 entry = channel_entry;
844 /* Get the channel founder key if it was set */
845 tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
847 if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key))
849 if (!channel->founder_key) {
850 channel->founder_key = founder_key;
856 cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
859 hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
861 unsigned char hash[SILC_HASH_MAXLEN];
864 if (!silc_hmac_alloc(hmac, NULL, &newhmac))
867 /* Get HMAC key from the old HMAC context, and update it to the new one */
868 tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
870 silc_hash_make(silc_hmac_get_hash(newhmac), tmp, tmp_len, hash);
871 silc_hmac_set_key(newhmac, hash,
872 silc_hash_len(silc_hmac_get_hash(newhmac)));
873 if (channel->internal.hmac)
874 silc_hmac_free(channel->internal.hmac);
875 channel->internal.hmac = newhmac;
876 memset(hash, 0, sizeof(hash));
880 /* Get the passphrase if it was set */
881 passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
884 tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
885 if (tmp && tmp_len == 4)
886 SILC_GET32_MSB(channel->user_limit, tmp);
887 if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
888 channel->user_limit = 0;
890 /* Save the new mode */
891 channel->mode = mode;
893 /* Get the channel public key that was added or removed */
894 tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
896 chpks = silc_argument_list_parse_decoded(tmp, tmp_len,
897 SILC_ARGUMENT_PUBLIC_KEY);
899 /* Notify application. */
900 NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
901 passphrase, channel->founder_key, chpks, channel);
905 silc_pkcs_public_key_free(founder_key);
907 silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
909 silc_client_unref_client(client, conn, client_entry);
911 silc_client_unref_server(client, conn, server);
913 silc_client_unref_channel(client, conn, channel_entry);
914 silc_client_unref_channel(client, conn, channel);
916 /** Notify processed */
917 silc_fsm_next(fsm, silc_client_notify_processed);
918 return SILC_FSM_CONTINUE;
921 /***************************** CUMODE_CHANGE ********************************/
923 /* Someone changed a user's mode on a channel */
925 SILC_FSM_STATE(silc_client_notify_cumode_change)
927 SilcClientConnection conn = fsm_context;
928 SilcClient client = conn->client;
929 SilcClientNotify notify = state_context;
930 SilcNotifyPayload payload = notify->payload;
931 SilcPacket packet = notify->packet;
932 SilcNotifyType type = silc_notify_get_type(payload);
933 SilcArgumentPayload args = silc_notify_get_args(payload);
934 SilcClientEntry client_entry = NULL, client_entry2 = NULL;
935 SilcChannelEntry channel = NULL, channel_entry = NULL;
936 SilcServerEntry server = NULL;
940 SilcUInt32 tmp_len, mode;
943 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
945 /* Get channel entry */
946 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
947 &id.u.channel_id, sizeof(id.u.channel_id)))
949 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
953 /* If channel is being resolved handle notify after resolving */
954 if (channel->internal.resolve_cmd_ident) {
955 silc_client_unref_channel(client, conn, channel);
956 SILC_FSM_CALL(silc_client_command_pending(
957 conn, SILC_COMMAND_NONE,
958 channel->internal.resolve_cmd_ident,
959 silc_client_notify_wait_continue,
965 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
968 SILC_GET32_MSB(mode, tmp);
971 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
974 if (id.type == SILC_ID_CLIENT) {
975 /* Find Client entry */
976 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
977 if (!client_entry || !client_entry->nickname[0]) {
978 /** Resolve client */
979 silc_client_unref_client(client, conn, client_entry);
980 notify->channel = channel;
981 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
982 silc_client_get_client_by_id_resolve(
983 client, conn, &id.u.client_id, NULL,
984 silc_client_notify_resolved,
988 entry = client_entry;
989 } else if (id.type == SILC_ID_SERVER) {
990 /* Find Server entry */
991 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
993 /** Resolve server */
994 notify->channel = channel;
995 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
996 silc_client_get_server_by_id_resolve(
997 client, conn, &id.u.server_id,
998 silc_client_notify_resolved,
1004 /* Find Channel entry */
1005 channel_entry = silc_client_get_channel_by_id(client, conn,
1007 if (!channel_entry) {
1008 /** Resolve channel */
1009 notify->channel = channel;
1010 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1011 silc_client_get_channel_by_id_resolve(
1012 client, conn, &id.u.channel_id,
1013 silc_client_notify_resolved,
1017 entry = channel_entry;
1020 /* Get target Client ID */
1021 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
1024 /* Find target Client entry */
1025 client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
1026 if (!client_entry2 || !client_entry2->nickname[0]) {
1027 /** Resolve client */
1028 silc_client_unref_client(client, conn, client_entry);
1029 silc_client_unref_client(client, conn, client_entry2);
1030 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1031 client, conn, &id2.u.client_id, NULL,
1032 silc_client_notify_resolved,
1038 chu = silc_client_on_channel(channel, client_entry2);
1042 /* Notify application. */
1043 NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
1046 silc_client_unref_client(client, conn, client_entry2);
1048 silc_client_unref_client(client, conn, client_entry);
1050 silc_client_unref_server(client, conn, server);
1052 silc_client_unref_channel(client, conn, channel_entry);
1053 silc_client_unref_channel(client, conn, channel);
1055 /** Notify processed */
1056 silc_fsm_next(fsm, silc_client_notify_processed);
1057 return SILC_FSM_CONTINUE;
1060 /********************************* MOTD *************************************/
1062 /* Received Message of the day */
1064 SILC_FSM_STATE(silc_client_notify_motd)
1066 SilcClientConnection conn = fsm_context;
1067 SilcClient client = conn->client;
1068 SilcClientNotify notify = state_context;
1069 SilcNotifyPayload payload = notify->payload;
1070 SilcNotifyType type = silc_notify_get_type(payload);
1071 SilcArgumentPayload args = silc_notify_get_args(payload);
1075 SILC_LOG_DEBUG(("Notify: MOTD"));
1078 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1082 /* Notify application */
1083 NOTIFY(client, conn, type, tmp);
1086 /** Notify processed */
1087 silc_fsm_next(fsm, silc_client_notify_processed);
1088 return SILC_FSM_CONTINUE;
1091 /**************************** CHANNEL CHANGE ********************************/
1093 /* Router has enforced a new ID to a channel, change it */
1095 SILC_FSM_STATE(silc_client_notify_channel_change)
1097 SilcClientConnection conn = fsm_context;
1098 SilcClient client = conn->client;
1099 SilcClientNotify notify = state_context;
1100 SilcNotifyPayload payload = notify->payload;
1101 SilcNotifyType type = silc_notify_get_type(payload);
1102 SilcArgumentPayload args = silc_notify_get_args(payload);
1103 SilcChannelEntry channel = NULL;
1106 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
1108 /* Get the old ID */
1109 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1112 /* Get the channel entry */
1113 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1117 /* If channel is being resolved handle notify after resolving */
1118 if (channel->internal.resolve_cmd_ident) {
1119 silc_client_unref_channel(client, conn, channel);
1120 SILC_FSM_CALL(silc_client_command_pending(
1121 conn, SILC_COMMAND_NONE,
1122 channel->internal.resolve_cmd_ident,
1123 silc_client_notify_wait_continue,
1128 /* Get the new ID */
1129 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1132 /* Replace the Channel ID */
1133 if (!silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id))
1136 /* Notify application */
1137 NOTIFY(client, conn, type, channel, channel);
1140 /** Notify processed */
1141 silc_client_unref_channel(client, conn, channel);
1142 silc_fsm_next(fsm, silc_client_notify_processed);
1143 return SILC_FSM_CONTINUE;
1146 /******************************** KICKED ************************************/
1148 /* Some client was kicked from a channel */
1150 SILC_FSM_STATE(silc_client_notify_kicked)
1152 SilcClientConnection conn = fsm_context;
1153 SilcClient client = conn->client;
1154 SilcClientNotify notify = state_context;
1155 SilcNotifyPayload payload = notify->payload;
1156 SilcPacket packet = notify->packet;
1157 SilcNotifyType type = silc_notify_get_type(payload);
1158 SilcArgumentPayload args = silc_notify_get_args(payload);
1159 SilcClientEntry client_entry, client_entry2;
1160 SilcChannelEntry channel = NULL;
1161 SilcChannelUser chu;
1166 SILC_LOG_DEBUG(("Notify: KICKED"));
1168 /* Get channel entry */
1169 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
1170 &id.u.channel_id, sizeof(id.u.channel_id)))
1172 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1176 /* If channel is being resolved handle notify after resolving */
1177 if (channel->internal.resolve_cmd_ident) {
1178 silc_client_unref_channel(client, conn, channel);
1179 SILC_FSM_CALL(silc_client_command_pending(
1180 conn, SILC_COMMAND_NONE,
1181 channel->internal.resolve_cmd_ident,
1182 silc_client_notify_wait_continue,
1188 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1191 /* Find Client entry */
1192 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1196 /* Get kicker's Client ID */
1197 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1200 /* Find kicker's client entry and if not found resolve it */
1201 client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1202 if (!client_entry2 || !client_entry2->nickname[0]) {
1203 /** Resolve client */
1204 silc_client_unref_client(client, conn, client_entry);
1205 silc_client_unref_client(client, conn, client_entry2);
1206 notify->channel = channel;
1207 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1208 silc_client_get_client_by_id_resolve(
1209 client, conn, &id.u.client_id, NULL,
1210 silc_client_notify_resolved,
1216 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1218 /* Remove kicked client from channel */
1219 if (client_entry != conn->local_entry) {
1220 chu = silc_client_on_channel(channel, client_entry);
1222 silc_hash_table_del(client_entry->channels, channel);
1223 silc_hash_table_del(channel->user_list, client_entry);
1228 /* Notify application. */
1229 NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
1231 /* If I was kicked from channel, remove the channel */
1232 if (client_entry == conn->local_entry) {
1233 if (conn->current_channel == channel)
1234 conn->current_channel = NULL;
1235 silc_client_del_channel(client, conn, channel);
1238 silc_client_unref_client(client, conn, client_entry);
1239 silc_client_unref_client(client, conn, client_entry2);
1242 /** Notify processed */
1243 silc_client_unref_channel(client, conn, channel);
1244 silc_fsm_next(fsm, silc_client_notify_processed);
1245 return SILC_FSM_CONTINUE;
1248 /******************************** KILLED ************************************/
1250 /* Some client was killed from the network */
1252 SILC_FSM_STATE(silc_client_notify_killed)
1254 SilcClientConnection conn = fsm_context;
1255 SilcClient client = conn->client;
1256 SilcClientNotify notify = state_context;
1257 SilcNotifyPayload payload = notify->payload;
1258 SilcNotifyType type = silc_notify_get_type(payload);
1259 SilcArgumentPayload args = silc_notify_get_args(payload);
1260 SilcClientEntry client_entry = NULL, client_entry2 = NULL;
1261 SilcChannelEntry channel_entry = NULL;
1262 SilcServerEntry server = NULL;
1265 SilcUInt32 comment_len;
1268 SILC_LOG_DEBUG(("Notify: KILLED"));
1271 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1274 /* Find Client entry */
1275 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1280 comment = silc_argument_get_arg_type(args, 2, &comment_len);
1282 /* Get killer's ID */
1283 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1286 if (id.type == SILC_ID_CLIENT) {
1287 /* Find Client entry */
1288 client_entry2 = silc_client_get_client_by_id(client, conn,
1290 if (!client_entry2 || !client_entry2->nickname[0]) {
1291 /** Resolve client */
1292 silc_client_unref_client(client, conn, client_entry);
1293 silc_client_unref_client(client, conn, client_entry2);
1294 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1295 client, conn, &id.u.client_id, NULL,
1296 silc_client_notify_resolved,
1300 entry = client_entry2;
1301 } else if (id.type == SILC_ID_SERVER) {
1302 /* Find Server entry */
1303 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1305 /** Resolve server */
1306 SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
1307 client, conn, &id.u.server_id,
1308 silc_client_notify_resolved,
1314 /* Find Channel entry */
1315 channel_entry = silc_client_get_channel_by_id(client, conn,
1317 if (!channel_entry) {
1318 /** Resolve channel */
1319 SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1320 client, conn, &id.u.channel_id,
1321 silc_client_notify_resolved,
1325 entry = channel_entry;
1328 /* Notify application. */
1329 NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
1331 /* Delete the killed client */
1332 if (client_entry != conn->local_entry)
1333 silc_client_del_client(client, conn, client_entry);
1336 silc_client_unref_client(client, conn, client_entry);
1338 silc_client_unref_client(client, conn, client_entry2);
1340 silc_client_unref_server(client, conn, server);
1342 silc_client_unref_channel(client, conn, channel_entry);
1344 /** Notify processed */
1345 silc_fsm_next(fsm, silc_client_notify_processed);
1346 return SILC_FSM_CONTINUE;
1349 /**************************** SERVER SIGNOFF ********************************/
1351 /* Some server quit SILC network. Remove its clients from channels. */
1353 SILC_FSM_STATE(silc_client_notify_server_signoff)
1355 SilcClientConnection conn = fsm_context;
1356 SilcClient client = conn->client;
1357 SilcClientNotify notify = state_context;
1358 SilcNotifyPayload payload = notify->payload;
1359 SilcNotifyType type = silc_notify_get_type(payload);
1360 SilcArgumentPayload args = silc_notify_get_args(payload);
1361 SilcClientEntry client_entry;
1366 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1368 clients = silc_dlist_init();
1372 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1374 if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
1377 /* Get the client entry */
1378 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1380 silc_dlist_add(clients, client_entry);
1383 /* Notify application. We don't keep server entries so the server
1384 entry is returned as NULL. The client's are returned as list. */
1385 NOTIFY(client, conn, type, NULL, clients);
1387 /* Delete the clients */
1388 silc_dlist_start(clients);
1389 while ((client_entry = silc_dlist_get(clients)))
1390 silc_client_del_client(client, conn, client_entry);
1393 /** Notify processed */
1394 silc_client_list_free(client, conn, clients);
1395 silc_fsm_next(fsm, silc_client_notify_processed);
1396 return SILC_FSM_CONTINUE;
1399 /******************************** ERROR *************************************/
1401 /* Some error occurred */
1403 SILC_FSM_STATE(silc_client_notify_error)
1405 SilcClientConnection conn = fsm_context;
1406 SilcClient client = conn->client;
1407 SilcClientNotify notify = state_context;
1408 SilcNotifyPayload payload = notify->payload;
1409 SilcNotifyType type = silc_notify_get_type(payload);
1410 SilcArgumentPayload args = silc_notify_get_args(payload);
1411 SilcClientEntry client_entry;
1418 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1419 if (!tmp && tmp_len != 1)
1421 error = (SilcStatus)tmp[0];
1423 SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1425 /* Handle the error */
1426 if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1427 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1429 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1431 silc_client_del_client(client, conn, client_entry);
1432 silc_client_unref_client(client, conn, client_entry);
1436 /* Notify application. */
1437 NOTIFY(client, conn, type, error);
1440 /** Notify processed */
1441 silc_fsm_next(fsm, silc_client_notify_processed);
1442 return SILC_FSM_CONTINUE;
1445 /******************************** WATCH *************************************/
1447 /* Received notify about some client we are watching */
1449 SILC_FSM_STATE(silc_client_notify_watch)
1451 SilcClientConnection conn = fsm_context;
1452 SilcClient client = conn->client;
1453 SilcClientNotify notify = state_context;
1454 SilcNotifyPayload payload = notify->payload;
1455 SilcNotifyType type = silc_notify_get_type(payload);
1456 SilcArgumentPayload args = silc_notify_get_args(payload);
1457 SilcClientEntry client_entry = NULL;
1458 SilcNotifyType ntype = 0;
1459 SilcBool del_client = FALSE;
1460 unsigned char *pk, *tmp;
1461 SilcUInt32 mode, pk_len, tmp_len;
1462 SilcPublicKey public_key = NULL;
1465 SILC_LOG_DEBUG(("Notify: WATCH"));
1467 /* Get sender Client ID */
1468 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1471 /* Find Client entry and if not found resolve it */
1472 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1473 if (!client_entry || !client_entry->nickname[0]) {
1474 /** Resolve client */
1475 silc_client_unref_client(client, conn, client_entry);
1476 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1477 client, conn, &id.u.client_id, NULL,
1478 silc_client_notify_resolved,
1484 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1485 if (!tmp || tmp_len != 4)
1487 SILC_GET32_MSB(mode, tmp);
1489 /* Get notify type */
1490 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1491 if (tmp && tmp_len != 2)
1494 SILC_GET16_MSB(ntype, tmp);
1497 tmp = silc_argument_get_arg_type(args, 2, NULL);
1499 char *tmp_nick = NULL;
1501 if (client->internal->params->nickname_parse)
1502 client->internal->params->nickname_parse(client_entry->nickname,
1505 tmp_nick = strdup(tmp);
1507 /* If same nick, the client was new to us and has become "present"
1508 to network. Send NULL as nick to application. */
1509 if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
1512 silc_free(tmp_nick);
1515 /* Get public key, if present */
1516 pk = silc_argument_get_arg_type(args, 5, &pk_len);
1517 if (pk && !client_entry->public_key) {
1518 if (silc_public_key_payload_decode(pk, pk_len, &public_key)) {
1519 client_entry->public_key = public_key;
1524 /* Notify application. */
1525 NOTIFY(client, conn, type, client_entry, tmp, mode, ntype,
1526 client_entry->public_key);
1528 client_entry->mode = mode;
1530 /* If nickname was changed, remove the client entry unless the
1531 client is on some channel */
1532 /* XXX, why do we need to remove the client entry?? */
1533 if (tmp && ntype == SILC_NOTIFY_TYPE_NICK_CHANGE &&
1534 !silc_hash_table_count(client_entry->channels))
1536 else if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
1537 ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1538 ntype == SILC_NOTIFY_TYPE_KILLED)
1542 silc_client_del_client(client, conn, client_entry);
1545 silc_pkcs_public_key_free(public_key);
1548 /** Notify processed */
1549 silc_client_unref_client(client, conn, client_entry);
1550 silc_fsm_next(fsm, silc_client_notify_processed);
1551 return SILC_FSM_CONTINUE;