5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "chat-protocols.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32 #include "silc-cmdqueue.h"
38 #include "special-vars.h"
39 #include "fe-common/core/printtext.h"
40 #include "fe-common/core/fe-channels.h"
41 #include "fe-common/core/keyboard.h"
42 #include "fe-common/core/window-items.h"
43 #include "fe-common/silc/module-formats.h"
48 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
49 const char *name, SilcConnectionType conn_type,
50 SilcPublicKey public_key,
51 SilcVerifyPublicKey completion, void *context);
53 char *silc_get_session_filename(SILC_SERVER_REC *server)
55 char *file, *expanded;
57 expanded = parse_special_string(settings_get_str("session_filename"),
58 SERVER(server), NULL, "", NULL, 0);
60 file = silc_calloc(1, strlen(expanded) + 255);
61 snprintf(file, strlen(expanded) + 255, "%s/%s", get_irssi_dir(), expanded);
67 static void silc_get_umode_string(SilcUInt32 mode, char *buf,
70 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
71 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
72 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
74 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
75 "[SILC operator]" : "[unknown mode]");
77 if (mode & SILC_UMODE_GONE)
78 strcat(buf, " [away]");
79 if (mode & SILC_UMODE_INDISPOSED)
80 strcat(buf, " [indisposed]");
81 if (mode & SILC_UMODE_BUSY)
82 strcat(buf, " [busy]");
83 if (mode & SILC_UMODE_PAGE)
84 strcat(buf, " [page to reach]");
85 if (mode & SILC_UMODE_HYPER)
86 strcat(buf, " [hyper active]");
87 if (mode & SILC_UMODE_ROBOT)
88 strcat(buf, " [robot]");
89 if (mode & SILC_UMODE_ANONYMOUS)
90 strcat(buf, " [anonymous]");
91 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
92 strcat(buf, " [blocks private messages]");
93 if (mode & SILC_UMODE_DETACHED)
94 strcat(buf, " [detached]");
95 if (mode & SILC_UMODE_REJECT_WATCHING)
96 strcat(buf, " [rejects watching]");
97 if (mode & SILC_UMODE_BLOCK_INVITE)
98 strcat(buf, " [blocks invites]");
101 /* converts an utf-8 string to current locale */
102 char * silc_convert_utf8_string(const char *str)
104 int message_len = (str != NULL ? strlen(str) : 0);
105 char *message = silc_calloc(message_len + 1, sizeof(*message));
107 g_return_val_if_fail(message != NULL, NULL);
114 if (!silc_term_utf8() && silc_utf8_valid(str, message_len))
115 silc_utf8_decode(str, message_len, SILC_STRING_LOCALE,
116 message, message_len);
118 strcpy(message, str);
123 /* print "nick appears as" message to every channel of a server */
125 silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
126 const char *newnick, const char *oldnick,
129 if (ignore_check(SERVER(server), oldnick, address,
130 channel, newnick, MSGLEVEL_NICKS))
133 printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS,
134 SILCTXT_CHANNEL_APPEARS,
135 oldnick, newnick, channel, address);
139 silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
140 const char *oldnick, const char *address)
142 GSList *tmp, *windows;
144 /* Print to each channel/query where the nick is.
145 Don't print more than once to the same window. */
148 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
149 CHANNEL_REC *channel = tmp->data;
150 WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
152 if (nicklist_find(channel, newnick) == NULL ||
153 g_slist_find(windows, window) != NULL)
156 windows = g_slist_append(windows, window);
157 silc_print_nick_change_channel(server, channel->visible_name,
158 newnick, oldnick, address);
161 g_slist_free(windows);
164 static void silc_parse_channel_public_keys(SILC_SERVER_REC *server,
165 SilcChannelEntry channel_entry,
166 SilcDList channel_pubkeys)
168 SilcArgumentDecodedList e;
169 SilcPublicKey pubkey;
170 SilcSILCPublicKey silc_pubkey;
171 SilcUInt32 pk_len, type;
173 char *fingerprint, *babbleprint;
176 printformat_module("fe-common/silc", server, NULL,
177 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST,
178 channel_entry->channel_name);
180 silc_dlist_start(channel_pubkeys);
181 while ((e = silc_dlist_get(channel_pubkeys))) {
182 pubkey = e->argument;
185 if (silc_pkcs_get_type(pubkey) != SILC_PKCS_SILC)
188 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
192 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
193 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
194 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, pubkey);
196 printformat_module("fe-common/silc", server, NULL,
197 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST_ENTRY,
198 c++, channel_entry->channel_name,
199 type == 0x00 ? "Added" : "Removed",
200 silc_pubkey->identifier.realname ?
201 silc_pubkey->identifier.realname : "",
202 fingerprint, babbleprint);
204 silc_free(fingerprint);
205 silc_free(babbleprint);
210 void silc_say(SilcClient client, SilcClientConnection conn,
211 SilcClientMessageType type, char *msg, ...)
213 SILC_SERVER_REC *server;
217 server = conn == NULL ? NULL : conn->context;
220 str = g_strdup_vprintf(msg, va);
221 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
226 void silc_say_error(char *msg, ...)
232 str = g_strdup_vprintf(msg, va);
233 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
239 /* Try to verify a message using locally stored public key data */
241 int verify_message_signature(SilcClientEntry sender,
242 SilcMessagePayload message)
245 char file[256], filename[256];
246 char *fingerprint, *fingerprint2;
247 const unsigned char *pk_data;
248 SilcUInt32 pk_datalen;
250 int ret = SILC_MSG_SIGNED_VERIFIED, i;
252 /* get public key from the signature payload and compare it with the
253 one stored in the client entry */
254 pk = silc_message_signed_get_public_key(message, &pk_data, &pk_datalen);
257 fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen);
259 if (sender->fingerprint) {
260 fingerprint2 = silc_fingerprint(sender->fingerprint,
261 sizeof(sender->fingerprint));
262 if (strcmp(fingerprint, fingerprint2)) {
263 /* since the public key differs from the senders public key, the
264 verification _failed_ */
265 silc_pkcs_public_key_free(pk);
266 silc_free(fingerprint);
267 ret = SILC_MSG_SIGNED_UNKNOWN;
269 silc_free(fingerprint2);
271 } else if (sender->fingerprint)
272 fingerprint = silc_fingerprint(sender->fingerprint,
273 sizeof(sender->fingerprint));
275 /* no idea, who or what signed that message ... */
276 return SILC_MSG_SIGNED_UNKNOWN;
278 /* search our local client key cache */
279 for (i = 0; i < strlen(fingerprint); i++)
280 if (fingerprint[i] == ' ')
281 fingerprint[i] = '_';
283 snprintf(file, sizeof(file) - 1, "clientkey_%s.pub", fingerprint);
284 snprintf(filename, sizeof(filename) - 1, "%s/clientkeys/%s",
285 get_irssi_dir(), file);
286 silc_free(fingerprint);
288 if (stat(filename, &st) < 0)
289 /* we don't have the public key cached ... use the one from the sig */
290 ret = SILC_MSG_SIGNED_UNKNOWN;
292 SilcPublicKey cached_pk=NULL;
294 /* try to load the file */
295 if (!silc_pkcs_load_public_key(filename, &cached_pk)) {
296 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
297 SILCTXT_PUBKEY_COULD_NOT_LOAD, "client");
299 return SILC_MSG_SIGNED_UNKNOWN;
301 ret = SILC_MSG_SIGNED_UNKNOWN;
306 silc_pkcs_public_key_free(pk);
311 /* the public key is now in pk, our "level of trust" in ret */
312 if ((pk) && silc_message_signed_verify(message, pk,
313 sha1hash)!= SILC_AUTH_OK)
314 ret = SILC_MSG_SIGNED_FAILED;
317 silc_pkcs_public_key_free(pk);
322 char *silc_unescape_data(const char *escaped_data, SilcUInt32 *length)
325 int i = 0, j = 0, len = strlen(escaped_data);
327 data = silc_calloc(len, sizeof(char));
330 ptr = memchr(escaped_data + i, 1, len - i);
332 int inc = (ptr - escaped_data) - i;
333 memcpy(data + j, escaped_data + i, inc);
336 data[j++] = *(ptr + 1) - 1;
338 memcpy(data + j, escaped_data + i, len - i);
348 char *silc_escape_data(const char *data, SilcUInt32 len)
350 char *escaped_data, *ptr, *ptr0, *ptr1;
353 escaped_data = silc_calloc(2 * len, sizeof(char));
356 ptr0 = memchr(data + i, 0, len - i);
357 ptr1 = memchr(data + i, 1, len - i);
359 ptr = (ptr0 < ptr1 ? (ptr0 ? ptr0 : ptr1) : (ptr1 ? ptr1 : ptr0));
362 int inc = (ptr - data) - i;
364 memcpy(escaped_data + j, data + i, inc);
367 escaped_data[j++] = 1;
368 escaped_data[j++] = *(data + i++) + 1;
370 memcpy(escaped_data + j, data + i, len - i);
379 void silc_emit_mime_sig(SILC_SERVER_REC *server, WI_ITEM_REC *item,
380 const char *data, SilcUInt32 data_len,
381 const char *nick, int verified)
385 escaped_data = silc_escape_data(data, data_len);
387 signal_emit("mime", 5, server, item, escaped_data, nick, verified);
389 silc_free(escaped_data);
393 /* Message for a channel. The `sender' is the nickname of the sender
394 received in the packet. The `channel_name' is the name of the channel. */
396 void silc_channel_message(SilcClient client, SilcClientConnection conn,
397 SilcClientEntry sender, SilcChannelEntry channel,
398 SilcMessagePayload payload,
399 SilcChannelPrivateKey key,
400 SilcMessageFlags flags, const unsigned char *message,
401 SilcUInt32 message_len)
403 SILC_SERVER_REC *server;
405 SILC_CHANNEL_REC *chanrec;
408 SILC_LOG_DEBUG(("Start"));
413 server = conn == NULL ? NULL : conn->context;
414 chanrec = silc_channel_find_entry(server, channel);
418 nick = silc_nicklist_find(chanrec, sender);
420 /* We didn't find client but it clearly exists, add it. */
421 SilcChannelUser chu = silc_client_on_channel(channel, sender);
423 nick = silc_nicklist_insert(chanrec, chu, FALSE);
426 /* If the messages is digitally signed, verify it, if possible. */
427 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
428 if (!settings_get_bool("ignore_message_signatures")) {
429 verified = verify_message_signature(sender, payload);
431 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
435 if (flags & SILC_MESSAGE_FLAG_DATA) {
436 silc_emit_mime_sig(server, (WI_ITEM_REC *)chanrec, message, message_len,
437 nick == NULL ? NULL : nick->nick,
438 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
445 if (flags & SILC_MESSAGE_FLAG_ACTION)
446 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
447 char tmp[256], *cp, *dm = NULL;
448 memset(tmp, 0, sizeof(tmp));
450 if(message_len > sizeof(tmp) - 1) {
451 dm = silc_calloc(message_len + 1, sizeof(*dm));
454 silc_utf8_decode(message, message_len, SILC_STRING_LOCALE,
456 if (flags & SILC_MESSAGE_FLAG_SIGNED)
457 signal_emit("message silc signed_action", 6, server, cp, nick->nick,
458 nick->host, channel->channel_name, verified);
460 signal_emit("message silc action", 5, server, cp, nick->nick,
461 nick->host, channel->channel_name);
464 if (flags & SILC_MESSAGE_FLAG_SIGNED)
465 signal_emit("message silc signed_action", 6, server, message,
466 nick->nick, nick->host, channel->channel_name, verified);
468 signal_emit("message silc action", 5, server, message,
469 nick->nick, nick->host, channel->channel_name);
471 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
472 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
473 char tmp[256], *cp, *dm = NULL;
474 memset(tmp, 0, sizeof(tmp));
476 if(message_len > sizeof(tmp) - 1) {
477 dm = silc_calloc(message_len + 1, sizeof(*dm));
480 silc_utf8_decode(message, message_len, SILC_STRING_LOCALE,
482 if (flags & SILC_MESSAGE_FLAG_SIGNED)
483 signal_emit("message silc signed_notice", 6, server, cp, nick->nick,
484 nick->host, channel->channel_name, verified);
486 signal_emit("message silc notice", 5, server, cp, nick->nick,
487 nick->host, channel->channel_name);
490 if (flags & SILC_MESSAGE_FLAG_SIGNED)
491 signal_emit("message silc signed_notice", 6, server, message,
492 nick->nick, nick->host, channel->channel_name, verified);
494 signal_emit("message silc notice", 5, server, message,
495 nick->nick, nick->host, channel->channel_name);
498 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
499 char tmp[256], *cp, *dm = NULL;
501 memset(tmp, 0, sizeof(tmp));
503 if (message_len > sizeof(tmp) - 1) {
504 dm = silc_calloc(message_len + 1, sizeof(*dm));
508 silc_utf8_decode(message, message_len, SILC_STRING_LOCALE,
510 if (flags & SILC_MESSAGE_FLAG_SIGNED)
511 signal_emit("message signed_public", 6, server, cp,
512 nick == NULL ? "[<unknown>]" : nick->nick,
513 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
514 chanrec->name, verified);
516 signal_emit("message public", 6, server, cp,
517 nick == NULL ? "[<unknown>]" : nick->nick,
518 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
519 chanrec->name, nick);
524 if (flags & SILC_MESSAGE_FLAG_SIGNED)
525 signal_emit("message signed_public", 6, server, message,
526 nick == NULL ? "[<unknown>]" : nick->nick,
527 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
528 chanrec->name, verified);
530 signal_emit("message public", 6, server, message,
531 nick == NULL ? "[<unknown>]" : nick->nick,
532 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
533 chanrec->name, nick);
537 /* Private message to the client. The `sender' is the nickname of the
538 sender received in the packet. */
540 void silc_private_message(SilcClient client, SilcClientConnection conn,
541 SilcClientEntry sender, SilcMessagePayload payload,
542 SilcMessageFlags flags,
543 const unsigned char *message,
544 SilcUInt32 message_len)
546 SILC_SERVER_REC *server;
550 SILC_LOG_DEBUG(("Start"));
552 server = conn == NULL ? NULL : conn->context;
553 memset(userhost, 0, sizeof(userhost));
554 if (sender->username)
555 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
556 sender->username, sender->hostname);
558 /* If the messages is digitally signed, verify it, if possible. */
559 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
560 if (!settings_get_bool("ignore_message_signatures")) {
561 verified = verify_message_signature(sender, payload);
563 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
567 if (flags & SILC_MESSAGE_FLAG_DATA) {
568 silc_emit_mime_sig(server,
570 (WI_ITEM_REC *)query_find(SERVER(server), sender->nickname) :
572 message, message_len,
573 sender->nickname ? sender->nickname : "[<unknown>]",
574 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
581 if (flags & SILC_MESSAGE_FLAG_ACTION)
582 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
583 char tmp[256], *cp, *dm = NULL;
584 memset(tmp, 0, sizeof(tmp));
586 if(message_len > sizeof(tmp) - 1) {
587 dm = silc_calloc(message_len + 1, sizeof(*dm));
590 silc_utf8_decode(message, message_len, SILC_STRING_LOCALE,
592 if (flags & SILC_MESSAGE_FLAG_SIGNED)
593 signal_emit("message silc signed_private_action", 6, server, cp,
594 sender->nickname ? sender->nickname : "[<unknown>]",
595 sender->username ? userhost : NULL,
598 signal_emit("message silc private_action", 5, server, cp,
599 sender->nickname ? sender->nickname : "[<unknown>]",
600 sender->username ? userhost : NULL, NULL);
603 if (flags & SILC_MESSAGE_FLAG_SIGNED)
604 signal_emit("message silc signed_private_action", 6, server, message,
605 sender->nickname ? sender->nickname : "[<unknown>]",
606 sender->username ? userhost : NULL,
609 signal_emit("message silc private_action", 5, server, message,
610 sender->nickname ? sender->nickname : "[<unknown>]",
611 sender->username ? userhost : NULL, NULL);
613 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
614 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
615 char tmp[256], *cp, *dm = NULL;
616 memset(tmp, 0, sizeof(tmp));
618 if(message_len > sizeof(tmp) - 1) {
619 dm = silc_calloc(message_len + 1, sizeof(*dm));
622 silc_utf8_decode(message, message_len, SILC_STRING_LOCALE,
624 if (flags & SILC_MESSAGE_FLAG_SIGNED)
625 signal_emit("message silc signed_private_notice", 6, server, cp,
626 sender->nickname ? sender->nickname : "[<unknown>]",
627 sender->username ? userhost : NULL,
630 signal_emit("message silc private_notice", 5, server, cp,
631 sender->nickname ? sender->nickname : "[<unknown>]",
632 sender->username ? userhost : NULL, NULL);
635 if (flags & SILC_MESSAGE_FLAG_SIGNED)
636 signal_emit("message silc signed_private_notice", 6, server, message,
637 sender->nickname ? sender->nickname : "[<unknown>]",
638 sender->username ? userhost : NULL,
641 signal_emit("message silc private_notice", 5, server, message,
642 sender->nickname ? sender->nickname : "[<unknown>]",
643 sender->username ? userhost : NULL, NULL);
646 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
647 char tmp[256], *cp, *dm = NULL;
649 memset(tmp, 0, sizeof(tmp));
651 if (message_len > sizeof(tmp) - 1) {
652 dm = silc_calloc(message_len + 1, sizeof(*dm));
656 silc_utf8_decode(message, message_len, SILC_STRING_LOCALE,
658 if (flags & SILC_MESSAGE_FLAG_SIGNED)
659 signal_emit("message signed_private", 5, server, cp,
660 sender->nickname ? sender->nickname : "[<unknown>]",
661 sender->username ? userhost : NULL, verified);
663 signal_emit("message private", 4, server, cp,
664 sender->nickname ? sender->nickname : "[<unknown>]",
665 sender->username ? userhost : NULL);
670 if (flags & SILC_MESSAGE_FLAG_SIGNED)
671 signal_emit("message signed_private", 5, server, message,
672 sender->nickname ? sender->nickname : "[<unknown>]",
673 sender->username ? userhost : NULL, verified);
675 signal_emit("message private", 4, server, message,
676 sender->nickname ? sender->nickname : "[<unknown>]",
677 sender->username ? userhost : NULL);
681 /* Notify message to the client. The notify arguments are sent in the
682 same order as servers sends them. The arguments are same as received
683 from the server except for ID's. If ID is received application receives
684 the corresponding entry to the ID. For example, if Client ID is received
685 application receives SilcClientEntry. Also, if the notify type is
686 for channel the channel entry is sent to application (even if server
687 does not send it). */
689 void silc_notify(SilcClient client, SilcClientConnection conn,
690 SilcNotifyType type, ...)
693 SILC_SERVER_REC *server;
694 SILC_CHANNEL_REC *chanrec;
695 SILC_NICK_REC *nickrec;
696 SilcClientEntry client_entry, client_entry2;
697 SilcChannelEntry channel, channel2;
698 SilcServerEntry server_entry;
703 char *name, *tmp, *cipher, *hmac;
704 GSList *list1, *list_tmp;
707 SILC_LOG_DEBUG(("Start"));
711 server = conn == NULL ? NULL : conn->context;
714 case SILC_NOTIFY_TYPE_NONE:
715 /* Some generic notice from server */
716 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
719 case SILC_NOTIFY_TYPE_INVITE:
721 * Invited or modified invite list.
724 SILC_LOG_DEBUG(("Notify: INVITE"));
726 channel = va_arg(va, SilcChannelEntry);
727 name = va_arg(va, char *);
728 client_entry = va_arg(va, SilcClientEntry);
730 memset(buf, 0, sizeof(buf));
731 snprintf(buf, sizeof(buf) - 1, "%s@%s",
732 client_entry->username, client_entry->hostname);
733 signal_emit("message invite", 4, server, channel ? channel->channel_name :
734 name, client_entry->nickname, buf);
737 case SILC_NOTIFY_TYPE_JOIN:
742 SILC_LOG_DEBUG(("Notify: JOIN"));
744 client_entry = va_arg(va, SilcClientEntry);
745 channel = va_arg(va, SilcChannelEntry);
747 if (client_entry == server->conn->local_entry) {
748 /* You joined to channel */
749 chanrec = silc_channel_find(server, channel->channel_name);
750 if (chanrec != NULL && !chanrec->joined)
751 chanrec->entry = channel;
753 chanrec = silc_channel_find_entry(server, channel);
754 if (chanrec != NULL) {
755 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
757 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
761 memset(buf, 0, sizeof(buf));
762 if (client_entry->username)
763 snprintf(buf, sizeof(buf) - 1, "%s@%s",
764 client_entry->username, client_entry->hostname);
765 signal_emit("message join", 4, server, channel->channel_name,
766 client_entry->nickname,
767 client_entry->username == NULL ? "" : buf);
770 case SILC_NOTIFY_TYPE_LEAVE:
775 SILC_LOG_DEBUG(("Notify: LEAVE"));
777 client_entry = va_arg(va, SilcClientEntry);
778 channel = va_arg(va, SilcChannelEntry);
780 memset(buf, 0, sizeof(buf));
781 if (client_entry->username)
782 snprintf(buf, sizeof(buf) - 1, "%s@%s",
783 client_entry->username, client_entry->hostname);
784 signal_emit("message part", 5, server, channel->channel_name,
785 client_entry->nickname, client_entry->username ?
786 buf : "", client_entry->nickname);
788 chanrec = silc_channel_find_entry(server, channel);
789 if (chanrec != NULL) {
790 nickrec = silc_nicklist_find(chanrec, client_entry);
792 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
796 case SILC_NOTIFY_TYPE_SIGNOFF:
801 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
803 client_entry = va_arg(va, SilcClientEntry);
804 tmp = va_arg(va, char *);
807 silc_server_free_ftp(server, client_entry);
810 /* Print only if we have the nickname. If this cliente has just quit
811 when we were only resolving it, it is possible we don't have the
813 if (client_entry->nickname) {
814 memset(buf, 0, sizeof(buf));
815 if (client_entry->username)
816 snprintf(buf, sizeof(buf) - 1, "%s@%s",
817 client_entry->username, client_entry->hostname);
818 signal_emit("message quit", 4, server, client_entry->nickname,
819 client_entry->username ? buf : "",
823 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
824 for (list_tmp = list1; list_tmp != NULL; list_tmp =
825 list_tmp->next->next) {
826 CHANNEL_REC *channel = list_tmp->data;
827 NICK_REC *nickrec = list_tmp->next->data;
829 nicklist_remove(channel, nickrec);
833 case SILC_NOTIFY_TYPE_TOPIC_SET:
838 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
840 idtype = va_arg(va, int);
841 entry = va_arg(va, void *);
842 tmp = va_arg(va, char *);
843 channel = va_arg(va, SilcChannelEntry);
845 chanrec = silc_channel_find_entry(server, channel);
846 if (chanrec != NULL) {
847 char tmp2[256], *cp, *dm = NULL;
849 g_free_not_null(chanrec->topic);
850 if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) {
851 memset(tmp2, 0, sizeof(tmp2));
853 if (strlen(tmp) > sizeof(tmp2) - 1) {
854 dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm));
858 silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE,
863 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
864 signal_emit("channel topic changed", 1, chanrec);
869 if (idtype == SILC_ID_CLIENT) {
870 client_entry = (SilcClientEntry)entry;
871 memset(buf, 0, sizeof(buf));
872 snprintf(buf, sizeof(buf) - 1, "%s@%s",
873 client_entry->username, client_entry->hostname);
874 signal_emit("message topic", 5, server, channel->channel_name,
875 tmp, client_entry->nickname, buf);
876 } else if (idtype == SILC_ID_SERVER) {
877 server_entry = (SilcServerEntry)entry;
878 signal_emit("message topic", 5, server, channel->channel_name,
879 tmp, server_entry->server_name,
880 server_entry->server_name);
881 } else if (idtype == SILC_ID_CHANNEL) {
882 channel = (SilcChannelEntry)entry;
883 signal_emit("message topic", 5, server, channel->channel_name,
884 tmp, channel->channel_name, channel->channel_name);
888 case SILC_NOTIFY_TYPE_NICK_CHANGE:
893 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
895 client_entry = va_arg(va, SilcClientEntry);
896 client_entry2 = va_arg(va, SilcClientEntry);
898 if (!strcmp(client_entry->nickname, client_entry2->nickname))
901 memset(buf, 0, sizeof(buf));
902 snprintf(buf, sizeof(buf) - 1, "%s@%s",
903 client_entry2->username, client_entry2->hostname);
904 nicklist_rename_unique(SERVER(server),
905 client_entry, client_entry->nickname,
906 client_entry2, client_entry2->nickname);
907 signal_emit("message nick", 4, server, client_entry2->nickname,
908 client_entry->nickname, buf);
911 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
913 * Changed channel mode.
916 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
918 idtype = va_arg(va, int);
919 entry = va_arg(va, void *);
920 mode = va_arg(va, SilcUInt32);
921 cipher = va_arg(va, char *); /* cipher */
922 hmac = va_arg(va, char *); /* hmac */
923 (void)va_arg(va, char *); /* passphrase */
924 (void)va_arg(va, SilcPublicKey); /* founder key */
925 chpks = va_arg(va, SilcDList); /* channel public keys */
926 channel = va_arg(va, SilcChannelEntry);
928 tmp = silc_client_chmode(mode, cipher ? cipher : "",
931 chanrec = silc_channel_find_entry(server, channel);
932 if (chanrec != NULL) {
933 g_free_not_null(chanrec->mode);
934 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
935 signal_emit("channel mode changed", 1, chanrec);
938 if (idtype == SILC_ID_CLIENT) {
939 client_entry = (SilcClientEntry)entry;
940 printformat_module("fe-common/silc", server, channel->channel_name,
941 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
942 channel->channel_name, tmp ? tmp : "removed all",
943 client_entry->nickname);
944 } else if (idtype == SILC_ID_SERVER) {
945 server_entry = (SilcServerEntry)entry;
946 printformat_module("fe-common/silc", server, channel->channel_name,
947 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
948 channel->channel_name, tmp ? tmp : "removed all",
949 server_entry->server_name);
950 } else if (idtype == SILC_ID_CHANNEL) {
951 channel2 = (SilcChannelEntry)entry;
952 printformat_module("fe-common/silc", server, channel->channel_name,
953 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
954 channel->channel_name, tmp ? tmp : "removed all",
955 channel2->channel_name);
958 /* Print the channel public key list */
960 silc_parse_channel_public_keys(server, channel, chpks);
965 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
967 * Changed user's mode on channel.
970 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
972 idtype = va_arg(va, int);
973 entry = va_arg(va, void *);
974 mode = va_arg(va, SilcUInt32);
975 client_entry2 = va_arg(va, SilcClientEntry);
976 channel = va_arg(va, SilcChannelEntry);
978 tmp = silc_client_chumode(mode);
979 chanrec = silc_channel_find_entry(server, channel);
980 if (chanrec != NULL) {
983 if (client_entry2 == server->conn->local_entry)
984 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
986 nick = silc_nicklist_find(chanrec, client_entry2);
988 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
989 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
990 signal_emit("nick mode changed", 2, chanrec, nick);
994 if (idtype == SILC_ID_CLIENT) {
995 client_entry = (SilcClientEntry)entry;
996 printformat_module("fe-common/silc", server, channel->channel_name,
997 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
998 channel->channel_name, client_entry2->nickname,
999 tmp ? tmp : "removed all",
1000 client_entry->nickname);
1001 } else if (idtype == SILC_ID_SERVER) {
1002 server_entry = (SilcServerEntry)entry;
1003 printformat_module("fe-common/silc", server, channel->channel_name,
1004 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1005 channel->channel_name, client_entry2->nickname,
1006 tmp ? tmp : "removed all",
1007 server_entry->server_name);
1008 } else if (idtype == SILC_ID_CHANNEL) {
1009 channel2 = (SilcChannelEntry)entry;
1010 printformat_module("fe-common/silc", server, channel->channel_name,
1011 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1012 channel->channel_name, client_entry2->nickname,
1013 tmp ? tmp : "removed all",
1014 channel2->channel_name);
1017 if (mode & SILC_CHANNEL_UMODE_CHANFO)
1018 printformat_module("fe-common/silc",
1019 server, channel->channel_name, MSGLEVEL_CRAP,
1020 SILCTXT_CHANNEL_FOUNDER,
1021 channel->channel_name, client_entry2->nickname);
1023 if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
1024 printformat_module("fe-common/silc",
1025 server, channel->channel_name, MSGLEVEL_CRAP,
1026 SILCTXT_CHANNEL_QUIETED, channel->channel_name);
1031 case SILC_NOTIFY_TYPE_MOTD:
1036 SILC_LOG_DEBUG(("Notify: MOTD"));
1038 tmp = va_arg(va, char *);
1040 if (!settings_get_bool("skip_motd"))
1041 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
1044 case SILC_NOTIFY_TYPE_KICKED:
1046 * Someone was kicked from channel.
1049 SILC_LOG_DEBUG(("Notify: KICKED"));
1051 client_entry = va_arg(va, SilcClientEntry);
1052 tmp = va_arg(va, char *);
1053 client_entry2 = va_arg(va, SilcClientEntry);
1054 channel = va_arg(va, SilcChannelEntry);
1056 chanrec = silc_channel_find_entry(server, channel);
1058 if (client_entry == conn->local_entry) {
1059 printformat_module("fe-common/silc", server, channel->channel_name,
1060 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
1061 channel->channel_name,
1062 client_entry ? client_entry2->nickname : "",
1065 chanrec->kicked = TRUE;
1066 channel_destroy((CHANNEL_REC *)chanrec);
1069 printformat_module("fe-common/silc", server, channel->channel_name,
1070 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
1071 client_entry->nickname, channel->channel_name,
1072 client_entry2 ? client_entry2->nickname : "",
1076 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
1077 if (nickrec != NULL)
1078 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
1083 case SILC_NOTIFY_TYPE_KILLED:
1085 * Someone was killed from the network.
1088 SILC_LOG_DEBUG(("Notify: KILLED"));
1090 client_entry = va_arg(va, SilcClientEntry);
1091 tmp = va_arg(va, char *);
1092 idtype = va_arg(va, int);
1093 entry = va_arg(va, SilcClientEntry);
1095 if (client_entry == conn->local_entry) {
1096 if (idtype == SILC_ID_CLIENT) {
1097 client_entry2 = (SilcClientEntry)entry;
1098 printformat_module("fe-common/silc", server, NULL,
1099 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1100 client_entry2 ? client_entry2->nickname : "",
1102 } else if (idtype == SILC_ID_SERVER) {
1103 server_entry = (SilcServerEntry)entry;
1104 printformat_module("fe-common/silc", server, NULL,
1105 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1106 server_entry->server_name, tmp ? tmp : "");
1107 } else if (idtype == SILC_ID_CHANNEL) {
1108 channel = (SilcChannelEntry)entry;
1109 printformat_module("fe-common/silc", server, NULL,
1110 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1111 channel->channel_name, tmp ? tmp : "");
1114 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1115 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1116 list_tmp->next->next) {
1117 CHANNEL_REC *channel = list_tmp->data;
1118 NICK_REC *nickrec = list_tmp->next->data;
1119 nicklist_remove(channel, nickrec);
1122 if (idtype == SILC_ID_CLIENT) {
1123 client_entry2 = (SilcClientEntry)entry;
1124 printformat_module("fe-common/silc", server, NULL,
1125 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1126 client_entry->nickname,
1127 client_entry2 ? client_entry2->nickname : "",
1129 } else if (idtype == SILC_ID_SERVER) {
1130 server_entry = (SilcServerEntry)entry;
1131 printformat_module("fe-common/silc", server, NULL,
1132 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1133 client_entry->nickname,
1134 server_entry->server_name, tmp ? tmp : "");
1135 } else if (idtype == SILC_ID_CHANNEL) {
1136 channel = (SilcChannelEntry)entry;
1137 printformat_module("fe-common/silc", server, NULL,
1138 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1139 client_entry->nickname,
1140 channel->channel_name, tmp ? tmp : "");
1145 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
1148 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1151 * Server has quit the network.
1154 SilcClientEntry *clients;
1155 SilcUInt32 clients_count;
1157 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1159 (void)va_arg(va, void *);
1160 clients = va_arg(va, SilcClientEntry *);
1161 clients_count = va_arg(va, SilcUInt32);
1163 for (i = 0; i < clients_count; i++) {
1164 memset(buf, 0, sizeof(buf));
1166 /* Print only if we have the nickname. If this client has just quit
1167 when we were only resolving it, it is possible we don't have the
1169 if (clients[i]->nickname) {
1170 if (clients[i]->username)
1171 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1172 clients[i]->username, clients[i]->hostname);
1173 signal_emit("message quit", 4, server, clients[i]->nickname,
1174 clients[i]->username ? buf : "",
1179 silc_server_free_ftp(server, clients[i]);
1182 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
1183 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1184 list_tmp->next->next) {
1185 CHANNEL_REC *channel = list_tmp->data;
1186 NICK_REC *nickrec = list_tmp->next->data;
1187 nicklist_remove(channel, nickrec);
1193 case SILC_NOTIFY_TYPE_ERROR:
1195 SilcStatus error = va_arg(va, int);
1197 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1198 "%s", silc_get_status_message(error));
1202 case SILC_NOTIFY_TYPE_WATCH:
1204 SilcNotifyType notify;
1206 client_entry = va_arg(va, SilcClientEntry);
1207 name = va_arg(va, char *); /* Maybe NULL */
1208 mode = va_arg(va, SilcUInt32);
1209 notify = va_arg(va, int);
1211 if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1213 printformat_module("fe-common/silc", server, NULL,
1214 MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1215 client_entry->nickname, name);
1217 printformat_module("fe-common/silc", server, NULL,
1218 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1219 client_entry->nickname);
1220 } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1221 /* See if client was away and is now present */
1222 if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1223 SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1224 SILC_UMODE_DETACHED)) &&
1225 (client_entry->mode & SILC_UMODE_GONE ||
1226 client_entry->mode & SILC_UMODE_INDISPOSED ||
1227 client_entry->mode & SILC_UMODE_BUSY ||
1228 client_entry->mode & SILC_UMODE_PAGE ||
1229 client_entry->mode & SILC_UMODE_DETACHED)) {
1230 printformat_module("fe-common/silc", server, NULL,
1231 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1232 client_entry->nickname);
1236 memset(buf, 0, sizeof(buf));
1237 silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1238 printformat_module("fe-common/silc", server, NULL,
1239 MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1240 client_entry->nickname, buf);
1242 } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1243 printformat_module("fe-common/silc", server, NULL,
1244 MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1245 client_entry->nickname);
1246 } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1247 notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1248 printformat_module("fe-common/silc", server, NULL,
1249 MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1250 client_entry->nickname);
1251 } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1252 /* Client logged in to the network */
1253 printformat_module("fe-common/silc", server, NULL,
1254 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1255 client_entry->nickname);
1261 /* Unknown notify */
1262 printformat_module("fe-common/silc", server, NULL,
1263 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1270 /* Command handler. This function is called always in the command function.
1271 If error occurs it will be called as well. `conn' is the associated
1272 client connection. `cmd_context' is the command context that was
1273 originally sent to the command. `success' is FALSE if error occured
1274 during command. `command' is the command being processed. It must be
1275 noted that this is not reply from server. This is merely called just
1276 after application has called the command. Just to tell application
1277 that the command really was processed. */
1279 static bool cmode_list_chpks = FALSE;
1281 void silc_command(SilcClient client, SilcClientConnection conn,
1282 SilcBool success, SilcCommand command, SilcStatus status,
1283 SilcUInt32 argc, unsigned char **argv)
1285 SILC_SERVER_REC *server = conn->context;
1287 SILC_LOG_DEBUG(("Start"));
1290 silc_say_error("%s", silc_get_status_message(status));
1296 case SILC_COMMAND_INVITE:
1298 printformat_module("fe-common/silc", server, NULL,
1299 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1301 (argv[1][0] == '*' ?
1302 (char *)conn->current_channel->channel_name :
1306 case SILC_COMMAND_DETACH:
1307 server->no_reconnect = TRUE;
1310 case SILC_COMMAND_CMODE:
1311 if (argc == 3 && !strcmp(argv[2], "+C"))
1312 cmode_list_chpks = TRUE;
1314 cmode_list_chpks = FALSE;
1324 SilcClientConnection conn;
1329 void silc_getkey_cb(bool success, void *context)
1331 GetkeyContext getkey = (GetkeyContext)context;
1332 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1333 char *name = (getkey->id_type == SILC_ID_CLIENT ?
1334 ((SilcClientEntry)getkey->entry)->nickname :
1335 ((SilcServerEntry)getkey->entry)->server_name);
1338 printformat_module("fe-common/silc", NULL, NULL,
1339 MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1341 printformat_module("fe-common/silc", NULL, NULL,
1342 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1349 /* Parse an invite or ban list */
1350 void silc_parse_inviteban_list(SilcClient client,
1351 SilcClientConnection conn,
1352 SILC_SERVER_REC *server,
1353 SilcChannelEntry channel,
1354 const char *list_type,
1355 SilcArgumentPayload list)
1358 SilcUInt32 type, len;
1359 SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1360 int counter=0, resolving = FALSE;
1362 if (!silc_argument_get_arg_num(list)) {
1363 printformat_module("fe-common/silc", server,
1364 (chanrec ? chanrec->visible_name : NULL),
1365 MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1366 channel->channel_name, list_type);
1370 printformat_module("fe-common/silc", server,
1371 (chanrec ? chanrec->visible_name : NULL),
1372 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1373 channel->channel_name, list_type);
1375 /* Parse the list */
1376 tmp = silc_argument_get_first_arg(list, &type, &len);
1381 /* An invite string */
1385 if (tmp[len-1] == ',')
1388 list = g_strsplit(tmp, ",", -1);
1390 printformat_module("fe-common/silc", server,
1391 (chanrec ? chanrec->visible_name : NULL),
1392 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1393 ++counter, channel->channel_name, list_type,
1402 char *fingerprint, *babbleprint;
1404 /* tmp is Public Key Payload, take public key from it. */
1405 fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1406 babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1408 printformat_module("fe-common/silc", server,
1409 (chanrec ? chanrec->visible_name : NULL),
1410 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1411 ++counter, channel->channel_name, list_type,
1412 fingerprint, babbleprint);
1419 SilcClientEntry client_entry;
1422 if (!silc_id_payload_parse_id(tmp, len, &id)) {
1423 silc_say_error("Invalid data in %s list encountered", list_type);
1427 client_entry = silc_client_get_client_by_id(client, conn,
1430 printformat_module("fe-common/silc", server,
1431 (chanrec ? chanrec->visible_name : NULL),
1432 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1433 ++counter, channel->channel_name, list_type,
1434 client_entry->nickname);
1435 silc_client_unref_client(client, conn, client_entry);
1438 silc_client_get_client_by_id_resolve(client, conn, &id.u.client_id,
1446 silc_say_error("Unkown type in %s list: %u (len %u)",
1447 list_type, type, len);
1450 tmp = silc_argument_get_next_arg(list, &type, &len);
1454 printformat_module("fe-common/silc", server,
1455 (chanrec ? chanrec->visible_name : NULL),
1456 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1457 list_type, channel->channel_name);
1460 /* Command reply handler. This function is called always in the command reply
1461 function. If error occurs it will be called as well. Normal scenario
1462 is that it will be called after the received command data has been parsed
1463 and processed. The function is used to pass the received command data to
1466 `conn' is the associated client connection. `cmd_payload' is the command
1467 payload data received from server and it can be ignored. It is provided
1468 if the application would like to re-parse the received command data,
1469 however, it must be noted that the data is parsed already by the library
1470 thus the payload can be ignored. `success' is FALSE if error occured.
1471 In this case arguments are not sent to the application. `command' is the
1472 command reply being processed. The function has variable argument list
1473 and each command defines the number and type of arguments it passes to the
1474 application (on error they are not sent). */
1476 void silc_command_reply(SilcClient client, SilcClientConnection conn,
1477 SilcCommand command, SilcStatus status,
1478 SilcStatus error, va_list vp)
1480 SILC_SERVER_REC *server = conn->context;
1481 SILC_CHANNEL_REC *chanrec;
1483 SILC_LOG_DEBUG(("Start"));
1486 case SILC_COMMAND_WHOIS:
1488 char buf[1024], *nickname, *username, *realname, nick[128 + 1];
1489 unsigned char *fingerprint;
1490 SilcUInt32 idle, mode, *user_modes;
1492 SilcClientEntry client_entry;
1495 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1496 /* Print the unknown nick for user */
1497 char *tmp = va_arg(vp, char *);
1499 silc_say_error("%s: %s", tmp, silc_get_status_message(status));
1501 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1502 /* Try to find the entry for the unknown client ID, since we
1503 might have, and print the nickname of it for user. */
1504 SilcClientID *id = va_arg(vp, SilcClientID *);
1506 client_entry = silc_client_get_client_by_id(client, conn, id);
1507 if (client_entry && client_entry->nickname[0])
1508 silc_say_error("%s: %s", client_entry->nickname,
1509 silc_get_status_message(status));
1510 silc_client_unref_client(client, conn, client_entry);
1513 } else if (SILC_STATUS_IS_ERROR(status)) {
1514 silc_say_error("WHOIS: %s", silc_get_status_message(status));
1518 client_entry = va_arg(vp, SilcClientEntry);
1519 nickname = va_arg(vp, char *);
1520 username = va_arg(vp, char *);
1521 realname = va_arg(vp, char *);
1522 channels = va_arg(vp, SilcDList);
1523 mode = va_arg(vp, SilcUInt32);
1524 idle = va_arg(vp, SilcUInt32);
1525 fingerprint = va_arg(vp, unsigned char *);
1526 user_modes = va_arg(vp, SilcUInt32 *);
1527 attrs = va_arg(vp, SilcDList);
1529 silc_parse_userfqdn(nickname, nick, sizeof(nick), NULL, 0);
1530 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1531 SILCTXT_WHOIS_USERINFO, nickname,
1532 client_entry->username, client_entry->hostname,
1533 nick, client_entry->nickname);
1534 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1535 SILCTXT_WHOIS_REALNAME, realname);
1537 if (channels && user_modes) {
1538 SilcChannelPayload entry;
1541 silc_dlist_start(channels);
1542 while ((entry = silc_dlist_get(channels))) {
1543 SilcUInt32 name_len;
1544 char *m = silc_client_chumode_char(user_modes[i++]);
1545 char *name = silc_channel_get_name(entry, &name_len);
1548 silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1549 silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1550 silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1554 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1555 SILCTXT_WHOIS_CHANNELS, buf);
1559 memset(buf, 0, sizeof(buf));
1560 silc_get_umode_string(mode, buf, sizeof(buf - 1));
1561 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1562 SILCTXT_WHOIS_MODES, buf);
1565 if (idle && nickname) {
1566 memset(buf, 0, sizeof(buf));
1567 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1568 idle > 60 ? (idle / 60) : idle,
1569 idle > 60 ? "minutes" : "seconds");
1571 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1572 SILCTXT_WHOIS_IDLE, buf);
1576 fingerprint = silc_fingerprint(fingerprint, 20);
1577 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1578 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1579 silc_free(fingerprint);
1583 silc_query_attributes_print(server, silc_client, conn, attrs,
1588 case SILC_COMMAND_WHOWAS:
1590 char *nickname, *username, *realname;
1592 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1593 char *tmp = va_arg(vp, char *);
1595 silc_say_error("%s: %s", tmp,
1596 silc_get_status_message(status));
1598 } else if (SILC_STATUS_IS_ERROR(status)) {
1599 silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1603 (void)va_arg(vp, SilcClientEntry);
1604 nickname = va_arg(vp, char *);
1605 username = va_arg(vp, char *);
1606 realname = va_arg(vp, char *);
1608 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1609 SILCTXT_WHOWAS_USERINFO, nickname, username,
1610 realname ? realname : "");
1614 case SILC_COMMAND_INVITE:
1616 SilcChannelEntry channel;
1617 SilcArgumentPayload invite_list;
1619 if (SILC_STATUS_IS_ERROR(status))
1622 channel = va_arg(vp, SilcChannelEntry);
1623 invite_list = va_arg(vp, SilcArgumentPayload);
1626 silc_parse_inviteban_list(client, conn, server, channel,
1627 "invite", invite_list);
1631 case SILC_COMMAND_JOIN:
1633 char *channel, *mode, *topic, *cipher, *hmac;
1635 SilcHashTableList *user_list;
1636 SilcChannelEntry channel_entry;
1637 SilcChannelUser chu;
1638 SilcClientEntry founder = NULL;
1641 if (SILC_STATUS_IS_ERROR(status))
1644 channel = va_arg(vp, char *);
1645 channel_entry = va_arg(vp, SilcChannelEntry);
1646 modei = va_arg(vp, SilcUInt32);
1647 user_list = va_arg(vp, SilcHashTableList *);
1648 topic = va_arg(vp, char *);
1649 cipher = va_arg(vp, char *);
1650 hmac = va_arg(vp, char *);
1652 chanrec = silc_channel_find(server, channel);
1654 chanrec = silc_channel_create(server, channel, channel, TRUE);
1657 char tmp[256], *cp, *dm = NULL;
1658 g_free_not_null(chanrec->topic);
1660 if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1661 memset(tmp, 0, sizeof(tmp));
1663 if (strlen(topic) > sizeof(tmp) - 1) {
1664 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1668 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1673 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1674 signal_emit("channel topic changed", 1, chanrec);
1679 mode = silc_client_chmode(modei, cipher ? cipher : "", hmac ? hmac : "");
1680 g_free_not_null(chanrec->mode);
1681 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1682 signal_emit("channel mode changed", 1, chanrec);
1685 while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
1686 if (!chu->client->nickname)
1688 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1689 founder = chu->client;
1690 silc_nicklist_insert(chanrec, chu, FALSE);
1693 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1694 nicklist_set_own(CHANNEL(chanrec), ownnick);
1695 signal_emit("channel joined", 1, chanrec);
1696 chanrec->entry = channel_entry;
1699 printformat_module("fe-common/silc", server,
1700 channel_entry->channel_name,
1701 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1702 channel_entry->channel_name, chanrec->topic);
1705 if (founder == conn->local_entry) {
1706 printformat_module("fe-common/silc",
1707 server, channel_entry->channel_name,
1708 MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER_YOU,
1709 channel_entry->channel_name);
1710 signal_emit("nick mode changed", 2, chanrec, ownnick);
1712 printformat_module("fe-common/silc",
1713 server, channel_entry->channel_name,
1714 MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER,
1715 channel_entry->channel_name, founder->nickname);
1721 case SILC_COMMAND_NICK:
1724 SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1727 if (SILC_STATUS_IS_ERROR(status))
1730 nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1731 if ((nicks != NULL) &&
1732 (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1734 SilcClientEntry collider, old;
1736 old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1737 collider = silc_client_get_client_by_id(client, conn, &old->id);
1738 if (collider != client_entry) {
1739 memset(buf, 0, sizeof(buf));
1740 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1741 collider->username, collider->hostname);
1742 nicklist_rename_unique(SERVER(server),
1744 collider, collider->nickname);
1745 silc_print_nick_change(server, collider->nickname,
1746 client_entry->nickname, buf);
1748 silc_client_unref_client(client, conn, collider);
1752 g_slist_free(nicks);
1754 old = g_strdup(server->nick);
1755 server_change_nick(SERVER(server), client_entry->nickname);
1756 nicklist_rename_unique(SERVER(server),
1757 server->conn->local_entry, server->nick,
1758 client_entry, client_entry->nickname);
1759 signal_emit("message own_nick", 4, server, server->nick, old, "");
1762 /* when connecting to a server, the last thing we receive
1763 is a SILC_COMMAND_LIST reply. Since we enable queueing
1764 during the connection, we can now safely disable it again */
1765 silc_queue_disable(conn);
1769 case SILC_COMMAND_LIST:
1774 char tmp[256], *cp, *dm = NULL;
1776 if (SILC_STATUS_IS_ERROR(status))
1779 (void)va_arg(vp, SilcChannelEntry);
1780 name = va_arg(vp, char *);
1781 topic = va_arg(vp, char *);
1782 usercount = va_arg(vp, int);
1784 if (topic && !silc_term_utf8() &&
1785 silc_utf8_valid(topic, strlen(topic))) {
1786 memset(tmp, 0, sizeof(tmp));
1788 if (strlen(topic) > sizeof(tmp) - 1) {
1789 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1793 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1798 if (status == SILC_STATUS_LIST_START ||
1799 status == SILC_STATUS_OK)
1800 printformat_module("fe-common/silc", server, NULL,
1801 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1804 snprintf(users, sizeof(users) - 1, "N/A");
1806 snprintf(users, sizeof(users) - 1, "%d", usercount);
1807 printformat_module("fe-common/silc", server, NULL,
1808 MSGLEVEL_CRAP, SILCTXT_LIST,
1809 name, users, topic ? topic : "");
1814 case SILC_COMMAND_UMODE:
1819 if (SILC_STATUS_IS_ERROR(status))
1822 mode = va_arg(vp, SilcUInt32);
1824 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1825 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1826 printformat_module("fe-common/silc", server, NULL,
1827 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1829 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1830 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1831 printformat_module("fe-common/silc", server, NULL,
1832 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1834 if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1835 if (mode & SILC_UMODE_GONE) {
1836 if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1837 reason = g_strdup(server->away_reason);
1839 reason = g_strdup("away");
1841 reason = g_strdup("");
1843 silc_set_away(reason, server);
1848 server->umode = mode;
1849 signal_emit("user mode changed", 2, server, NULL);
1853 case SILC_COMMAND_OPER:
1854 if (SILC_STATUS_IS_ERROR(status))
1857 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1858 signal_emit("user mode changed", 2, server, NULL);
1860 printformat_module("fe-common/silc", server, NULL,
1861 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1864 case SILC_COMMAND_SILCOPER:
1865 if (SILC_STATUS_IS_ERROR(status))
1868 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1869 signal_emit("user mode changed", 2, server, NULL);
1871 printformat_module("fe-common/silc", server, NULL,
1872 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1875 case SILC_COMMAND_USERS:
1877 SilcHashTableList htl;
1878 SilcChannelEntry channel;
1879 SilcChannelUser chu;
1881 if (SILC_STATUS_IS_ERROR(status))
1884 channel = va_arg(vp, SilcChannelEntry);
1886 printformat_module("fe-common/silc", server, channel->channel_name,
1887 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1888 channel->channel_name);
1890 silc_hash_table_list(channel->user_list, &htl);
1891 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1892 SilcClientEntry e = chu->client;
1893 char stat[5], *mode;
1898 memset(stat, 0, sizeof(stat));
1899 mode = silc_client_chumode_char(chu->mode);
1900 if (e->mode & SILC_UMODE_GONE)
1902 else if (e->mode & SILC_UMODE_INDISPOSED)
1904 else if (e->mode & SILC_UMODE_BUSY)
1906 else if (e->mode & SILC_UMODE_PAGE)
1908 else if (e->mode & SILC_UMODE_HYPER)
1910 else if (e->mode & SILC_UMODE_ROBOT)
1912 else if (e->mode & SILC_UMODE_ANONYMOUS)
1919 printformat_module("fe-common/silc", server, channel->channel_name,
1920 MSGLEVEL_CRAP, SILCTXT_USERS,
1922 e->username ? e->username : "",
1923 e->hostname ? e->hostname : "",
1924 e->realname ? e->realname : "");
1928 silc_hash_table_list_reset(&htl);
1932 case SILC_COMMAND_BAN:
1934 SilcChannelEntry channel;
1935 SilcArgumentPayload invite_list;
1937 if (SILC_STATUS_IS_ERROR(status))
1940 channel = va_arg(vp, SilcChannelEntry);
1941 invite_list = va_arg(vp, SilcArgumentPayload);
1944 silc_parse_inviteban_list(client, conn, server, channel,
1945 "ban", invite_list);
1949 case SILC_COMMAND_GETKEY:
1953 SilcPublicKey public_key;
1954 GetkeyContext getkey;
1957 if (SILC_STATUS_IS_ERROR(status))
1960 id_type = va_arg(vp, SilcUInt32);
1961 entry = va_arg(vp, void *);
1962 public_key = va_arg(vp, SilcPublicKey);
1965 getkey = silc_calloc(1, sizeof(*getkey));
1966 getkey->entry = entry;
1967 getkey->id_type = id_type;
1968 getkey->client = client;
1969 getkey->conn = conn;
1971 name = (id_type == SILC_ID_CLIENT ?
1972 ((SilcClientEntry)entry)->nickname :
1973 ((SilcServerEntry)entry)->server_name);
1975 silc_verify_public_key_internal(client, conn, name,
1976 (id_type == SILC_ID_CLIENT ?
1979 public_key, silc_getkey_cb, getkey);
1981 printformat_module("fe-common/silc", server, NULL,
1982 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1987 case SILC_COMMAND_INFO:
1989 SilcServerEntry server_entry;
1993 if (SILC_STATUS_IS_ERROR(status))
1996 server_entry = va_arg(vp, SilcServerEntry);
1997 server_name = va_arg(vp, char *);
1998 server_info = va_arg(vp, char *);
2000 if (server_name && server_info )
2002 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2003 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2008 case SILC_COMMAND_TOPIC:
2010 SilcChannelEntry channel;
2012 char tmp[256], *cp, *dm = NULL;
2014 if (SILC_STATUS_IS_ERROR(status))
2017 channel = va_arg(vp, SilcChannelEntry);
2018 topic = va_arg(vp, char *);
2020 if (topic && !silc_term_utf8() &&
2021 silc_utf8_valid(topic, strlen(topic))) {
2022 memset(tmp, 0, sizeof(tmp));
2024 if (strlen(topic) > sizeof(tmp) - 1) {
2025 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2029 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
2035 chanrec = silc_channel_find_entry(server, channel);
2037 g_free_not_null(chanrec->topic);
2038 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2039 signal_emit("channel topic changed", 1, chanrec);
2041 printformat_module("fe-common/silc", server, channel->channel_name,
2042 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2043 channel->channel_name, topic);
2045 printformat_module("fe-common/silc", server, channel->channel_name,
2046 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2047 channel->channel_name);
2053 case SILC_COMMAND_WATCH:
2056 case SILC_COMMAND_STATS:
2058 SilcClientStats *cstats;
2060 SilcBufferStruct buf;
2061 unsigned char *tmp_buf;
2063 const char *tmptime;
2064 int days, hours, mins, secs;
2066 if (SILC_STATUS_IS_ERROR(status))
2069 cstats = va_arg(vp, SilcClientStats *);
2071 printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2075 tmptime = silc_time_string(cstats->starttime);
2076 printformat_module("fe-common/silc", server, NULL,
2077 MSGLEVEL_CRAP, SILCTXT_STATS,
2078 "Local server start time", tmptime);
2080 days = cstats->uptime / (24 * 60 * 60);
2081 cstats->uptime -= days * (24 * 60 * 60);
2082 hours = cstats->uptime / (60 * 60);
2083 cstats->uptime -= hours * (60 * 60);
2084 mins = cstats->uptime / 60;
2085 cstats->uptime -= mins * 60;
2086 secs = cstats->uptime;
2087 snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2088 days, hours, mins, secs);
2089 printformat_module("fe-common/silc", server, NULL,
2090 MSGLEVEL_CRAP, SILCTXT_STATS,
2091 "Local server uptime", tmp);
2093 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_clients);
2094 printformat_module("fe-common/silc", server, NULL,
2095 MSGLEVEL_CRAP, SILCTXT_STATS,
2096 "Local server clients", tmp);
2098 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_channels);
2099 printformat_module("fe-common/silc", server, NULL,
2100 MSGLEVEL_CRAP, SILCTXT_STATS,
2101 "Local server channels", tmp);
2103 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_server_ops);
2104 printformat_module("fe-common/silc", server, NULL,
2105 MSGLEVEL_CRAP, SILCTXT_STATS,
2106 "Local server operators", tmp);
2108 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_router_ops);
2109 printformat_module("fe-common/silc", server, NULL,
2110 MSGLEVEL_CRAP, SILCTXT_STATS,
2111 "Local router operators", tmp);
2113 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_clients);
2114 printformat_module("fe-common/silc", server, NULL,
2115 MSGLEVEL_CRAP, SILCTXT_STATS,
2116 "Local cell clients", tmp);
2118 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_channels);
2119 printformat_module("fe-common/silc", server, NULL,
2120 MSGLEVEL_CRAP, SILCTXT_STATS,
2121 "Local cell channels", tmp);
2123 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_servers);
2124 printformat_module("fe-common/silc", server, NULL,
2125 MSGLEVEL_CRAP, SILCTXT_STATS,
2126 "Local cell servers", tmp);
2128 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->clients);
2129 printformat_module("fe-common/silc", server, NULL,
2130 MSGLEVEL_CRAP, SILCTXT_STATS,
2131 "Total clients", tmp);
2133 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->channels);
2134 printformat_module("fe-common/silc", server, NULL,
2135 MSGLEVEL_CRAP, SILCTXT_STATS,
2136 "Total channels", tmp);
2138 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->servers);
2139 printformat_module("fe-common/silc", server, NULL,
2140 MSGLEVEL_CRAP, SILCTXT_STATS,
2141 "Total servers", tmp);
2143 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->routers);
2144 printformat_module("fe-common/silc", server, NULL,
2145 MSGLEVEL_CRAP, SILCTXT_STATS,
2146 "Total routers", tmp);
2148 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->server_ops);
2149 printformat_module("fe-common/silc", server, NULL,
2150 MSGLEVEL_CRAP, SILCTXT_STATS,
2151 "Total server operators", tmp);
2153 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->router_ops);
2154 printformat_module("fe-common/silc", server, NULL,
2155 MSGLEVEL_CRAP, SILCTXT_STATS,
2156 "Total router operators", tmp);
2160 case SILC_COMMAND_CMODE:
2162 SilcChannelEntry channel_entry;
2165 channel_entry = va_arg(vp, SilcChannelEntry);
2166 (void)va_arg(vp, SilcUInt32);
2167 (void)va_arg(vp, SilcPublicKey);
2168 chpks = va_arg(vp, SilcDList);
2170 if (SILC_STATUS_IS_ERROR(status) || !cmode_list_chpks ||
2171 !channel_entry || !channel_entry->channel_name)
2174 /* Print the channel public key list */
2176 silc_parse_channel_public_keys(server, channel_entry, chpks);
2178 printformat_module("fe-common/silc", server, NULL,
2179 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2180 channel_entry->channel_name);
2185 case SILC_COMMAND_LEAVE:
2187 /* we might be cycling, so disable queueing again */
2188 silc_queue_disable(conn);
2197 SilcClientConnection conn;
2201 SilcPublicKey public_key;
2202 SilcVerifyPublicKey completion;
2206 static void verify_public_key_completion(const char *line, void *context)
2208 PublicKeyVerify verify = (PublicKeyVerify)context;
2210 if (line[0] == 'Y' || line[0] == 'y') {
2211 /* Call the completion */
2212 if (verify->completion)
2213 verify->completion(TRUE, verify->context);
2215 /* Save the key for future checking */
2216 silc_pkcs_save_public_key(verify->filename, verify->public_key,
2217 SILC_PKCS_FILE_BASE64);
2219 /* Call the completion */
2220 if (verify->completion)
2221 verify->completion(FALSE, verify->context);
2223 printformat_module("fe-common/silc", NULL, NULL,
2224 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2225 verify->entity_name ? verify->entity_name :
2229 silc_free(verify->filename);
2230 silc_free(verify->entity);
2231 silc_free(verify->entity_name);
2235 /* Internal routine to verify public key. If the `completion' is provided
2236 it will be called to indicate whether public was verified or not. For
2237 server/router public key this will check for filename that includes the
2238 remote host's IP address and remote host's hostname. */
2241 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2243 SilcConnectionType conn_type,
2244 SilcPublicKey public_key,
2245 SilcVerifyPublicKey completion, void *context)
2247 PublicKeyVerify verify;
2248 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2249 char *fingerprint, *babbleprint, *format;
2250 SilcPublicKey local_pubkey;
2252 const char *hostname, *ip;
2257 char *entity = ((conn_type == SILC_CONN_SERVER ||
2258 conn_type == SILC_CONN_ROUTER) ?
2259 "server" : "client");
2262 if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2263 printformat_module("fe-common/silc", NULL, NULL,
2264 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2265 entity, silc_pkcs_get_type(public_key));
2267 completion(FALSE, context);
2271 /* Encode public key */
2272 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2275 completion(FALSE, context);
2279 pw = getpwuid(getuid());
2282 completion(FALSE, context);
2286 memset(filename, 0, sizeof(filename));
2287 memset(filename2, 0, sizeof(filename2));
2288 memset(file, 0, sizeof(file));
2290 /* Get remote host information */
2291 silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2292 NULL, &hostname, &ip, &port);
2294 if (conn_type == SILC_CONN_SERVER ||
2295 conn_type == SILC_CONN_ROUTER) {
2297 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2298 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2299 get_irssi_dir(), entity, file);
2301 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2303 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2304 get_irssi_dir(), entity, file);
2309 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2311 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2312 get_irssi_dir(), entity, file);
2317 /* Replace all whitespaces with `_'. */
2318 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2319 for (i = 0; i < strlen(fingerprint); i++)
2320 if (fingerprint[i] == ' ')
2321 fingerprint[i] = '_';
2323 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2324 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2325 get_irssi_dir(), entity, file);
2326 silc_free(fingerprint);
2331 /* Take fingerprint of the public key */
2332 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2333 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2335 verify = silc_calloc(1, sizeof(*verify));
2336 verify->client = client;
2337 verify->conn = conn;
2338 verify->filename = strdup(ipf);
2339 verify->entity = strdup(entity);
2340 verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2341 (name ? strdup(name) : strdup(hostname))
2343 verify->public_key = public_key;
2344 verify->completion = completion;
2345 verify->context = context;
2347 /* Check whether this key already exists */
2348 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2349 /* Key does not exist, ask user to verify the key and save it */
2351 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2352 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2353 verify->entity_name : entity);
2354 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2355 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2356 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2357 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2358 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2359 SILCTXT_PUBKEY_ACCEPT);
2360 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2363 silc_free(fingerprint);
2366 /* The key already exists, verify it. */
2367 unsigned char *encpk;
2368 SilcUInt32 encpk_len;
2370 /* Load the key file, try for both IP filename and hostname filename */
2371 if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2372 (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2373 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2374 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2375 verify->entity_name : entity);
2376 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2377 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2378 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2379 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2380 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2381 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2382 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2383 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2384 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2387 silc_free(fingerprint);
2391 /* Encode the key data */
2392 encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2394 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2395 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2396 verify->entity_name : entity);
2397 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2398 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2399 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2400 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2401 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2402 SILCTXT_PUBKEY_MALFORMED, entity);
2403 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2404 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2405 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2408 silc_free(fingerprint);
2412 /* Compare the keys */
2413 if (memcmp(encpk, pk, encpk_len)) {
2414 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2415 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2416 verify->entity_name : entity);
2417 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2418 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2419 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2420 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2421 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2422 SILCTXT_PUBKEY_NO_MATCH, entity);
2423 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2424 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2425 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2426 SILCTXT_PUBKEY_MITM_ATTACK, entity);
2428 /* Ask user to verify the key and save it */
2429 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2430 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2431 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2434 silc_free(fingerprint);
2439 /* Local copy matched */
2441 completion(TRUE, context);
2443 silc_free(fingerprint);
2444 silc_free(verify->filename);
2445 silc_free(verify->entity);
2446 silc_free(verify->entity_name);
2451 /* Verifies received public key. The `conn_type' indicates which entity
2452 (server, client etc.) has sent the public key. If user decides to trust
2453 the key may be saved as trusted public key for later use. The
2454 `completion' must be called after the public key has been verified. */
2457 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2458 SilcConnectionType conn_type,
2459 SilcPublicKey public_key,
2460 SilcVerifyPublicKey completion, void *context)
2462 silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2463 completion, context);
2466 /* Asks passphrase from user on the input line. */
2469 SilcAskPassphrase completion;
2473 void ask_passphrase_completion(const char *passphrase, void *context)
2475 AskPassphrase p = (AskPassphrase)context;
2476 if (passphrase && passphrase[0] == '\0')
2478 p->completion((unsigned char *)passphrase,
2479 passphrase ? strlen(passphrase) : 0, p->context);
2483 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2484 SilcAskPassphrase completion, void *context)
2486 AskPassphrase p = silc_calloc(1, sizeof(*p));
2487 p->completion = completion;
2488 p->context = context;
2490 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2491 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2495 SilcGetAuthMeth completion;
2497 } *InternalGetAuthMethod;
2499 /* Callback called when we've received the authentication method information
2500 from the server after we've requested it. This will get the authentication
2501 data from the user if needed. */
2503 static void silc_get_auth_method_callback(SilcClient client,
2504 SilcClientConnection conn,
2505 SilcAuthMethod auth_meth,
2508 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2510 SILC_LOG_DEBUG(("Start"));
2512 switch (auth_meth) {
2513 case SILC_AUTH_NONE:
2514 /* No authentication required. */
2515 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2517 case SILC_AUTH_PASSWORD:
2519 /* Check whether we find the password for this server in our
2520 configuration. If not, then don't provide so library will ask
2521 it from the user. */
2522 SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2524 if (!setup || !setup->password) {
2525 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2529 (*internal->completion)(TRUE, auth_meth, setup->password,
2530 strlen(setup->password), internal->context);
2533 case SILC_AUTH_PUBLIC_KEY:
2534 /* Do not get the authentication data now, the library will generate
2535 it using our default key, if we do not provide it here. */
2536 /* XXX In the future when we support multiple local keys and multiple
2537 local certificates we will need to ask from user which one to use. */
2538 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2542 silc_free(internal);
2545 /* Find authentication method and authentication data by hostname and
2546 port. The hostname may be IP address as well. The found authentication
2547 method and authentication data is returned to `auth_meth', `auth_data'
2548 and `auth_data_len'. The function returns TRUE if authentication method
2549 is found and FALSE if not. `conn' may be NULL. */
2551 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2552 char *hostname, SilcUInt16 port,
2553 SilcGetAuthMeth completion, void *context)
2555 InternalGetAuthMethod internal;
2557 SILC_LOG_DEBUG(("Start"));
2559 /* If we do not have this connection configured by the user in a
2560 configuration file then resolve the authentication method from the
2561 server for this session. */
2562 internal = silc_calloc(1, sizeof(*internal));
2563 internal->completion = completion;
2564 internal->context = context;
2567 silc_client_request_authentication_method(client, conn,
2568 silc_get_auth_method_callback,
2571 completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
2575 /* Asks whether the user would like to perform the key agreement protocol.
2576 This is called after we have received an key agreement packet or an
2577 reply to our key agreement packet. This returns TRUE if the user wants
2578 the library to perform the key agreement protocol and FALSE if it is not
2579 desired (application may start it later by calling the function
2580 silc_client_perform_key_agreement). */
2582 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2583 SilcClientEntry client_entry, const char *hostname,
2584 SilcUInt16 port, SilcKeyAgreementCallback *completion,
2589 SILC_LOG_DEBUG(("Start"));
2591 /* We will just display the info on the screen and return FALSE and user
2592 will have to start the key agreement with a command. */
2595 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2598 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2599 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2601 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2602 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2603 client_entry->nickname, hostname, portstr);
2611 /* Notifies application that file transfer protocol session is being
2612 requested by the remote client indicated by the `client_entry' from
2613 the `hostname' and `port'. The `session_id' is the file transfer
2614 session and it can be used to either accept or reject the file
2615 transfer request, by calling the silc_client_file_receive or
2616 silc_client_file_close, respectively. */
2618 void silc_ftp(SilcClient client, SilcClientConnection conn,
2619 SilcClientEntry client_entry, SilcUInt32 session_id,
2620 const char *hostname, SilcUInt16 port)
2622 SILC_SERVER_REC *server;
2624 FtpSession ftp = NULL;
2626 SILC_LOG_DEBUG(("Start"));
2628 server = conn->context;
2630 silc_dlist_start(server->ftp_sessions);
2631 while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2632 if (ftp->client_entry == client_entry &&
2633 ftp->session_id == session_id) {
2634 server->current_session = ftp;
2638 if (ftp == SILC_LIST_END) {
2639 ftp = silc_calloc(1, sizeof(*ftp));
2640 ftp->client_entry = client_entry;
2641 ftp->session_id = session_id;
2644 silc_dlist_add(server->ftp_sessions, ftp);
2645 server->current_session = ftp;
2649 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2652 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2653 SILCTXT_FILE_REQUEST, client_entry->nickname);
2655 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2656 SILCTXT_FILE_REQUEST_HOST,
2657 client_entry->nickname, hostname, portstr);
2660 /* Delivers SILC session detachment data indicated by `detach_data' to the
2661 application. If application has issued SILC_COMMAND_DETACH command
2662 the client session in the SILC network is not quit. The client remains
2663 in the network but is detached. The detachment data may be used later
2664 to resume the session in the SILC Network. The appliation is
2665 responsible of saving the `detach_data', to for example in a file.
2667 The detachment data can be given as argument to the functions
2668 silc_client_connect_to_server, or silc_client_add_connection when
2669 creating connection to remote server, inside SilcClientConnectionParams
2670 structure. If it is provided the client library will attempt to resume
2671 the session in the network. After the connection is created
2672 successfully, the application is responsible of setting the user
2673 interface for user into the same state it was before detaching (showing
2674 same channels, channel modes, etc). It can do this by fetching the
2675 information (like joined channels) from the client library. */
2678 silc_detach(SilcClient client, SilcClientConnection conn,
2679 const unsigned char *detach_data, SilcUInt32 detach_data_len)
2681 SILC_SERVER_REC *server = conn->context;
2684 /* Save the detachment data to file. */
2686 file = silc_get_session_filename(server);
2687 silc_file_writefile(file, detach_data, detach_data_len);
2691 /* Called to indicate the client library is running. */
2694 silc_running(SilcClient client, void *application)
2696 SILC_LOG_DEBUG(("Client library is running"));
2699 /* SILC client operations */
2700 SilcClientOperations ops = {
2702 silc_channel_message,
2703 silc_private_message,
2707 silc_get_auth_method,
2708 silc_verify_public_key,
2709 silc_ask_passphrase,