5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 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_SYNC(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 %d", silc_notify_get_type(payload)));
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 notify->packet = packet;
116 notify->payload = payload;
118 silc_fsm_set_state_context(fsm, notify);
120 /* Process the notify */
121 switch (silc_notify_get_type(payload)) {
123 case SILC_NOTIFY_TYPE_NONE:
125 silc_fsm_next(fsm, silc_client_notify_none);
128 case SILC_NOTIFY_TYPE_INVITE:
130 silc_fsm_next(fsm, silc_client_notify_invite);
133 case SILC_NOTIFY_TYPE_JOIN:
135 silc_fsm_next(fsm, silc_client_notify_join);
138 case SILC_NOTIFY_TYPE_LEAVE:
140 silc_fsm_next(fsm, silc_client_notify_leave);
143 case SILC_NOTIFY_TYPE_SIGNOFF:
145 silc_fsm_next(fsm, silc_client_notify_signoff);
148 case SILC_NOTIFY_TYPE_TOPIC_SET:
150 silc_fsm_next(fsm, silc_client_notify_topic_set);
153 case SILC_NOTIFY_TYPE_NICK_CHANGE:
155 silc_fsm_next(fsm, silc_client_notify_nick_change);
158 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
160 silc_fsm_next(fsm, silc_client_notify_cmode_change);
163 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
165 silc_fsm_next(fsm, silc_client_notify_cumode_change);
168 case SILC_NOTIFY_TYPE_MOTD:
170 silc_fsm_next(fsm, silc_client_notify_motd);
173 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
174 /** CHANNEL_CHANGE */
175 silc_fsm_next(fsm, silc_client_notify_channel_change);
178 case SILC_NOTIFY_TYPE_KICKED:
180 silc_fsm_next(fsm, silc_client_notify_kicked);
183 case SILC_NOTIFY_TYPE_KILLED:
185 silc_fsm_next(fsm, silc_client_notify_killed);
188 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
189 /** SERVER_SIGNOFF */
190 silc_fsm_next(fsm, silc_client_notify_server_signoff);
193 case SILC_NOTIFY_TYPE_ERROR:
195 silc_fsm_next(fsm, silc_client_notify_error);
198 case SILC_NOTIFY_TYPE_WATCH:
200 silc_fsm_next(fsm, silc_client_notify_watch);
204 /** Unknown notify */
205 silc_notify_payload_free(payload);
206 silc_packet_free(packet);
208 return SILC_FSM_FINISH;
212 return SILC_FSM_YIELD;
215 /* Notify processed, finish the packet processing thread */
217 SILC_FSM_STATE(silc_client_notify_processed)
219 SilcClientNotify notify = state_context;
220 SilcPacket packet = notify->packet;
221 SilcNotifyPayload payload = notify->payload;
223 silc_notify_payload_free(payload);
224 silc_packet_free(packet);
226 return SILC_FSM_FINISH;
229 /********************************** NONE ************************************/
231 SILC_FSM_STATE(silc_client_notify_none)
233 SilcClientConnection conn = fsm_context;
234 SilcClient client = conn->client;
235 SilcClientNotify notify = state_context;
236 SilcNotifyPayload payload = notify->payload;
237 SilcNotifyType type = silc_notify_get_type(payload);
238 SilcArgumentPayload args = silc_notify_get_args(payload);
240 SILC_LOG_DEBUG(("Notify: NONE"));
242 /* Notify application */
243 NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL));
245 /** Notify processed */
246 silc_fsm_next(fsm, silc_client_notify_processed);
247 return SILC_FSM_CONTINUE;
250 /********************************* INVITE ***********************************/
252 /* Someone invite me to a channel */
254 SILC_FSM_STATE(silc_client_notify_invite)
256 SilcClientConnection conn = fsm_context;
257 SilcClient client = conn->client;
258 SilcClientNotify notify = state_context;
259 SilcNotifyPayload payload = notify->payload;
260 SilcNotifyType type = silc_notify_get_type(payload);
261 SilcArgumentPayload args = silc_notify_get_args(payload);
262 SilcClientEntry client_entry;
263 SilcChannelEntry channel = NULL;
268 SILC_LOG_DEBUG(("Notify: INVITE"));
271 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
274 /* Get the channel name */
275 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
279 /* Get the channel entry */
280 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
284 /* If channel is being resolved handle notify after resolving */
285 if (channel->internal.resolve_cmd_ident) {
286 silc_client_unref_channel(client, conn, channel);
287 SILC_FSM_CALL(silc_client_command_pending(
288 conn, SILC_COMMAND_NONE,
289 channel->internal.resolve_cmd_ident,
290 silc_client_notify_wait_continue,
295 /* Get sender Client ID */
296 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
299 /* Find Client entry and if not found query it */
300 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
301 if (!client_entry || !client_entry->nickname[0]) {
302 /** Resolve client */
303 silc_client_unref_client(client, conn, client_entry);
304 notify->channel = channel;
305 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
306 silc_client_get_client_by_id_resolve(
307 client, conn, &id.u.client_id, NULL,
308 silc_client_notify_resolved,
313 /* Notify application */
314 NOTIFY(client, conn, type, channel, tmp, client_entry);
316 silc_client_unref_client(client, conn, client_entry);
319 /** Notify processed */
320 silc_client_unref_channel(client, conn, channel);
321 silc_fsm_next(fsm, silc_client_notify_processed);
322 return SILC_FSM_CONTINUE;
325 /********************************** JOIN ************************************/
327 /* Someone joined a channel */
329 SILC_FSM_STATE(silc_client_notify_join)
331 SilcClientConnection conn = fsm_context;
332 SilcClient client = conn->client;
333 SilcClientNotify notify = state_context;
334 SilcNotifyPayload payload = notify->payload;
335 SilcNotifyType type = silc_notify_get_type(payload);
336 SilcArgumentPayload args = silc_notify_get_args(payload);
337 SilcClientEntry client_entry;
338 SilcChannelEntry channel = NULL;
341 SILC_LOG_DEBUG(("Notify: JOIN"));
344 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
347 /* Get channel entry */
348 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
352 /* If channel is being resolved handle notify after resolving */
353 if (channel->internal.resolve_cmd_ident) {
354 silc_client_unref_channel(client, conn, channel);
355 SILC_FSM_CALL(silc_client_command_pending(
356 conn, SILC_COMMAND_NONE,
357 channel->internal.resolve_cmd_ident,
358 silc_client_notify_wait_continue,
364 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
367 /* Find Client entry and if not found query it */
368 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
369 if (!client_entry || !client_entry->nickname[0] ||
370 !client_entry->username[0]) {
371 /** Resolve client */
372 silc_client_unref_client(client, conn, client_entry);
373 notify->channel = channel;
374 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
375 silc_client_get_client_by_id_resolve(
376 client, conn, &id.u.client_id, NULL,
377 silc_client_notify_resolved,
382 silc_rwlock_wrlock(client_entry->internal.lock);
383 silc_rwlock_wrlock(channel->internal.lock);
385 if (client_entry != conn->local_entry)
386 silc_client_nickname_format(client, conn, client_entry, FALSE);
388 /* Join the client to channel */
389 if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0)) {
390 silc_rwlock_unlock(channel->internal.lock);
391 silc_rwlock_unlock(client_entry->internal.lock);
395 silc_rwlock_unlock(channel->internal.lock);
396 silc_rwlock_unlock(client_entry->internal.lock);
398 /* Notify application. */
399 NOTIFY(client, conn, type, client_entry, channel);
401 silc_client_unref_client(client, conn, client_entry);
404 /** Notify processed */
405 silc_client_unref_channel(client, conn, channel);
406 silc_fsm_next(fsm, silc_client_notify_processed);
407 return SILC_FSM_CONTINUE;
410 /********************************** LEAVE ***********************************/
412 /* Someone left a channel */
414 SILC_FSM_STATE(silc_client_notify_leave)
416 SilcClientConnection conn = fsm_context;
417 SilcClient client = conn->client;
418 SilcClientNotify notify = state_context;
419 SilcNotifyPayload payload = notify->payload;
420 SilcPacket packet = notify->packet;
421 SilcNotifyType type = silc_notify_get_type(payload);
422 SilcArgumentPayload args = silc_notify_get_args(payload);
423 SilcClientEntry client_entry = NULL;
424 SilcChannelEntry channel = NULL;
427 SILC_LOG_DEBUG(("Notify: LEAVE"));
429 /* Get channel entry */
430 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
431 &id.u.channel_id, sizeof(id.u.channel_id)))
433 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
437 /* If channel is being resolved handle notify after resolving */
438 if (channel->internal.resolve_cmd_ident) {
439 silc_client_unref_channel(client, conn, channel);
440 SILC_FSM_CALL(silc_client_command_pending(
441 conn, SILC_COMMAND_NONE,
442 channel->internal.resolve_cmd_ident,
443 silc_client_notify_wait_continue,
449 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
452 /* Find Client entry */
453 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
457 /* Remove client from channel */
458 silc_client_remove_from_channel(client, conn, channel, client_entry);
460 /* Notify application. */
461 NOTIFY(client, conn, type, client_entry, channel);
463 silc_client_unref_client(client, conn, client_entry);
466 /** Notify processed */
467 silc_client_unref_channel(client, conn, channel);
468 silc_fsm_next(fsm, silc_client_notify_processed);
469 return SILC_FSM_CONTINUE;
472 /********************************* SIGNOFF **********************************/
474 /* Someone quit SILC network */
476 SILC_FSM_STATE(silc_client_notify_signoff)
478 SilcClientConnection conn = fsm_context;
479 SilcClient client = conn->client;
480 SilcClientNotify notify = state_context;
481 SilcNotifyPayload payload = notify->payload;
482 SilcPacket packet = notify->packet;
483 SilcNotifyType type = silc_notify_get_type(payload);
484 SilcArgumentPayload args = silc_notify_get_args(payload);
485 SilcClientEntry client_entry;
486 SilcChannelEntry channel;
491 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
494 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
497 /* Find Client entry */
498 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
502 /* Get signoff message */
503 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
504 if (tmp && tmp_len > 128)
507 /* Notify application */
508 NOTIFY(client, conn, type, client_entry, tmp);
510 /* Remove from channel */
511 if (packet->dst_id_type == SILC_ID_CHANNEL) {
512 if (silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
513 &id.u.channel_id, sizeof(id.u.channel_id))) {
514 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
516 silc_client_remove_from_channel(client, conn, channel, client_entry);
517 silc_client_unref_channel(client, conn, channel);
523 silc_client_del_client(client, conn, client_entry);
524 silc_client_unref_client(client, conn, client_entry);
527 /** Notify processed */
528 silc_fsm_next(fsm, silc_client_notify_processed);
529 return SILC_FSM_CONTINUE;
532 /******************************** TOPIC_SET *********************************/
534 /* Someone set topic on a channel */
536 SILC_FSM_STATE(silc_client_notify_topic_set)
538 SilcClientConnection conn = fsm_context;
539 SilcClient client = conn->client;
540 SilcClientNotify notify = state_context;
541 SilcNotifyPayload payload = notify->payload;
542 SilcPacket packet = notify->packet;
543 SilcNotifyType type = silc_notify_get_type(payload);
544 SilcArgumentPayload args = silc_notify_get_args(payload);
545 SilcClientEntry client_entry = NULL;
546 SilcChannelEntry channel = NULL, channel_entry = NULL;
547 SilcServerEntry server = NULL;
553 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
555 /* Get channel entry */
556 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
557 &id.u.channel_id, sizeof(id.u.channel_id)))
559 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
563 /* If channel is being resolved handle notify after resolving */
564 if (channel->internal.resolve_cmd_ident) {
565 silc_client_unref_channel(client, conn, channel);
566 SILC_FSM_CALL(silc_client_command_pending(
567 conn, SILC_COMMAND_NONE,
568 channel->internal.resolve_cmd_ident,
569 silc_client_notify_wait_continue,
575 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
579 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
583 if (id.type == SILC_ID_CLIENT) {
584 /* Find Client entry */
585 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
586 if (!client_entry || !client_entry->nickname[0]) {
587 /** Resolve client */
588 silc_client_unref_client(client, conn, client_entry);
589 notify->channel = channel;
590 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
591 silc_client_get_client_by_id_resolve(
592 client, conn, &id.u.client_id, NULL,
593 silc_client_notify_resolved,
597 entry = client_entry;
598 } else if (id.type == SILC_ID_SERVER) {
599 /* Find Server entry */
600 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
602 /** Resolve server */
603 notify->channel = channel;
604 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
605 silc_client_get_server_by_id_resolve(
606 client, conn, &id.u.server_id,
607 silc_client_notify_resolved,
613 /* Find Channel entry */
614 channel_entry = silc_client_get_channel_by_id(client, conn,
616 if (!channel_entry) {
617 /** Resolve channel */
618 notify->channel = channel;
619 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
620 silc_client_get_channel_by_id_resolve(
621 client, conn, &id.u.channel_id,
622 silc_client_notify_resolved,
626 entry = channel_entry;
629 silc_rwlock_wrlock(channel->internal.lock);
630 silc_free(channel->topic);
631 channel->topic = silc_memdup(tmp, strlen(tmp));
632 silc_rwlock_unlock(channel->internal.lock);
634 /* Notify application. */
635 NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
638 silc_client_unref_client(client, conn, client_entry);
640 silc_client_unref_server(client, conn, server);
642 silc_client_unref_channel(client, conn, channel_entry);
645 /** Notify processed */
646 silc_client_unref_channel(client, conn, channel);
647 silc_fsm_next(fsm, silc_client_notify_processed);
648 return SILC_FSM_CONTINUE;
651 /****************************** NICK_CHANGE *********************************/
653 /* Someone changed their nickname on a channel */
655 SILC_FSM_STATE(silc_client_notify_nick_change)
657 SilcClientConnection conn = fsm_context;
658 SilcClient client = conn->client;
659 SilcClientNotify notify = state_context;
660 SilcNotifyPayload payload = notify->payload;
661 SilcNotifyType type = silc_notify_get_type(payload);
662 SilcArgumentPayload args = silc_notify_get_args(payload);
663 SilcClientEntry client_entry = NULL;
664 unsigned char *tmp, oldnick[128 + 1];
668 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
671 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
675 if (conn->local_id &&
676 SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
679 /* Get new Client ID */
680 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id2, NULL))
684 if (conn->local_id &&
685 SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
688 /* Find old Client entry */
689 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
690 if (!client_entry || !client_entry->nickname[0]) {
691 /** Resolve client */
692 silc_client_unref_client(client, conn, client_entry);
693 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
694 client, conn, &id.u.client_id, NULL,
695 silc_client_notify_resolved,
700 /* Take the new nickname */
701 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
705 silc_rwlock_wrlock(client_entry->internal.lock);
707 /* Check whether nickname changed at all. It is possible that nick
708 change notify is received but nickname didn't change, only the
709 ID changes. If Client ID hash match, nickname didn't change. */
710 if (SILC_ID_COMPARE_HASH(&client_entry->id, &id2.u.client_id) &&
711 silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
712 /* Nickname didn't change. Update only Client ID. We don't notify
713 application because nickname didn't change. */
714 silc_mutex_lock(conn->internal->lock);
715 silc_idcache_update_by_context(conn->internal->client_cache, client_entry,
716 &id2.u.client_id, NULL, FALSE);
717 silc_mutex_unlock(conn->internal->lock);
718 silc_rwlock_unlock(client_entry->internal.lock);
722 /* Change the nickname */
723 memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
724 if (!silc_client_change_nickname(client, conn, client_entry, tmp,
725 &id2.u.client_id, NULL, 0)) {
726 silc_rwlock_unlock(client_entry->internal.lock);
730 silc_rwlock_unlock(client_entry->internal.lock);
732 /* Notify application */
733 NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
736 /** Notify processed */
737 silc_client_unref_client(client, conn, client_entry);
738 silc_fsm_next(fsm, silc_client_notify_processed);
739 return SILC_FSM_CONTINUE;
742 /****************************** CMODE_CHANGE ********************************/
744 /* Someone changed channel mode */
746 SILC_FSM_STATE(silc_client_notify_cmode_change)
748 SilcClientConnection conn = fsm_context;
749 SilcClient client = conn->client;
750 SilcClientNotify notify = state_context;
751 SilcNotifyPayload payload = notify->payload;
752 SilcPacket packet = notify->packet;
753 SilcNotifyType type = silc_notify_get_type(payload);
754 SilcArgumentPayload args = silc_notify_get_args(payload);
755 SilcClientEntry client_entry = NULL;
756 SilcChannelEntry channel = NULL, channel_entry = NULL;
757 SilcServerEntry server = NULL;
760 SilcUInt32 tmp_len, mode;
762 char *passphrase, *cipher, *hmac;
763 SilcPublicKey founder_key = NULL;
764 SilcDList chpks = NULL;
766 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
768 /* Get channel entry */
769 if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
770 &id.u.channel_id, sizeof(id.u.channel_id)))
772 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
776 /* If channel is being resolved handle notify after resolving */
777 if (channel->internal.resolve_cmd_ident) {
778 silc_client_unref_channel(client, conn, channel);
779 SILC_FSM_CALL(silc_client_command_pending(
780 conn, SILC_COMMAND_NONE,
781 channel->internal.resolve_cmd_ident,
782 silc_client_notify_wait_continue,
788 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
791 SILC_GET32_MSB(mode, tmp);
794 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
797 if (id.type == SILC_ID_CLIENT) {
798 /* Find Client entry */
799 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
800 if (!client_entry || !client_entry->nickname[0]) {
801 /** Resolve client */
802 silc_client_unref_client(client, conn, client_entry);
803 notify->channel = channel;
804 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
805 silc_client_get_client_by_id_resolve(
806 client, conn, &id.u.client_id, NULL,
807 silc_client_notify_resolved,
811 entry = client_entry;
812 } else if (id.type == SILC_ID_SERVER) {
813 /* Find Server entry */
814 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
816 /** Resolve server */
817 notify->channel = channel;
818 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
819 silc_client_get_server_by_id_resolve(
820 client, conn, &id.u.server_id,
821 silc_client_notify_resolved,
827 /* Find Channel entry */
828 channel_entry = silc_client_get_channel_by_id(client, conn,
830 if (!channel_entry) {
831 /** Resolve channel */
832 notify->channel = channel;
833 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
834 silc_client_get_channel_by_id_resolve(
835 client, conn, &id.u.channel_id,
836 silc_client_notify_resolved,
840 entry = channel_entry;
843 silc_rwlock_wrlock(channel->internal.lock);
845 /* Get the channel founder key if it was set */
846 tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
848 if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
849 silc_rwlock_unlock(channel->internal.lock);
852 if (!channel->founder_key) {
853 channel->founder_key = founder_key;
859 cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
862 hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
864 unsigned char hash[SILC_HASH_MAXLEN];
867 if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
868 silc_rwlock_unlock(channel->internal.lock);
872 /* Get HMAC key from the old HMAC context, and update it to the new one */
873 tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
875 silc_hash_make(silc_hmac_get_hash(newhmac), tmp, tmp_len, hash);
876 silc_hmac_set_key(newhmac, hash,
877 silc_hash_len(silc_hmac_get_hash(newhmac)));
878 if (channel->internal.hmac)
879 silc_hmac_free(channel->internal.hmac);
880 channel->internal.hmac = newhmac;
881 memset(hash, 0, sizeof(hash));
885 /* Get the passphrase if it was set */
886 passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
889 tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
890 if (tmp && tmp_len == 4)
891 SILC_GET32_MSB(channel->user_limit, tmp);
892 if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
893 channel->user_limit = 0;
895 /* Save the new mode */
896 channel->mode = mode;
898 /* Get the channel public key that was added or removed */
899 tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
901 silc_client_channel_save_public_keys(channel, tmp, tmp_len);
903 silc_rwlock_unlock(channel->internal.lock);
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,
970 /* Get target Client ID */
971 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
974 /* Find target Client entry */
975 client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
976 if (!client_entry2 || !client_entry2->nickname[0]) {
977 /** Resolve client */
978 silc_client_unref_client(client, conn, client_entry2);
979 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
980 client, conn, &id2.u.client_id, NULL,
981 silc_client_notify_resolved,
987 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
990 SILC_GET32_MSB(mode, tmp);
993 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
996 if (id.type == SILC_ID_CLIENT) {
997 /* Find Client entry */
998 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
999 if (!client_entry || !client_entry->nickname[0]) {
1000 /** Resolve client */
1001 silc_client_unref_client(client, conn, client_entry);
1002 notify->channel = channel;
1003 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1004 silc_client_get_client_by_id_resolve(
1005 client, conn, &id.u.client_id, NULL,
1006 silc_client_notify_resolved,
1010 entry = client_entry;
1011 } else if (id.type == SILC_ID_SERVER) {
1012 /* Find Server entry */
1013 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1015 /** Resolve server */
1016 notify->channel = channel;
1017 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1018 silc_client_get_server_by_id_resolve(
1019 client, conn, &id.u.server_id,
1020 silc_client_notify_resolved,
1026 /* Find Channel entry */
1027 channel_entry = silc_client_get_channel_by_id(client, conn,
1029 if (!channel_entry) {
1030 /** Resolve channel */
1031 notify->channel = channel;
1032 SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1033 silc_client_get_channel_by_id_resolve(
1034 client, conn, &id.u.channel_id,
1035 silc_client_notify_resolved,
1039 entry = channel_entry;
1043 silc_rwlock_wrlock(channel->internal.lock);
1044 chu = silc_client_on_channel(channel, client_entry2);
1047 silc_rwlock_unlock(channel->internal.lock);
1049 /* Notify application. */
1050 NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
1053 silc_client_unref_client(client, conn, client_entry2);
1055 silc_client_unref_client(client, conn, client_entry);
1057 silc_client_unref_server(client, conn, server);
1059 silc_client_unref_channel(client, conn, channel_entry);
1060 silc_client_unref_channel(client, conn, channel);
1062 /** Notify processed */
1063 silc_fsm_next(fsm, silc_client_notify_processed);
1064 return SILC_FSM_CONTINUE;
1067 /********************************* MOTD *************************************/
1069 /* Received Message of the day */
1071 SILC_FSM_STATE(silc_client_notify_motd)
1073 SilcClientConnection conn = fsm_context;
1074 SilcClient client = conn->client;
1075 SilcClientNotify notify = state_context;
1076 SilcNotifyPayload payload = notify->payload;
1077 SilcNotifyType type = silc_notify_get_type(payload);
1078 SilcArgumentPayload args = silc_notify_get_args(payload);
1082 SILC_LOG_DEBUG(("Notify: MOTD"));
1085 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1089 /* Notify application */
1090 NOTIFY(client, conn, type, tmp);
1093 /** Notify processed */
1094 silc_fsm_next(fsm, silc_client_notify_processed);
1095 return SILC_FSM_CONTINUE;
1098 /**************************** CHANNEL CHANGE ********************************/
1100 /* Router has enforced a new ID to a channel, change it */
1102 SILC_FSM_STATE(silc_client_notify_channel_change)
1104 SilcClientConnection conn = fsm_context;
1105 SilcClient client = conn->client;
1106 SilcClientNotify notify = state_context;
1107 SilcNotifyPayload payload = notify->payload;
1108 SilcNotifyType type = silc_notify_get_type(payload);
1109 SilcArgumentPayload args = silc_notify_get_args(payload);
1110 SilcChannelEntry channel = NULL;
1113 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
1115 /* Get the old ID */
1116 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1119 /* Get the channel entry */
1120 channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1124 /* If channel is being resolved handle notify after resolving */
1125 if (channel->internal.resolve_cmd_ident) {
1126 silc_client_unref_channel(client, conn, channel);
1127 SILC_FSM_CALL(silc_client_command_pending(
1128 conn, SILC_COMMAND_NONE,
1129 channel->internal.resolve_cmd_ident,
1130 silc_client_notify_wait_continue,
1135 /* Get the new ID */
1136 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1139 /* Replace the Channel ID */
1140 if (!silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id))
1143 /* Notify application */
1144 NOTIFY(client, conn, type, channel, channel);
1147 /** Notify processed */
1148 silc_client_unref_channel(client, conn, channel);
1149 silc_fsm_next(fsm, silc_client_notify_processed);
1150 return SILC_FSM_CONTINUE;
1153 /******************************** KICKED ************************************/
1155 /* Some client was kicked from a channel */
1157 SILC_FSM_STATE(silc_client_notify_kicked)
1159 SilcClientConnection conn = fsm_context;
1160 SilcClient client = conn->client;
1161 SilcClientNotify notify = state_context;
1162 SilcNotifyPayload payload = notify->payload;
1163 SilcPacket packet = notify->packet;
1164 SilcNotifyType type = silc_notify_get_type(payload);
1165 SilcArgumentPayload args = silc_notify_get_args(payload);
1166 SilcClientEntry client_entry, client_entry2;
1167 SilcChannelEntry channel = NULL;
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 silc_client_remove_from_channel(client, conn, channel, 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_empty_channel(client, conn, channel);
1236 silc_client_del_channel(client, conn, channel);
1239 silc_client_unref_client(client, conn, client_entry);
1240 silc_client_unref_client(client, conn, client_entry2);
1243 /** Notify processed */
1244 silc_client_unref_channel(client, conn, channel);
1245 silc_fsm_next(fsm, silc_client_notify_processed);
1246 return SILC_FSM_CONTINUE;
1249 /******************************** KILLED ************************************/
1251 /* Some client was killed from the network */
1253 SILC_FSM_STATE(silc_client_notify_killed)
1255 SilcClientConnection conn = fsm_context;
1256 SilcClient client = conn->client;
1257 SilcClientNotify notify = state_context;
1258 SilcNotifyPayload payload = notify->payload;
1259 SilcNotifyType type = silc_notify_get_type(payload);
1260 SilcArgumentPayload args = silc_notify_get_args(payload);
1261 SilcClientEntry client_entry = NULL, client_entry2 = NULL;
1262 SilcChannelEntry channel_entry = NULL;
1263 SilcServerEntry server = NULL;
1266 SilcUInt32 comment_len;
1269 SILC_LOG_DEBUG(("Notify: KILLED"));
1272 if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1275 /* Find Client entry */
1276 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1281 comment = silc_argument_get_arg_type(args, 2, &comment_len);
1283 /* Get killer's ID */
1284 if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1287 if (id.type == SILC_ID_CLIENT) {
1288 /* Find Client entry */
1289 client_entry2 = silc_client_get_client_by_id(client, conn,
1291 if (!client_entry2 || !client_entry2->nickname[0]) {
1292 /** Resolve client */
1293 silc_client_unref_client(client, conn, client_entry);
1294 silc_client_unref_client(client, conn, client_entry2);
1295 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1296 client, conn, &id.u.client_id, NULL,
1297 silc_client_notify_resolved,
1301 entry = client_entry2;
1302 } else if (id.type == SILC_ID_SERVER) {
1303 /* Find Server entry */
1304 server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1306 /** Resolve server */
1307 SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
1308 client, conn, &id.u.server_id,
1309 silc_client_notify_resolved,
1315 /* Find Channel entry */
1316 channel_entry = silc_client_get_channel_by_id(client, conn,
1318 if (!channel_entry) {
1319 /** Resolve channel */
1320 SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1321 client, conn, &id.u.channel_id,
1322 silc_client_notify_resolved,
1326 entry = channel_entry;
1329 /* Notify application. */
1330 NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
1332 /* Delete the killed client */
1333 if (client_entry != conn->local_entry) {
1334 silc_client_remove_from_channels(client, conn, client_entry);
1335 silc_client_del_client(client, conn, client_entry);
1339 silc_client_unref_client(client, conn, client_entry);
1341 silc_client_unref_client(client, conn, client_entry2);
1343 silc_client_unref_server(client, conn, server);
1345 silc_client_unref_channel(client, conn, channel_entry);
1347 /** Notify processed */
1348 silc_fsm_next(fsm, silc_client_notify_processed);
1349 return SILC_FSM_CONTINUE;
1352 /**************************** SERVER SIGNOFF ********************************/
1354 /* Some server quit SILC network. Remove its clients from channels. */
1356 SILC_FSM_STATE(silc_client_notify_server_signoff)
1358 SilcClientConnection conn = fsm_context;
1359 SilcClient client = conn->client;
1360 SilcClientNotify notify = state_context;
1361 SilcNotifyPayload payload = notify->payload;
1362 SilcNotifyType type = silc_notify_get_type(payload);
1363 SilcArgumentPayload args = silc_notify_get_args(payload);
1364 SilcClientEntry client_entry;
1369 SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
1371 clients = silc_dlist_init();
1375 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1377 if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
1380 /* Get the client entry */
1381 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1383 silc_dlist_add(clients, client_entry);
1386 /* Notify application. We don't keep server entries so the server
1387 entry is returned as NULL. The client's are returned as list. */
1388 NOTIFY(client, conn, type, NULL, clients);
1390 /* Delete the clients */
1391 silc_dlist_start(clients);
1392 while ((client_entry = silc_dlist_get(clients))) {
1393 silc_client_remove_from_channels(client, conn, client_entry);
1394 silc_client_del_client(client, conn, client_entry);
1398 /** Notify processed */
1399 silc_client_list_free(client, conn, clients);
1400 silc_fsm_next(fsm, silc_client_notify_processed);
1401 return SILC_FSM_CONTINUE;
1404 /******************************** ERROR *************************************/
1406 /* Some error occurred */
1408 SILC_FSM_STATE(silc_client_notify_error)
1410 SilcClientConnection conn = fsm_context;
1411 SilcClient client = conn->client;
1412 SilcClientNotify notify = state_context;
1413 SilcNotifyPayload payload = notify->payload;
1414 SilcNotifyType type = silc_notify_get_type(payload);
1415 SilcArgumentPayload args = silc_notify_get_args(payload);
1416 SilcClientEntry client_entry;
1423 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1424 if (!tmp && tmp_len != 1)
1426 error = (SilcStatus)tmp[0];
1428 SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1430 /* Handle the error */
1431 if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1432 if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1434 client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1436 silc_client_remove_from_channels(client, conn, client_entry);
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 silc_client_nickname_parse(client, conn, client_entry->nickname,
1510 /* If same nick, the client was new to us and has become "present"
1511 to network. Send NULL as nick to application. */
1512 if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
1515 silc_free(tmp_nick);
1518 /* Get public key, if present */
1519 pk = silc_argument_get_arg_type(args, 5, &pk_len);
1520 if (pk && !client_entry->public_key) {
1521 if (silc_public_key_payload_decode(pk, pk_len, &public_key)) {
1522 client_entry->public_key = public_key;
1527 /* Notify application. */
1528 NOTIFY(client, conn, type, client_entry, tmp, mode, ntype,
1529 client_entry->public_key);
1531 client_entry->mode = mode;
1533 /* If nickname was changed, remove the client entry unless the
1534 client is on some channel */
1535 /* XXX, why do we need to remove the client entry?? */
1536 if (tmp && ntype == SILC_NOTIFY_TYPE_NICK_CHANGE &&
1537 !silc_hash_table_count(client_entry->channels))
1539 else if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
1540 ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1541 ntype == SILC_NOTIFY_TYPE_KILLED)
1545 silc_client_remove_from_channels(client, conn, client_entry);
1546 silc_client_del_client(client, conn, client_entry);
1550 silc_pkcs_public_key_free(public_key);
1553 /** Notify processed */
1554 silc_client_unref_client(client, conn, client_entry);
1555 silc_fsm_next(fsm, silc_client_notify_processed);
1556 return SILC_FSM_CONTINUE;