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 /* Get new Client ID */
684 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id2, NULL))
688 if (conn->local_id &&
689 SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
692 /* Find old Client entry */
693 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
694 if (!client_entry || !client_entry->nickname[0]) {
695 /** Resolve client */
696 silc_client_unref_client(client, conn, client_entry);
697 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
698 client, conn, &id.u.client_id, NULL,
699 silc_client_notify_resolved,
704 /* Take the new nickname */
705 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
709 /* Check whether nickname changed at all. It is possible that nick
710 change notify is received but nickname didn't change, only the
711 ID changes. If Client ID hash match, nickname didn't change. */
712 if (SILC_ID_COMPARE_HASH(&client_entry->id, &id2.u.client_id) &&
713 silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
714 /* Nickname didn't change. Update only Client ID. We don't notify
715 application because nickname didn't change. */
716 silc_idcache_update(conn->internal->client_cache, client_entry,
717 &client_entry->id, &id2.u.client_id, NULL,
722 /* Normalize nickname */
723 nick = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, 128, NULL);
727 /* Update nickname */
728 if (!silc_idcache_update(conn->internal->client_cache, client_entry,
729 NULL, NULL, client_entry->nickname_normalized,
734 memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
735 memcpy(client_entry->nickname, tmp, tmp_len);
736 client_entry->nickname_normalized = nick;
737 silc_client_nickname_format(client, conn, client_entry);
739 /* Notify application */
740 NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
743 /** Notify processed */
744 silc_client_unref_client(client, conn, client_entry);
745 silc_fsm_next(fsm, silc_client_notify_processed);
746 return SILC_FSM_CONTINUE;
749 /****************************** CMODE_CHANGE ********************************/
751 /* Someone changed channel mode */
753 SILC_FSM_STATE(silc_client_notify_cmode_change)
755 SilcClientConnection conn = fsm_context;
756 SilcClient client = conn->client;
757 SilcClientNotify notify = state_context;
758 SilcNotifyPayload payload = notify->payload;
759 SilcPacket packet = notify->packet;
760 SilcNotifyType type = silc_notify_get_type(payload);
761 SilcArgumentPayload args = silc_notify_get_args(payload);
762 SilcClientEntry client_entry = NULL;
763 SilcChannelEntry channel = NULL, channel_entry = NULL;
764 SilcServerEntry server = NULL;
767 SilcUInt32 tmp_len, mode;
769 char *passphrase, *cipher, *hmac;
770 SilcPublicKey founder_key = NULL;
771 SilcDList chpks = NULL;
773 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
775 /* Get channel entry */
776 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
777 &id.u.channel_id, sizeof(id.u.channel_id)))
779 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
783 /* If channel is being resolved handle notify after resolving */
784 if (channel->internal.resolve_cmd_ident) {
785 silc_client_unref_channel(client, conn, channel);
786 SILC_FSM_CALL(silc_client_command_pending(
787 conn, SILC_COMMAND_NONE,
788 channel->internal.resolve_cmd_ident,
789 silc_client_notify_wait_continue,
795 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
798 SILC_GET32_MSB(mode, tmp);
801 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
804 if (id.type == SILC_ID_CLIENT) {
805 /* Find Client entry */
806 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
807 if (!client_entry || !client_entry->nickname[0]) {
808 /** Resolve client */
809 silc_client_unref_client(client, conn, client_entry);
810 notify->channel = channel;
811 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
812 silc_client_get_client_by_id_resolve(
813 client, conn, &id.u.client_id, NULL,
814 silc_client_notify_resolved,
818 entry = client_entry;
819 } else if (id.type == SILC_ID_SERVER) {
820 /* Find Server entry */
821 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
823 /** Resolve server */
824 notify->channel = channel;
825 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
826 silc_client_get_server_by_id_resolve(
827 client, conn, &id.u.server_id,
828 silc_client_notify_resolved,
834 /* Find Channel entry */
835 channel_entry = silc_client_get_channel_by_id(client, conn,
837 if (!channel_entry) {
838 /** Resolve channel */
839 notify->channel = channel;
840 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
841 silc_client_get_channel_by_id_resolve(
842 client, conn, &id.u.channel_id,
843 silc_client_notify_resolved,
847 entry = channel_entry;
850 /* Get the channel founder key if it was set */
851 tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
853 if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key))
855 if (!channel->founder_key) {
856 channel->founder_key = founder_key;
862 cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
865 hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
867 unsigned char hash[SILC_HASH_MAXLEN];
870 if (!silc_hmac_alloc(hmac, NULL, &newhmac))
873 /* Get HMAC key from the old HMAC context, and update it to the new one */
874 tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
876 silc_hash_make(silc_hmac_get_hash(newhmac), tmp, tmp_len, hash);
877 silc_hmac_set_key(newhmac, hash,
878 silc_hash_len(silc_hmac_get_hash(newhmac)));
879 if (channel->internal.hmac)
880 silc_hmac_free(channel->internal.hmac);
881 channel->internal.hmac = newhmac;
882 memset(hash, 0, sizeof(hash));
886 /* Get the passphrase if it was set */
887 passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
890 tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
891 if (tmp && tmp_len == 4)
892 SILC_GET32_MSB(channel->user_limit, tmp);
893 if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
894 channel->user_limit = 0;
896 /* Save the new mode */
897 channel->mode = mode;
899 /* Get the channel public key that was added or removed */
900 tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
902 chpks = silc_argument_list_parse_decoded(tmp, tmp_len,
903 SILC_ARGUMENT_PUBLIC_KEY);
905 /* Notify application. */
906 NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
907 passphrase, channel->founder_key, chpks, channel);
911 silc_pkcs_public_key_free(founder_key);
913 silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
915 silc_client_unref_client(client, conn, client_entry);
917 silc_client_unref_server(client, conn, server);
919 silc_client_unref_channel(client, conn, channel_entry);
920 silc_client_unref_channel(client, conn, channel);
922 /** Notify processed */
923 silc_fsm_next(fsm, silc_client_notify_processed);
924 return SILC_FSM_CONTINUE;
927 /***************************** CUMODE_CHANGE ********************************/
929 /* Someone changed a user's mode on a channel */
931 SILC_FSM_STATE(silc_client_notify_cumode_change)
933 SilcClientConnection conn = fsm_context;
934 SilcClient client = conn->client;
935 SilcClientNotify notify = state_context;
936 SilcNotifyPayload payload = notify->payload;
937 SilcPacket packet = notify->packet;
938 SilcNotifyType type = silc_notify_get_type(payload);
939 SilcArgumentPayload args = silc_notify_get_args(payload);
940 SilcClientEntry client_entry = NULL, client_entry2 = NULL;
941 SilcChannelEntry channel = NULL, channel_entry = NULL;
942 SilcServerEntry server = NULL;
946 SilcUInt32 tmp_len, mode;
949 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
951 /* Get channel entry */
952 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
953 &id.u.channel_id, sizeof(id.u.channel_id)))
955 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
959 /* If channel is being resolved handle notify after resolving */
960 if (channel->internal.resolve_cmd_ident) {
961 silc_client_unref_channel(client, conn, channel);
962 SILC_FSM_CALL(silc_client_command_pending(
963 conn, SILC_COMMAND_NONE,
964 channel->internal.resolve_cmd_ident,
965 silc_client_notify_wait_continue,
971 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
974 SILC_GET32_MSB(mode, tmp);
977 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
980 if (id.type == SILC_ID_CLIENT) {
981 /* Find Client entry */
982 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
983 if (!client_entry || !client_entry->nickname[0]) {
984 /** Resolve client */
985 silc_client_unref_client(client, conn, client_entry);
986 notify->channel = channel;
987 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
988 silc_client_get_client_by_id_resolve(
989 client, conn, &id.u.client_id, NULL,
990 silc_client_notify_resolved,
994 entry = client_entry;
995 } else if (id.type == SILC_ID_SERVER) {
996 /* Find Server entry */
997 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
999 /** Resolve server */
1000 notify->channel = channel;
1001 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1002 silc_client_get_server_by_id_resolve(
1003 client, conn, &id.u.server_id,
1004 silc_client_notify_resolved,
1010 /* Find Channel entry */
1011 channel_entry = silc_client_get_channel_by_id(client, conn,
1013 if (!channel_entry) {
1014 /** Resolve channel */
1015 notify->channel = channel;
1016 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1017 silc_client_get_channel_by_id_resolve(
1018 client, conn, &id.u.channel_id,
1019 silc_client_notify_resolved,
1023 entry = channel_entry;
1026 /* Get target Client ID */
1027 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
1030 /* Find target Client entry */
1031 client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
1032 if (!client_entry2 || !client_entry2->nickname[0]) {
1033 /** Resolve client */
1034 silc_client_unref_client(client, conn, client_entry);
1035 silc_client_unref_client(client, conn, client_entry2);
1036 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1037 client, conn, &id2.u.client_id, NULL,
1038 silc_client_notify_resolved,
1044 chu = silc_client_on_channel(channel, client_entry2);
1048 /* Notify application. */
1049 NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
1052 silc_client_unref_client(client, conn, client_entry2);
1054 silc_client_unref_client(client, conn, client_entry);
1056 silc_client_unref_server(client, conn, server);
1058 silc_client_unref_channel(client, conn, channel_entry);
1059 silc_client_unref_channel(client, conn, channel);
1061 /** Notify processed */
1062 silc_fsm_next(fsm, silc_client_notify_processed);
1063 return SILC_FSM_CONTINUE;
1066 /********************************* MOTD *************************************/
1068 /* Received Message of the day */
1070 SILC_FSM_STATE(silc_client_notify_motd)
1072 SilcClientConnection conn = fsm_context;
1073 SilcClient client = conn->client;
1074 SilcClientNotify notify = state_context;
1075 SilcNotifyPayload payload = notify->payload;
1076 SilcNotifyType type = silc_notify_get_type(payload);
1077 SilcArgumentPayload args = silc_notify_get_args(payload);
1081 SILC_LOG_DEBUG(("Notify: MOTD"));
1084 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1088 /* Notify application */
1089 NOTIFY(client, conn, type, tmp);
1092 /** Notify processed */
1093 silc_fsm_next(fsm, silc_client_notify_processed);
1094 return SILC_FSM_CONTINUE;
1097 /**************************** CHANNEL CHANGE ********************************/
1099 /* Router has enforced a new ID to a channel, change it */
1101 SILC_FSM_STATE(silc_client_notify_channel_change)
1103 SilcClientConnection conn = fsm_context;
1104 SilcClient client = conn->client;
1105 SilcClientNotify notify = state_context;
1106 SilcNotifyPayload payload = notify->payload;
1107 SilcNotifyType type = silc_notify_get_type(payload);
1108 SilcArgumentPayload args = silc_notify_get_args(payload);
1109 SilcChannelEntry channel = NULL;
1112 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
1114 /* Get the old ID */
1115 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1118 /* Get the channel entry */
1119 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1123 /* If channel is being resolved handle notify after resolving */
1124 if (channel->internal.resolve_cmd_ident) {
1125 silc_client_unref_channel(client, conn, channel);
1126 SILC_FSM_CALL(silc_client_command_pending(
1127 conn, SILC_COMMAND_NONE,
1128 channel->internal.resolve_cmd_ident,
1129 silc_client_notify_wait_continue,
1134 /* Get the new ID */
1135 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1138 /* Replace the Channel ID */
1139 if (!silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id))
1142 /* Notify application */
1143 NOTIFY(client, conn, type, channel, channel);
1146 /** Notify processed */
1147 silc_client_unref_channel(client, conn, channel);
1148 silc_fsm_next(fsm, silc_client_notify_processed);
1149 return SILC_FSM_CONTINUE;
1152 /******************************** KICKED ************************************/
1154 /* Some client was kicked from a channel */
1156 SILC_FSM_STATE(silc_client_notify_kicked)
1158 SilcClientConnection conn = fsm_context;
1159 SilcClient client = conn->client;
1160 SilcClientNotify notify = state_context;
1161 SilcNotifyPayload payload = notify->payload;
1162 SilcPacket packet = notify->packet;
1163 SilcNotifyType type = silc_notify_get_type(payload);
1164 SilcArgumentPayload args = silc_notify_get_args(payload);
1165 SilcClientEntry client_entry, client_entry2;
1166 SilcChannelEntry channel = NULL;
1167 SilcChannelUser chu;
1172 SILC_LOG_DEBUG(("Notify: KICKED"));
1174 /* Get channel entry */
1175 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
1176 &id.u.channel_id, sizeof(id.u.channel_id)))
1178 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1182 /* If channel is being resolved handle notify after resolving */
1183 if (channel->internal.resolve_cmd_ident) {
1184 silc_client_unref_channel(client, conn, channel);
1185 SILC_FSM_CALL(silc_client_command_pending(
1186 conn, SILC_COMMAND_NONE,
1187 channel->internal.resolve_cmd_ident,
1188 silc_client_notify_wait_continue,
1194 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1197 /* Find Client entry */
1198 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1202 /* Get kicker's Client ID */
1203 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1206 /* Find kicker's client entry and if not found resolve it */
1207 client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1208 if (!client_entry2 || !client_entry2->nickname[0]) {
1209 /** Resolve client */
1210 silc_client_unref_client(client, conn, client_entry);
1211 silc_client_unref_client(client, conn, client_entry2);
1212 notify->channel = channel;
1213 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1214 silc_client_get_client_by_id_resolve(
1215 client, conn, &id.u.client_id, NULL,
1216 silc_client_notify_resolved,
1222 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1224 /* Remove kicked client from channel */
1225 if (client_entry != conn->local_entry) {
1226 chu = silc_client_on_channel(channel, client_entry);
1228 silc_hash_table_del(client_entry->channels, channel);
1229 silc_hash_table_del(channel->user_list, client_entry);
1234 /* Notify application. */
1235 NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
1237 /* If I was kicked from channel, remove the channel */
1238 if (client_entry == conn->local_entry) {
1239 if (conn->current_channel == channel)
1240 conn->current_channel = NULL;
1241 silc_client_del_channel(client, conn, channel);
1244 silc_client_unref_client(client, conn, client_entry);
1245 silc_client_unref_client(client, conn, client_entry2);
1248 /** Notify processed */
1249 silc_client_unref_channel(client, conn, channel);
1250 silc_fsm_next(fsm, silc_client_notify_processed);
1251 return SILC_FSM_CONTINUE;
1254 /******************************** KILLED ************************************/
1256 /* Some client was killed from the network */
1258 SILC_FSM_STATE(silc_client_notify_killed)
1260 SilcClientConnection conn = fsm_context;
1261 SilcClient client = conn->client;
1262 SilcClientNotify notify = state_context;
1263 SilcNotifyPayload payload = notify->payload;
1264 SilcNotifyType type = silc_notify_get_type(payload);
1265 SilcArgumentPayload args = silc_notify_get_args(payload);
1266 SilcClientEntry client_entry = NULL, client_entry2 = NULL;
1267 SilcChannelEntry channel_entry = NULL;
1268 SilcServerEntry server = NULL;
1271 SilcUInt32 comment_len;
1274 SILC_LOG_DEBUG(("Notify: KILLED"));
1277 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1280 /* Find Client entry */
1281 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1286 comment = silc_argument_get_arg_type(args, 2, &comment_len);
1288 /* Get killer's ID */
1289 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1292 if (id.type == SILC_ID_CLIENT) {
1293 /* Find Client entry */
1294 client_entry2 = silc_client_get_client_by_id(client, conn,
1296 if (!client_entry2 || !client_entry2->nickname[0]) {
1297 /** Resolve client */
1298 silc_client_unref_client(client, conn, client_entry);
1299 silc_client_unref_client(client, conn, client_entry2);
1300 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1301 client, conn, &id.u.client_id, NULL,
1302 silc_client_notify_resolved,
1306 entry = client_entry2;
1307 } else if (id.type == SILC_ID_SERVER) {
1308 /* Find Server entry */
1309 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1311 /** Resolve server */
1312 SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
1313 client, conn, &id.u.server_id,
1314 silc_client_notify_resolved,
1320 /* Find Channel entry */
1321 channel_entry = silc_client_get_channel_by_id(client, conn,
1323 if (!channel_entry) {
1324 /** Resolve channel */
1325 SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1326 client, conn, &id.u.channel_id,
1327 silc_client_notify_resolved,
1331 entry = channel_entry;
1334 /* Notify application. */
1335 NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
1337 /* Delete the killed client */
1338 if (client_entry != conn->local_entry)
1339 silc_client_del_client(client, conn, client_entry);
1342 silc_client_unref_client(client, conn, client_entry);
1344 silc_client_unref_client(client, conn, client_entry2);
1346 silc_client_unref_server(client, conn, server);
1348 silc_client_unref_channel(client, conn, channel_entry);
1350 /** Notify processed */
1351 silc_fsm_next(fsm, silc_client_notify_processed);
1352 return SILC_FSM_CONTINUE;
1355 /**************************** SERVER SIGNOFF ********************************/
1357 /* Some server quit SILC network. Remove its clients from channels. */
1359 SILC_FSM_STATE(silc_client_notify_server_signoff)
1361 SilcClientConnection conn = fsm_context;
1362 SilcClient client = conn->client;
1363 SilcClientNotify notify = state_context;
1364 SilcNotifyPayload payload = notify->payload;
1365 SilcNotifyType type = silc_notify_get_type(payload);
1366 SilcArgumentPayload args = silc_notify_get_args(payload);
1367 SilcClientEntry client_entry;
1372 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1374 clients = silc_dlist_init();
1378 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1380 if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
1383 /* Get the client entry */
1384 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1386 silc_dlist_add(clients, client_entry);
1389 /* Notify application. We don't keep server entries so the server
1390 entry is returned as NULL. The client's are returned as list. */
1391 NOTIFY(client, conn, type, NULL, clients);
1393 /* Delete the clients */
1394 silc_dlist_start(clients);
1395 while ((client_entry = silc_dlist_get(clients)))
1396 silc_client_del_client(client, conn, client_entry);
1399 /** Notify processed */
1400 silc_client_list_free(client, conn, clients);
1401 silc_fsm_next(fsm, silc_client_notify_processed);
1402 return SILC_FSM_CONTINUE;
1405 /******************************** ERROR *************************************/
1407 /* Some error occurred */
1409 SILC_FSM_STATE(silc_client_notify_error)
1411 SilcClientConnection conn = fsm_context;
1412 SilcClient client = conn->client;
1413 SilcClientNotify notify = state_context;
1414 SilcNotifyPayload payload = notify->payload;
1415 SilcNotifyType type = silc_notify_get_type(payload);
1416 SilcArgumentPayload args = silc_notify_get_args(payload);
1417 SilcClientEntry client_entry;
1424 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1425 if (!tmp && tmp_len != 1)
1427 error = (SilcStatus)tmp[0];
1429 SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1431 /* Handle the error */
1432 if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1433 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1435 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1437 silc_client_del_client(client, conn, client_entry);
1438 silc_client_unref_client(client, conn, client_entry);
1442 /* Notify application. */
1443 NOTIFY(client, conn, type, error);
1446 /** Notify processed */
1447 silc_fsm_next(fsm, silc_client_notify_processed);
1448 return SILC_FSM_CONTINUE;
1451 /******************************** WATCH *************************************/
1453 /* Received notify about some client we are watching */
1455 SILC_FSM_STATE(silc_client_notify_watch)
1457 SilcClientConnection conn = fsm_context;
1458 SilcClient client = conn->client;
1459 SilcClientNotify notify = state_context;
1460 SilcNotifyPayload payload = notify->payload;
1461 SilcNotifyType type = silc_notify_get_type(payload);
1462 SilcArgumentPayload args = silc_notify_get_args(payload);
1463 SilcClientEntry client_entry = NULL;
1464 SilcNotifyType ntype = 0;
1465 SilcBool del_client = FALSE;
1466 unsigned char *pk, *tmp;
1467 SilcUInt32 mode, pk_len, tmp_len;
1468 SilcPublicKey public_key = NULL;
1471 SILC_LOG_DEBUG(("Notify: WATCH"));
1473 /* Get sender Client ID */
1474 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1477 /* Find Client entry and if not found resolve it */
1478 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1479 if (!client_entry || !client_entry->nickname[0]) {
1480 /** Resolve client */
1481 silc_client_unref_client(client, conn, client_entry);
1482 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1483 client, conn, &id.u.client_id, NULL,
1484 silc_client_notify_resolved,
1490 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1491 if (!tmp || tmp_len != 4)
1493 SILC_GET32_MSB(mode, tmp);
1495 /* Get notify type */
1496 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1497 if (tmp && tmp_len != 2)
1500 SILC_GET16_MSB(ntype, tmp);
1503 tmp = silc_argument_get_arg_type(args, 2, NULL);
1505 char *tmp_nick = NULL;
1507 if (client->internal->params->nickname_parse)
1508 client->internal->params->nickname_parse(client_entry->nickname,
1511 tmp_nick = strdup(tmp);
1513 /* If same nick, the client was new to us and has become "present"
1514 to network. Send NULL as nick to application. */
1515 if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
1518 silc_free(tmp_nick);
1521 /* Get public key, if present */
1522 pk = silc_argument_get_arg_type(args, 5, &pk_len);
1523 if (pk && !client_entry->public_key) {
1524 if (silc_public_key_payload_decode(pk, pk_len, &public_key)) {
1525 client_entry->public_key = public_key;
1530 /* Notify application. */
1531 NOTIFY(client, conn, type, client_entry, tmp, mode, ntype,
1532 client_entry->public_key);
1534 client_entry->mode = mode;
1536 /* If nickname was changed, remove the client entry unless the
1537 client is on some channel */
1538 /* XXX, why do we need to remove the client entry?? */
1539 if (tmp && ntype == SILC_NOTIFY_TYPE_NICK_CHANGE &&
1540 !silc_hash_table_count(client_entry->channels))
1542 else if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
1543 ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1544 ntype == SILC_NOTIFY_TYPE_KILLED)
1548 silc_client_del_client(client, conn, client_entry);
1551 silc_pkcs_public_key_free(public_key);
1554 /** Notify processed */
1555 silc_client_unref_client(client, conn, client_entry);
1556 silc_fsm_next(fsm, silc_client_notify_processed);
1557 return SILC_FSM_CONTINUE;