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)
293 SilcUInt32 ctr, dest=0;
296 data = silc_calloc(strlen(escaped_data), sizeof(char));
298 for (ctr = 0; ctr < strlen(escaped_data); ctr++)
299 if (escaped_data[ctr] == 1)
300 data[dest++] = escaped_data[++ctr] - 1;
302 data[dest++] = escaped_data[ctr];
308 char * silc_escape_data(const char *data, SilcUInt32 len)
311 SilcUInt32 ctr, zeros=0;
313 for (ctr = 0; ctr < len; ctr++)
314 if (data[ctr] == 0 || data[ctr] == 1)
317 escaped_data = silc_calloc(zeros + len, sizeof(char));
320 for (ctr = 0; ctr < len; ctr++)
323 escaped_data[zeros++] = 1;
324 escaped_data[zeros++] = 1;
328 escaped_data[zeros++] = 1;
329 escaped_data[zeros++] = 2;
333 escaped_data[zeros++] = data[ctr];
339 void silc_emit_mime_sig(SILC_SERVER_REC *server, SILC_CHANNEL_REC *channel,
340 const char *data, SilcUInt32 data_len,
341 const char *encoding, const char *type, const char *nick)
345 escaped_data = silc_escape_data(data, data_len);
347 signal_emit("mime", 6, server, channel, escaped_data, encoding, type, nick);
349 silc_free(escaped_data);
353 /* Message for a channel. The `sender' is the nickname of the sender
354 received in the packet. The `channel_name' is the name of the channel. */
356 void silc_channel_message(SilcClient client, SilcClientConnection conn,
357 SilcClientEntry sender, SilcChannelEntry channel,
358 SilcMessagePayload payload,
359 SilcMessageFlags flags, const unsigned char *message,
360 SilcUInt32 message_len)
362 SILC_SERVER_REC *server;
364 SILC_CHANNEL_REC *chanrec;
367 SILC_LOG_DEBUG(("Start"));
372 server = conn == NULL ? NULL : conn->context;
373 chanrec = silc_channel_find_entry(server, channel);
377 nick = silc_nicklist_find(chanrec, sender);
379 /* We didn't find client but it clearly exists, add it. */
380 SilcChannelUser chu = silc_client_on_channel(channel, sender);
382 nick = silc_nicklist_insert(chanrec, chu, FALSE);
385 /* If the messages is digitally signed, verify it, if possible. */
386 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
387 if (!settings_get_bool("ignore_message_signatures")) {
388 SilcMessageSignedPayload sig = silc_message_get_signature(payload);
389 verified = verify_message_signature(sender, sig, payload);
391 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
395 if (flags & SILC_MESSAGE_FLAG_DATA) {
396 /* MIME object received, try to display it as well as we can */
397 char type[128], enc[128];
401 memset(type, 0, sizeof(type));
402 memset(enc, 0, sizeof(enc));
403 if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
404 enc, sizeof(enc) - 1, &data, &data_len))
407 /* Then figure out what we can display */
408 if (strstr(type, "text/") && !strstr(type, "text/t140") &&
409 !strstr(type, "text/vnd")) {
410 /* It is something textual, display it */
411 message = (const unsigned char *)data;
413 silc_emit_mime_sig(server, chanrec, data, data_len,
414 enc, type, nick == NULL ? NULL : nick->nick);
422 /* FIXME: replace those printformat calls with signals and add signature
423 information to them (if present) */
424 if (flags & SILC_MESSAGE_FLAG_ACTION)
425 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
426 char tmp[256], *cp, *dm = NULL;
427 memset(tmp, 0, sizeof(tmp));
429 if(message_len > sizeof(tmp) - 1) {
430 dm = silc_calloc(message_len + 1, sizeof(*dm));
433 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
435 printformat_module("fe-common/silc", server, channel->channel_name,
436 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
437 nick == NULL ? "[<unknown>]" : nick->nick, cp);
440 printformat_module("fe-common/silc", server, channel->channel_name,
441 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
442 nick == NULL ? "[<unknown>]" : nick->nick,
445 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
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_LANGUAGE,
456 printformat_module("fe-common/silc", server, channel->channel_name,
457 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
458 nick == NULL ? "[<unknown>]" : nick->nick, cp);
461 printformat_module("fe-common/silc", server, channel->channel_name,
462 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
463 nick == NULL ? "[<unknown>]" : nick->nick,
467 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
468 char tmp[256], *cp, *dm = NULL;
470 memset(tmp, 0, sizeof(tmp));
472 if (message_len > sizeof(tmp) - 1) {
473 dm = silc_calloc(message_len + 1, sizeof(*dm));
477 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
479 if (flags & SILC_MESSAGE_FLAG_SIGNED)
480 signal_emit("message signed_public", 6, server, cp,
481 nick == NULL ? "[<unknown>]" : nick->nick,
482 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
483 chanrec->name, verified);
485 signal_emit("message public", 6, server, cp,
486 nick == NULL ? "[<unknown>]" : nick->nick,
487 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
488 chanrec->name, nick);
493 if (flags & SILC_MESSAGE_FLAG_SIGNED)
494 signal_emit("message signed_public", 6, server, message,
495 nick == NULL ? "[<unknown>]" : nick->nick,
496 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
497 chanrec->name, verified);
499 signal_emit("message public", 6, server, message,
500 nick == NULL ? "[<unknown>]" : nick->nick,
501 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
502 chanrec->name, nick);
506 /* Private message to the client. The `sender' is the nickname of the
507 sender received in the packet. */
509 void silc_private_message(SilcClient client, SilcClientConnection conn,
510 SilcClientEntry sender, SilcMessagePayload payload,
511 SilcMessageFlags flags,
512 const unsigned char *message,
513 SilcUInt32 message_len)
515 SILC_SERVER_REC *server;
519 SILC_LOG_DEBUG(("Start"));
521 server = conn == NULL ? NULL : conn->context;
522 memset(userhost, 0, sizeof(userhost));
523 if (sender->username)
524 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
525 sender->username, sender->hostname);
527 /* If the messages is digitally signed, verify it, if possible. */
528 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
529 if (!settings_get_bool("ignore_message_signatures")) {
530 SilcMessageSignedPayload sig = silc_message_get_signature(payload);
531 verified = verify_message_signature(sender, sig, payload);
533 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
537 if (flags & SILC_MESSAGE_FLAG_DATA) {
538 /* MIME object received, try to display it as well as we can */
539 char type[128], enc[128];
543 memset(type, 0, sizeof(type));
544 memset(enc, 0, sizeof(enc));
545 if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
546 enc, sizeof(enc) - 1, &data, &data_len))
549 /* Then figure out what we can display */
550 if (strstr(type, "text/") && !strstr(type, "text/t140") &&
551 !strstr(type, "text/vnd")) {
552 /* It is something textual, display it */
553 message = (const unsigned char *)data;
555 silc_emit_mime_sig(server, NULL, data, data_len,
556 enc, type, sender->nickname ? sender->nickname :
565 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
566 char tmp[256], *cp, *dm = NULL;
568 memset(tmp, 0, sizeof(tmp));
570 if (message_len > sizeof(tmp) - 1) {
571 dm = silc_calloc(message_len + 1, sizeof(*dm));
575 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
577 if (flags & SILC_MESSAGE_FLAG_SIGNED)
578 signal_emit("message signed_private", 5, server, cp,
579 sender->nickname ? sender->nickname : "[<unknown>]",
580 sender->username ? userhost : NULL, verified);
582 signal_emit("message private", 4, server, cp,
583 sender->nickname ? sender->nickname : "[<unknown>]",
584 sender->username ? userhost : NULL);
589 if (flags & SILC_MESSAGE_FLAG_SIGNED)
590 signal_emit("message signed_private", 5, server, message,
591 sender->nickname ? sender->nickname : "[<unknown>]",
592 sender->username ? userhost : NULL, verified);
594 signal_emit("message private", 4, server, message,
595 sender->nickname ? sender->nickname : "[<unknown>]",
596 sender->username ? userhost : NULL);
599 /* Notify message to the client. The notify arguments are sent in the
600 same order as servers sends them. The arguments are same as received
601 from the server except for ID's. If ID is received application receives
602 the corresponding entry to the ID. For example, if Client ID is received
603 application receives SilcClientEntry. Also, if the notify type is
604 for channel the channel entry is sent to application (even if server
605 does not send it). */
607 void silc_notify(SilcClient client, SilcClientConnection conn,
608 SilcNotifyType type, ...)
611 SILC_SERVER_REC *server;
612 SILC_CHANNEL_REC *chanrec;
613 SILC_NICK_REC *nickrec;
614 SilcClientEntry client_entry, client_entry2;
615 SilcChannelEntry channel, channel2;
616 SilcServerEntry server_entry;
622 GSList *list1, *list_tmp;
625 SILC_LOG_DEBUG(("Start"));
629 server = conn == NULL ? NULL : conn->context;
632 case SILC_NOTIFY_TYPE_NONE:
633 /* Some generic notice from server */
634 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
637 case SILC_NOTIFY_TYPE_INVITE:
639 * Invited or modified invite list.
642 SILC_LOG_DEBUG(("Notify: INVITE"));
644 channel = va_arg(va, SilcChannelEntry);
645 name = va_arg(va, char *);
646 client_entry = va_arg(va, SilcClientEntry);
648 memset(buf, 0, sizeof(buf));
649 snprintf(buf, sizeof(buf) - 1, "%s@%s",
650 client_entry->username, client_entry->hostname);
651 signal_emit("message invite", 4, server, channel ? channel->channel_name :
652 name, client_entry->nickname, buf);
655 case SILC_NOTIFY_TYPE_JOIN:
660 SILC_LOG_DEBUG(("Notify: JOIN"));
662 client_entry = va_arg(va, SilcClientEntry);
663 channel = va_arg(va, SilcChannelEntry);
665 if (client_entry == server->conn->local_entry) {
666 /* You joined to channel */
667 chanrec = silc_channel_find(server, channel->channel_name);
668 if (chanrec != NULL && !chanrec->joined)
669 chanrec->entry = channel;
671 chanrec = silc_channel_find_entry(server, channel);
672 if (chanrec != NULL) {
673 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
675 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
679 memset(buf, 0, sizeof(buf));
680 if (client_entry->username)
681 snprintf(buf, sizeof(buf) - 1, "%s@%s",
682 client_entry->username, client_entry->hostname);
683 signal_emit("message join", 4, server, channel->channel_name,
684 client_entry->nickname,
685 client_entry->username == NULL ? "" : buf);
688 case SILC_NOTIFY_TYPE_LEAVE:
693 SILC_LOG_DEBUG(("Notify: LEAVE"));
695 client_entry = va_arg(va, SilcClientEntry);
696 channel = va_arg(va, SilcChannelEntry);
698 memset(buf, 0, sizeof(buf));
699 if (client_entry->username)
700 snprintf(buf, sizeof(buf) - 1, "%s@%s",
701 client_entry->username, client_entry->hostname);
702 signal_emit("message part", 5, server, channel->channel_name,
703 client_entry->nickname, client_entry->username ?
704 buf : "", client_entry->nickname);
706 chanrec = silc_channel_find_entry(server, channel);
707 if (chanrec != NULL) {
708 nickrec = silc_nicklist_find(chanrec, client_entry);
710 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
714 case SILC_NOTIFY_TYPE_SIGNOFF:
719 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
721 client_entry = va_arg(va, SilcClientEntry);
722 tmp = va_arg(va, char *);
724 silc_server_free_ftp(server, client_entry);
726 /* Print only if we have the nickname. If this cliente has just quit
727 when we were only resolving it, it is possible we don't have the
729 if (client_entry->nickname) {
730 memset(buf, 0, sizeof(buf));
731 if (client_entry->username)
732 snprintf(buf, sizeof(buf) - 1, "%s@%s",
733 client_entry->username, client_entry->hostname);
734 signal_emit("message quit", 4, server, client_entry->nickname,
735 client_entry->username ? buf : "",
739 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
740 for (list_tmp = list1; list_tmp != NULL; list_tmp =
741 list_tmp->next->next) {
742 CHANNEL_REC *channel = list_tmp->data;
743 NICK_REC *nickrec = list_tmp->next->data;
745 nicklist_remove(channel, nickrec);
749 case SILC_NOTIFY_TYPE_TOPIC_SET:
754 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
756 idtype = va_arg(va, int);
757 entry = va_arg(va, void *);
758 tmp = va_arg(va, char *);
759 channel = va_arg(va, SilcChannelEntry);
761 chanrec = silc_channel_find_entry(server, channel);
762 if (chanrec != NULL) {
763 char tmp2[256], *cp, *dm = NULL;
765 g_free_not_null(chanrec->topic);
766 if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) {
767 memset(tmp2, 0, sizeof(tmp2));
769 if (strlen(tmp) > sizeof(tmp2) - 1) {
770 dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm));
774 silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE,
779 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
780 signal_emit("channel topic changed", 1, chanrec);
785 if (idtype == SILC_ID_CLIENT) {
786 client_entry = (SilcClientEntry)entry;
787 memset(buf, 0, sizeof(buf));
788 snprintf(buf, sizeof(buf) - 1, "%s@%s",
789 client_entry->username, client_entry->hostname);
790 signal_emit("message topic", 5, server, channel->channel_name,
791 tmp, client_entry->nickname, buf);
792 } else if (idtype == SILC_ID_SERVER) {
793 server_entry = (SilcServerEntry)entry;
794 signal_emit("message topic", 5, server, channel->channel_name,
795 tmp, server_entry->server_name,
796 server_entry->server_name);
797 } else if (idtype == SILC_ID_CHANNEL) {
798 channel = (SilcChannelEntry)entry;
799 signal_emit("message topic", 5, server, channel->channel_name,
800 tmp, channel->channel_name, channel->channel_name);
804 case SILC_NOTIFY_TYPE_NICK_CHANGE:
809 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
811 client_entry = va_arg(va, SilcClientEntry);
812 client_entry2 = va_arg(va, SilcClientEntry);
814 if (!strcmp(client_entry->nickname, client_entry2->nickname))
817 memset(buf, 0, sizeof(buf));
818 snprintf(buf, sizeof(buf) - 1, "%s@%s",
819 client_entry2->username, client_entry2->hostname);
820 nicklist_rename_unique(SERVER(server),
821 client_entry, client_entry->nickname,
822 client_entry2, client_entry2->nickname);
823 signal_emit("message nick", 4, server, client_entry2->nickname,
824 client_entry->nickname, buf);
827 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
829 * Changed channel mode.
832 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
834 idtype = va_arg(va, int);
835 entry = va_arg(va, void *);
836 mode = va_arg(va, SilcUInt32);
837 (void)va_arg(va, char *); /* cipher */
838 (void)va_arg(va, char *); /* hmac */
839 (void)va_arg(va, char *); /* passphrase */
840 (void)va_arg(va, SilcPublicKey); /* founder key */
841 buffer = va_arg(va, SilcBuffer); /* channel public keys */
842 channel = va_arg(va, SilcChannelEntry);
844 tmp = silc_client_chmode(mode,
845 channel->channel_key ?
846 silc_cipher_get_name(channel->channel_key) : "",
848 silc_hmac_get_name(channel->hmac) : "");
850 chanrec = silc_channel_find_entry(server, channel);
851 if (chanrec != NULL) {
852 g_free_not_null(chanrec->mode);
853 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
854 signal_emit("channel mode changed", 1, chanrec);
857 if (idtype == SILC_ID_CLIENT) {
858 client_entry = (SilcClientEntry)entry;
859 printformat_module("fe-common/silc", server, channel->channel_name,
860 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
861 channel->channel_name, tmp ? tmp : "removed all",
862 client_entry->nickname);
863 } else if (idtype == SILC_ID_SERVER) {
864 server_entry = (SilcServerEntry)entry;
865 printformat_module("fe-common/silc", server, channel->channel_name,
866 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
867 channel->channel_name, tmp ? tmp : "removed all",
868 server_entry->server_name);
869 } else if (idtype == SILC_ID_CHANNEL) {
870 channel2 = (SilcChannelEntry)entry;
871 printformat_module("fe-common/silc", server, channel->channel_name,
872 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
873 channel->channel_name, tmp ? tmp : "removed all",
874 channel2->channel_name);
877 /* Print the channel public key list */
879 silc_parse_channel_public_keys(server, channel, buffer);
884 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
886 * Changed user's mode on channel.
889 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
891 idtype = va_arg(va, int);
892 entry = va_arg(va, void *);
893 mode = va_arg(va, SilcUInt32);
894 client_entry2 = va_arg(va, SilcClientEntry);
895 channel = va_arg(va, SilcChannelEntry);
897 tmp = silc_client_chumode(mode);
898 chanrec = silc_channel_find_entry(server, channel);
899 if (chanrec != NULL) {
902 if (client_entry2 == server->conn->local_entry)
903 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
905 nick = silc_nicklist_find(chanrec, client_entry2);
907 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
908 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
909 signal_emit("nick mode changed", 2, chanrec, nick);
913 if (idtype == SILC_ID_CLIENT) {
914 client_entry = (SilcClientEntry)entry;
915 printformat_module("fe-common/silc", server, channel->channel_name,
916 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
917 channel->channel_name, client_entry2->nickname,
918 tmp ? tmp : "removed all",
919 client_entry->nickname);
920 } else if (idtype == SILC_ID_SERVER) {
921 server_entry = (SilcServerEntry)entry;
922 printformat_module("fe-common/silc", server, channel->channel_name,
923 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
924 channel->channel_name, client_entry2->nickname,
925 tmp ? tmp : "removed all",
926 server_entry->server_name);
927 } else if (idtype == SILC_ID_CHANNEL) {
928 channel2 = (SilcChannelEntry)entry;
929 printformat_module("fe-common/silc", server, channel->channel_name,
930 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
931 channel->channel_name, client_entry2->nickname,
932 tmp ? tmp : "removed all",
933 channel2->channel_name);
936 if (mode & SILC_CHANNEL_UMODE_CHANFO)
937 printformat_module("fe-common/silc",
938 server, channel->channel_name, MSGLEVEL_CRAP,
939 SILCTXT_CHANNEL_FOUNDER,
940 channel->channel_name, client_entry2->nickname);
942 if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
943 printformat_module("fe-common/silc",
944 server, channel->channel_name, MSGLEVEL_CRAP,
945 SILCTXT_CHANNEL_QUIETED, channel->channel_name);
950 case SILC_NOTIFY_TYPE_MOTD:
955 SILC_LOG_DEBUG(("Notify: MOTD"));
957 tmp = va_arg(va, char *);
959 if (!settings_get_bool("skip_motd"))
960 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
963 case SILC_NOTIFY_TYPE_KICKED:
965 * Someone was kicked from channel.
968 SILC_LOG_DEBUG(("Notify: KICKED"));
970 client_entry = va_arg(va, SilcClientEntry);
971 tmp = va_arg(va, char *);
972 client_entry2 = va_arg(va, SilcClientEntry);
973 channel = va_arg(va, SilcChannelEntry);
975 chanrec = silc_channel_find_entry(server, channel);
977 if (client_entry == conn->local_entry) {
978 printformat_module("fe-common/silc", server, channel->channel_name,
979 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
980 channel->channel_name,
981 client_entry ? client_entry2->nickname : "",
984 chanrec->kicked = TRUE;
985 channel_destroy((CHANNEL_REC *)chanrec);
988 printformat_module("fe-common/silc", server, channel->channel_name,
989 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
990 client_entry->nickname, channel->channel_name,
991 client_entry2 ? client_entry2->nickname : "",
995 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
997 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
1002 case SILC_NOTIFY_TYPE_KILLED:
1004 * Someone was killed from the network.
1007 SILC_LOG_DEBUG(("Notify: KILLED"));
1009 client_entry = va_arg(va, SilcClientEntry);
1010 tmp = va_arg(va, char *);
1011 idtype = va_arg(va, int);
1012 entry = va_arg(va, SilcClientEntry);
1014 if (client_entry == conn->local_entry) {
1015 if (idtype == SILC_ID_CLIENT) {
1016 client_entry2 = (SilcClientEntry)entry;
1017 printformat_module("fe-common/silc", server, NULL,
1018 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1019 client_entry2 ? client_entry2->nickname : "",
1021 } else if (idtype == SILC_ID_SERVER) {
1022 server_entry = (SilcServerEntry)entry;
1023 printformat_module("fe-common/silc", server, NULL,
1024 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1025 server_entry->server_name, tmp ? tmp : "");
1026 } else if (idtype == SILC_ID_CHANNEL) {
1027 channel = (SilcChannelEntry)entry;
1028 printformat_module("fe-common/silc", server, NULL,
1029 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1030 channel->channel_name, tmp ? tmp : "");
1033 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1034 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1035 list_tmp->next->next) {
1036 CHANNEL_REC *channel = list_tmp->data;
1037 NICK_REC *nickrec = list_tmp->next->data;
1038 nicklist_remove(channel, nickrec);
1041 if (idtype == SILC_ID_CLIENT) {
1042 client_entry2 = (SilcClientEntry)entry;
1043 printformat_module("fe-common/silc", server, NULL,
1044 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1045 client_entry->nickname,
1046 client_entry2 ? client_entry2->nickname : "",
1048 } else if (idtype == SILC_ID_SERVER) {
1049 server_entry = (SilcServerEntry)entry;
1050 printformat_module("fe-common/silc", server, NULL,
1051 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1052 client_entry->nickname,
1053 server_entry->server_name, tmp ? tmp : "");
1054 } else if (idtype == SILC_ID_CHANNEL) {
1055 channel = (SilcChannelEntry)entry;
1056 printformat_module("fe-common/silc", server, NULL,
1057 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1058 client_entry->nickname,
1059 channel->channel_name, tmp ? tmp : "");
1064 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
1067 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1070 * Server has quit the network.
1073 SilcClientEntry *clients;
1074 SilcUInt32 clients_count;
1076 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1078 (void)va_arg(va, void *);
1079 clients = va_arg(va, SilcClientEntry *);
1080 clients_count = va_arg(va, SilcUInt32);
1082 for (i = 0; i < clients_count; i++) {
1083 memset(buf, 0, sizeof(buf));
1085 /* Print only if we have the nickname. If this client has just quit
1086 when we were only resolving it, it is possible we don't have the
1088 if (clients[i]->nickname) {
1089 if (clients[i]->username)
1090 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1091 clients[i]->username, clients[i]->hostname);
1092 signal_emit("message quit", 4, server, clients[i]->nickname,
1093 clients[i]->username ? buf : "",
1097 silc_server_free_ftp(server, clients[i]);
1099 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
1100 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1101 list_tmp->next->next) {
1102 CHANNEL_REC *channel = list_tmp->data;
1103 NICK_REC *nickrec = list_tmp->next->data;
1104 nicklist_remove(channel, nickrec);
1110 case SILC_NOTIFY_TYPE_ERROR:
1112 SilcStatus error = va_arg(va, int);
1114 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1115 "%s", silc_get_status_message(error));
1119 case SILC_NOTIFY_TYPE_WATCH:
1121 SilcNotifyType notify;
1123 client_entry = va_arg(va, SilcClientEntry);
1124 name = va_arg(va, char *); /* Maybe NULL */
1125 mode = va_arg(va, SilcUInt32);
1126 notify = va_arg(va, int);
1128 if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1130 printformat_module("fe-common/silc", server, NULL,
1131 MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1132 client_entry->nickname, name);
1134 printformat_module("fe-common/silc", server, NULL,
1135 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1136 client_entry->nickname);
1137 } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1138 /* See if client was away and is now present */
1139 if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1140 SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1141 SILC_UMODE_DETACHED)) &&
1142 (client_entry->mode & SILC_UMODE_GONE ||
1143 client_entry->mode & SILC_UMODE_INDISPOSED ||
1144 client_entry->mode & SILC_UMODE_BUSY ||
1145 client_entry->mode & SILC_UMODE_PAGE ||
1146 client_entry->mode & SILC_UMODE_DETACHED)) {
1147 printformat_module("fe-common/silc", server, NULL,
1148 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1149 client_entry->nickname);
1153 memset(buf, 0, sizeof(buf));
1154 silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1155 printformat_module("fe-common/silc", server, NULL,
1156 MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1157 client_entry->nickname, buf);
1159 } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1160 printformat_module("fe-common/silc", server, NULL,
1161 MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1162 client_entry->nickname);
1163 } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1164 notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1165 printformat_module("fe-common/silc", server, NULL,
1166 MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1167 client_entry->nickname);
1168 } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1169 /* Client logged in to the network */
1170 printformat_module("fe-common/silc", server, NULL,
1171 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1172 client_entry->nickname);
1178 /* Unknown notify */
1179 printformat_module("fe-common/silc", server, NULL,
1180 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1187 /* Called to indicate that connection was either successfully established
1188 or connecting failed. This is also the first time application receives
1189 the SilcClientConnection object which it should save somewhere. */
1191 void silc_connect(SilcClient client, SilcClientConnection conn,
1192 SilcClientConnectionStatus status)
1194 SILC_SERVER_REC *server = conn->context;
1196 if (!server || server->disconnected) {
1197 silc_client_close_connection(client, conn);
1202 case SILC_CLIENT_CONN_SUCCESS:
1203 /* We have successfully connected to server */
1204 server->connected = TRUE;
1205 signal_emit("event connected", 1, server);
1208 case SILC_CLIENT_CONN_SUCCESS_RESUME:
1209 /* We have successfully resumed old detached session */
1210 server->connected = TRUE;
1211 signal_emit("event connected", 1, server);
1213 /* If we resumed old session check whether we need to update
1215 if (strcmp(server->nick, conn->local_entry->nickname)) {
1217 old = g_strdup(server->nick);
1218 server_change_nick(SERVER(server), conn->local_entry->nickname);
1219 nicklist_rename_unique(SERVER(server),
1220 conn->local_entry, server->nick,
1221 conn->local_entry, conn->local_entry->nickname);
1222 signal_emit("message own_nick", 4, server, server->nick, old, "");
1228 server->connection_lost = TRUE;
1230 server->conn->context = NULL;
1231 server_disconnect(SERVER(server));
1236 /* Called to indicate that connection was disconnected to the server. */
1238 void silc_disconnect(SilcClient client, SilcClientConnection conn,
1239 SilcStatus status, const char *message)
1241 SILC_SERVER_REC *server = conn->context;
1243 SILC_LOG_DEBUG(("Start"));
1245 if (!server || server->connection_lost)
1248 if (server->conn && server->conn->local_entry) {
1249 nicklist_rename_unique(SERVER(server),
1250 server->conn->local_entry, server->nick,
1251 server->conn->local_entry,
1252 silc_client->username);
1253 silc_change_nick(server, silc_client->username);
1257 silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
1258 "Server closed connection: %s (%d) %s",
1259 silc_get_status_message(status), status,
1260 message ? message : "");
1263 server->conn->context = NULL;
1264 server->conn = NULL;
1265 server->connection_lost = TRUE;
1266 server_disconnect(SERVER(server));
1269 /* Command handler. This function is called always in the command function.
1270 If error occurs it will be called as well. `conn' is the associated
1271 client connection. `cmd_context' is the command context that was
1272 originally sent to the command. `success' is FALSE if error occured
1273 during command. `command' is the command being processed. It must be
1274 noted that this is not reply from server. This is merely called just
1275 after application has called the command. Just to tell application
1276 that the command really was processed. */
1278 static bool cmode_list_chpks = FALSE;
1280 void silc_command(SilcClient client, SilcClientConnection conn,
1281 SilcClientCommandContext cmd_context, bool success,
1282 SilcCommand command, SilcStatus status)
1284 SILC_SERVER_REC *server = conn->context;
1286 SILC_LOG_DEBUG(("Start"));
1289 silc_say_error("%s", silc_get_status_message(status));
1295 case SILC_COMMAND_INVITE:
1296 if (cmd_context->argc > 2)
1297 printformat_module("fe-common/silc", server, NULL,
1298 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1299 cmd_context->argv[2],
1300 (cmd_context->argv[1][0] == '*' ?
1301 (char *)conn->current_channel->channel_name :
1302 (char *)cmd_context->argv[1]));
1305 case SILC_COMMAND_DETACH:
1306 server->no_reconnect = TRUE;
1309 case SILC_COMMAND_CMODE:
1310 if (cmd_context->argc == 3 &&
1311 !strcmp(cmd_context->argv[2], "+C"))
1312 cmode_list_chpks = TRUE;
1314 cmode_list_chpks = FALSE;
1323 SilcChannelEntry channel;
1327 /* Client info resolving callback when JOIN command reply is received.
1328 This will cache all users on the channel. */
1330 static void silc_client_join_get_users(SilcClient client,
1331 SilcClientConnection conn,
1332 SilcClientEntry *clients,
1333 SilcUInt32 clients_count,
1336 SilcJoinResolve r = context;
1337 SilcChannelEntry channel = r->channel;
1338 SilcHashTableList htl;
1339 SilcChannelUser chu;
1340 SILC_SERVER_REC *server = conn->context;
1341 SILC_CHANNEL_REC *chanrec;
1342 SilcClientEntry founder = NULL;
1345 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
1346 silc_hash_table_count(channel->user_list)));
1348 if (!clients && r->retry < 1) {
1349 /* Retry to resolve */
1351 silc_client_get_clients_by_channel(client, conn, channel,
1352 silc_client_join_get_users, context);
1356 chanrec = silc_channel_find(server, channel->channel_name);
1357 if (chanrec == NULL)
1360 silc_hash_table_list(channel->user_list, &htl);
1361 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1362 if (!chu->client->nickname)
1364 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1365 founder = chu->client;
1366 silc_nicklist_insert(chanrec, chu, FALSE);
1368 silc_hash_table_list_reset(&htl);
1370 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1371 nicklist_set_own(CHANNEL(chanrec), ownnick);
1372 signal_emit("channel joined", 1, chanrec);
1373 chanrec->entry = channel;
1376 printformat_module("fe-common/silc", server, channel->channel_name,
1377 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1378 channel->channel_name, chanrec->topic);
1381 if (founder == conn->local_entry) {
1382 printformat_module("fe-common/silc",
1383 server, channel->channel_name, MSGLEVEL_CRAP,
1384 SILCTXT_CHANNEL_FOUNDER_YOU,
1385 channel->channel_name);
1386 signal_emit("nick mode changed", 2, chanrec, ownnick);
1388 printformat_module("fe-common/silc",
1389 server, channel->channel_name, MSGLEVEL_CRAP,
1390 SILCTXT_CHANNEL_FOUNDER,
1391 channel->channel_name, founder->nickname);
1397 SilcClientConnection conn;
1403 void silc_getkey_cb(bool success, void *context)
1405 GetkeyContext getkey = (GetkeyContext)context;
1406 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1407 char *name = (getkey->id_type == SILC_ID_CLIENT ?
1408 ((SilcClientEntry)getkey->entry)->nickname :
1409 ((SilcServerEntry)getkey->entry)->server_name);
1412 printformat_module("fe-common/silc", NULL, NULL,
1413 MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1415 printformat_module("fe-common/silc", NULL, NULL,
1416 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1420 silc_free(getkey->fingerprint);
1424 /* Parse an invite or ban list */
1425 void silc_parse_inviteban_list(SilcClient client,
1426 SilcClientConnection conn,
1427 SILC_SERVER_REC *server,
1428 SilcChannelEntry channel,
1429 const char *list_type,
1430 SilcArgumentPayload list)
1433 SilcUInt32 type, len;
1434 SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1435 int counter=0, resolving = FALSE;
1437 if (!silc_argument_get_arg_num(list)) {
1438 printformat_module("fe-common/silc", server,
1439 (chanrec ? chanrec->visible_name : NULL),
1440 MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1441 channel->channel_name, list_type);
1445 printformat_module("fe-common/silc", server,
1446 (chanrec ? chanrec->visible_name : NULL),
1447 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1448 channel->channel_name, list_type);
1450 /* parse the list */
1451 tmp = silc_argument_get_first_arg(list, &type, &len);
1456 /* an invite string */
1460 if (tmp[len-1] == ',')
1463 list = g_strsplit(tmp, ",", -1);
1465 printformat_module("fe-common/silc", server,
1466 (chanrec ? chanrec->visible_name : NULL),
1467 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1468 ++counter, channel->channel_name, list_type,
1477 char *fingerprint, *babbleprint;
1479 /* tmp is Public Key Payload, take public key from it. */
1480 fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1481 babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1483 printformat_module("fe-common/silc", server,
1484 (chanrec ? chanrec->visible_name : NULL),
1485 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1486 ++counter, channel->channel_name, list_type,
1487 fingerprint, babbleprint);
1494 SilcClientID *client_id;
1495 SilcClientEntry client_entry;
1497 client_id = silc_id_payload_parse_id(tmp, len, NULL);
1499 if (client_id == NULL) {
1500 silc_say_error("Invalid data in %s list encountered", list_type);
1504 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1507 printformat_module("fe-common/silc", server,
1508 (chanrec ? chanrec->visible_name : NULL),
1509 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1510 ++counter, channel->channel_name, list_type,
1511 client_entry->nickname);
1514 silc_client_get_client_by_id_resolve(client, conn, client_id,
1518 silc_free(client_id);
1524 silc_say_error("Unkown type in %s list: %u (len %u)",
1525 list_type, type, len);
1527 tmp = silc_argument_get_next_arg(list, &type, &len);
1531 printformat_module("fe-common/silc", server,
1532 (chanrec ? chanrec->visible_name : NULL),
1533 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1534 list_type, channel->channel_name);
1537 /* Command reply handler. This function is called always in the command reply
1538 function. If error occurs it will be called as well. Normal scenario
1539 is that it will be called after the received command data has been parsed
1540 and processed. The function is used to pass the received command data to
1543 `conn' is the associated client connection. `cmd_payload' is the command
1544 payload data received from server and it can be ignored. It is provided
1545 if the application would like to re-parse the received command data,
1546 however, it must be noted that the data is parsed already by the library
1547 thus the payload can be ignored. `success' is FALSE if error occured.
1548 In this case arguments are not sent to the application. `command' is the
1549 command reply being processed. The function has variable argument list
1550 and each command defines the number and type of arguments it passes to the
1551 application (on error they are not sent). */
1554 silc_command_reply(SilcClient client, SilcClientConnection conn,
1555 SilcCommandPayload cmd_payload, bool success,
1556 SilcCommand command, SilcStatus status, ...)
1559 SILC_SERVER_REC *server = conn->context;
1560 SILC_CHANNEL_REC *chanrec;
1563 va_start(vp, status);
1565 SILC_LOG_DEBUG(("Start"));
1568 case SILC_COMMAND_WHOIS:
1570 char buf[1024], *nickname, *username, *realname, *nick;
1571 unsigned char *fingerprint;
1572 SilcUInt32 idle, mode;
1573 SilcBuffer channels, user_modes;
1574 SilcClientEntry client_entry;
1577 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1578 /* Print the unknown nick for user */
1579 unsigned char *tmp =
1580 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1583 silc_say_error("%s: %s", tmp,
1584 silc_get_status_message(status));
1586 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1587 /* Try to find the entry for the unknown client ID, since we
1588 might have, and print the nickname of it for user. */
1590 unsigned char *tmp =
1591 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1594 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1597 client_entry = silc_client_get_client_by_id(client, conn,
1599 if (client_entry && client_entry->nickname)
1600 silc_say_error("%s: %s", client_entry->nickname,
1601 silc_get_status_message(status));
1602 silc_free(client_id);
1606 } else if (!success) {
1607 silc_say_error("WHOIS: %s", silc_get_status_message(status));
1611 client_entry = va_arg(vp, SilcClientEntry);
1612 nickname = va_arg(vp, char *);
1613 username = va_arg(vp, char *);
1614 realname = va_arg(vp, char *);
1615 channels = va_arg(vp, SilcBuffer);
1616 mode = va_arg(vp, SilcUInt32);
1617 idle = va_arg(vp, SilcUInt32);
1618 fingerprint = va_arg(vp, unsigned char *);
1619 user_modes = va_arg(vp, SilcBuffer);
1620 attrs = va_arg(vp, SilcDList);
1622 silc_parse_userfqdn(nickname, &nick, NULL);
1623 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1624 SILCTXT_WHOIS_USERINFO, nickname,
1625 client_entry->username, client_entry->hostname,
1626 nick, client_entry->nickname);
1627 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1628 SILCTXT_WHOIS_REALNAME, realname);
1631 if (channels && user_modes) {
1633 SilcDList list = silc_channel_payload_parse_list(channels->data,
1635 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1637 SilcChannelPayload entry;
1640 memset(buf, 0, sizeof(buf));
1641 silc_dlist_start(list);
1642 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1643 SilcUInt32 name_len;
1644 char *m = silc_client_chumode_char(umodes[i++]);
1645 char *name = silc_channel_get_name(entry, &name_len);
1648 silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1649 silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1650 silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1654 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1655 SILCTXT_WHOIS_CHANNELS, buf);
1656 silc_channel_payload_list_free(list);
1662 memset(buf, 0, sizeof(buf));
1663 silc_get_umode_string(mode, buf, sizeof(buf - 1));
1664 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1665 SILCTXT_WHOIS_MODES, buf);
1668 if (idle && nickname) {
1669 memset(buf, 0, sizeof(buf));
1670 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1671 idle > 60 ? (idle / 60) : idle,
1672 idle > 60 ? "minutes" : "seconds");
1674 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1675 SILCTXT_WHOIS_IDLE, buf);
1679 fingerprint = silc_fingerprint(fingerprint, 20);
1680 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1681 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1682 silc_free(fingerprint);
1686 silc_query_attributes_print(server, silc_client, conn, attrs,
1691 case SILC_COMMAND_IDENTIFY:
1693 SilcClientEntry client_entry;
1695 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1696 /* Print the unknown nick for user */
1697 unsigned char *tmp =
1698 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1701 silc_say_error("%s: %s", tmp,
1702 silc_get_status_message(status));
1704 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1705 /* Try to find the entry for the unknown client ID, since we
1706 might have, and print the nickname of it for user. */
1708 unsigned char *tmp =
1709 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1712 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1715 client_entry = silc_client_get_client_by_id(client, conn,
1717 if (client_entry && client_entry->nickname)
1718 silc_say_error("%s: %s", client_entry->nickname,
1719 silc_get_status_message(status));
1720 silc_free(client_id);
1729 case SILC_COMMAND_WHOWAS:
1731 char *nickname, *username, *realname;
1733 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1734 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1736 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1739 silc_say_error("%s: %s", tmp,
1740 silc_get_status_message(status));
1742 } else if (!success) {
1743 silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1747 (void)va_arg(vp, SilcClientEntry);
1748 nickname = va_arg(vp, char *);
1749 username = va_arg(vp, char *);
1750 realname = va_arg(vp, char *);
1752 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1753 SILCTXT_WHOWAS_USERINFO, nickname, username,
1754 realname ? realname : "");
1758 case SILC_COMMAND_INVITE:
1760 SilcChannelEntry channel;
1762 SilcArgumentPayload invite_list;
1768 channel = va_arg(vp, SilcChannelEntry);
1769 payload = va_arg(vp, SilcBuffer);
1772 SILC_GET16_MSB(argc, payload->data);
1773 invite_list = silc_argument_payload_parse(payload->data + 2,
1774 payload->len - 2, argc);
1776 silc_parse_inviteban_list(client, conn, server, channel,
1777 "invite", invite_list);
1778 silc_argument_payload_free(invite_list);
1784 case SILC_COMMAND_JOIN:
1786 char *channel, *mode, *topic;
1788 SilcChannelEntry channel_entry;
1789 SilcBuffer client_id_list;
1790 SilcUInt32 list_count;
1795 channel = va_arg(vp, char *);
1796 channel_entry = va_arg(vp, SilcChannelEntry);
1797 modei = va_arg(vp, SilcUInt32);
1798 (void)va_arg(vp, SilcUInt32);
1799 (void)va_arg(vp, unsigned char *);
1800 (void)va_arg(vp, unsigned char *);
1801 (void)va_arg(vp, unsigned char *);
1802 topic = va_arg(vp, char *);
1803 (void)va_arg(vp, unsigned char *);
1804 list_count = va_arg(vp, SilcUInt32);
1805 client_id_list = va_arg(vp, SilcBuffer);
1807 chanrec = silc_channel_find(server, channel);
1809 chanrec = silc_channel_create(server, channel, channel, TRUE);
1812 char tmp[256], *cp, *dm = NULL;
1813 g_free_not_null(chanrec->topic);
1815 if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1816 memset(tmp, 0, sizeof(tmp));
1818 if (strlen(topic) > sizeof(tmp) - 1) {
1819 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1823 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1828 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1829 signal_emit("channel topic changed", 1, chanrec);
1834 mode = silc_client_chmode(modei,
1835 channel_entry->channel_key ?
1836 silc_cipher_get_name(channel_entry->
1838 channel_entry->hmac ?
1839 silc_hmac_get_name(channel_entry->hmac) : "");
1840 g_free_not_null(chanrec->mode);
1841 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1842 signal_emit("channel mode changed", 1, chanrec);
1844 /* Resolve the client information */
1846 SilcJoinResolve r = silc_calloc(1, sizeof(*r));
1847 r->channel = channel_entry;
1848 silc_client_get_clients_by_list(client, conn, list_count,
1850 silc_client_join_get_users, r);
1856 case SILC_COMMAND_NICK:
1859 SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1865 nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1866 if ((nicks != NULL) &&
1867 (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1869 SilcClientEntry collider, old;
1871 old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1872 collider = silc_client_get_client_by_id(client, conn,
1875 memset(buf, 0, sizeof(buf));
1876 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1877 collider->username, collider->hostname);
1878 nicklist_rename_unique(SERVER(server),
1880 collider, collider->nickname);
1881 silc_print_nick_change(server, collider->nickname,
1882 client_entry->nickname, buf);
1883 g_slist_free(nicks);
1886 old = g_strdup(server->nick);
1887 server_change_nick(SERVER(server), client_entry->nickname);
1888 nicklist_rename_unique(SERVER(server),
1889 server->conn->local_entry, server->nick,
1890 client_entry, client_entry->nickname);
1891 signal_emit("message own_nick", 4, server, server->nick, old, "");
1896 case SILC_COMMAND_LIST:
1901 char tmp[256], *cp, *dm = NULL;
1906 (void)va_arg(vp, SilcChannelEntry);
1907 name = va_arg(vp, char *);
1908 topic = va_arg(vp, char *);
1909 usercount = va_arg(vp, int);
1911 if (topic && !silc_term_utf8() &&
1912 silc_utf8_valid(topic, strlen(topic))) {
1913 memset(tmp, 0, sizeof(tmp));
1915 if (strlen(topic) > sizeof(tmp) - 1) {
1916 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1920 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1925 if (status == SILC_STATUS_LIST_START ||
1926 status == SILC_STATUS_OK)
1927 printformat_module("fe-common/silc", server, NULL,
1928 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1931 snprintf(users, sizeof(users) - 1, "N/A");
1933 snprintf(users, sizeof(users) - 1, "%d", usercount);
1934 printformat_module("fe-common/silc", server, NULL,
1935 MSGLEVEL_CRAP, SILCTXT_LIST,
1936 name, users, topic ? topic : "");
1941 case SILC_COMMAND_UMODE:
1949 mode = va_arg(vp, SilcUInt32);
1951 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1952 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1953 printformat_module("fe-common/silc", server, NULL,
1954 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1956 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1957 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1958 printformat_module("fe-common/silc", server, NULL,
1959 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1961 if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1962 if (mode & SILC_UMODE_GONE) {
1963 if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1964 reason = g_strdup(server->away_reason);
1966 reason = g_strdup("away");
1968 reason = g_strdup("");
1970 silc_set_away(reason, server);
1975 server->umode = mode;
1976 signal_emit("user mode changed", 2, server, NULL);
1980 case SILC_COMMAND_OPER:
1984 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1985 signal_emit("user mode changed", 2, server, NULL);
1987 printformat_module("fe-common/silc", server, NULL,
1988 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1991 case SILC_COMMAND_SILCOPER:
1995 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1996 signal_emit("user mode changed", 2, server, NULL);
1998 printformat_module("fe-common/silc", server, NULL,
1999 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
2002 case SILC_COMMAND_USERS:
2004 SilcHashTableList htl;
2005 SilcChannelEntry channel;
2006 SilcChannelUser chu;
2011 channel = va_arg(vp, SilcChannelEntry);
2013 printformat_module("fe-common/silc", server, channel->channel_name,
2014 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
2015 channel->channel_name);
2017 silc_hash_table_list(channel->user_list, &htl);
2018 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
2019 SilcClientEntry e = chu->client;
2020 char stat[5], *mode;
2025 memset(stat, 0, sizeof(stat));
2026 mode = silc_client_chumode_char(chu->mode);
2027 if (e->mode & SILC_UMODE_GONE)
2029 else if (e->mode & SILC_UMODE_INDISPOSED)
2031 else if (e->mode & SILC_UMODE_BUSY)
2033 else if (e->mode & SILC_UMODE_PAGE)
2035 else if (e->mode & SILC_UMODE_HYPER)
2037 else if (e->mode & SILC_UMODE_ROBOT)
2039 else if (e->mode & SILC_UMODE_ANONYMOUS)
2046 printformat_module("fe-common/silc", server, channel->channel_name,
2047 MSGLEVEL_CRAP, SILCTXT_USERS,
2049 e->username ? e->username : "",
2050 e->hostname ? e->hostname : "",
2051 e->realname ? e->realname : "");
2055 silc_hash_table_list_reset(&htl);
2059 case SILC_COMMAND_BAN:
2061 SilcChannelEntry channel;
2063 SilcArgumentPayload ban_list;
2069 channel = va_arg(vp, SilcChannelEntry);
2070 payload = va_arg(vp, SilcBuffer);
2073 SILC_GET16_MSB(argc, payload->data);
2074 ban_list = silc_argument_payload_parse(payload->data + 2,
2075 payload->len - 2, argc);
2077 silc_parse_inviteban_list(client, conn, server, channel,
2079 silc_argument_payload_free(ban_list);
2085 case SILC_COMMAND_GETKEY:
2089 SilcPublicKey public_key;
2092 GetkeyContext getkey;
2098 id_type = va_arg(vp, SilcUInt32);
2099 entry = va_arg(vp, void *);
2100 public_key = va_arg(vp, SilcPublicKey);
2103 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2105 getkey = silc_calloc(1, sizeof(*getkey));
2106 getkey->entry = entry;
2107 getkey->id_type = id_type;
2108 getkey->client = client;
2109 getkey->conn = conn;
2110 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2112 name = (id_type == SILC_ID_CLIENT ?
2113 ((SilcClientEntry)entry)->nickname :
2114 ((SilcServerEntry)entry)->server_name);
2116 silc_verify_public_key_internal(client, conn, name,
2117 (id_type == SILC_ID_CLIENT ?
2118 SILC_SOCKET_TYPE_CLIENT :
2119 SILC_SOCKET_TYPE_SERVER),
2120 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
2121 silc_getkey_cb, getkey);
2124 printformat_module("fe-common/silc", server, NULL,
2125 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2130 case SILC_COMMAND_INFO:
2132 SilcServerEntry server_entry;
2139 server_entry = va_arg(vp, SilcServerEntry);
2140 server_name = va_arg(vp, char *);
2141 server_info = va_arg(vp, char *);
2143 if (server_name && server_info )
2145 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2146 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2151 case SILC_COMMAND_TOPIC:
2153 SilcChannelEntry channel;
2155 char tmp[256], *cp, *dm = NULL;
2160 channel = va_arg(vp, SilcChannelEntry);
2161 topic = va_arg(vp, char *);
2163 if (topic && !silc_term_utf8() &&
2164 silc_utf8_valid(topic, strlen(topic))) {
2165 memset(tmp, 0, sizeof(tmp));
2167 if (strlen(topic) > sizeof(tmp) - 1) {
2168 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2172 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
2178 chanrec = silc_channel_find_entry(server, channel);
2180 g_free_not_null(chanrec->topic);
2181 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2182 signal_emit("channel topic changed", 1, chanrec);
2184 printformat_module("fe-common/silc", server, channel->channel_name,
2185 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2186 channel->channel_name, topic);
2188 printformat_module("fe-common/silc", server, channel->channel_name,
2189 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2190 channel->channel_name);
2196 case SILC_COMMAND_WATCH:
2199 case SILC_COMMAND_STATS:
2201 SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2202 my_router_ops, cell_clients, cell_channels, cell_servers,
2203 clients, channels, servers, routers, server_ops, router_ops;
2205 SilcBufferStruct buf;
2206 unsigned char *tmp_buf;
2208 const char *tmptime;
2209 int days, hours, mins, secs;
2214 tmp_buf = va_arg(vp, unsigned char *);
2215 buf_len = va_arg(vp, SilcUInt32);
2217 if (!tmp_buf || !buf_len) {
2218 printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2222 /* Get statistics structure */
2223 silc_buffer_set(&buf, tmp_buf, buf_len);
2224 silc_buffer_unformat(&buf,
2225 SILC_STR_UI_INT(&starttime),
2226 SILC_STR_UI_INT(&uptime),
2227 SILC_STR_UI_INT(&my_clients),
2228 SILC_STR_UI_INT(&my_channels),
2229 SILC_STR_UI_INT(&my_server_ops),
2230 SILC_STR_UI_INT(&my_router_ops),
2231 SILC_STR_UI_INT(&cell_clients),
2232 SILC_STR_UI_INT(&cell_channels),
2233 SILC_STR_UI_INT(&cell_servers),
2234 SILC_STR_UI_INT(&clients),
2235 SILC_STR_UI_INT(&channels),
2236 SILC_STR_UI_INT(&servers),
2237 SILC_STR_UI_INT(&routers),
2238 SILC_STR_UI_INT(&server_ops),
2239 SILC_STR_UI_INT(&router_ops),
2242 tmptime = silc_get_time(starttime);
2243 printformat_module("fe-common/silc", server, NULL,
2244 MSGLEVEL_CRAP, SILCTXT_STATS,
2245 "Local server start time", tmptime);
2247 days = uptime / (24 * 60 * 60);
2248 uptime -= days * (24 * 60 * 60);
2249 hours = uptime / (60 * 60);
2250 uptime -= hours * (60 * 60);
2252 uptime -= mins * 60;
2254 snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2255 days, hours, mins, secs);
2256 printformat_module("fe-common/silc", server, NULL,
2257 MSGLEVEL_CRAP, SILCTXT_STATS,
2258 "Local server uptime", tmp);
2260 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2261 printformat_module("fe-common/silc", server, NULL,
2262 MSGLEVEL_CRAP, SILCTXT_STATS,
2263 "Local server clients", tmp);
2265 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2266 printformat_module("fe-common/silc", server, NULL,
2267 MSGLEVEL_CRAP, SILCTXT_STATS,
2268 "Local server channels", tmp);
2270 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2271 printformat_module("fe-common/silc", server, NULL,
2272 MSGLEVEL_CRAP, SILCTXT_STATS,
2273 "Local server operators", tmp);
2275 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2276 printformat_module("fe-common/silc", server, NULL,
2277 MSGLEVEL_CRAP, SILCTXT_STATS,
2278 "Local router operators", tmp);
2280 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2281 printformat_module("fe-common/silc", server, NULL,
2282 MSGLEVEL_CRAP, SILCTXT_STATS,
2283 "Local cell clients", tmp);
2285 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2286 printformat_module("fe-common/silc", server, NULL,
2287 MSGLEVEL_CRAP, SILCTXT_STATS,
2288 "Local cell channels", tmp);
2290 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2291 printformat_module("fe-common/silc", server, NULL,
2292 MSGLEVEL_CRAP, SILCTXT_STATS,
2293 "Local cell servers", tmp);
2295 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2296 printformat_module("fe-common/silc", server, NULL,
2297 MSGLEVEL_CRAP, SILCTXT_STATS,
2298 "Total clients", tmp);
2300 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2301 printformat_module("fe-common/silc", server, NULL,
2302 MSGLEVEL_CRAP, SILCTXT_STATS,
2303 "Total channels", tmp);
2305 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2306 printformat_module("fe-common/silc", server, NULL,
2307 MSGLEVEL_CRAP, SILCTXT_STATS,
2308 "Total servers", tmp);
2310 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2311 printformat_module("fe-common/silc", server, NULL,
2312 MSGLEVEL_CRAP, SILCTXT_STATS,
2313 "Total routers", tmp);
2315 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2316 printformat_module("fe-common/silc", server, NULL,
2317 MSGLEVEL_CRAP, SILCTXT_STATS,
2318 "Total server operators", tmp);
2320 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2321 printformat_module("fe-common/silc", server, NULL,
2322 MSGLEVEL_CRAP, SILCTXT_STATS,
2323 "Total router operators", tmp);
2327 case SILC_COMMAND_CMODE:
2329 SilcChannelEntry channel_entry;
2330 SilcBuffer channel_pubkeys;
2332 channel_entry = va_arg(vp, SilcChannelEntry);
2333 (void)va_arg(vp, SilcUInt32);
2334 (void)va_arg(vp, SilcPublicKey);
2335 channel_pubkeys = va_arg(vp, SilcBuffer);
2337 if (!success || !cmode_list_chpks ||
2338 !channel_entry || !channel_entry->channel_name)
2341 /* Print the channel public key list */
2342 if (channel_pubkeys)
2343 silc_parse_channel_public_keys(server, channel_entry, channel_pubkeys);
2345 printformat_module("fe-common/silc", server, NULL,
2346 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2347 channel_entry->channel_name);
2358 SilcClientConnection conn;
2364 SilcSKEPKType pk_type;
2365 SilcVerifyPublicKey completion;
2369 static void verify_public_key_completion(const char *line, void *context)
2371 PublicKeyVerify verify = (PublicKeyVerify)context;
2373 if (line[0] == 'Y' || line[0] == 'y') {
2374 /* Call the completion */
2375 if (verify->completion)
2376 verify->completion(TRUE, verify->context);
2378 /* Save the key for future checking */
2379 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
2380 verify->pk_len, SILC_PKCS_FILE_PEM);
2382 /* Call the completion */
2383 if (verify->completion)
2384 verify->completion(FALSE, verify->context);
2386 printformat_module("fe-common/silc", NULL, NULL,
2387 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2388 verify->entity_name ? verify->entity_name :
2392 silc_free(verify->filename);
2393 silc_free(verify->entity);
2394 silc_free(verify->entity_name);
2395 silc_free(verify->pk);
2399 /* Internal routine to verify public key. If the `completion' is provided
2400 it will be called to indicate whether public was verified or not. For
2401 server/router public key this will check for filename that includes the
2402 remote host's IP address and remote host's hostname. */
2405 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2406 const char *name, SilcSocketType conn_type,
2407 unsigned char *pk, SilcUInt32 pk_len,
2408 SilcSKEPKType pk_type,
2409 SilcVerifyPublicKey completion, void *context)
2412 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2413 char *fingerprint, *babbleprint, *format;
2416 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2417 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
2418 "server" : "client");
2419 PublicKeyVerify verify;
2421 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2422 printformat_module("fe-common/silc", NULL, NULL,
2423 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2426 completion(FALSE, context);
2430 pw = getpwuid(getuid());
2433 completion(FALSE, context);
2437 memset(filename, 0, sizeof(filename));
2438 memset(filename2, 0, sizeof(filename2));
2439 memset(file, 0, sizeof(file));
2441 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2442 conn_type == SILC_SOCKET_TYPE_ROUTER) {
2444 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2445 conn->sock->ip, conn->sock->port);
2446 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2447 get_irssi_dir(), entity, file);
2449 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2450 conn->sock->hostname, conn->sock->port);
2451 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2452 get_irssi_dir(), entity, file);
2457 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2458 name, conn->sock->port);
2459 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2460 get_irssi_dir(), entity, file);
2465 /* Replace all whitespaces with `_'. */
2466 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2467 for (i = 0; i < strlen(fingerprint); i++)
2468 if (fingerprint[i] == ' ')
2469 fingerprint[i] = '_';
2471 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2472 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2473 get_irssi_dir(), entity, file);
2474 silc_free(fingerprint);
2479 /* Take fingerprint of the public key */
2480 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2481 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2483 verify = silc_calloc(1, sizeof(*verify));
2484 verify->client = client;
2485 verify->conn = conn;
2486 verify->filename = strdup(ipf);
2487 verify->entity = strdup(entity);
2488 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2489 (name ? strdup(name) : strdup(conn->sock->hostname))
2491 verify->pk = silc_memdup(pk, pk_len);
2492 verify->pk_len = pk_len;
2493 verify->pk_type = pk_type;
2494 verify->completion = completion;
2495 verify->context = context;
2497 /* Check whether this key already exists */
2498 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2499 /* Key does not exist, ask user to verify the key and save it */
2501 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2502 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2503 verify->entity_name : entity);
2504 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2505 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2506 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2507 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2508 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2509 SILCTXT_PUBKEY_ACCEPT);
2510 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2513 silc_free(fingerprint);
2516 /* The key already exists, verify it. */
2517 SilcPublicKey public_key;
2518 unsigned char *encpk;
2519 SilcUInt32 encpk_len;
2521 /* Load the key file, try for both IP filename and hostname filename */
2522 if (!silc_pkcs_load_public_key(ipf, &public_key,
2523 SILC_PKCS_FILE_PEM) &&
2524 !silc_pkcs_load_public_key(ipf, &public_key,
2525 SILC_PKCS_FILE_BIN) &&
2526 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
2527 SILC_PKCS_FILE_PEM) &&
2528 !silc_pkcs_load_public_key(hostf, &public_key,
2529 SILC_PKCS_FILE_BIN)))) {
2530 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2531 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2532 verify->entity_name : entity);
2533 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2534 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2535 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2536 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2537 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2538 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2539 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2540 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2541 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2544 silc_free(fingerprint);
2548 /* Encode the key data */
2549 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2551 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2552 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2553 verify->entity_name : entity);
2554 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2555 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2556 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2557 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2558 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2559 SILCTXT_PUBKEY_MALFORMED, entity);
2560 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2561 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2562 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2565 silc_free(fingerprint);
2569 /* Compare the keys */
2570 if (memcmp(encpk, pk, encpk_len)) {
2571 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2572 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2573 verify->entity_name : entity);
2574 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2575 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2576 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2577 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2578 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2579 SILCTXT_PUBKEY_NO_MATCH, entity);
2580 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2581 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2582 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2583 SILCTXT_PUBKEY_MITM_ATTACK, entity);
2585 /* Ask user to verify the key and save it */
2586 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2587 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2588 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2591 silc_free(fingerprint);
2595 /* Local copy matched */
2597 completion(TRUE, context);
2598 silc_free(fingerprint);
2599 silc_free(verify->filename);
2600 silc_free(verify->entity);
2601 silc_free(verify->entity_name);
2602 silc_free(verify->pk);
2607 /* Verifies received public key. The `conn_type' indicates which entity
2608 (server, client etc.) has sent the public key. If user decides to trust
2609 the key may be saved as trusted public key for later use. The
2610 `completion' must be called after the public key has been verified. */
2613 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2614 SilcSocketType conn_type, unsigned char *pk,
2615 SilcUInt32 pk_len, SilcSKEPKType pk_type,
2616 SilcVerifyPublicKey completion, void *context)
2618 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2620 completion, context);
2623 /* Asks passphrase from user on the input line. */
2626 SilcAskPassphrase completion;
2630 void ask_passphrase_completion(const char *passphrase, void *context)
2632 AskPassphrase p = (AskPassphrase)context;
2633 if (passphrase && passphrase[0] == '\0')
2635 p->completion((unsigned char *)passphrase,
2636 passphrase ? strlen(passphrase) : 0, p->context);
2640 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2641 SilcAskPassphrase completion, void *context)
2643 AskPassphrase p = silc_calloc(1, sizeof(*p));
2644 p->completion = completion;
2645 p->context = context;
2647 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2648 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2652 SilcGetAuthMeth completion;
2654 } *InternalGetAuthMethod;
2656 /* Callback called when we've received the authentication method information
2657 from the server after we've requested it. This will get the authentication
2658 data from the user if needed. */
2660 static void silc_get_auth_method_callback(SilcClient client,
2661 SilcClientConnection conn,
2662 SilcAuthMethod auth_meth,
2665 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2667 SILC_LOG_DEBUG(("Start"));
2669 switch (auth_meth) {
2670 case SILC_AUTH_NONE:
2671 /* No authentication required. */
2672 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2674 case SILC_AUTH_PASSWORD:
2676 /* Check whether we find the password for this server in our
2677 configuration. If not, then don't provide so library will ask
2678 it from the user. */
2679 SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2681 if (!setup || !setup->password) {
2682 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2686 (*internal->completion)(TRUE, auth_meth, setup->password,
2687 strlen(setup->password), internal->context);
2690 case SILC_AUTH_PUBLIC_KEY:
2691 /* Do not get the authentication data now, the library will generate
2692 it using our default key, if we do not provide it here. */
2693 /* XXX In the future when we support multiple local keys and multiple
2694 local certificates we will need to ask from user which one to use. */
2695 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2699 silc_free(internal);
2702 /* Find authentication method and authentication data by hostname and
2703 port. The hostname may be IP address as well. The found authentication
2704 method and authentication data is returned to `auth_meth', `auth_data'
2705 and `auth_data_len'. The function returns TRUE if authentication method
2706 is found and FALSE if not. `conn' may be NULL. */
2708 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2709 char *hostname, SilcUInt16 port,
2710 SilcGetAuthMeth completion, void *context)
2712 InternalGetAuthMethod internal;
2714 SILC_LOG_DEBUG(("Start"));
2716 /* If we do not have this connection configured by the user in a
2717 configuration file then resolve the authentication method from the
2718 server for this session. */
2719 internal = silc_calloc(1, sizeof(*internal));
2720 internal->completion = completion;
2721 internal->context = context;
2723 silc_client_request_authentication_method(client, conn,
2724 silc_get_auth_method_callback,
2728 /* Notifies application that failure packet was received. This is called
2729 if there is some protocol active in the client. The `protocol' is the
2730 protocol context. The `failure' is opaque pointer to the failure
2731 indication. Note, that the `failure' is protocol dependant and application
2732 must explicitly cast it to correct type. Usually `failure' is 32 bit
2733 failure type (see protocol specs for all protocol failure types). */
2735 void silc_failure(SilcClient client, SilcClientConnection conn,
2736 SilcProtocol protocol, void *failure)
2738 SILC_LOG_DEBUG(("Start"));
2740 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2741 SilcSKEStatus status = (SilcSKEStatus)failure;
2743 if (status == SILC_SKE_STATUS_BAD_VERSION)
2744 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2745 SILCTXT_KE_BAD_VERSION);
2746 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2747 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2748 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2749 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2750 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2751 SILCTXT_KE_UNKNOWN_GROUP);
2752 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2753 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2754 SILCTXT_KE_UNKNOWN_CIPHER);
2755 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2756 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2757 SILCTXT_KE_UNKNOWN_PKCS);
2758 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2759 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2760 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2761 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2762 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2763 SILCTXT_KE_UNKNOWN_HMAC);
2764 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2765 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2766 SILCTXT_KE_INCORRECT_SIGNATURE);
2767 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2768 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2769 SILCTXT_KE_INVALID_COOKIE);
2772 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2773 SilcUInt32 err = (SilcUInt32)failure;
2775 if (err == SILC_AUTH_FAILED)
2776 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2777 SILCTXT_AUTH_FAILED);
2781 /* Asks whether the user would like to perform the key agreement protocol.
2782 This is called after we have received an key agreement packet or an
2783 reply to our key agreement packet. This returns TRUE if the user wants
2784 the library to perform the key agreement protocol and FALSE if it is not
2785 desired (application may start it later by calling the function
2786 silc_client_perform_key_agreement). */
2788 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2789 SilcClientEntry client_entry, const char *hostname,
2790 SilcUInt16 port, SilcKeyAgreementCallback *completion,
2795 SILC_LOG_DEBUG(("Start"));
2797 /* We will just display the info on the screen and return FALSE and user
2798 will have to start the key agreement with a command. */
2801 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2804 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2805 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2807 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2808 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2809 client_entry->nickname, hostname, portstr);
2817 /* Notifies application that file transfer protocol session is being
2818 requested by the remote client indicated by the `client_entry' from
2819 the `hostname' and `port'. The `session_id' is the file transfer
2820 session and it can be used to either accept or reject the file
2821 transfer request, by calling the silc_client_file_receive or
2822 silc_client_file_close, respectively. */
2824 void silc_ftp(SilcClient client, SilcClientConnection conn,
2825 SilcClientEntry client_entry, SilcUInt32 session_id,
2826 const char *hostname, SilcUInt16 port)
2828 SILC_SERVER_REC *server;
2830 FtpSession ftp = NULL;
2832 SILC_LOG_DEBUG(("Start"));
2834 server = conn->context;
2836 silc_dlist_start(server->ftp_sessions);
2837 while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2838 if (ftp->client_entry == client_entry &&
2839 ftp->session_id == session_id) {
2840 server->current_session = ftp;
2844 if (ftp == SILC_LIST_END) {
2845 ftp = silc_calloc(1, sizeof(*ftp));
2846 ftp->client_entry = client_entry;
2847 ftp->session_id = session_id;
2850 silc_dlist_add(server->ftp_sessions, ftp);
2851 server->current_session = ftp;
2855 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2858 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2859 SILCTXT_FILE_REQUEST, client_entry->nickname);
2861 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2862 SILCTXT_FILE_REQUEST_HOST,
2863 client_entry->nickname, hostname, portstr);
2866 /* Delivers SILC session detachment data indicated by `detach_data' to the
2867 application. If application has issued SILC_COMMAND_DETACH command
2868 the client session in the SILC network is not quit. The client remains
2869 in the network but is detached. The detachment data may be used later
2870 to resume the session in the SILC Network. The appliation is
2871 responsible of saving the `detach_data', to for example in a file.
2873 The detachment data can be given as argument to the functions
2874 silc_client_connect_to_server, or silc_client_add_connection when
2875 creating connection to remote server, inside SilcClientConnectionParams
2876 structure. If it is provided the client library will attempt to resume
2877 the session in the network. After the connection is created
2878 successfully, the application is responsible of setting the user
2879 interface for user into the same state it was before detaching (showing
2880 same channels, channel modes, etc). It can do this by fetching the
2881 information (like joined channels) from the client library. */
2884 silc_detach(SilcClient client, SilcClientConnection conn,
2885 const unsigned char *detach_data, SilcUInt32 detach_data_len)
2889 /* Save the detachment data to file. */
2891 memset(file, 0, sizeof(file));
2892 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2893 silc_file_writefile(file, detach_data, detach_data_len);
2897 /* SILC client operations */
2898 SilcClientOperations ops = {
2900 silc_channel_message,
2901 silc_private_message,
2907 silc_get_auth_method,
2908 silc_verify_public_key,
2909 silc_ask_passphrase,