5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2001 - 2003 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"
37 #include "fe-common/core/printtext.h"
38 #include "fe-common/core/fe-channels.h"
39 #include "fe-common/core/keyboard.h"
40 #include "fe-common/core/window-items.h"
41 #include "fe-common/silc/module-formats.h"
46 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
47 const char *name, SilcSocketType conn_type,
48 unsigned char *pk, SilcUInt32 pk_len,
49 SilcSKEPKType pk_type,
50 SilcVerifyPublicKey completion, void *context);
52 static void silc_get_umode_string(SilcUInt32 mode, char *buf,
55 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
56 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
57 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
59 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
60 "[SILC operator]" : "[unknown mode]");
62 if (mode & SILC_UMODE_GONE)
63 strcat(buf, " [away]");
64 if (mode & SILC_UMODE_INDISPOSED)
65 strcat(buf, " [indisposed]");
66 if (mode & SILC_UMODE_BUSY)
67 strcat(buf, " [busy]");
68 if (mode & SILC_UMODE_PAGE)
69 strcat(buf, " [page to reach]");
70 if (mode & SILC_UMODE_HYPER)
71 strcat(buf, " [hyper active]");
72 if (mode & SILC_UMODE_ROBOT)
73 strcat(buf, " [robot]");
74 if (mode & SILC_UMODE_ANONYMOUS)
75 strcat(buf, " [anonymous]");
76 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
77 strcat(buf, " [blocks private messages]");
78 if (mode & SILC_UMODE_DETACHED)
79 strcat(buf, " [detached]");
80 if (mode & SILC_UMODE_REJECT_WATCHING)
81 strcat(buf, " [rejects watching]");
82 if (mode & SILC_UMODE_BLOCK_INVITE)
83 strcat(buf, " [blocks invites]");
86 /* print "nick appears as" message to every channel of a server */
88 silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
89 const char *newnick, const char *oldnick,
92 if (ignore_check(SERVER(server), oldnick, address,
93 channel, newnick, MSGLEVEL_NICKS))
96 printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS,
97 SILCTXT_CHANNEL_APPEARS,
98 oldnick, newnick, channel, address);
102 silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
103 const char *oldnick, const char *address)
105 GSList *tmp, *windows;
107 /* Print to each channel/query where the nick is.
108 Don't print more than once to the same window. */
111 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
112 CHANNEL_REC *channel = tmp->data;
113 WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
115 if (nicklist_find(channel, newnick) == NULL ||
116 g_slist_find(windows, window) != NULL)
119 windows = g_slist_append(windows, window);
120 silc_print_nick_change_channel(server, channel->visible_name,
121 newnick, oldnick, address);
124 g_slist_free(windows);
127 static void silc_parse_channel_public_keys(SILC_SERVER_REC *server,
128 SilcChannelEntry channel_entry,
129 SilcBuffer channel_pubkeys)
132 SilcArgumentPayload chpks;
134 SilcUInt32 pk_len, type;
136 char *fingerprint, *babbleprint;
137 SilcPublicKey pubkey;
138 SilcPublicKeyIdentifier ident;
140 printformat_module("fe-common/silc", server, NULL,
141 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST,
142 channel_entry->channel_name);
144 SILC_GET16_MSB(argc, channel_pubkeys->data);
145 chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
146 channel_pubkeys->len - 2, argc);
150 pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
152 fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
153 babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
154 silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
155 ident = silc_pkcs_decode_identifier(pubkey->identifier);
157 printformat_module("fe-common/silc", server, NULL,
158 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST_ENTRY,
159 c++, channel_entry->channel_name,
160 type == 0x00 ? "Added" : "Removed",
161 ident->realname ? ident->realname : "",
162 fingerprint, babbleprint);
164 silc_free(fingerprint);
165 silc_free(babbleprint);
166 silc_pkcs_public_key_free(pubkey);
167 silc_pkcs_free_identifier(ident);
168 pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
171 silc_argument_payload_free(chpks);
174 void silc_say(SilcClient client, SilcClientConnection conn,
175 SilcClientMessageType type, char *msg, ...)
177 SILC_SERVER_REC *server;
181 server = conn == NULL ? NULL : conn->context;
184 str = g_strdup_vprintf(msg, va);
185 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
190 void silc_say_error(char *msg, ...)
196 str = g_strdup_vprintf(msg, va);
197 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
203 /* try to verify a message using locally stored public key data */
204 int verify_message_signature(SilcClientEntry sender,
205 SilcMessageSignedPayload sig,
206 SilcMessagePayload message)
209 char file[256], filename[256];
210 char *fingerprint, *fingerprint2;
211 unsigned char *pk_data;
212 SilcUInt32 pk_datalen;
214 int ret = SILC_MSG_SIGNED_VERIFIED, i;
217 return SILC_MSG_SIGNED_UNKNOWN;
219 /* get public key from the signature payload and compare it with the
220 one stored in the client entry */
221 pk = silc_message_signed_get_public_key(sig, &pk_data, &pk_datalen);
224 fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen);
226 if (sender->fingerprint) {
227 fingerprint2 = silc_fingerprint(sender->fingerprint,
228 sender->fingerprint_len);
229 if (strcmp(fingerprint, fingerprint2)) {
230 /* since the public key differs from the senders public key, the
231 verification _failed_ */
232 silc_pkcs_public_key_free(pk);
233 silc_free(fingerprint);
234 ret = SILC_MSG_SIGNED_UNKNOWN;
236 silc_free(fingerprint2);
238 } else if (sender->fingerprint)
239 fingerprint = silc_fingerprint(sender->fingerprint,
240 sender->fingerprint_len);
242 /* no idea, who or what signed that message ... */
243 return SILC_MSG_SIGNED_UNKNOWN;
245 /* search our local client key cache */
246 for (i = 0; i < strlen(fingerprint); i++)
247 if (fingerprint[i] == ' ')
248 fingerprint[i] = '_';
250 snprintf(file, sizeof(file) - 1, "clientkey_%s.pub", fingerprint);
251 snprintf(filename, sizeof(filename) - 1, "%s/clientkeys/%s",
252 get_irssi_dir(), file);
253 silc_free(fingerprint);
255 if (stat(filename, &st) < 0)
256 /* we don't have the public key cached ... use the one from the sig */
257 ret = SILC_MSG_SIGNED_UNKNOWN;
259 SilcPublicKey cached_pk=NULL;
261 /* try to load the file */
262 if (!silc_pkcs_load_public_key(filename, &cached_pk, SILC_PKCS_FILE_PEM) &&
263 !silc_pkcs_load_public_key(filename, &cached_pk,
264 SILC_PKCS_FILE_BIN)) {
265 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
266 SILCTXT_PUBKEY_COULD_NOT_LOAD, "client");
268 return SILC_MSG_SIGNED_UNKNOWN;
270 ret = SILC_MSG_SIGNED_UNKNOWN;
275 silc_pkcs_public_key_free(pk);
280 /* the public key is now in pk, our "level of trust" in ret */
281 if ((pk) && silc_message_signed_verify(sig, message, pk,
282 silc_client->sha1hash)!= SILC_AUTH_OK)
283 ret = SILC_MSG_SIGNED_FAILED;
286 silc_pkcs_public_key_free(pk);
291 char * silc_unescape_data(const char *escaped_data, SilcUInt32 *length)
294 int i = 0, j = 0, len = strlen(escaped_data);
296 data = silc_calloc(len, sizeof(char));
299 ptr = memchr(escaped_data + i, 1, len - i);
301 int inc = (ptr - escaped_data) - i;
302 memcpy(data + j, escaped_data + i, inc);
305 data[j++] = *(ptr + 1) - 1;
307 memcpy(data + j, escaped_data + i, len - i);
317 char * silc_escape_data(const char *data, SilcUInt32 len)
319 char *escaped_data, *ptr, *ptr0, *ptr1;
322 escaped_data = silc_calloc(2 * len, sizeof(char));
325 ptr0 = memchr(data + i, 0, len - i);
326 ptr1 = memchr(data + i, 1, len - i);
328 ptr = (ptr0 < ptr1 ? (ptr0 ? ptr0 : ptr1) : (ptr1 ? ptr1 : ptr0));
331 int inc = (ptr - data) - i;
333 memcpy(escaped_data + j, data + i, inc);
336 escaped_data[j++] = 1;
337 escaped_data[j++] = *(data + i++) + 1;
339 memcpy(escaped_data + j, data + i, len - i);
348 void silc_emit_mime_sig(SILC_SERVER_REC *server, WI_ITEM_REC *item,
349 const char *data, SilcUInt32 data_len, const char *nick,
354 escaped_data = silc_escape_data(data, data_len);
356 signal_emit("mime", 5, server, item, escaped_data, nick, verified);
358 silc_free(escaped_data);
362 /* Message for a channel. The `sender' is the nickname of the sender
363 received in the packet. The `channel_name' is the name of the channel. */
365 void silc_channel_message(SilcClient client, SilcClientConnection conn,
366 SilcClientEntry sender, SilcChannelEntry channel,
367 SilcMessagePayload payload,
368 SilcMessageFlags flags, const unsigned char *message,
369 SilcUInt32 message_len)
371 SILC_SERVER_REC *server;
373 SILC_CHANNEL_REC *chanrec;
376 SILC_LOG_DEBUG(("Start"));
381 server = conn == NULL ? NULL : conn->context;
382 chanrec = silc_channel_find_entry(server, channel);
386 nick = silc_nicklist_find(chanrec, sender);
388 /* We didn't find client but it clearly exists, add it. */
389 SilcChannelUser chu = silc_client_on_channel(channel, sender);
391 nick = silc_nicklist_insert(chanrec, chu, FALSE);
394 /* If the messages is digitally signed, verify it, if possible. */
395 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
396 if (!settings_get_bool("ignore_message_signatures")) {
397 SilcMessageSignedPayload sig = silc_message_get_signature(payload);
398 verified = verify_message_signature(sender, sig, payload);
400 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
404 if (flags & SILC_MESSAGE_FLAG_DATA) {
405 silc_emit_mime_sig(server, (WI_ITEM_REC *)chanrec, message, message_len,
406 nick == NULL ? NULL : nick->nick,
407 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
414 /* FIXME: replace those printformat calls with signals and add signature
415 information to them (if present) */
416 if (flags & SILC_MESSAGE_FLAG_ACTION)
417 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
418 char tmp[256], *cp, *dm = NULL;
419 memset(tmp, 0, sizeof(tmp));
421 if(message_len > sizeof(tmp) - 1) {
422 dm = silc_calloc(message_len + 1, sizeof(*dm));
425 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
427 printformat_module("fe-common/silc", server, channel->channel_name,
428 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
429 nick == NULL ? "[<unknown>]" : nick->nick, cp);
432 printformat_module("fe-common/silc", server, channel->channel_name,
433 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
434 nick == NULL ? "[<unknown>]" : nick->nick,
437 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
438 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
439 char tmp[256], *cp, *dm = NULL;
440 memset(tmp, 0, sizeof(tmp));
442 if(message_len > sizeof(tmp) - 1) {
443 dm = silc_calloc(message_len + 1, sizeof(*dm));
446 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
448 printformat_module("fe-common/silc", server, channel->channel_name,
449 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
450 nick == NULL ? "[<unknown>]" : nick->nick, cp);
453 printformat_module("fe-common/silc", server, channel->channel_name,
454 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
455 nick == NULL ? "[<unknown>]" : nick->nick,
459 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
460 char tmp[256], *cp, *dm = NULL;
462 memset(tmp, 0, sizeof(tmp));
464 if (message_len > sizeof(tmp) - 1) {
465 dm = silc_calloc(message_len + 1, sizeof(*dm));
469 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
471 if (flags & SILC_MESSAGE_FLAG_SIGNED)
472 signal_emit("message signed_public", 6, server, cp,
473 nick == NULL ? "[<unknown>]" : nick->nick,
474 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
475 chanrec->name, verified);
477 signal_emit("message public", 6, server, cp,
478 nick == NULL ? "[<unknown>]" : nick->nick,
479 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
480 chanrec->name, nick);
485 if (flags & SILC_MESSAGE_FLAG_SIGNED)
486 signal_emit("message signed_public", 6, server, message,
487 nick == NULL ? "[<unknown>]" : nick->nick,
488 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
489 chanrec->name, verified);
491 signal_emit("message public", 6, server, message,
492 nick == NULL ? "[<unknown>]" : nick->nick,
493 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
494 chanrec->name, nick);
498 /* Private message to the client. The `sender' is the nickname of the
499 sender received in the packet. */
501 void silc_private_message(SilcClient client, SilcClientConnection conn,
502 SilcClientEntry sender, SilcMessagePayload payload,
503 SilcMessageFlags flags,
504 const unsigned char *message,
505 SilcUInt32 message_len)
507 SILC_SERVER_REC *server;
511 SILC_LOG_DEBUG(("Start"));
513 server = conn == NULL ? NULL : conn->context;
514 memset(userhost, 0, sizeof(userhost));
515 if (sender->username)
516 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
517 sender->username, sender->hostname);
519 /* If the messages is digitally signed, verify it, if possible. */
520 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
521 if (!settings_get_bool("ignore_message_signatures")) {
522 SilcMessageSignedPayload sig = silc_message_get_signature(payload);
523 verified = verify_message_signature(sender, sig, payload);
525 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
529 if (flags & SILC_MESSAGE_FLAG_DATA) {
530 silc_emit_mime_sig(server,
532 (WI_ITEM_REC *)query_find(SERVER(server), sender->nickname) :
534 message, message_len,
535 sender->nickname ? sender->nickname : "[<unknown>]",
536 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
543 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
544 char tmp[256], *cp, *dm = NULL;
546 memset(tmp, 0, sizeof(tmp));
548 if (message_len > sizeof(tmp) - 1) {
549 dm = silc_calloc(message_len + 1, sizeof(*dm));
553 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
555 if (flags & SILC_MESSAGE_FLAG_SIGNED)
556 signal_emit("message signed_private", 5, server, cp,
557 sender->nickname ? sender->nickname : "[<unknown>]",
558 sender->username ? userhost : NULL, verified);
560 signal_emit("message private", 4, server, cp,
561 sender->nickname ? sender->nickname : "[<unknown>]",
562 sender->username ? userhost : NULL);
567 if (flags & SILC_MESSAGE_FLAG_SIGNED)
568 signal_emit("message signed_private", 5, server, message,
569 sender->nickname ? sender->nickname : "[<unknown>]",
570 sender->username ? userhost : NULL, verified);
572 signal_emit("message private", 4, server, message,
573 sender->nickname ? sender->nickname : "[<unknown>]",
574 sender->username ? userhost : NULL);
577 /* Notify message to the client. The notify arguments are sent in the
578 same order as servers sends them. The arguments are same as received
579 from the server except for ID's. If ID is received application receives
580 the corresponding entry to the ID. For example, if Client ID is received
581 application receives SilcClientEntry. Also, if the notify type is
582 for channel the channel entry is sent to application (even if server
583 does not send it). */
585 void silc_notify(SilcClient client, SilcClientConnection conn,
586 SilcNotifyType type, ...)
589 SILC_SERVER_REC *server;
590 SILC_CHANNEL_REC *chanrec;
591 SILC_NICK_REC *nickrec;
592 SilcClientEntry client_entry, client_entry2;
593 SilcChannelEntry channel, channel2;
594 SilcServerEntry server_entry;
600 GSList *list1, *list_tmp;
603 SILC_LOG_DEBUG(("Start"));
607 server = conn == NULL ? NULL : conn->context;
610 case SILC_NOTIFY_TYPE_NONE:
611 /* Some generic notice from server */
612 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
615 case SILC_NOTIFY_TYPE_INVITE:
617 * Invited or modified invite list.
620 SILC_LOG_DEBUG(("Notify: INVITE"));
622 channel = va_arg(va, SilcChannelEntry);
623 name = va_arg(va, char *);
624 client_entry = va_arg(va, SilcClientEntry);
626 memset(buf, 0, sizeof(buf));
627 snprintf(buf, sizeof(buf) - 1, "%s@%s",
628 client_entry->username, client_entry->hostname);
629 signal_emit("message invite", 4, server, channel ? channel->channel_name :
630 name, client_entry->nickname, buf);
633 case SILC_NOTIFY_TYPE_JOIN:
638 SILC_LOG_DEBUG(("Notify: JOIN"));
640 client_entry = va_arg(va, SilcClientEntry);
641 channel = va_arg(va, SilcChannelEntry);
643 if (client_entry == server->conn->local_entry) {
644 /* You joined to channel */
645 chanrec = silc_channel_find(server, channel->channel_name);
646 if (chanrec != NULL && !chanrec->joined)
647 chanrec->entry = channel;
649 chanrec = silc_channel_find_entry(server, channel);
650 if (chanrec != NULL) {
651 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
653 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
657 memset(buf, 0, sizeof(buf));
658 if (client_entry->username)
659 snprintf(buf, sizeof(buf) - 1, "%s@%s",
660 client_entry->username, client_entry->hostname);
661 signal_emit("message join", 4, server, channel->channel_name,
662 client_entry->nickname,
663 client_entry->username == NULL ? "" : buf);
666 case SILC_NOTIFY_TYPE_LEAVE:
671 SILC_LOG_DEBUG(("Notify: LEAVE"));
673 client_entry = va_arg(va, SilcClientEntry);
674 channel = va_arg(va, SilcChannelEntry);
676 memset(buf, 0, sizeof(buf));
677 if (client_entry->username)
678 snprintf(buf, sizeof(buf) - 1, "%s@%s",
679 client_entry->username, client_entry->hostname);
680 signal_emit("message part", 5, server, channel->channel_name,
681 client_entry->nickname, client_entry->username ?
682 buf : "", client_entry->nickname);
684 chanrec = silc_channel_find_entry(server, channel);
685 if (chanrec != NULL) {
686 nickrec = silc_nicklist_find(chanrec, client_entry);
688 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
692 case SILC_NOTIFY_TYPE_SIGNOFF:
697 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
699 client_entry = va_arg(va, SilcClientEntry);
700 tmp = va_arg(va, char *);
702 silc_server_free_ftp(server, client_entry);
704 /* Print only if we have the nickname. If this cliente has just quit
705 when we were only resolving it, it is possible we don't have the
707 if (client_entry->nickname) {
708 memset(buf, 0, sizeof(buf));
709 if (client_entry->username)
710 snprintf(buf, sizeof(buf) - 1, "%s@%s",
711 client_entry->username, client_entry->hostname);
712 signal_emit("message quit", 4, server, client_entry->nickname,
713 client_entry->username ? buf : "",
717 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
718 for (list_tmp = list1; list_tmp != NULL; list_tmp =
719 list_tmp->next->next) {
720 CHANNEL_REC *channel = list_tmp->data;
721 NICK_REC *nickrec = list_tmp->next->data;
723 nicklist_remove(channel, nickrec);
727 case SILC_NOTIFY_TYPE_TOPIC_SET:
732 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
734 idtype = va_arg(va, int);
735 entry = va_arg(va, void *);
736 tmp = va_arg(va, char *);
737 channel = va_arg(va, SilcChannelEntry);
739 chanrec = silc_channel_find_entry(server, channel);
740 if (chanrec != NULL) {
741 char tmp2[256], *cp, *dm = NULL;
743 g_free_not_null(chanrec->topic);
744 if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) {
745 memset(tmp2, 0, sizeof(tmp2));
747 if (strlen(tmp) > sizeof(tmp2) - 1) {
748 dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm));
752 silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE,
757 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
758 signal_emit("channel topic changed", 1, chanrec);
763 if (idtype == SILC_ID_CLIENT) {
764 client_entry = (SilcClientEntry)entry;
765 memset(buf, 0, sizeof(buf));
766 snprintf(buf, sizeof(buf) - 1, "%s@%s",
767 client_entry->username, client_entry->hostname);
768 signal_emit("message topic", 5, server, channel->channel_name,
769 tmp, client_entry->nickname, buf);
770 } else if (idtype == SILC_ID_SERVER) {
771 server_entry = (SilcServerEntry)entry;
772 signal_emit("message topic", 5, server, channel->channel_name,
773 tmp, server_entry->server_name,
774 server_entry->server_name);
775 } else if (idtype == SILC_ID_CHANNEL) {
776 channel = (SilcChannelEntry)entry;
777 signal_emit("message topic", 5, server, channel->channel_name,
778 tmp, channel->channel_name, channel->channel_name);
782 case SILC_NOTIFY_TYPE_NICK_CHANGE:
787 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
789 client_entry = va_arg(va, SilcClientEntry);
790 client_entry2 = va_arg(va, SilcClientEntry);
792 if (!strcmp(client_entry->nickname, client_entry2->nickname))
795 memset(buf, 0, sizeof(buf));
796 snprintf(buf, sizeof(buf) - 1, "%s@%s",
797 client_entry2->username, client_entry2->hostname);
798 nicklist_rename_unique(SERVER(server),
799 client_entry, client_entry->nickname,
800 client_entry2, client_entry2->nickname);
801 signal_emit("message nick", 4, server, client_entry2->nickname,
802 client_entry->nickname, buf);
805 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
807 * Changed channel mode.
810 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
812 idtype = va_arg(va, int);
813 entry = va_arg(va, void *);
814 mode = va_arg(va, SilcUInt32);
815 (void)va_arg(va, char *); /* cipher */
816 (void)va_arg(va, char *); /* hmac */
817 (void)va_arg(va, char *); /* passphrase */
818 (void)va_arg(va, SilcPublicKey); /* founder key */
819 buffer = va_arg(va, SilcBuffer); /* channel public keys */
820 channel = va_arg(va, SilcChannelEntry);
822 tmp = silc_client_chmode(mode,
823 channel->channel_key ?
824 silc_cipher_get_name(channel->channel_key) : "",
826 silc_hmac_get_name(channel->hmac) : "");
828 chanrec = silc_channel_find_entry(server, channel);
829 if (chanrec != NULL) {
830 g_free_not_null(chanrec->mode);
831 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
832 signal_emit("channel mode changed", 1, chanrec);
835 if (idtype == SILC_ID_CLIENT) {
836 client_entry = (SilcClientEntry)entry;
837 printformat_module("fe-common/silc", server, channel->channel_name,
838 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
839 channel->channel_name, tmp ? tmp : "removed all",
840 client_entry->nickname);
841 } else if (idtype == SILC_ID_SERVER) {
842 server_entry = (SilcServerEntry)entry;
843 printformat_module("fe-common/silc", server, channel->channel_name,
844 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
845 channel->channel_name, tmp ? tmp : "removed all",
846 server_entry->server_name);
847 } else if (idtype == SILC_ID_CHANNEL) {
848 channel2 = (SilcChannelEntry)entry;
849 printformat_module("fe-common/silc", server, channel->channel_name,
850 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
851 channel->channel_name, tmp ? tmp : "removed all",
852 channel2->channel_name);
855 /* Print the channel public key list */
857 silc_parse_channel_public_keys(server, channel, buffer);
862 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
864 * Changed user's mode on channel.
867 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
869 idtype = va_arg(va, int);
870 entry = va_arg(va, void *);
871 mode = va_arg(va, SilcUInt32);
872 client_entry2 = va_arg(va, SilcClientEntry);
873 channel = va_arg(va, SilcChannelEntry);
875 tmp = silc_client_chumode(mode);
876 chanrec = silc_channel_find_entry(server, channel);
877 if (chanrec != NULL) {
880 if (client_entry2 == server->conn->local_entry)
881 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
883 nick = silc_nicklist_find(chanrec, client_entry2);
885 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
886 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
887 signal_emit("nick mode changed", 2, chanrec, nick);
891 if (idtype == SILC_ID_CLIENT) {
892 client_entry = (SilcClientEntry)entry;
893 printformat_module("fe-common/silc", server, channel->channel_name,
894 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
895 channel->channel_name, client_entry2->nickname,
896 tmp ? tmp : "removed all",
897 client_entry->nickname);
898 } else if (idtype == SILC_ID_SERVER) {
899 server_entry = (SilcServerEntry)entry;
900 printformat_module("fe-common/silc", server, channel->channel_name,
901 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
902 channel->channel_name, client_entry2->nickname,
903 tmp ? tmp : "removed all",
904 server_entry->server_name);
905 } else if (idtype == SILC_ID_CHANNEL) {
906 channel2 = (SilcChannelEntry)entry;
907 printformat_module("fe-common/silc", server, channel->channel_name,
908 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
909 channel->channel_name, client_entry2->nickname,
910 tmp ? tmp : "removed all",
911 channel2->channel_name);
914 if (mode & SILC_CHANNEL_UMODE_CHANFO)
915 printformat_module("fe-common/silc",
916 server, channel->channel_name, MSGLEVEL_CRAP,
917 SILCTXT_CHANNEL_FOUNDER,
918 channel->channel_name, client_entry2->nickname);
920 if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
921 printformat_module("fe-common/silc",
922 server, channel->channel_name, MSGLEVEL_CRAP,
923 SILCTXT_CHANNEL_QUIETED, channel->channel_name);
928 case SILC_NOTIFY_TYPE_MOTD:
933 SILC_LOG_DEBUG(("Notify: MOTD"));
935 tmp = va_arg(va, char *);
937 if (!settings_get_bool("skip_motd"))
938 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
941 case SILC_NOTIFY_TYPE_KICKED:
943 * Someone was kicked from channel.
946 SILC_LOG_DEBUG(("Notify: KICKED"));
948 client_entry = va_arg(va, SilcClientEntry);
949 tmp = va_arg(va, char *);
950 client_entry2 = va_arg(va, SilcClientEntry);
951 channel = va_arg(va, SilcChannelEntry);
953 chanrec = silc_channel_find_entry(server, channel);
955 if (client_entry == conn->local_entry) {
956 printformat_module("fe-common/silc", server, channel->channel_name,
957 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
958 channel->channel_name,
959 client_entry ? client_entry2->nickname : "",
962 chanrec->kicked = TRUE;
963 channel_destroy((CHANNEL_REC *)chanrec);
966 printformat_module("fe-common/silc", server, channel->channel_name,
967 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
968 client_entry->nickname, channel->channel_name,
969 client_entry2 ? client_entry2->nickname : "",
973 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
975 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
980 case SILC_NOTIFY_TYPE_KILLED:
982 * Someone was killed from the network.
985 SILC_LOG_DEBUG(("Notify: KILLED"));
987 client_entry = va_arg(va, SilcClientEntry);
988 tmp = va_arg(va, char *);
989 idtype = va_arg(va, int);
990 entry = va_arg(va, SilcClientEntry);
992 if (client_entry == conn->local_entry) {
993 if (idtype == SILC_ID_CLIENT) {
994 client_entry2 = (SilcClientEntry)entry;
995 printformat_module("fe-common/silc", server, NULL,
996 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
997 client_entry2 ? client_entry2->nickname : "",
999 } else if (idtype == SILC_ID_SERVER) {
1000 server_entry = (SilcServerEntry)entry;
1001 printformat_module("fe-common/silc", server, NULL,
1002 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1003 server_entry->server_name, tmp ? tmp : "");
1004 } else if (idtype == SILC_ID_CHANNEL) {
1005 channel = (SilcChannelEntry)entry;
1006 printformat_module("fe-common/silc", server, NULL,
1007 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1008 channel->channel_name, tmp ? tmp : "");
1011 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1012 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1013 list_tmp->next->next) {
1014 CHANNEL_REC *channel = list_tmp->data;
1015 NICK_REC *nickrec = list_tmp->next->data;
1016 nicklist_remove(channel, nickrec);
1019 if (idtype == SILC_ID_CLIENT) {
1020 client_entry2 = (SilcClientEntry)entry;
1021 printformat_module("fe-common/silc", server, NULL,
1022 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1023 client_entry->nickname,
1024 client_entry2 ? client_entry2->nickname : "",
1026 } else if (idtype == SILC_ID_SERVER) {
1027 server_entry = (SilcServerEntry)entry;
1028 printformat_module("fe-common/silc", server, NULL,
1029 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1030 client_entry->nickname,
1031 server_entry->server_name, tmp ? tmp : "");
1032 } else if (idtype == SILC_ID_CHANNEL) {
1033 channel = (SilcChannelEntry)entry;
1034 printformat_module("fe-common/silc", server, NULL,
1035 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1036 client_entry->nickname,
1037 channel->channel_name, tmp ? tmp : "");
1042 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
1045 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1048 * Server has quit the network.
1051 SilcClientEntry *clients;
1052 SilcUInt32 clients_count;
1054 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1056 (void)va_arg(va, void *);
1057 clients = va_arg(va, SilcClientEntry *);
1058 clients_count = va_arg(va, SilcUInt32);
1060 for (i = 0; i < clients_count; i++) {
1061 memset(buf, 0, sizeof(buf));
1063 /* Print only if we have the nickname. If this client has just quit
1064 when we were only resolving it, it is possible we don't have the
1066 if (clients[i]->nickname) {
1067 if (clients[i]->username)
1068 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1069 clients[i]->username, clients[i]->hostname);
1070 signal_emit("message quit", 4, server, clients[i]->nickname,
1071 clients[i]->username ? buf : "",
1075 silc_server_free_ftp(server, clients[i]);
1077 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
1078 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1079 list_tmp->next->next) {
1080 CHANNEL_REC *channel = list_tmp->data;
1081 NICK_REC *nickrec = list_tmp->next->data;
1082 nicklist_remove(channel, nickrec);
1088 case SILC_NOTIFY_TYPE_ERROR:
1090 SilcStatus error = va_arg(va, int);
1092 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1093 "%s", silc_get_status_message(error));
1097 case SILC_NOTIFY_TYPE_WATCH:
1099 SilcNotifyType notify;
1101 client_entry = va_arg(va, SilcClientEntry);
1102 name = va_arg(va, char *); /* Maybe NULL */
1103 mode = va_arg(va, SilcUInt32);
1104 notify = va_arg(va, int);
1106 if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1108 printformat_module("fe-common/silc", server, NULL,
1109 MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1110 client_entry->nickname, name);
1112 printformat_module("fe-common/silc", server, NULL,
1113 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1114 client_entry->nickname);
1115 } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1116 /* See if client was away and is now present */
1117 if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1118 SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1119 SILC_UMODE_DETACHED)) &&
1120 (client_entry->mode & SILC_UMODE_GONE ||
1121 client_entry->mode & SILC_UMODE_INDISPOSED ||
1122 client_entry->mode & SILC_UMODE_BUSY ||
1123 client_entry->mode & SILC_UMODE_PAGE ||
1124 client_entry->mode & SILC_UMODE_DETACHED)) {
1125 printformat_module("fe-common/silc", server, NULL,
1126 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1127 client_entry->nickname);
1131 memset(buf, 0, sizeof(buf));
1132 silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1133 printformat_module("fe-common/silc", server, NULL,
1134 MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1135 client_entry->nickname, buf);
1137 } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1138 printformat_module("fe-common/silc", server, NULL,
1139 MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1140 client_entry->nickname);
1141 } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1142 notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1143 printformat_module("fe-common/silc", server, NULL,
1144 MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1145 client_entry->nickname);
1146 } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1147 /* Client logged in to the network */
1148 printformat_module("fe-common/silc", server, NULL,
1149 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1150 client_entry->nickname);
1156 /* Unknown notify */
1157 printformat_module("fe-common/silc", server, NULL,
1158 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1165 /* Called to indicate that connection was either successfully established
1166 or connecting failed. This is also the first time application receives
1167 the SilcClientConnection object which it should save somewhere. */
1169 void silc_connect(SilcClient client, SilcClientConnection conn,
1170 SilcClientConnectionStatus status)
1172 SILC_SERVER_REC *server = conn->context;
1174 if (!server || server->disconnected) {
1175 silc_client_close_connection(client, conn);
1180 case SILC_CLIENT_CONN_SUCCESS:
1181 /* We have successfully connected to server */
1182 server->connected = TRUE;
1183 signal_emit("event connected", 1, server);
1186 case SILC_CLIENT_CONN_SUCCESS_RESUME:
1187 /* We have successfully resumed old detached session */
1188 server->connected = TRUE;
1189 signal_emit("event connected", 1, server);
1191 /* If we resumed old session check whether we need to update
1193 if (strcmp(server->nick, conn->local_entry->nickname)) {
1195 old = g_strdup(server->nick);
1196 server_change_nick(SERVER(server), conn->local_entry->nickname);
1197 nicklist_rename_unique(SERVER(server),
1198 conn->local_entry, server->nick,
1199 conn->local_entry, conn->local_entry->nickname);
1200 signal_emit("message own_nick", 4, server, server->nick, old, "");
1206 server->connection_lost = TRUE;
1208 server->conn->context = NULL;
1209 server_disconnect(SERVER(server));
1214 /* Called to indicate that connection was disconnected to the server. */
1216 void silc_disconnect(SilcClient client, SilcClientConnection conn,
1217 SilcStatus status, const char *message)
1219 SILC_SERVER_REC *server = conn->context;
1221 SILC_LOG_DEBUG(("Start"));
1223 if (!server || server->connection_lost)
1226 if (server->conn && server->conn->local_entry) {
1227 nicklist_rename_unique(SERVER(server),
1228 server->conn->local_entry, server->nick,
1229 server->conn->local_entry,
1230 silc_client->username);
1231 silc_change_nick(server, silc_client->username);
1235 silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
1236 "Server closed connection: %s (%d) %s",
1237 silc_get_status_message(status), status,
1238 message ? message : "");
1241 server->conn->context = NULL;
1242 server->conn = NULL;
1243 server->connection_lost = TRUE;
1244 server_disconnect(SERVER(server));
1247 /* Command handler. This function is called always in the command function.
1248 If error occurs it will be called as well. `conn' is the associated
1249 client connection. `cmd_context' is the command context that was
1250 originally sent to the command. `success' is FALSE if error occured
1251 during command. `command' is the command being processed. It must be
1252 noted that this is not reply from server. This is merely called just
1253 after application has called the command. Just to tell application
1254 that the command really was processed. */
1256 static bool cmode_list_chpks = FALSE;
1258 void silc_command(SilcClient client, SilcClientConnection conn,
1259 SilcClientCommandContext cmd_context, bool success,
1260 SilcCommand command, SilcStatus status)
1262 SILC_SERVER_REC *server = conn->context;
1264 SILC_LOG_DEBUG(("Start"));
1267 silc_say_error("%s", silc_get_status_message(status));
1273 case SILC_COMMAND_INVITE:
1274 if (cmd_context->argc > 2)
1275 printformat_module("fe-common/silc", server, NULL,
1276 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1277 cmd_context->argv[2],
1278 (cmd_context->argv[1][0] == '*' ?
1279 (char *)conn->current_channel->channel_name :
1280 (char *)cmd_context->argv[1]));
1283 case SILC_COMMAND_DETACH:
1284 server->no_reconnect = TRUE;
1287 case SILC_COMMAND_CMODE:
1288 if (cmd_context->argc == 3 &&
1289 !strcmp(cmd_context->argv[2], "+C"))
1290 cmode_list_chpks = TRUE;
1292 cmode_list_chpks = FALSE;
1301 SilcChannelEntry channel;
1305 /* Client info resolving callback when JOIN command reply is received.
1306 This will cache all users on the channel. */
1308 static void silc_client_join_get_users(SilcClient client,
1309 SilcClientConnection conn,
1310 SilcClientEntry *clients,
1311 SilcUInt32 clients_count,
1314 SilcJoinResolve r = context;
1315 SilcChannelEntry channel = r->channel;
1316 SilcHashTableList htl;
1317 SilcChannelUser chu;
1318 SILC_SERVER_REC *server = conn->context;
1319 SILC_CHANNEL_REC *chanrec;
1320 SilcClientEntry founder = NULL;
1323 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
1324 silc_hash_table_count(channel->user_list)));
1326 if (!clients && r->retry < 1) {
1327 /* Retry to resolve */
1329 silc_client_get_clients_by_channel(client, conn, channel,
1330 silc_client_join_get_users, context);
1334 chanrec = silc_channel_find(server, channel->channel_name);
1335 if (chanrec == NULL)
1338 silc_hash_table_list(channel->user_list, &htl);
1339 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1340 if (!chu->client->nickname)
1342 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1343 founder = chu->client;
1344 silc_nicklist_insert(chanrec, chu, FALSE);
1346 silc_hash_table_list_reset(&htl);
1348 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1349 nicklist_set_own(CHANNEL(chanrec), ownnick);
1350 signal_emit("channel joined", 1, chanrec);
1351 chanrec->entry = channel;
1354 printformat_module("fe-common/silc", server, channel->channel_name,
1355 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1356 channel->channel_name, chanrec->topic);
1359 if (founder == conn->local_entry) {
1360 printformat_module("fe-common/silc",
1361 server, channel->channel_name, MSGLEVEL_CRAP,
1362 SILCTXT_CHANNEL_FOUNDER_YOU,
1363 channel->channel_name);
1364 signal_emit("nick mode changed", 2, chanrec, ownnick);
1366 printformat_module("fe-common/silc",
1367 server, channel->channel_name, MSGLEVEL_CRAP,
1368 SILCTXT_CHANNEL_FOUNDER,
1369 channel->channel_name, founder->nickname);
1375 SilcClientConnection conn;
1381 void silc_getkey_cb(bool success, void *context)
1383 GetkeyContext getkey = (GetkeyContext)context;
1384 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1385 char *name = (getkey->id_type == SILC_ID_CLIENT ?
1386 ((SilcClientEntry)getkey->entry)->nickname :
1387 ((SilcServerEntry)getkey->entry)->server_name);
1390 printformat_module("fe-common/silc", NULL, NULL,
1391 MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1393 printformat_module("fe-common/silc", NULL, NULL,
1394 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1398 silc_free(getkey->fingerprint);
1402 /* Parse an invite or ban list */
1403 void silc_parse_inviteban_list(SilcClient client,
1404 SilcClientConnection conn,
1405 SILC_SERVER_REC *server,
1406 SilcChannelEntry channel,
1407 const char *list_type,
1408 SilcArgumentPayload list)
1411 SilcUInt32 type, len;
1412 SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1413 int counter=0, resolving = FALSE;
1415 if (!silc_argument_get_arg_num(list)) {
1416 printformat_module("fe-common/silc", server,
1417 (chanrec ? chanrec->visible_name : NULL),
1418 MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1419 channel->channel_name, list_type);
1423 printformat_module("fe-common/silc", server,
1424 (chanrec ? chanrec->visible_name : NULL),
1425 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1426 channel->channel_name, list_type);
1428 /* parse the list */
1429 tmp = silc_argument_get_first_arg(list, &type, &len);
1434 /* an invite string */
1438 if (tmp[len-1] == ',')
1441 list = g_strsplit(tmp, ",", -1);
1443 printformat_module("fe-common/silc", server,
1444 (chanrec ? chanrec->visible_name : NULL),
1445 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1446 ++counter, channel->channel_name, list_type,
1455 char *fingerprint, *babbleprint;
1457 /* tmp is Public Key Payload, take public key from it. */
1458 fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1459 babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1461 printformat_module("fe-common/silc", server,
1462 (chanrec ? chanrec->visible_name : NULL),
1463 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1464 ++counter, channel->channel_name, list_type,
1465 fingerprint, babbleprint);
1472 SilcClientID *client_id;
1473 SilcClientEntry client_entry;
1475 client_id = silc_id_payload_parse_id(tmp, len, NULL);
1477 if (client_id == NULL) {
1478 silc_say_error("Invalid data in %s list encountered", list_type);
1482 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1485 printformat_module("fe-common/silc", server,
1486 (chanrec ? chanrec->visible_name : NULL),
1487 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1488 ++counter, channel->channel_name, list_type,
1489 client_entry->nickname);
1492 silc_client_get_client_by_id_resolve(client, conn, client_id,
1496 silc_free(client_id);
1502 silc_say_error("Unkown type in %s list: %u (len %u)",
1503 list_type, type, len);
1505 tmp = silc_argument_get_next_arg(list, &type, &len);
1509 printformat_module("fe-common/silc", server,
1510 (chanrec ? chanrec->visible_name : NULL),
1511 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1512 list_type, channel->channel_name);
1515 /* Command reply handler. This function is called always in the command reply
1516 function. If error occurs it will be called as well. Normal scenario
1517 is that it will be called after the received command data has been parsed
1518 and processed. The function is used to pass the received command data to
1521 `conn' is the associated client connection. `cmd_payload' is the command
1522 payload data received from server and it can be ignored. It is provided
1523 if the application would like to re-parse the received command data,
1524 however, it must be noted that the data is parsed already by the library
1525 thus the payload can be ignored. `success' is FALSE if error occured.
1526 In this case arguments are not sent to the application. `command' is the
1527 command reply being processed. The function has variable argument list
1528 and each command defines the number and type of arguments it passes to the
1529 application (on error they are not sent). */
1532 silc_command_reply(SilcClient client, SilcClientConnection conn,
1533 SilcCommandPayload cmd_payload, bool success,
1534 SilcCommand command, SilcStatus status, ...)
1537 SILC_SERVER_REC *server = conn->context;
1538 SILC_CHANNEL_REC *chanrec;
1541 va_start(vp, status);
1543 SILC_LOG_DEBUG(("Start"));
1546 case SILC_COMMAND_WHOIS:
1548 char buf[1024], *nickname, *username, *realname, *nick;
1549 unsigned char *fingerprint;
1550 SilcUInt32 idle, mode;
1551 SilcBuffer channels, user_modes;
1552 SilcClientEntry client_entry;
1555 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1556 /* Print the unknown nick for user */
1557 unsigned char *tmp =
1558 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1561 silc_say_error("%s: %s", tmp,
1562 silc_get_status_message(status));
1564 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1565 /* Try to find the entry for the unknown client ID, since we
1566 might have, and print the nickname of it for user. */
1568 unsigned char *tmp =
1569 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1572 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1575 client_entry = silc_client_get_client_by_id(client, conn,
1577 if (client_entry && client_entry->nickname)
1578 silc_say_error("%s: %s", client_entry->nickname,
1579 silc_get_status_message(status));
1580 silc_free(client_id);
1584 } else if (!success) {
1585 silc_say_error("WHOIS: %s", silc_get_status_message(status));
1589 client_entry = va_arg(vp, SilcClientEntry);
1590 nickname = va_arg(vp, char *);
1591 username = va_arg(vp, char *);
1592 realname = va_arg(vp, char *);
1593 channels = va_arg(vp, SilcBuffer);
1594 mode = va_arg(vp, SilcUInt32);
1595 idle = va_arg(vp, SilcUInt32);
1596 fingerprint = va_arg(vp, unsigned char *);
1597 user_modes = va_arg(vp, SilcBuffer);
1598 attrs = va_arg(vp, SilcDList);
1600 silc_parse_userfqdn(nickname, &nick, NULL);
1601 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1602 SILCTXT_WHOIS_USERINFO, nickname,
1603 client_entry->username, client_entry->hostname,
1604 nick, client_entry->nickname);
1605 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1606 SILCTXT_WHOIS_REALNAME, realname);
1609 if (channels && user_modes) {
1611 SilcDList list = silc_channel_payload_parse_list(channels->data,
1613 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1615 SilcChannelPayload entry;
1618 memset(buf, 0, sizeof(buf));
1619 silc_dlist_start(list);
1620 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1621 SilcUInt32 name_len;
1622 char *m = silc_client_chumode_char(umodes[i++]);
1623 char *name = silc_channel_get_name(entry, &name_len);
1626 silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1627 silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1628 silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1632 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1633 SILCTXT_WHOIS_CHANNELS, buf);
1634 silc_channel_payload_list_free(list);
1640 memset(buf, 0, sizeof(buf));
1641 silc_get_umode_string(mode, buf, sizeof(buf - 1));
1642 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1643 SILCTXT_WHOIS_MODES, buf);
1646 if (idle && nickname) {
1647 memset(buf, 0, sizeof(buf));
1648 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1649 idle > 60 ? (idle / 60) : idle,
1650 idle > 60 ? "minutes" : "seconds");
1652 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1653 SILCTXT_WHOIS_IDLE, buf);
1657 fingerprint = silc_fingerprint(fingerprint, 20);
1658 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1659 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1660 silc_free(fingerprint);
1664 silc_query_attributes_print(server, silc_client, conn, attrs,
1669 case SILC_COMMAND_IDENTIFY:
1671 SilcClientEntry client_entry;
1673 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1674 /* Print the unknown nick for user */
1675 unsigned char *tmp =
1676 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1679 silc_say_error("%s: %s", tmp,
1680 silc_get_status_message(status));
1682 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1683 /* Try to find the entry for the unknown client ID, since we
1684 might have, and print the nickname of it for user. */
1686 unsigned char *tmp =
1687 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1690 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1693 client_entry = silc_client_get_client_by_id(client, conn,
1695 if (client_entry && client_entry->nickname)
1696 silc_say_error("%s: %s", client_entry->nickname,
1697 silc_get_status_message(status));
1698 silc_free(client_id);
1707 case SILC_COMMAND_WHOWAS:
1709 char *nickname, *username, *realname;
1711 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1712 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1714 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1717 silc_say_error("%s: %s", tmp,
1718 silc_get_status_message(status));
1720 } else if (!success) {
1721 silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1725 (void)va_arg(vp, SilcClientEntry);
1726 nickname = va_arg(vp, char *);
1727 username = va_arg(vp, char *);
1728 realname = va_arg(vp, char *);
1730 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1731 SILCTXT_WHOWAS_USERINFO, nickname, username,
1732 realname ? realname : "");
1736 case SILC_COMMAND_INVITE:
1738 SilcChannelEntry channel;
1740 SilcArgumentPayload invite_list;
1746 channel = va_arg(vp, SilcChannelEntry);
1747 payload = va_arg(vp, SilcBuffer);
1750 SILC_GET16_MSB(argc, payload->data);
1751 invite_list = silc_argument_payload_parse(payload->data + 2,
1752 payload->len - 2, argc);
1754 silc_parse_inviteban_list(client, conn, server, channel,
1755 "invite", invite_list);
1756 silc_argument_payload_free(invite_list);
1762 case SILC_COMMAND_JOIN:
1764 char *channel, *mode, *topic;
1766 SilcChannelEntry channel_entry;
1767 SilcBuffer client_id_list;
1768 SilcUInt32 list_count;
1773 channel = va_arg(vp, char *);
1774 channel_entry = va_arg(vp, SilcChannelEntry);
1775 modei = va_arg(vp, SilcUInt32);
1776 (void)va_arg(vp, SilcUInt32);
1777 (void)va_arg(vp, unsigned char *);
1778 (void)va_arg(vp, unsigned char *);
1779 (void)va_arg(vp, unsigned char *);
1780 topic = va_arg(vp, char *);
1781 (void)va_arg(vp, unsigned char *);
1782 list_count = va_arg(vp, SilcUInt32);
1783 client_id_list = va_arg(vp, SilcBuffer);
1785 chanrec = silc_channel_find(server, channel);
1787 chanrec = silc_channel_create(server, channel, channel, TRUE);
1790 char tmp[256], *cp, *dm = NULL;
1791 g_free_not_null(chanrec->topic);
1793 if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1794 memset(tmp, 0, sizeof(tmp));
1796 if (strlen(topic) > sizeof(tmp) - 1) {
1797 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1801 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1806 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1807 signal_emit("channel topic changed", 1, chanrec);
1812 mode = silc_client_chmode(modei,
1813 channel_entry->channel_key ?
1814 silc_cipher_get_name(channel_entry->
1816 channel_entry->hmac ?
1817 silc_hmac_get_name(channel_entry->hmac) : "");
1818 g_free_not_null(chanrec->mode);
1819 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1820 signal_emit("channel mode changed", 1, chanrec);
1822 /* Resolve the client information */
1824 SilcJoinResolve r = silc_calloc(1, sizeof(*r));
1825 r->channel = channel_entry;
1826 silc_client_get_clients_by_list(client, conn, list_count,
1828 silc_client_join_get_users, r);
1834 case SILC_COMMAND_NICK:
1837 SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1843 nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1844 if ((nicks != NULL) &&
1845 (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1847 SilcClientEntry collider, old;
1849 old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1850 collider = silc_client_get_client_by_id(client, conn,
1853 if (collider != client_entry) {
1855 memset(buf, 0, sizeof(buf));
1856 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1857 collider->username, collider->hostname);
1858 nicklist_rename_unique(SERVER(server),
1860 collider, collider->nickname);
1861 silc_print_nick_change(server, collider->nickname,
1862 client_entry->nickname, buf);
1867 g_slist_free(nicks);
1869 old = g_strdup(server->nick);
1870 server_change_nick(SERVER(server), client_entry->nickname);
1871 nicklist_rename_unique(SERVER(server),
1872 server->conn->local_entry, server->nick,
1873 client_entry, client_entry->nickname);
1874 signal_emit("message own_nick", 4, server, server->nick, old, "");
1879 case SILC_COMMAND_LIST:
1884 char tmp[256], *cp, *dm = NULL;
1889 (void)va_arg(vp, SilcChannelEntry);
1890 name = va_arg(vp, char *);
1891 topic = va_arg(vp, char *);
1892 usercount = va_arg(vp, int);
1894 if (topic && !silc_term_utf8() &&
1895 silc_utf8_valid(topic, strlen(topic))) {
1896 memset(tmp, 0, sizeof(tmp));
1898 if (strlen(topic) > sizeof(tmp) - 1) {
1899 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1903 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1908 if (status == SILC_STATUS_LIST_START ||
1909 status == SILC_STATUS_OK)
1910 printformat_module("fe-common/silc", server, NULL,
1911 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1914 snprintf(users, sizeof(users) - 1, "N/A");
1916 snprintf(users, sizeof(users) - 1, "%d", usercount);
1917 printformat_module("fe-common/silc", server, NULL,
1918 MSGLEVEL_CRAP, SILCTXT_LIST,
1919 name, users, topic ? topic : "");
1924 case SILC_COMMAND_UMODE:
1932 mode = va_arg(vp, SilcUInt32);
1934 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1935 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1936 printformat_module("fe-common/silc", server, NULL,
1937 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1939 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1940 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1941 printformat_module("fe-common/silc", server, NULL,
1942 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1944 if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1945 if (mode & SILC_UMODE_GONE) {
1946 if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1947 reason = g_strdup(server->away_reason);
1949 reason = g_strdup("away");
1951 reason = g_strdup("");
1953 silc_set_away(reason, server);
1958 server->umode = mode;
1959 signal_emit("user mode changed", 2, server, NULL);
1963 case SILC_COMMAND_OPER:
1967 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1968 signal_emit("user mode changed", 2, server, NULL);
1970 printformat_module("fe-common/silc", server, NULL,
1971 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1974 case SILC_COMMAND_SILCOPER:
1978 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1979 signal_emit("user mode changed", 2, server, NULL);
1981 printformat_module("fe-common/silc", server, NULL,
1982 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1985 case SILC_COMMAND_USERS:
1987 SilcHashTableList htl;
1988 SilcChannelEntry channel;
1989 SilcChannelUser chu;
1994 channel = va_arg(vp, SilcChannelEntry);
1996 printformat_module("fe-common/silc", server, channel->channel_name,
1997 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1998 channel->channel_name);
2000 silc_hash_table_list(channel->user_list, &htl);
2001 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
2002 SilcClientEntry e = chu->client;
2003 char stat[5], *mode;
2008 memset(stat, 0, sizeof(stat));
2009 mode = silc_client_chumode_char(chu->mode);
2010 if (e->mode & SILC_UMODE_GONE)
2012 else if (e->mode & SILC_UMODE_INDISPOSED)
2014 else if (e->mode & SILC_UMODE_BUSY)
2016 else if (e->mode & SILC_UMODE_PAGE)
2018 else if (e->mode & SILC_UMODE_HYPER)
2020 else if (e->mode & SILC_UMODE_ROBOT)
2022 else if (e->mode & SILC_UMODE_ANONYMOUS)
2029 printformat_module("fe-common/silc", server, channel->channel_name,
2030 MSGLEVEL_CRAP, SILCTXT_USERS,
2032 e->username ? e->username : "",
2033 e->hostname ? e->hostname : "",
2034 e->realname ? e->realname : "");
2038 silc_hash_table_list_reset(&htl);
2042 case SILC_COMMAND_BAN:
2044 SilcChannelEntry channel;
2046 SilcArgumentPayload ban_list;
2052 channel = va_arg(vp, SilcChannelEntry);
2053 payload = va_arg(vp, SilcBuffer);
2056 SILC_GET16_MSB(argc, payload->data);
2057 ban_list = silc_argument_payload_parse(payload->data + 2,
2058 payload->len - 2, argc);
2060 silc_parse_inviteban_list(client, conn, server, channel,
2062 silc_argument_payload_free(ban_list);
2068 case SILC_COMMAND_GETKEY:
2072 SilcPublicKey public_key;
2075 GetkeyContext getkey;
2081 id_type = va_arg(vp, SilcUInt32);
2082 entry = va_arg(vp, void *);
2083 public_key = va_arg(vp, SilcPublicKey);
2086 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2088 getkey = silc_calloc(1, sizeof(*getkey));
2089 getkey->entry = entry;
2090 getkey->id_type = id_type;
2091 getkey->client = client;
2092 getkey->conn = conn;
2093 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2095 name = (id_type == SILC_ID_CLIENT ?
2096 ((SilcClientEntry)entry)->nickname :
2097 ((SilcServerEntry)entry)->server_name);
2099 silc_verify_public_key_internal(client, conn, name,
2100 (id_type == SILC_ID_CLIENT ?
2101 SILC_SOCKET_TYPE_CLIENT :
2102 SILC_SOCKET_TYPE_SERVER),
2103 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
2104 silc_getkey_cb, getkey);
2107 printformat_module("fe-common/silc", server, NULL,
2108 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2113 case SILC_COMMAND_INFO:
2115 SilcServerEntry server_entry;
2122 server_entry = va_arg(vp, SilcServerEntry);
2123 server_name = va_arg(vp, char *);
2124 server_info = va_arg(vp, char *);
2126 if (server_name && server_info )
2128 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2129 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2134 case SILC_COMMAND_TOPIC:
2136 SilcChannelEntry channel;
2138 char tmp[256], *cp, *dm = NULL;
2143 channel = va_arg(vp, SilcChannelEntry);
2144 topic = va_arg(vp, char *);
2146 if (topic && !silc_term_utf8() &&
2147 silc_utf8_valid(topic, strlen(topic))) {
2148 memset(tmp, 0, sizeof(tmp));
2150 if (strlen(topic) > sizeof(tmp) - 1) {
2151 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2155 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
2161 chanrec = silc_channel_find_entry(server, channel);
2163 g_free_not_null(chanrec->topic);
2164 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2165 signal_emit("channel topic changed", 1, chanrec);
2167 printformat_module("fe-common/silc", server, channel->channel_name,
2168 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2169 channel->channel_name, topic);
2171 printformat_module("fe-common/silc", server, channel->channel_name,
2172 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2173 channel->channel_name);
2179 case SILC_COMMAND_WATCH:
2182 case SILC_COMMAND_STATS:
2184 SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2185 my_router_ops, cell_clients, cell_channels, cell_servers,
2186 clients, channels, servers, routers, server_ops, router_ops;
2188 SilcBufferStruct buf;
2189 unsigned char *tmp_buf;
2191 const char *tmptime;
2192 int days, hours, mins, secs;
2197 tmp_buf = va_arg(vp, unsigned char *);
2198 buf_len = va_arg(vp, SilcUInt32);
2200 if (!tmp_buf || !buf_len) {
2201 printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2205 /* Get statistics structure */
2206 silc_buffer_set(&buf, tmp_buf, buf_len);
2207 silc_buffer_unformat(&buf,
2208 SILC_STR_UI_INT(&starttime),
2209 SILC_STR_UI_INT(&uptime),
2210 SILC_STR_UI_INT(&my_clients),
2211 SILC_STR_UI_INT(&my_channels),
2212 SILC_STR_UI_INT(&my_server_ops),
2213 SILC_STR_UI_INT(&my_router_ops),
2214 SILC_STR_UI_INT(&cell_clients),
2215 SILC_STR_UI_INT(&cell_channels),
2216 SILC_STR_UI_INT(&cell_servers),
2217 SILC_STR_UI_INT(&clients),
2218 SILC_STR_UI_INT(&channels),
2219 SILC_STR_UI_INT(&servers),
2220 SILC_STR_UI_INT(&routers),
2221 SILC_STR_UI_INT(&server_ops),
2222 SILC_STR_UI_INT(&router_ops),
2225 tmptime = silc_get_time(starttime);
2226 printformat_module("fe-common/silc", server, NULL,
2227 MSGLEVEL_CRAP, SILCTXT_STATS,
2228 "Local server start time", tmptime);
2230 days = uptime / (24 * 60 * 60);
2231 uptime -= days * (24 * 60 * 60);
2232 hours = uptime / (60 * 60);
2233 uptime -= hours * (60 * 60);
2235 uptime -= mins * 60;
2237 snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2238 days, hours, mins, secs);
2239 printformat_module("fe-common/silc", server, NULL,
2240 MSGLEVEL_CRAP, SILCTXT_STATS,
2241 "Local server uptime", tmp);
2243 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2244 printformat_module("fe-common/silc", server, NULL,
2245 MSGLEVEL_CRAP, SILCTXT_STATS,
2246 "Local server clients", tmp);
2248 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2249 printformat_module("fe-common/silc", server, NULL,
2250 MSGLEVEL_CRAP, SILCTXT_STATS,
2251 "Local server channels", tmp);
2253 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2254 printformat_module("fe-common/silc", server, NULL,
2255 MSGLEVEL_CRAP, SILCTXT_STATS,
2256 "Local server operators", tmp);
2258 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2259 printformat_module("fe-common/silc", server, NULL,
2260 MSGLEVEL_CRAP, SILCTXT_STATS,
2261 "Local router operators", tmp);
2263 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2264 printformat_module("fe-common/silc", server, NULL,
2265 MSGLEVEL_CRAP, SILCTXT_STATS,
2266 "Local cell clients", tmp);
2268 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2269 printformat_module("fe-common/silc", server, NULL,
2270 MSGLEVEL_CRAP, SILCTXT_STATS,
2271 "Local cell channels", tmp);
2273 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2274 printformat_module("fe-common/silc", server, NULL,
2275 MSGLEVEL_CRAP, SILCTXT_STATS,
2276 "Local cell servers", tmp);
2278 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2279 printformat_module("fe-common/silc", server, NULL,
2280 MSGLEVEL_CRAP, SILCTXT_STATS,
2281 "Total clients", tmp);
2283 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2284 printformat_module("fe-common/silc", server, NULL,
2285 MSGLEVEL_CRAP, SILCTXT_STATS,
2286 "Total channels", tmp);
2288 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2289 printformat_module("fe-common/silc", server, NULL,
2290 MSGLEVEL_CRAP, SILCTXT_STATS,
2291 "Total servers", tmp);
2293 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2294 printformat_module("fe-common/silc", server, NULL,
2295 MSGLEVEL_CRAP, SILCTXT_STATS,
2296 "Total routers", tmp);
2298 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2299 printformat_module("fe-common/silc", server, NULL,
2300 MSGLEVEL_CRAP, SILCTXT_STATS,
2301 "Total server operators", tmp);
2303 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2304 printformat_module("fe-common/silc", server, NULL,
2305 MSGLEVEL_CRAP, SILCTXT_STATS,
2306 "Total router operators", tmp);
2310 case SILC_COMMAND_CMODE:
2312 SilcChannelEntry channel_entry;
2313 SilcBuffer channel_pubkeys;
2315 channel_entry = va_arg(vp, SilcChannelEntry);
2316 (void)va_arg(vp, SilcUInt32);
2317 (void)va_arg(vp, SilcPublicKey);
2318 channel_pubkeys = va_arg(vp, SilcBuffer);
2320 if (!success || !cmode_list_chpks ||
2321 !channel_entry || !channel_entry->channel_name)
2324 /* Print the channel public key list */
2325 if (channel_pubkeys)
2326 silc_parse_channel_public_keys(server, channel_entry, channel_pubkeys);
2328 printformat_module("fe-common/silc", server, NULL,
2329 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2330 channel_entry->channel_name);
2341 SilcClientConnection conn;
2347 SilcSKEPKType pk_type;
2348 SilcVerifyPublicKey completion;
2352 static void verify_public_key_completion(const char *line, void *context)
2354 PublicKeyVerify verify = (PublicKeyVerify)context;
2356 if (line[0] == 'Y' || line[0] == 'y') {
2357 /* Call the completion */
2358 if (verify->completion)
2359 verify->completion(TRUE, verify->context);
2361 /* Save the key for future checking */
2362 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
2363 verify->pk_len, SILC_PKCS_FILE_PEM);
2365 /* Call the completion */
2366 if (verify->completion)
2367 verify->completion(FALSE, verify->context);
2369 printformat_module("fe-common/silc", NULL, NULL,
2370 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2371 verify->entity_name ? verify->entity_name :
2375 silc_free(verify->filename);
2376 silc_free(verify->entity);
2377 silc_free(verify->entity_name);
2378 silc_free(verify->pk);
2382 /* Internal routine to verify public key. If the `completion' is provided
2383 it will be called to indicate whether public was verified or not. For
2384 server/router public key this will check for filename that includes the
2385 remote host's IP address and remote host's hostname. */
2388 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2389 const char *name, SilcSocketType conn_type,
2390 unsigned char *pk, SilcUInt32 pk_len,
2391 SilcSKEPKType pk_type,
2392 SilcVerifyPublicKey completion, void *context)
2395 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2396 char *fingerprint, *babbleprint, *format;
2399 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2400 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
2401 "server" : "client");
2402 PublicKeyVerify verify;
2404 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2405 printformat_module("fe-common/silc", NULL, NULL,
2406 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2409 completion(FALSE, context);
2413 pw = getpwuid(getuid());
2416 completion(FALSE, context);
2420 memset(filename, 0, sizeof(filename));
2421 memset(filename2, 0, sizeof(filename2));
2422 memset(file, 0, sizeof(file));
2424 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2425 conn_type == SILC_SOCKET_TYPE_ROUTER) {
2427 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2428 conn->sock->ip, conn->sock->port);
2429 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2430 get_irssi_dir(), entity, file);
2432 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2433 conn->sock->hostname, conn->sock->port);
2434 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2435 get_irssi_dir(), entity, file);
2440 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2441 name, conn->sock->port);
2442 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2443 get_irssi_dir(), entity, file);
2448 /* Replace all whitespaces with `_'. */
2449 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2450 for (i = 0; i < strlen(fingerprint); i++)
2451 if (fingerprint[i] == ' ')
2452 fingerprint[i] = '_';
2454 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2455 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2456 get_irssi_dir(), entity, file);
2457 silc_free(fingerprint);
2462 /* Take fingerprint of the public key */
2463 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2464 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2466 verify = silc_calloc(1, sizeof(*verify));
2467 verify->client = client;
2468 verify->conn = conn;
2469 verify->filename = strdup(ipf);
2470 verify->entity = strdup(entity);
2471 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2472 (name ? strdup(name) : strdup(conn->sock->hostname))
2474 verify->pk = silc_memdup(pk, pk_len);
2475 verify->pk_len = pk_len;
2476 verify->pk_type = pk_type;
2477 verify->completion = completion;
2478 verify->context = context;
2480 /* Check whether this key already exists */
2481 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2482 /* Key does not exist, ask user to verify the key and save it */
2484 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2485 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2486 verify->entity_name : entity);
2487 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2488 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2489 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2490 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2491 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2492 SILCTXT_PUBKEY_ACCEPT);
2493 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2496 silc_free(fingerprint);
2499 /* The key already exists, verify it. */
2500 SilcPublicKey public_key;
2501 unsigned char *encpk;
2502 SilcUInt32 encpk_len;
2504 /* Load the key file, try for both IP filename and hostname filename */
2505 if (!silc_pkcs_load_public_key(ipf, &public_key,
2506 SILC_PKCS_FILE_PEM) &&
2507 !silc_pkcs_load_public_key(ipf, &public_key,
2508 SILC_PKCS_FILE_BIN) &&
2509 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
2510 SILC_PKCS_FILE_PEM) &&
2511 !silc_pkcs_load_public_key(hostf, &public_key,
2512 SILC_PKCS_FILE_BIN)))) {
2513 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2514 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2515 verify->entity_name : entity);
2516 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2517 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2518 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2519 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2520 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2521 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2522 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2523 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2524 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2527 silc_free(fingerprint);
2531 /* Encode the key data */
2532 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2534 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2535 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2536 verify->entity_name : entity);
2537 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2538 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2539 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2540 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2541 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2542 SILCTXT_PUBKEY_MALFORMED, entity);
2543 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2544 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2545 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2548 silc_free(fingerprint);
2552 /* Compare the keys */
2553 if (memcmp(encpk, pk, encpk_len)) {
2554 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2555 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2556 verify->entity_name : entity);
2557 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2558 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2559 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2560 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2561 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2562 SILCTXT_PUBKEY_NO_MATCH, entity);
2563 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2564 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2565 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2566 SILCTXT_PUBKEY_MITM_ATTACK, entity);
2568 /* Ask user to verify the key and save it */
2569 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2570 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2571 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2574 silc_free(fingerprint);
2578 /* Local copy matched */
2580 completion(TRUE, context);
2581 silc_free(fingerprint);
2582 silc_free(verify->filename);
2583 silc_free(verify->entity);
2584 silc_free(verify->entity_name);
2585 silc_free(verify->pk);
2590 /* Verifies received public key. The `conn_type' indicates which entity
2591 (server, client etc.) has sent the public key. If user decides to trust
2592 the key may be saved as trusted public key for later use. The
2593 `completion' must be called after the public key has been verified. */
2596 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2597 SilcSocketType conn_type, unsigned char *pk,
2598 SilcUInt32 pk_len, SilcSKEPKType pk_type,
2599 SilcVerifyPublicKey completion, void *context)
2601 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2603 completion, context);
2606 /* Asks passphrase from user on the input line. */
2609 SilcAskPassphrase completion;
2613 void ask_passphrase_completion(const char *passphrase, void *context)
2615 AskPassphrase p = (AskPassphrase)context;
2616 if (passphrase && passphrase[0] == '\0')
2618 p->completion((unsigned char *)passphrase,
2619 passphrase ? strlen(passphrase) : 0, p->context);
2623 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2624 SilcAskPassphrase completion, void *context)
2626 AskPassphrase p = silc_calloc(1, sizeof(*p));
2627 p->completion = completion;
2628 p->context = context;
2630 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2631 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2635 SilcGetAuthMeth completion;
2637 } *InternalGetAuthMethod;
2639 /* Callback called when we've received the authentication method information
2640 from the server after we've requested it. This will get the authentication
2641 data from the user if needed. */
2643 static void silc_get_auth_method_callback(SilcClient client,
2644 SilcClientConnection conn,
2645 SilcAuthMethod auth_meth,
2648 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2650 SILC_LOG_DEBUG(("Start"));
2652 switch (auth_meth) {
2653 case SILC_AUTH_NONE:
2654 /* No authentication required. */
2655 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2657 case SILC_AUTH_PASSWORD:
2659 /* Check whether we find the password for this server in our
2660 configuration. If not, then don't provide so library will ask
2661 it from the user. */
2662 SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2664 if (!setup || !setup->password) {
2665 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2669 (*internal->completion)(TRUE, auth_meth, setup->password,
2670 strlen(setup->password), internal->context);
2673 case SILC_AUTH_PUBLIC_KEY:
2674 /* Do not get the authentication data now, the library will generate
2675 it using our default key, if we do not provide it here. */
2676 /* XXX In the future when we support multiple local keys and multiple
2677 local certificates we will need to ask from user which one to use. */
2678 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2682 silc_free(internal);
2685 /* Find authentication method and authentication data by hostname and
2686 port. The hostname may be IP address as well. The found authentication
2687 method and authentication data is returned to `auth_meth', `auth_data'
2688 and `auth_data_len'. The function returns TRUE if authentication method
2689 is found and FALSE if not. `conn' may be NULL. */
2691 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2692 char *hostname, SilcUInt16 port,
2693 SilcGetAuthMeth completion, void *context)
2695 InternalGetAuthMethod internal;
2697 SILC_LOG_DEBUG(("Start"));
2699 /* If we do not have this connection configured by the user in a
2700 configuration file then resolve the authentication method from the
2701 server for this session. */
2702 internal = silc_calloc(1, sizeof(*internal));
2703 internal->completion = completion;
2704 internal->context = context;
2706 silc_client_request_authentication_method(client, conn,
2707 silc_get_auth_method_callback,
2711 /* Notifies application that failure packet was received. This is called
2712 if there is some protocol active in the client. The `protocol' is the
2713 protocol context. The `failure' is opaque pointer to the failure
2714 indication. Note, that the `failure' is protocol dependant and application
2715 must explicitly cast it to correct type. Usually `failure' is 32 bit
2716 failure type (see protocol specs for all protocol failure types). */
2718 void silc_failure(SilcClient client, SilcClientConnection conn,
2719 SilcProtocol protocol, void *failure)
2721 SILC_LOG_DEBUG(("Start"));
2723 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2724 SilcSKEStatus status = (SilcSKEStatus)failure;
2726 if (status == SILC_SKE_STATUS_BAD_VERSION)
2727 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2728 SILCTXT_KE_BAD_VERSION);
2729 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2730 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2731 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2732 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2733 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2734 SILCTXT_KE_UNKNOWN_GROUP);
2735 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2736 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2737 SILCTXT_KE_UNKNOWN_CIPHER);
2738 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2739 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2740 SILCTXT_KE_UNKNOWN_PKCS);
2741 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2742 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2743 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2744 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2745 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2746 SILCTXT_KE_UNKNOWN_HMAC);
2747 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2748 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2749 SILCTXT_KE_INCORRECT_SIGNATURE);
2750 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2751 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2752 SILCTXT_KE_INVALID_COOKIE);
2755 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2756 SilcUInt32 err = (SilcUInt32)failure;
2758 if (err == SILC_AUTH_FAILED)
2759 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2760 SILCTXT_AUTH_FAILED);
2764 /* Asks whether the user would like to perform the key agreement protocol.
2765 This is called after we have received an key agreement packet or an
2766 reply to our key agreement packet. This returns TRUE if the user wants
2767 the library to perform the key agreement protocol and FALSE if it is not
2768 desired (application may start it later by calling the function
2769 silc_client_perform_key_agreement). */
2771 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2772 SilcClientEntry client_entry, const char *hostname,
2773 SilcUInt16 port, SilcKeyAgreementCallback *completion,
2778 SILC_LOG_DEBUG(("Start"));
2780 /* We will just display the info on the screen and return FALSE and user
2781 will have to start the key agreement with a command. */
2784 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2787 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2788 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2790 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2791 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2792 client_entry->nickname, hostname, portstr);
2800 /* Notifies application that file transfer protocol session is being
2801 requested by the remote client indicated by the `client_entry' from
2802 the `hostname' and `port'. The `session_id' is the file transfer
2803 session and it can be used to either accept or reject the file
2804 transfer request, by calling the silc_client_file_receive or
2805 silc_client_file_close, respectively. */
2807 void silc_ftp(SilcClient client, SilcClientConnection conn,
2808 SilcClientEntry client_entry, SilcUInt32 session_id,
2809 const char *hostname, SilcUInt16 port)
2811 SILC_SERVER_REC *server;
2813 FtpSession ftp = NULL;
2815 SILC_LOG_DEBUG(("Start"));
2817 server = conn->context;
2819 silc_dlist_start(server->ftp_sessions);
2820 while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2821 if (ftp->client_entry == client_entry &&
2822 ftp->session_id == session_id) {
2823 server->current_session = ftp;
2827 if (ftp == SILC_LIST_END) {
2828 ftp = silc_calloc(1, sizeof(*ftp));
2829 ftp->client_entry = client_entry;
2830 ftp->session_id = session_id;
2833 silc_dlist_add(server->ftp_sessions, ftp);
2834 server->current_session = ftp;
2838 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2841 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2842 SILCTXT_FILE_REQUEST, client_entry->nickname);
2844 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2845 SILCTXT_FILE_REQUEST_HOST,
2846 client_entry->nickname, hostname, portstr);
2849 /* Delivers SILC session detachment data indicated by `detach_data' to the
2850 application. If application has issued SILC_COMMAND_DETACH command
2851 the client session in the SILC network is not quit. The client remains
2852 in the network but is detached. The detachment data may be used later
2853 to resume the session in the SILC Network. The appliation is
2854 responsible of saving the `detach_data', to for example in a file.
2856 The detachment data can be given as argument to the functions
2857 silc_client_connect_to_server, or silc_client_add_connection when
2858 creating connection to remote server, inside SilcClientConnectionParams
2859 structure. If it is provided the client library will attempt to resume
2860 the session in the network. After the connection is created
2861 successfully, the application is responsible of setting the user
2862 interface for user into the same state it was before detaching (showing
2863 same channels, channel modes, etc). It can do this by fetching the
2864 information (like joined channels) from the client library. */
2867 silc_detach(SilcClient client, SilcClientConnection conn,
2868 const unsigned char *detach_data, SilcUInt32 detach_data_len)
2872 /* Save the detachment data to file. */
2874 memset(file, 0, sizeof(file));
2875 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2876 silc_file_writefile(file, detach_data, detach_data_len);
2880 /* SILC client operations */
2881 SilcClientOperations ops = {
2883 silc_channel_message,
2884 silc_private_message,
2890 silc_get_auth_method,
2891 silc_verify_public_key,
2892 silc_ask_passphrase,