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 "special-vars.h"
38 #include "fe-common/core/printtext.h"
39 #include "fe-common/core/fe-channels.h"
40 #include "fe-common/core/keyboard.h"
41 #include "fe-common/core/window-items.h"
42 #include "fe-common/silc/module-formats.h"
47 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
48 const char *name, SilcSocketType conn_type,
49 unsigned char *pk, SilcUInt32 pk_len,
50 SilcSKEPKType pk_type,
51 SilcVerifyPublicKey completion, void *context);
53 char *silc_get_session_filename(SILC_SERVER_REC *server)
55 char *file, *expanded;
57 expanded = parse_special_string(settings_get_str("session_filename"),
58 SERVER(server), NULL, "", NULL, 0);
60 file = silc_calloc(1, strlen(expanded) + 255);
61 snprintf(file, strlen(expanded) + 255, "%s/%s", get_irssi_dir(), expanded);
67 static void silc_get_umode_string(SilcUInt32 mode, char *buf,
70 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
71 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
72 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
74 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
75 "[SILC operator]" : "[unknown mode]");
77 if (mode & SILC_UMODE_GONE)
78 strcat(buf, " [away]");
79 if (mode & SILC_UMODE_INDISPOSED)
80 strcat(buf, " [indisposed]");
81 if (mode & SILC_UMODE_BUSY)
82 strcat(buf, " [busy]");
83 if (mode & SILC_UMODE_PAGE)
84 strcat(buf, " [page to reach]");
85 if (mode & SILC_UMODE_HYPER)
86 strcat(buf, " [hyper active]");
87 if (mode & SILC_UMODE_ROBOT)
88 strcat(buf, " [robot]");
89 if (mode & SILC_UMODE_ANONYMOUS)
90 strcat(buf, " [anonymous]");
91 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
92 strcat(buf, " [blocks private messages]");
93 if (mode & SILC_UMODE_DETACHED)
94 strcat(buf, " [detached]");
95 if (mode & SILC_UMODE_REJECT_WATCHING)
96 strcat(buf, " [rejects watching]");
97 if (mode & SILC_UMODE_BLOCK_INVITE)
98 strcat(buf, " [blocks invites]");
101 /* print "nick appears as" message to every channel of a server */
103 silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
104 const char *newnick, const char *oldnick,
107 if (ignore_check(SERVER(server), oldnick, address,
108 channel, newnick, MSGLEVEL_NICKS))
111 printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS,
112 SILCTXT_CHANNEL_APPEARS,
113 oldnick, newnick, channel, address);
117 silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
118 const char *oldnick, const char *address)
120 GSList *tmp, *windows;
122 /* Print to each channel/query where the nick is.
123 Don't print more than once to the same window. */
126 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
127 CHANNEL_REC *channel = tmp->data;
128 WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
130 if (nicklist_find(channel, newnick) == NULL ||
131 g_slist_find(windows, window) != NULL)
134 windows = g_slist_append(windows, window);
135 silc_print_nick_change_channel(server, channel->visible_name,
136 newnick, oldnick, address);
139 g_slist_free(windows);
142 static void silc_parse_channel_public_keys(SILC_SERVER_REC *server,
143 SilcChannelEntry channel_entry,
144 SilcBuffer channel_pubkeys)
147 SilcArgumentPayload chpks;
149 SilcUInt32 pk_len, type;
151 char *fingerprint, *babbleprint;
152 SilcPublicKey pubkey;
153 SilcPublicKeyIdentifier ident;
155 printformat_module("fe-common/silc", server, NULL,
156 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST,
157 channel_entry->channel_name);
159 SILC_GET16_MSB(argc, channel_pubkeys->data);
160 chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
161 channel_pubkeys->len - 2, argc);
165 pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
167 fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
168 babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
169 silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
170 ident = silc_pkcs_decode_identifier(pubkey->identifier);
172 printformat_module("fe-common/silc", server, NULL,
173 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST_ENTRY,
174 c++, channel_entry->channel_name,
175 type == 0x00 ? "Added" : "Removed",
176 ident->realname ? ident->realname : "",
177 fingerprint, babbleprint);
179 silc_free(fingerprint);
180 silc_free(babbleprint);
181 silc_pkcs_public_key_free(pubkey);
182 silc_pkcs_free_identifier(ident);
183 pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
186 silc_argument_payload_free(chpks);
189 void silc_say(SilcClient client, SilcClientConnection conn,
190 SilcClientMessageType type, char *msg, ...)
192 SILC_SERVER_REC *server;
196 server = conn == NULL ? NULL : conn->context;
199 str = g_strdup_vprintf(msg, va);
200 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
205 void silc_say_error(char *msg, ...)
211 str = g_strdup_vprintf(msg, va);
212 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
218 /* try to verify a message using locally stored public key data */
219 int verify_message_signature(SilcClientEntry sender,
220 SilcMessageSignedPayload sig,
221 SilcMessagePayload message)
224 char file[256], filename[256];
225 char *fingerprint, *fingerprint2;
226 unsigned char *pk_data;
227 SilcUInt32 pk_datalen;
229 int ret = SILC_MSG_SIGNED_VERIFIED, i;
232 return SILC_MSG_SIGNED_UNKNOWN;
234 /* get public key from the signature payload and compare it with the
235 one stored in the client entry */
236 pk = silc_message_signed_get_public_key(sig, &pk_data, &pk_datalen);
239 fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen);
241 if (sender->fingerprint) {
242 fingerprint2 = silc_fingerprint(sender->fingerprint,
243 sender->fingerprint_len);
244 if (strcmp(fingerprint, fingerprint2)) {
245 /* since the public key differs from the senders public key, the
246 verification _failed_ */
247 silc_pkcs_public_key_free(pk);
248 silc_free(fingerprint);
249 ret = SILC_MSG_SIGNED_UNKNOWN;
251 silc_free(fingerprint2);
253 } else if (sender->fingerprint)
254 fingerprint = silc_fingerprint(sender->fingerprint,
255 sender->fingerprint_len);
257 /* no idea, who or what signed that message ... */
258 return SILC_MSG_SIGNED_UNKNOWN;
260 /* search our local client key cache */
261 for (i = 0; i < strlen(fingerprint); i++)
262 if (fingerprint[i] == ' ')
263 fingerprint[i] = '_';
265 snprintf(file, sizeof(file) - 1, "clientkey_%s.pub", fingerprint);
266 snprintf(filename, sizeof(filename) - 1, "%s/clientkeys/%s",
267 get_irssi_dir(), file);
268 silc_free(fingerprint);
270 if (stat(filename, &st) < 0)
271 /* we don't have the public key cached ... use the one from the sig */
272 ret = SILC_MSG_SIGNED_UNKNOWN;
274 SilcPublicKey cached_pk=NULL;
276 /* try to load the file */
277 if (!silc_pkcs_load_public_key(filename, &cached_pk, SILC_PKCS_FILE_PEM) &&
278 !silc_pkcs_load_public_key(filename, &cached_pk,
279 SILC_PKCS_FILE_BIN)) {
280 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
281 SILCTXT_PUBKEY_COULD_NOT_LOAD, "client");
283 return SILC_MSG_SIGNED_UNKNOWN;
285 ret = SILC_MSG_SIGNED_UNKNOWN;
290 silc_pkcs_public_key_free(pk);
295 /* the public key is now in pk, our "level of trust" in ret */
296 if ((pk) && silc_message_signed_verify(sig, message, pk,
297 silc_client->sha1hash)!= SILC_AUTH_OK)
298 ret = SILC_MSG_SIGNED_FAILED;
301 silc_pkcs_public_key_free(pk);
306 char * silc_unescape_data(const char *escaped_data, SilcUInt32 *length)
309 int i = 0, j = 0, len = strlen(escaped_data);
311 data = silc_calloc(len, sizeof(char));
314 ptr = memchr(escaped_data + i, 1, len - i);
316 int inc = (ptr - escaped_data) - i;
317 memcpy(data + j, escaped_data + i, inc);
320 data[j++] = *(ptr + 1) - 1;
322 memcpy(data + j, escaped_data + i, len - i);
332 char * silc_escape_data(const char *data, SilcUInt32 len)
334 char *escaped_data, *ptr, *ptr0, *ptr1;
337 escaped_data = silc_calloc(2 * len, sizeof(char));
340 ptr0 = memchr(data + i, 0, len - i);
341 ptr1 = memchr(data + i, 1, len - i);
343 ptr = (ptr0 < ptr1 ? (ptr0 ? ptr0 : ptr1) : (ptr1 ? ptr1 : ptr0));
346 int inc = (ptr - data) - i;
348 memcpy(escaped_data + j, data + i, inc);
351 escaped_data[j++] = 1;
352 escaped_data[j++] = *(data + i++) + 1;
354 memcpy(escaped_data + j, data + i, len - i);
363 void silc_emit_mime_sig(SILC_SERVER_REC *server, WI_ITEM_REC *item,
364 const char *data, SilcUInt32 data_len, const char *nick,
369 escaped_data = silc_escape_data(data, data_len);
371 signal_emit("mime", 5, server, item, escaped_data, nick, verified);
373 silc_free(escaped_data);
377 /* Message for a channel. The `sender' is the nickname of the sender
378 received in the packet. The `channel_name' is the name of the channel. */
380 void silc_channel_message(SilcClient client, SilcClientConnection conn,
381 SilcClientEntry sender, SilcChannelEntry channel,
382 SilcMessagePayload payload,
383 SilcMessageFlags flags, const unsigned char *message,
384 SilcUInt32 message_len)
386 SILC_SERVER_REC *server;
388 SILC_CHANNEL_REC *chanrec;
391 SILC_LOG_DEBUG(("Start"));
396 server = conn == NULL ? NULL : conn->context;
397 chanrec = silc_channel_find_entry(server, channel);
401 nick = silc_nicklist_find(chanrec, sender);
403 /* We didn't find client but it clearly exists, add it. */
404 SilcChannelUser chu = silc_client_on_channel(channel, sender);
406 nick = silc_nicklist_insert(chanrec, chu, FALSE);
409 /* If the messages is digitally signed, verify it, if possible. */
410 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
411 if (!settings_get_bool("ignore_message_signatures")) {
412 SilcMessageSignedPayload sig = silc_message_get_signature(payload);
413 verified = verify_message_signature(sender, sig, payload);
415 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
419 if (flags & SILC_MESSAGE_FLAG_DATA) {
420 silc_emit_mime_sig(server, (WI_ITEM_REC *)chanrec, message, message_len,
421 nick == NULL ? NULL : nick->nick,
422 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
429 /* FIXME: replace those printformat calls with signals and add signature
430 information to them (if present) */
431 if (flags & SILC_MESSAGE_FLAG_ACTION)
432 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
433 char tmp[256], *cp, *dm = NULL;
434 memset(tmp, 0, sizeof(tmp));
436 if(message_len > sizeof(tmp) - 1) {
437 dm = silc_calloc(message_len + 1, sizeof(*dm));
440 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
442 printformat_module("fe-common/silc", server, channel->channel_name,
443 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
444 nick == NULL ? "[<unknown>]" : nick->nick, cp);
447 printformat_module("fe-common/silc", server, channel->channel_name,
448 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
449 nick == NULL ? "[<unknown>]" : nick->nick,
452 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
453 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
454 char tmp[256], *cp, *dm = NULL;
455 memset(tmp, 0, sizeof(tmp));
457 if(message_len > sizeof(tmp) - 1) {
458 dm = silc_calloc(message_len + 1, sizeof(*dm));
461 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
463 printformat_module("fe-common/silc", server, channel->channel_name,
464 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
465 nick == NULL ? "[<unknown>]" : nick->nick, cp);
468 printformat_module("fe-common/silc", server, channel->channel_name,
469 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
470 nick == NULL ? "[<unknown>]" : nick->nick,
474 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
475 char tmp[256], *cp, *dm = NULL;
477 memset(tmp, 0, sizeof(tmp));
479 if (message_len > sizeof(tmp) - 1) {
480 dm = silc_calloc(message_len + 1, sizeof(*dm));
484 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
486 if (flags & SILC_MESSAGE_FLAG_SIGNED)
487 signal_emit("message signed_public", 6, server, cp,
488 nick == NULL ? "[<unknown>]" : nick->nick,
489 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
490 chanrec->name, verified);
492 signal_emit("message public", 6, server, cp,
493 nick == NULL ? "[<unknown>]" : nick->nick,
494 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
495 chanrec->name, nick);
500 if (flags & SILC_MESSAGE_FLAG_SIGNED)
501 signal_emit("message signed_public", 6, server, message,
502 nick == NULL ? "[<unknown>]" : nick->nick,
503 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
504 chanrec->name, verified);
506 signal_emit("message public", 6, server, message,
507 nick == NULL ? "[<unknown>]" : nick->nick,
508 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
509 chanrec->name, nick);
513 /* Private message to the client. The `sender' is the nickname of the
514 sender received in the packet. */
516 void silc_private_message(SilcClient client, SilcClientConnection conn,
517 SilcClientEntry sender, SilcMessagePayload payload,
518 SilcMessageFlags flags,
519 const unsigned char *message,
520 SilcUInt32 message_len)
522 SILC_SERVER_REC *server;
526 SILC_LOG_DEBUG(("Start"));
528 server = conn == NULL ? NULL : conn->context;
529 memset(userhost, 0, sizeof(userhost));
530 if (sender->username)
531 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
532 sender->username, sender->hostname);
534 /* If the messages is digitally signed, verify it, if possible. */
535 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
536 if (!settings_get_bool("ignore_message_signatures")) {
537 SilcMessageSignedPayload sig = silc_message_get_signature(payload);
538 verified = verify_message_signature(sender, sig, payload);
540 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
544 if (flags & SILC_MESSAGE_FLAG_DATA) {
545 silc_emit_mime_sig(server,
547 (WI_ITEM_REC *)query_find(SERVER(server), sender->nickname) :
549 message, message_len,
550 sender->nickname ? sender->nickname : "[<unknown>]",
551 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
558 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
559 char tmp[256], *cp, *dm = NULL;
561 memset(tmp, 0, sizeof(tmp));
563 if (message_len > sizeof(tmp) - 1) {
564 dm = silc_calloc(message_len + 1, sizeof(*dm));
568 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
570 if (flags & SILC_MESSAGE_FLAG_SIGNED)
571 signal_emit("message signed_private", 5, server, cp,
572 sender->nickname ? sender->nickname : "[<unknown>]",
573 sender->username ? userhost : NULL, verified);
575 signal_emit("message private", 4, server, cp,
576 sender->nickname ? sender->nickname : "[<unknown>]",
577 sender->username ? userhost : NULL);
582 if (flags & SILC_MESSAGE_FLAG_SIGNED)
583 signal_emit("message signed_private", 5, server, message,
584 sender->nickname ? sender->nickname : "[<unknown>]",
585 sender->username ? userhost : NULL, verified);
587 signal_emit("message private", 4, server, message,
588 sender->nickname ? sender->nickname : "[<unknown>]",
589 sender->username ? userhost : NULL);
592 /* Notify message to the client. The notify arguments are sent in the
593 same order as servers sends them. The arguments are same as received
594 from the server except for ID's. If ID is received application receives
595 the corresponding entry to the ID. For example, if Client ID is received
596 application receives SilcClientEntry. Also, if the notify type is
597 for channel the channel entry is sent to application (even if server
598 does not send it). */
600 void silc_notify(SilcClient client, SilcClientConnection conn,
601 SilcNotifyType type, ...)
604 SILC_SERVER_REC *server;
605 SILC_CHANNEL_REC *chanrec;
606 SILC_NICK_REC *nickrec;
607 SilcClientEntry client_entry, client_entry2;
608 SilcChannelEntry channel, channel2;
609 SilcServerEntry server_entry;
615 GSList *list1, *list_tmp;
618 SILC_LOG_DEBUG(("Start"));
622 server = conn == NULL ? NULL : conn->context;
625 case SILC_NOTIFY_TYPE_NONE:
626 /* Some generic notice from server */
627 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
630 case SILC_NOTIFY_TYPE_INVITE:
632 * Invited or modified invite list.
635 SILC_LOG_DEBUG(("Notify: INVITE"));
637 channel = va_arg(va, SilcChannelEntry);
638 name = va_arg(va, char *);
639 client_entry = va_arg(va, SilcClientEntry);
641 memset(buf, 0, sizeof(buf));
642 snprintf(buf, sizeof(buf) - 1, "%s@%s",
643 client_entry->username, client_entry->hostname);
644 signal_emit("message invite", 4, server, channel ? channel->channel_name :
645 name, client_entry->nickname, buf);
648 case SILC_NOTIFY_TYPE_JOIN:
653 SILC_LOG_DEBUG(("Notify: JOIN"));
655 client_entry = va_arg(va, SilcClientEntry);
656 channel = va_arg(va, SilcChannelEntry);
658 if (client_entry == server->conn->local_entry) {
659 /* You joined to channel */
660 chanrec = silc_channel_find(server, channel->channel_name);
661 if (chanrec != NULL && !chanrec->joined)
662 chanrec->entry = channel;
664 chanrec = silc_channel_find_entry(server, channel);
665 if (chanrec != NULL) {
666 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
668 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
672 memset(buf, 0, sizeof(buf));
673 if (client_entry->username)
674 snprintf(buf, sizeof(buf) - 1, "%s@%s",
675 client_entry->username, client_entry->hostname);
676 signal_emit("message join", 4, server, channel->channel_name,
677 client_entry->nickname,
678 client_entry->username == NULL ? "" : buf);
681 case SILC_NOTIFY_TYPE_LEAVE:
686 SILC_LOG_DEBUG(("Notify: LEAVE"));
688 client_entry = va_arg(va, SilcClientEntry);
689 channel = va_arg(va, SilcChannelEntry);
691 memset(buf, 0, sizeof(buf));
692 if (client_entry->username)
693 snprintf(buf, sizeof(buf) - 1, "%s@%s",
694 client_entry->username, client_entry->hostname);
695 signal_emit("message part", 5, server, channel->channel_name,
696 client_entry->nickname, client_entry->username ?
697 buf : "", client_entry->nickname);
699 chanrec = silc_channel_find_entry(server, channel);
700 if (chanrec != NULL) {
701 nickrec = silc_nicklist_find(chanrec, client_entry);
703 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
707 case SILC_NOTIFY_TYPE_SIGNOFF:
712 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
714 client_entry = va_arg(va, SilcClientEntry);
715 tmp = va_arg(va, char *);
717 silc_server_free_ftp(server, client_entry);
719 /* Print only if we have the nickname. If this cliente has just quit
720 when we were only resolving it, it is possible we don't have the
722 if (client_entry->nickname) {
723 memset(buf, 0, sizeof(buf));
724 if (client_entry->username)
725 snprintf(buf, sizeof(buf) - 1, "%s@%s",
726 client_entry->username, client_entry->hostname);
727 signal_emit("message quit", 4, server, client_entry->nickname,
728 client_entry->username ? buf : "",
732 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
733 for (list_tmp = list1; list_tmp != NULL; list_tmp =
734 list_tmp->next->next) {
735 CHANNEL_REC *channel = list_tmp->data;
736 NICK_REC *nickrec = list_tmp->next->data;
738 nicklist_remove(channel, nickrec);
742 case SILC_NOTIFY_TYPE_TOPIC_SET:
747 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
749 idtype = va_arg(va, int);
750 entry = va_arg(va, void *);
751 tmp = va_arg(va, char *);
752 channel = va_arg(va, SilcChannelEntry);
754 chanrec = silc_channel_find_entry(server, channel);
755 if (chanrec != NULL) {
756 char tmp2[256], *cp, *dm = NULL;
758 g_free_not_null(chanrec->topic);
759 if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) {
760 memset(tmp2, 0, sizeof(tmp2));
762 if (strlen(tmp) > sizeof(tmp2) - 1) {
763 dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm));
767 silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE,
772 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
773 signal_emit("channel topic changed", 1, chanrec);
778 if (idtype == SILC_ID_CLIENT) {
779 client_entry = (SilcClientEntry)entry;
780 memset(buf, 0, sizeof(buf));
781 snprintf(buf, sizeof(buf) - 1, "%s@%s",
782 client_entry->username, client_entry->hostname);
783 signal_emit("message topic", 5, server, channel->channel_name,
784 tmp, client_entry->nickname, buf);
785 } else if (idtype == SILC_ID_SERVER) {
786 server_entry = (SilcServerEntry)entry;
787 signal_emit("message topic", 5, server, channel->channel_name,
788 tmp, server_entry->server_name,
789 server_entry->server_name);
790 } else if (idtype == SILC_ID_CHANNEL) {
791 channel = (SilcChannelEntry)entry;
792 signal_emit("message topic", 5, server, channel->channel_name,
793 tmp, channel->channel_name, channel->channel_name);
797 case SILC_NOTIFY_TYPE_NICK_CHANGE:
802 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
804 client_entry = va_arg(va, SilcClientEntry);
805 client_entry2 = va_arg(va, SilcClientEntry);
807 if (!strcmp(client_entry->nickname, client_entry2->nickname))
810 memset(buf, 0, sizeof(buf));
811 snprintf(buf, sizeof(buf) - 1, "%s@%s",
812 client_entry2->username, client_entry2->hostname);
813 nicklist_rename_unique(SERVER(server),
814 client_entry, client_entry->nickname,
815 client_entry2, client_entry2->nickname);
816 signal_emit("message nick", 4, server, client_entry2->nickname,
817 client_entry->nickname, buf);
820 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
822 * Changed channel mode.
825 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
827 idtype = va_arg(va, int);
828 entry = va_arg(va, void *);
829 mode = va_arg(va, SilcUInt32);
830 (void)va_arg(va, char *); /* cipher */
831 (void)va_arg(va, char *); /* hmac */
832 (void)va_arg(va, char *); /* passphrase */
833 (void)va_arg(va, SilcPublicKey); /* founder key */
834 buffer = va_arg(va, SilcBuffer); /* channel public keys */
835 channel = va_arg(va, SilcChannelEntry);
837 tmp = silc_client_chmode(mode,
838 channel->channel_key ?
839 silc_cipher_get_name(channel->channel_key) : "",
841 silc_hmac_get_name(channel->hmac) : "");
843 chanrec = silc_channel_find_entry(server, channel);
844 if (chanrec != NULL) {
845 g_free_not_null(chanrec->mode);
846 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
847 signal_emit("channel mode changed", 1, chanrec);
850 if (idtype == SILC_ID_CLIENT) {
851 client_entry = (SilcClientEntry)entry;
852 printformat_module("fe-common/silc", server, channel->channel_name,
853 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
854 channel->channel_name, tmp ? tmp : "removed all",
855 client_entry->nickname);
856 } else if (idtype == SILC_ID_SERVER) {
857 server_entry = (SilcServerEntry)entry;
858 printformat_module("fe-common/silc", server, channel->channel_name,
859 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
860 channel->channel_name, tmp ? tmp : "removed all",
861 server_entry->server_name);
862 } else if (idtype == SILC_ID_CHANNEL) {
863 channel2 = (SilcChannelEntry)entry;
864 printformat_module("fe-common/silc", server, channel->channel_name,
865 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
866 channel->channel_name, tmp ? tmp : "removed all",
867 channel2->channel_name);
870 /* Print the channel public key list */
872 silc_parse_channel_public_keys(server, channel, buffer);
877 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
879 * Changed user's mode on channel.
882 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
884 idtype = va_arg(va, int);
885 entry = va_arg(va, void *);
886 mode = va_arg(va, SilcUInt32);
887 client_entry2 = va_arg(va, SilcClientEntry);
888 channel = va_arg(va, SilcChannelEntry);
890 tmp = silc_client_chumode(mode);
891 chanrec = silc_channel_find_entry(server, channel);
892 if (chanrec != NULL) {
895 if (client_entry2 == server->conn->local_entry)
896 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
898 nick = silc_nicklist_find(chanrec, client_entry2);
900 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
901 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
902 signal_emit("nick mode changed", 2, chanrec, nick);
906 if (idtype == SILC_ID_CLIENT) {
907 client_entry = (SilcClientEntry)entry;
908 printformat_module("fe-common/silc", server, channel->channel_name,
909 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
910 channel->channel_name, client_entry2->nickname,
911 tmp ? tmp : "removed all",
912 client_entry->nickname);
913 } else if (idtype == SILC_ID_SERVER) {
914 server_entry = (SilcServerEntry)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 server_entry->server_name);
920 } else if (idtype == SILC_ID_CHANNEL) {
921 channel2 = (SilcChannelEntry)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 channel2->channel_name);
929 if (mode & SILC_CHANNEL_UMODE_CHANFO)
930 printformat_module("fe-common/silc",
931 server, channel->channel_name, MSGLEVEL_CRAP,
932 SILCTXT_CHANNEL_FOUNDER,
933 channel->channel_name, client_entry2->nickname);
935 if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
936 printformat_module("fe-common/silc",
937 server, channel->channel_name, MSGLEVEL_CRAP,
938 SILCTXT_CHANNEL_QUIETED, channel->channel_name);
943 case SILC_NOTIFY_TYPE_MOTD:
948 SILC_LOG_DEBUG(("Notify: MOTD"));
950 tmp = va_arg(va, char *);
952 if (!settings_get_bool("skip_motd"))
953 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
956 case SILC_NOTIFY_TYPE_KICKED:
958 * Someone was kicked from channel.
961 SILC_LOG_DEBUG(("Notify: KICKED"));
963 client_entry = va_arg(va, SilcClientEntry);
964 tmp = va_arg(va, char *);
965 client_entry2 = va_arg(va, SilcClientEntry);
966 channel = va_arg(va, SilcChannelEntry);
968 chanrec = silc_channel_find_entry(server, channel);
970 if (client_entry == conn->local_entry) {
971 printformat_module("fe-common/silc", server, channel->channel_name,
972 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
973 channel->channel_name,
974 client_entry ? client_entry2->nickname : "",
977 chanrec->kicked = TRUE;
978 channel_destroy((CHANNEL_REC *)chanrec);
981 printformat_module("fe-common/silc", server, channel->channel_name,
982 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
983 client_entry->nickname, channel->channel_name,
984 client_entry2 ? client_entry2->nickname : "",
988 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
990 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
995 case SILC_NOTIFY_TYPE_KILLED:
997 * Someone was killed from the network.
1000 SILC_LOG_DEBUG(("Notify: KILLED"));
1002 client_entry = va_arg(va, SilcClientEntry);
1003 tmp = va_arg(va, char *);
1004 idtype = va_arg(va, int);
1005 entry = va_arg(va, SilcClientEntry);
1007 if (client_entry == conn->local_entry) {
1008 if (idtype == SILC_ID_CLIENT) {
1009 client_entry2 = (SilcClientEntry)entry;
1010 printformat_module("fe-common/silc", server, NULL,
1011 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1012 client_entry2 ? client_entry2->nickname : "",
1014 } else if (idtype == SILC_ID_SERVER) {
1015 server_entry = (SilcServerEntry)entry;
1016 printformat_module("fe-common/silc", server, NULL,
1017 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1018 server_entry->server_name, tmp ? tmp : "");
1019 } else if (idtype == SILC_ID_CHANNEL) {
1020 channel = (SilcChannelEntry)entry;
1021 printformat_module("fe-common/silc", server, NULL,
1022 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1023 channel->channel_name, tmp ? tmp : "");
1026 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1027 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1028 list_tmp->next->next) {
1029 CHANNEL_REC *channel = list_tmp->data;
1030 NICK_REC *nickrec = list_tmp->next->data;
1031 nicklist_remove(channel, nickrec);
1034 if (idtype == SILC_ID_CLIENT) {
1035 client_entry2 = (SilcClientEntry)entry;
1036 printformat_module("fe-common/silc", server, NULL,
1037 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1038 client_entry->nickname,
1039 client_entry2 ? client_entry2->nickname : "",
1041 } else if (idtype == SILC_ID_SERVER) {
1042 server_entry = (SilcServerEntry)entry;
1043 printformat_module("fe-common/silc", server, NULL,
1044 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1045 client_entry->nickname,
1046 server_entry->server_name, tmp ? tmp : "");
1047 } else if (idtype == SILC_ID_CHANNEL) {
1048 channel = (SilcChannelEntry)entry;
1049 printformat_module("fe-common/silc", server, NULL,
1050 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1051 client_entry->nickname,
1052 channel->channel_name, tmp ? tmp : "");
1057 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
1060 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1063 * Server has quit the network.
1066 SilcClientEntry *clients;
1067 SilcUInt32 clients_count;
1069 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1071 (void)va_arg(va, void *);
1072 clients = va_arg(va, SilcClientEntry *);
1073 clients_count = va_arg(va, SilcUInt32);
1075 for (i = 0; i < clients_count; i++) {
1076 memset(buf, 0, sizeof(buf));
1078 /* Print only if we have the nickname. If this client has just quit
1079 when we were only resolving it, it is possible we don't have the
1081 if (clients[i]->nickname) {
1082 if (clients[i]->username)
1083 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1084 clients[i]->username, clients[i]->hostname);
1085 signal_emit("message quit", 4, server, clients[i]->nickname,
1086 clients[i]->username ? buf : "",
1090 silc_server_free_ftp(server, clients[i]);
1092 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
1093 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1094 list_tmp->next->next) {
1095 CHANNEL_REC *channel = list_tmp->data;
1096 NICK_REC *nickrec = list_tmp->next->data;
1097 nicklist_remove(channel, nickrec);
1103 case SILC_NOTIFY_TYPE_ERROR:
1105 SilcStatus error = va_arg(va, int);
1107 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1108 "%s", silc_get_status_message(error));
1112 case SILC_NOTIFY_TYPE_WATCH:
1114 SilcNotifyType notify;
1116 client_entry = va_arg(va, SilcClientEntry);
1117 name = va_arg(va, char *); /* Maybe NULL */
1118 mode = va_arg(va, SilcUInt32);
1119 notify = va_arg(va, int);
1121 if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1123 printformat_module("fe-common/silc", server, NULL,
1124 MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1125 client_entry->nickname, name);
1127 printformat_module("fe-common/silc", server, NULL,
1128 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1129 client_entry->nickname);
1130 } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1131 /* See if client was away and is now present */
1132 if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1133 SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1134 SILC_UMODE_DETACHED)) &&
1135 (client_entry->mode & SILC_UMODE_GONE ||
1136 client_entry->mode & SILC_UMODE_INDISPOSED ||
1137 client_entry->mode & SILC_UMODE_BUSY ||
1138 client_entry->mode & SILC_UMODE_PAGE ||
1139 client_entry->mode & SILC_UMODE_DETACHED)) {
1140 printformat_module("fe-common/silc", server, NULL,
1141 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1142 client_entry->nickname);
1146 memset(buf, 0, sizeof(buf));
1147 silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1148 printformat_module("fe-common/silc", server, NULL,
1149 MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1150 client_entry->nickname, buf);
1152 } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1153 printformat_module("fe-common/silc", server, NULL,
1154 MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1155 client_entry->nickname);
1156 } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1157 notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1158 printformat_module("fe-common/silc", server, NULL,
1159 MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1160 client_entry->nickname);
1161 } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1162 /* Client logged in to the network */
1163 printformat_module("fe-common/silc", server, NULL,
1164 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1165 client_entry->nickname);
1171 /* Unknown notify */
1172 printformat_module("fe-common/silc", server, NULL,
1173 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1180 /* Called to indicate that connection was either successfully established
1181 or connecting failed. This is also the first time application receives
1182 the SilcClientConnection object which it should save somewhere. */
1184 void silc_connect(SilcClient client, SilcClientConnection conn,
1185 SilcClientConnectionStatus status)
1187 SILC_SERVER_REC *server = conn->context;
1189 if (!server || server->disconnected) {
1190 silc_client_close_connection(client, conn);
1195 case SILC_CLIENT_CONN_SUCCESS:
1196 /* We have successfully connected to server */
1197 server->connected = TRUE;
1198 signal_emit("event connected", 1, server);
1201 case SILC_CLIENT_CONN_SUCCESS_RESUME:
1202 /* We have successfully resumed old detached session */
1203 server->connected = TRUE;
1204 signal_emit("event connected", 1, server);
1206 /* If we resumed old session check whether we need to update
1208 if (strcmp(server->nick, conn->local_entry->nickname)) {
1210 old = g_strdup(server->nick);
1211 server_change_nick(SERVER(server), conn->local_entry->nickname);
1212 nicklist_rename_unique(SERVER(server),
1213 conn->local_entry, server->nick,
1214 conn->local_entry, conn->local_entry->nickname);
1215 signal_emit("message own_nick", 4, server, server->nick, old, "");
1219 /* remove the detach data now */
1223 file = silc_get_session_filename(server);
1231 server->connection_lost = TRUE;
1233 server->conn->context = NULL;
1234 server_disconnect(SERVER(server));
1239 /* Called to indicate that connection was disconnected to the server. */
1241 void silc_disconnect(SilcClient client, SilcClientConnection conn,
1242 SilcStatus status, const char *message)
1244 SILC_SERVER_REC *server = conn->context;
1246 SILC_LOG_DEBUG(("Start"));
1248 if (!server || server->connection_lost)
1251 if (server->conn && server->conn->local_entry) {
1252 nicklist_rename_unique(SERVER(server),
1253 server->conn->local_entry, server->nick,
1254 server->conn->local_entry,
1255 silc_client->username);
1256 silc_change_nick(server, silc_client->username);
1260 silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
1261 "Server closed connection: %s (%d) %s",
1262 silc_get_status_message(status), status,
1263 message ? message : "");
1266 server->conn->context = NULL;
1267 server->conn = NULL;
1268 server->connection_lost = TRUE;
1269 server_disconnect(SERVER(server));
1272 /* Command handler. This function is called always in the command function.
1273 If error occurs it will be called as well. `conn' is the associated
1274 client connection. `cmd_context' is the command context that was
1275 originally sent to the command. `success' is FALSE if error occured
1276 during command. `command' is the command being processed. It must be
1277 noted that this is not reply from server. This is merely called just
1278 after application has called the command. Just to tell application
1279 that the command really was processed. */
1281 static bool cmode_list_chpks = FALSE;
1283 void silc_command(SilcClient client, SilcClientConnection conn,
1284 SilcClientCommandContext cmd_context, bool success,
1285 SilcCommand command, SilcStatus status)
1287 SILC_SERVER_REC *server = conn->context;
1289 SILC_LOG_DEBUG(("Start"));
1292 silc_say_error("%s", silc_get_status_message(status));
1298 case SILC_COMMAND_INVITE:
1299 if (cmd_context->argc > 2)
1300 printformat_module("fe-common/silc", server, NULL,
1301 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1302 cmd_context->argv[2],
1303 (cmd_context->argv[1][0] == '*' ?
1304 (char *)conn->current_channel->channel_name :
1305 (char *)cmd_context->argv[1]));
1308 case SILC_COMMAND_DETACH:
1309 server->no_reconnect = TRUE;
1312 case SILC_COMMAND_CMODE:
1313 if (cmd_context->argc == 3 &&
1314 !strcmp(cmd_context->argv[2], "+C"))
1315 cmode_list_chpks = TRUE;
1317 cmode_list_chpks = FALSE;
1326 SilcChannelEntry channel;
1330 /* Client info resolving callback when JOIN command reply is received.
1331 This will cache all users on the channel. */
1333 static void silc_client_join_get_users(SilcClient client,
1334 SilcClientConnection conn,
1335 SilcClientEntry *clients,
1336 SilcUInt32 clients_count,
1339 SilcJoinResolve r = context;
1340 SilcChannelEntry channel = r->channel;
1341 SilcHashTableList htl;
1342 SilcChannelUser chu;
1343 SILC_SERVER_REC *server = conn->context;
1344 SILC_CHANNEL_REC *chanrec;
1345 SilcClientEntry founder = NULL;
1348 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
1349 silc_hash_table_count(channel->user_list)));
1351 if (!clients && r->retry < 1) {
1352 /* Retry to resolve */
1354 silc_client_get_clients_by_channel(client, conn, channel,
1355 silc_client_join_get_users, context);
1359 chanrec = silc_channel_find(server, channel->channel_name);
1360 if (chanrec == NULL)
1363 silc_hash_table_list(channel->user_list, &htl);
1364 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1365 if (!chu->client->nickname)
1367 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1368 founder = chu->client;
1369 silc_nicklist_insert(chanrec, chu, FALSE);
1371 silc_hash_table_list_reset(&htl);
1373 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1374 nicklist_set_own(CHANNEL(chanrec), ownnick);
1375 signal_emit("channel joined", 1, chanrec);
1376 chanrec->entry = channel;
1379 printformat_module("fe-common/silc", server, channel->channel_name,
1380 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1381 channel->channel_name, chanrec->topic);
1384 if (founder == conn->local_entry) {
1385 printformat_module("fe-common/silc",
1386 server, channel->channel_name, MSGLEVEL_CRAP,
1387 SILCTXT_CHANNEL_FOUNDER_YOU,
1388 channel->channel_name);
1389 signal_emit("nick mode changed", 2, chanrec, ownnick);
1391 printformat_module("fe-common/silc",
1392 server, channel->channel_name, MSGLEVEL_CRAP,
1393 SILCTXT_CHANNEL_FOUNDER,
1394 channel->channel_name, founder->nickname);
1400 SilcClientConnection conn;
1406 void silc_getkey_cb(bool success, void *context)
1408 GetkeyContext getkey = (GetkeyContext)context;
1409 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1410 char *name = (getkey->id_type == SILC_ID_CLIENT ?
1411 ((SilcClientEntry)getkey->entry)->nickname :
1412 ((SilcServerEntry)getkey->entry)->server_name);
1415 printformat_module("fe-common/silc", NULL, NULL,
1416 MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1418 printformat_module("fe-common/silc", NULL, NULL,
1419 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1423 silc_free(getkey->fingerprint);
1427 /* Parse an invite or ban list */
1428 void silc_parse_inviteban_list(SilcClient client,
1429 SilcClientConnection conn,
1430 SILC_SERVER_REC *server,
1431 SilcChannelEntry channel,
1432 const char *list_type,
1433 SilcArgumentPayload list)
1436 SilcUInt32 type, len;
1437 SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1438 int counter=0, resolving = FALSE;
1440 if (!silc_argument_get_arg_num(list)) {
1441 printformat_module("fe-common/silc", server,
1442 (chanrec ? chanrec->visible_name : NULL),
1443 MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1444 channel->channel_name, list_type);
1448 printformat_module("fe-common/silc", server,
1449 (chanrec ? chanrec->visible_name : NULL),
1450 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1451 channel->channel_name, list_type);
1453 /* parse the list */
1454 tmp = silc_argument_get_first_arg(list, &type, &len);
1459 /* an invite string */
1463 if (tmp[len-1] == ',')
1466 list = g_strsplit(tmp, ",", -1);
1468 printformat_module("fe-common/silc", server,
1469 (chanrec ? chanrec->visible_name : NULL),
1470 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1471 ++counter, channel->channel_name, list_type,
1480 char *fingerprint, *babbleprint;
1482 /* tmp is Public Key Payload, take public key from it. */
1483 fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1484 babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1486 printformat_module("fe-common/silc", server,
1487 (chanrec ? chanrec->visible_name : NULL),
1488 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1489 ++counter, channel->channel_name, list_type,
1490 fingerprint, babbleprint);
1497 SilcClientID *client_id;
1498 SilcClientEntry client_entry;
1500 client_id = silc_id_payload_parse_id(tmp, len, NULL);
1502 if (client_id == NULL) {
1503 silc_say_error("Invalid data in %s list encountered", list_type);
1507 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1510 printformat_module("fe-common/silc", server,
1511 (chanrec ? chanrec->visible_name : NULL),
1512 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1513 ++counter, channel->channel_name, list_type,
1514 client_entry->nickname);
1517 silc_client_get_client_by_id_resolve(client, conn, client_id,
1521 silc_free(client_id);
1527 silc_say_error("Unkown type in %s list: %u (len %u)",
1528 list_type, type, len);
1530 tmp = silc_argument_get_next_arg(list, &type, &len);
1534 printformat_module("fe-common/silc", server,
1535 (chanrec ? chanrec->visible_name : NULL),
1536 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1537 list_type, channel->channel_name);
1540 /* Command reply handler. This function is called always in the command reply
1541 function. If error occurs it will be called as well. Normal scenario
1542 is that it will be called after the received command data has been parsed
1543 and processed. The function is used to pass the received command data to
1546 `conn' is the associated client connection. `cmd_payload' is the command
1547 payload data received from server and it can be ignored. It is provided
1548 if the application would like to re-parse the received command data,
1549 however, it must be noted that the data is parsed already by the library
1550 thus the payload can be ignored. `success' is FALSE if error occured.
1551 In this case arguments are not sent to the application. `command' is the
1552 command reply being processed. The function has variable argument list
1553 and each command defines the number and type of arguments it passes to the
1554 application (on error they are not sent). */
1557 silc_command_reply(SilcClient client, SilcClientConnection conn,
1558 SilcCommandPayload cmd_payload, bool success,
1559 SilcCommand command, SilcStatus status, ...)
1562 SILC_SERVER_REC *server = conn->context;
1563 SILC_CHANNEL_REC *chanrec;
1566 va_start(vp, status);
1568 SILC_LOG_DEBUG(("Start"));
1571 case SILC_COMMAND_WHOIS:
1573 char buf[1024], *nickname, *username, *realname, *nick;
1574 unsigned char *fingerprint;
1575 SilcUInt32 idle, mode;
1576 SilcBuffer channels, user_modes;
1577 SilcClientEntry client_entry;
1580 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1581 /* Print the unknown nick for user */
1582 unsigned char *tmp =
1583 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1586 silc_say_error("%s: %s", tmp,
1587 silc_get_status_message(status));
1589 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1590 /* Try to find the entry for the unknown client ID, since we
1591 might have, and print the nickname of it for user. */
1593 unsigned char *tmp =
1594 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1597 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1600 client_entry = silc_client_get_client_by_id(client, conn,
1602 if (client_entry && client_entry->nickname)
1603 silc_say_error("%s: %s", client_entry->nickname,
1604 silc_get_status_message(status));
1605 silc_free(client_id);
1609 } else if (!success) {
1610 silc_say_error("WHOIS: %s", silc_get_status_message(status));
1614 client_entry = va_arg(vp, SilcClientEntry);
1615 nickname = va_arg(vp, char *);
1616 username = va_arg(vp, char *);
1617 realname = va_arg(vp, char *);
1618 channels = va_arg(vp, SilcBuffer);
1619 mode = va_arg(vp, SilcUInt32);
1620 idle = va_arg(vp, SilcUInt32);
1621 fingerprint = va_arg(vp, unsigned char *);
1622 user_modes = va_arg(vp, SilcBuffer);
1623 attrs = va_arg(vp, SilcDList);
1625 silc_parse_userfqdn(nickname, &nick, NULL);
1626 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1627 SILCTXT_WHOIS_USERINFO, nickname,
1628 client_entry->username, client_entry->hostname,
1629 nick, client_entry->nickname);
1630 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1631 SILCTXT_WHOIS_REALNAME, realname);
1634 if (channels && user_modes) {
1636 SilcDList list = silc_channel_payload_parse_list(channels->data,
1638 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1640 SilcChannelPayload entry;
1643 memset(buf, 0, sizeof(buf));
1644 silc_dlist_start(list);
1645 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1646 SilcUInt32 name_len;
1647 char *m = silc_client_chumode_char(umodes[i++]);
1648 char *name = silc_channel_get_name(entry, &name_len);
1651 silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1652 silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1653 silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1657 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1658 SILCTXT_WHOIS_CHANNELS, buf);
1659 silc_channel_payload_list_free(list);
1665 memset(buf, 0, sizeof(buf));
1666 silc_get_umode_string(mode, buf, sizeof(buf - 1));
1667 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1668 SILCTXT_WHOIS_MODES, buf);
1671 if (idle && nickname) {
1672 memset(buf, 0, sizeof(buf));
1673 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1674 idle > 60 ? (idle / 60) : idle,
1675 idle > 60 ? "minutes" : "seconds");
1677 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1678 SILCTXT_WHOIS_IDLE, buf);
1682 fingerprint = silc_fingerprint(fingerprint, 20);
1683 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1684 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1685 silc_free(fingerprint);
1689 silc_query_attributes_print(server, silc_client, conn, attrs,
1694 case SILC_COMMAND_IDENTIFY:
1696 SilcClientEntry client_entry;
1698 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1699 /* Print the unknown nick for user */
1700 unsigned char *tmp =
1701 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1704 silc_say_error("%s: %s", tmp,
1705 silc_get_status_message(status));
1707 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1708 /* Try to find the entry for the unknown client ID, since we
1709 might have, and print the nickname of it for user. */
1711 unsigned char *tmp =
1712 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1715 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1718 client_entry = silc_client_get_client_by_id(client, conn,
1720 if (client_entry && client_entry->nickname)
1721 silc_say_error("%s: %s", client_entry->nickname,
1722 silc_get_status_message(status));
1723 silc_free(client_id);
1732 case SILC_COMMAND_WHOWAS:
1734 char *nickname, *username, *realname;
1736 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1738 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1741 silc_say_error("%s: %s", tmp,
1742 silc_get_status_message(status));
1744 } else if (!success) {
1745 silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1749 (void)va_arg(vp, SilcClientEntry);
1750 nickname = va_arg(vp, char *);
1751 username = va_arg(vp, char *);
1752 realname = va_arg(vp, char *);
1754 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1755 SILCTXT_WHOWAS_USERINFO, nickname, username,
1756 realname ? realname : "");
1760 case SILC_COMMAND_INVITE:
1762 SilcChannelEntry channel;
1764 SilcArgumentPayload invite_list;
1770 channel = va_arg(vp, SilcChannelEntry);
1771 payload = va_arg(vp, SilcBuffer);
1774 SILC_GET16_MSB(argc, payload->data);
1775 invite_list = silc_argument_payload_parse(payload->data + 2,
1776 payload->len - 2, argc);
1778 silc_parse_inviteban_list(client, conn, server, channel,
1779 "invite", invite_list);
1780 silc_argument_payload_free(invite_list);
1786 case SILC_COMMAND_JOIN:
1788 char *channel, *mode, *topic;
1790 SilcChannelEntry channel_entry;
1791 SilcBuffer client_id_list;
1792 SilcUInt32 list_count;
1797 channel = va_arg(vp, char *);
1798 channel_entry = va_arg(vp, SilcChannelEntry);
1799 modei = va_arg(vp, SilcUInt32);
1800 (void)va_arg(vp, SilcUInt32);
1801 (void)va_arg(vp, unsigned char *);
1802 (void)va_arg(vp, unsigned char *);
1803 (void)va_arg(vp, unsigned char *);
1804 topic = va_arg(vp, char *);
1805 (void)va_arg(vp, unsigned char *);
1806 list_count = va_arg(vp, SilcUInt32);
1807 client_id_list = va_arg(vp, SilcBuffer);
1809 chanrec = silc_channel_find(server, channel);
1811 chanrec = silc_channel_create(server, channel, channel, TRUE);
1814 char tmp[256], *cp, *dm = NULL;
1815 g_free_not_null(chanrec->topic);
1817 if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1818 memset(tmp, 0, sizeof(tmp));
1820 if (strlen(topic) > sizeof(tmp) - 1) {
1821 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1825 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1830 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1831 signal_emit("channel topic changed", 1, chanrec);
1836 mode = silc_client_chmode(modei,
1837 channel_entry->channel_key ?
1838 silc_cipher_get_name(channel_entry->
1840 channel_entry->hmac ?
1841 silc_hmac_get_name(channel_entry->hmac) : "");
1842 g_free_not_null(chanrec->mode);
1843 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1844 signal_emit("channel mode changed", 1, chanrec);
1846 /* Resolve the client information */
1848 SilcJoinResolve r = silc_calloc(1, sizeof(*r));
1849 r->channel = channel_entry;
1850 silc_client_get_clients_by_list(client, conn, list_count,
1852 silc_client_join_get_users, r);
1858 case SILC_COMMAND_NICK:
1861 SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1867 nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1868 if ((nicks != NULL) &&
1869 (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1871 SilcClientEntry collider, old;
1873 old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1874 collider = silc_client_get_client_by_id(client, conn,
1877 if (collider != client_entry) {
1879 memset(buf, 0, sizeof(buf));
1880 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1881 collider->username, collider->hostname);
1882 nicklist_rename_unique(SERVER(server),
1884 collider, collider->nickname);
1885 silc_print_nick_change(server, collider->nickname,
1886 client_entry->nickname, buf);
1891 g_slist_free(nicks);
1893 old = g_strdup(server->nick);
1894 server_change_nick(SERVER(server), client_entry->nickname);
1895 nicklist_rename_unique(SERVER(server),
1896 server->conn->local_entry, server->nick,
1897 client_entry, client_entry->nickname);
1898 signal_emit("message own_nick", 4, server, server->nick, old, "");
1903 case SILC_COMMAND_LIST:
1908 char tmp[256], *cp, *dm = NULL;
1913 (void)va_arg(vp, SilcChannelEntry);
1914 name = va_arg(vp, char *);
1915 topic = va_arg(vp, char *);
1916 usercount = va_arg(vp, int);
1918 if (topic && !silc_term_utf8() &&
1919 silc_utf8_valid(topic, strlen(topic))) {
1920 memset(tmp, 0, sizeof(tmp));
1922 if (strlen(topic) > sizeof(tmp) - 1) {
1923 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1927 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1932 if (status == SILC_STATUS_LIST_START ||
1933 status == SILC_STATUS_OK)
1934 printformat_module("fe-common/silc", server, NULL,
1935 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1938 snprintf(users, sizeof(users) - 1, "N/A");
1940 snprintf(users, sizeof(users) - 1, "%d", usercount);
1941 printformat_module("fe-common/silc", server, NULL,
1942 MSGLEVEL_CRAP, SILCTXT_LIST,
1943 name, users, topic ? topic : "");
1948 case SILC_COMMAND_UMODE:
1956 mode = va_arg(vp, SilcUInt32);
1958 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1959 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1960 printformat_module("fe-common/silc", server, NULL,
1961 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1963 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1964 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1965 printformat_module("fe-common/silc", server, NULL,
1966 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1968 if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1969 if (mode & SILC_UMODE_GONE) {
1970 if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1971 reason = g_strdup(server->away_reason);
1973 reason = g_strdup("away");
1975 reason = g_strdup("");
1977 silc_set_away(reason, server);
1982 server->umode = mode;
1983 signal_emit("user mode changed", 2, server, NULL);
1987 case SILC_COMMAND_OPER:
1991 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1992 signal_emit("user mode changed", 2, server, NULL);
1994 printformat_module("fe-common/silc", server, NULL,
1995 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1998 case SILC_COMMAND_SILCOPER:
2002 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
2003 signal_emit("user mode changed", 2, server, NULL);
2005 printformat_module("fe-common/silc", server, NULL,
2006 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
2009 case SILC_COMMAND_USERS:
2011 SilcHashTableList htl;
2012 SilcChannelEntry channel;
2013 SilcChannelUser chu;
2018 channel = va_arg(vp, SilcChannelEntry);
2020 printformat_module("fe-common/silc", server, channel->channel_name,
2021 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
2022 channel->channel_name);
2024 silc_hash_table_list(channel->user_list, &htl);
2025 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
2026 SilcClientEntry e = chu->client;
2027 char stat[5], *mode;
2032 memset(stat, 0, sizeof(stat));
2033 mode = silc_client_chumode_char(chu->mode);
2034 if (e->mode & SILC_UMODE_GONE)
2036 else if (e->mode & SILC_UMODE_INDISPOSED)
2038 else if (e->mode & SILC_UMODE_BUSY)
2040 else if (e->mode & SILC_UMODE_PAGE)
2042 else if (e->mode & SILC_UMODE_HYPER)
2044 else if (e->mode & SILC_UMODE_ROBOT)
2046 else if (e->mode & SILC_UMODE_ANONYMOUS)
2053 printformat_module("fe-common/silc", server, channel->channel_name,
2054 MSGLEVEL_CRAP, SILCTXT_USERS,
2056 e->username ? e->username : "",
2057 e->hostname ? e->hostname : "",
2058 e->realname ? e->realname : "");
2062 silc_hash_table_list_reset(&htl);
2066 case SILC_COMMAND_BAN:
2068 SilcChannelEntry channel;
2070 SilcArgumentPayload ban_list;
2076 channel = va_arg(vp, SilcChannelEntry);
2077 payload = va_arg(vp, SilcBuffer);
2080 SILC_GET16_MSB(argc, payload->data);
2081 ban_list = silc_argument_payload_parse(payload->data + 2,
2082 payload->len - 2, argc);
2084 silc_parse_inviteban_list(client, conn, server, channel,
2086 silc_argument_payload_free(ban_list);
2092 case SILC_COMMAND_GETKEY:
2096 SilcPublicKey public_key;
2099 GetkeyContext getkey;
2105 id_type = va_arg(vp, SilcUInt32);
2106 entry = va_arg(vp, void *);
2107 public_key = va_arg(vp, SilcPublicKey);
2110 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2112 getkey = silc_calloc(1, sizeof(*getkey));
2113 getkey->entry = entry;
2114 getkey->id_type = id_type;
2115 getkey->client = client;
2116 getkey->conn = conn;
2117 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2119 name = (id_type == SILC_ID_CLIENT ?
2120 ((SilcClientEntry)entry)->nickname :
2121 ((SilcServerEntry)entry)->server_name);
2123 silc_verify_public_key_internal(client, conn, name,
2124 (id_type == SILC_ID_CLIENT ?
2125 SILC_SOCKET_TYPE_CLIENT :
2126 SILC_SOCKET_TYPE_SERVER),
2127 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
2128 silc_getkey_cb, getkey);
2131 printformat_module("fe-common/silc", server, NULL,
2132 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2137 case SILC_COMMAND_INFO:
2139 SilcServerEntry server_entry;
2146 server_entry = va_arg(vp, SilcServerEntry);
2147 server_name = va_arg(vp, char *);
2148 server_info = va_arg(vp, char *);
2150 if (server_name && server_info )
2152 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2153 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2158 case SILC_COMMAND_TOPIC:
2160 SilcChannelEntry channel;
2162 char tmp[256], *cp, *dm = NULL;
2167 channel = va_arg(vp, SilcChannelEntry);
2168 topic = va_arg(vp, char *);
2170 if (topic && !silc_term_utf8() &&
2171 silc_utf8_valid(topic, strlen(topic))) {
2172 memset(tmp, 0, sizeof(tmp));
2174 if (strlen(topic) > sizeof(tmp) - 1) {
2175 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2179 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
2185 chanrec = silc_channel_find_entry(server, channel);
2187 g_free_not_null(chanrec->topic);
2188 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2189 signal_emit("channel topic changed", 1, chanrec);
2191 printformat_module("fe-common/silc", server, channel->channel_name,
2192 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2193 channel->channel_name, topic);
2195 printformat_module("fe-common/silc", server, channel->channel_name,
2196 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2197 channel->channel_name);
2203 case SILC_COMMAND_WATCH:
2206 case SILC_COMMAND_STATS:
2208 SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2209 my_router_ops, cell_clients, cell_channels, cell_servers,
2210 clients, channels, servers, routers, server_ops, router_ops;
2212 SilcBufferStruct buf;
2213 unsigned char *tmp_buf;
2215 const char *tmptime;
2216 int days, hours, mins, secs;
2221 tmp_buf = va_arg(vp, unsigned char *);
2222 buf_len = va_arg(vp, SilcUInt32);
2224 if (!tmp_buf || !buf_len) {
2225 printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2229 /* Get statistics structure */
2230 silc_buffer_set(&buf, tmp_buf, buf_len);
2231 silc_buffer_unformat(&buf,
2232 SILC_STR_UI_INT(&starttime),
2233 SILC_STR_UI_INT(&uptime),
2234 SILC_STR_UI_INT(&my_clients),
2235 SILC_STR_UI_INT(&my_channels),
2236 SILC_STR_UI_INT(&my_server_ops),
2237 SILC_STR_UI_INT(&my_router_ops),
2238 SILC_STR_UI_INT(&cell_clients),
2239 SILC_STR_UI_INT(&cell_channels),
2240 SILC_STR_UI_INT(&cell_servers),
2241 SILC_STR_UI_INT(&clients),
2242 SILC_STR_UI_INT(&channels),
2243 SILC_STR_UI_INT(&servers),
2244 SILC_STR_UI_INT(&routers),
2245 SILC_STR_UI_INT(&server_ops),
2246 SILC_STR_UI_INT(&router_ops),
2249 tmptime = silc_get_time(starttime);
2250 printformat_module("fe-common/silc", server, NULL,
2251 MSGLEVEL_CRAP, SILCTXT_STATS,
2252 "Local server start time", tmptime);
2254 days = uptime / (24 * 60 * 60);
2255 uptime -= days * (24 * 60 * 60);
2256 hours = uptime / (60 * 60);
2257 uptime -= hours * (60 * 60);
2259 uptime -= mins * 60;
2261 snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2262 days, hours, mins, secs);
2263 printformat_module("fe-common/silc", server, NULL,
2264 MSGLEVEL_CRAP, SILCTXT_STATS,
2265 "Local server uptime", tmp);
2267 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2268 printformat_module("fe-common/silc", server, NULL,
2269 MSGLEVEL_CRAP, SILCTXT_STATS,
2270 "Local server clients", tmp);
2272 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2273 printformat_module("fe-common/silc", server, NULL,
2274 MSGLEVEL_CRAP, SILCTXT_STATS,
2275 "Local server channels", tmp);
2277 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2278 printformat_module("fe-common/silc", server, NULL,
2279 MSGLEVEL_CRAP, SILCTXT_STATS,
2280 "Local server operators", tmp);
2282 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2283 printformat_module("fe-common/silc", server, NULL,
2284 MSGLEVEL_CRAP, SILCTXT_STATS,
2285 "Local router operators", tmp);
2287 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2288 printformat_module("fe-common/silc", server, NULL,
2289 MSGLEVEL_CRAP, SILCTXT_STATS,
2290 "Local cell clients", tmp);
2292 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2293 printformat_module("fe-common/silc", server, NULL,
2294 MSGLEVEL_CRAP, SILCTXT_STATS,
2295 "Local cell channels", tmp);
2297 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2298 printformat_module("fe-common/silc", server, NULL,
2299 MSGLEVEL_CRAP, SILCTXT_STATS,
2300 "Local cell servers", tmp);
2302 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2303 printformat_module("fe-common/silc", server, NULL,
2304 MSGLEVEL_CRAP, SILCTXT_STATS,
2305 "Total clients", tmp);
2307 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2308 printformat_module("fe-common/silc", server, NULL,
2309 MSGLEVEL_CRAP, SILCTXT_STATS,
2310 "Total channels", tmp);
2312 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2313 printformat_module("fe-common/silc", server, NULL,
2314 MSGLEVEL_CRAP, SILCTXT_STATS,
2315 "Total servers", tmp);
2317 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2318 printformat_module("fe-common/silc", server, NULL,
2319 MSGLEVEL_CRAP, SILCTXT_STATS,
2320 "Total routers", tmp);
2322 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2323 printformat_module("fe-common/silc", server, NULL,
2324 MSGLEVEL_CRAP, SILCTXT_STATS,
2325 "Total server operators", tmp);
2327 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2328 printformat_module("fe-common/silc", server, NULL,
2329 MSGLEVEL_CRAP, SILCTXT_STATS,
2330 "Total router operators", tmp);
2334 case SILC_COMMAND_CMODE:
2336 SilcChannelEntry channel_entry;
2337 SilcBuffer channel_pubkeys;
2339 channel_entry = va_arg(vp, SilcChannelEntry);
2340 (void)va_arg(vp, SilcUInt32);
2341 (void)va_arg(vp, SilcPublicKey);
2342 channel_pubkeys = va_arg(vp, SilcBuffer);
2344 if (!success || !cmode_list_chpks ||
2345 !channel_entry || !channel_entry->channel_name)
2348 /* Print the channel public key list */
2349 if (channel_pubkeys)
2350 silc_parse_channel_public_keys(server, channel_entry, channel_pubkeys);
2352 printformat_module("fe-common/silc", server, NULL,
2353 MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2354 channel_entry->channel_name);
2365 SilcClientConnection conn;
2371 SilcSKEPKType pk_type;
2372 SilcVerifyPublicKey completion;
2376 static void verify_public_key_completion(const char *line, void *context)
2378 PublicKeyVerify verify = (PublicKeyVerify)context;
2380 if (line[0] == 'Y' || line[0] == 'y') {
2381 /* Call the completion */
2382 if (verify->completion)
2383 verify->completion(TRUE, verify->context);
2385 /* Save the key for future checking */
2386 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
2387 verify->pk_len, SILC_PKCS_FILE_PEM);
2389 /* Call the completion */
2390 if (verify->completion)
2391 verify->completion(FALSE, verify->context);
2393 printformat_module("fe-common/silc", NULL, NULL,
2394 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2395 verify->entity_name ? verify->entity_name :
2399 silc_free(verify->filename);
2400 silc_free(verify->entity);
2401 silc_free(verify->entity_name);
2402 silc_free(verify->pk);
2406 /* Internal routine to verify public key. If the `completion' is provided
2407 it will be called to indicate whether public was verified or not. For
2408 server/router public key this will check for filename that includes the
2409 remote host's IP address and remote host's hostname. */
2412 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2413 const char *name, SilcSocketType conn_type,
2414 unsigned char *pk, SilcUInt32 pk_len,
2415 SilcSKEPKType pk_type,
2416 SilcVerifyPublicKey completion, void *context)
2419 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2420 char *fingerprint, *babbleprint, *format;
2423 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2424 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
2425 "server" : "client");
2426 PublicKeyVerify verify;
2428 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2429 printformat_module("fe-common/silc", NULL, NULL,
2430 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2433 completion(FALSE, context);
2437 pw = getpwuid(getuid());
2440 completion(FALSE, context);
2444 memset(filename, 0, sizeof(filename));
2445 memset(filename2, 0, sizeof(filename2));
2446 memset(file, 0, sizeof(file));
2448 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2449 conn_type == SILC_SOCKET_TYPE_ROUTER) {
2451 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2452 conn->sock->ip, conn->sock->port);
2453 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2454 get_irssi_dir(), entity, file);
2456 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2457 conn->sock->hostname, conn->sock->port);
2458 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2459 get_irssi_dir(), entity, file);
2464 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2465 name, conn->sock->port);
2466 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2467 get_irssi_dir(), entity, file);
2472 /* Replace all whitespaces with `_'. */
2473 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2474 for (i = 0; i < strlen(fingerprint); i++)
2475 if (fingerprint[i] == ' ')
2476 fingerprint[i] = '_';
2478 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2479 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2480 get_irssi_dir(), entity, file);
2481 silc_free(fingerprint);
2486 /* Take fingerprint of the public key */
2487 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2488 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2490 verify = silc_calloc(1, sizeof(*verify));
2491 verify->client = client;
2492 verify->conn = conn;
2493 verify->filename = strdup(ipf);
2494 verify->entity = strdup(entity);
2495 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2496 (name ? strdup(name) : strdup(conn->sock->hostname))
2498 verify->pk = silc_memdup(pk, pk_len);
2499 verify->pk_len = pk_len;
2500 verify->pk_type = pk_type;
2501 verify->completion = completion;
2502 verify->context = context;
2504 /* Check whether this key already exists */
2505 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2506 /* Key does not exist, ask user to verify the key and save it */
2508 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2509 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2510 verify->entity_name : entity);
2511 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2512 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2513 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2514 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2515 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2516 SILCTXT_PUBKEY_ACCEPT);
2517 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2520 silc_free(fingerprint);
2523 /* The key already exists, verify it. */
2524 SilcPublicKey public_key;
2525 unsigned char *encpk;
2526 SilcUInt32 encpk_len;
2528 /* Load the key file, try for both IP filename and hostname filename */
2529 if (!silc_pkcs_load_public_key(ipf, &public_key,
2530 SILC_PKCS_FILE_PEM) &&
2531 !silc_pkcs_load_public_key(ipf, &public_key,
2532 SILC_PKCS_FILE_BIN) &&
2533 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
2534 SILC_PKCS_FILE_PEM) &&
2535 !silc_pkcs_load_public_key(hostf, &public_key,
2536 SILC_PKCS_FILE_BIN)))) {
2537 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2538 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2539 verify->entity_name : entity);
2540 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2541 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2542 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2543 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2544 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2545 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2546 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2547 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2548 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2551 silc_free(fingerprint);
2555 /* Encode the key data */
2556 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2558 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2559 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2560 verify->entity_name : entity);
2561 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2562 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2563 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2564 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2565 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2566 SILCTXT_PUBKEY_MALFORMED, entity);
2567 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2568 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2569 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2572 silc_free(fingerprint);
2576 /* Compare the keys */
2577 if (memcmp(encpk, pk, encpk_len)) {
2578 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2579 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2580 verify->entity_name : entity);
2581 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2582 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2583 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2584 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2585 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2586 SILCTXT_PUBKEY_NO_MATCH, entity);
2587 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2588 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2589 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2590 SILCTXT_PUBKEY_MITM_ATTACK, entity);
2592 /* Ask user to verify the key and save it */
2593 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2594 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2595 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2598 silc_free(fingerprint);
2602 /* Local copy matched */
2604 completion(TRUE, context);
2605 silc_free(fingerprint);
2606 silc_free(verify->filename);
2607 silc_free(verify->entity);
2608 silc_free(verify->entity_name);
2609 silc_free(verify->pk);
2614 /* Verifies received public key. The `conn_type' indicates which entity
2615 (server, client etc.) has sent the public key. If user decides to trust
2616 the key may be saved as trusted public key for later use. The
2617 `completion' must be called after the public key has been verified. */
2620 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2621 SilcSocketType conn_type, unsigned char *pk,
2622 SilcUInt32 pk_len, SilcSKEPKType pk_type,
2623 SilcVerifyPublicKey completion, void *context)
2625 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2627 completion, context);
2630 /* Asks passphrase from user on the input line. */
2633 SilcAskPassphrase completion;
2637 void ask_passphrase_completion(const char *passphrase, void *context)
2639 AskPassphrase p = (AskPassphrase)context;
2640 if (passphrase && passphrase[0] == '\0')
2642 p->completion((unsigned char *)passphrase,
2643 passphrase ? strlen(passphrase) : 0, p->context);
2647 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2648 SilcAskPassphrase completion, void *context)
2650 AskPassphrase p = silc_calloc(1, sizeof(*p));
2651 p->completion = completion;
2652 p->context = context;
2654 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2655 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2659 SilcGetAuthMeth completion;
2661 } *InternalGetAuthMethod;
2663 /* Callback called when we've received the authentication method information
2664 from the server after we've requested it. This will get the authentication
2665 data from the user if needed. */
2667 static void silc_get_auth_method_callback(SilcClient client,
2668 SilcClientConnection conn,
2669 SilcAuthMethod auth_meth,
2672 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2674 SILC_LOG_DEBUG(("Start"));
2676 switch (auth_meth) {
2677 case SILC_AUTH_NONE:
2678 /* No authentication required. */
2679 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2681 case SILC_AUTH_PASSWORD:
2683 /* Check whether we find the password for this server in our
2684 configuration. If not, then don't provide so library will ask
2685 it from the user. */
2686 SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2688 if (!setup || !setup->password) {
2689 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2693 (*internal->completion)(TRUE, auth_meth, setup->password,
2694 strlen(setup->password), internal->context);
2697 case SILC_AUTH_PUBLIC_KEY:
2698 /* Do not get the authentication data now, the library will generate
2699 it using our default key, if we do not provide it here. */
2700 /* XXX In the future when we support multiple local keys and multiple
2701 local certificates we will need to ask from user which one to use. */
2702 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2706 silc_free(internal);
2709 /* Find authentication method and authentication data by hostname and
2710 port. The hostname may be IP address as well. The found authentication
2711 method and authentication data is returned to `auth_meth', `auth_data'
2712 and `auth_data_len'. The function returns TRUE if authentication method
2713 is found and FALSE if not. `conn' may be NULL. */
2715 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2716 char *hostname, SilcUInt16 port,
2717 SilcGetAuthMeth completion, void *context)
2719 InternalGetAuthMethod internal;
2721 SILC_LOG_DEBUG(("Start"));
2723 /* If we do not have this connection configured by the user in a
2724 configuration file then resolve the authentication method from the
2725 server for this session. */
2726 internal = silc_calloc(1, sizeof(*internal));
2727 internal->completion = completion;
2728 internal->context = context;
2730 silc_client_request_authentication_method(client, conn,
2731 silc_get_auth_method_callback,
2735 /* Notifies application that failure packet was received. This is called
2736 if there is some protocol active in the client. The `protocol' is the
2737 protocol context. The `failure' is opaque pointer to the failure
2738 indication. Note, that the `failure' is protocol dependant and application
2739 must explicitly cast it to correct type. Usually `failure' is 32 bit
2740 failure type (see protocol specs for all protocol failure types). */
2742 void silc_failure(SilcClient client, SilcClientConnection conn,
2743 SilcProtocol protocol, void *failure)
2745 SILC_LOG_DEBUG(("Start"));
2747 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2748 SilcSKEStatus status = (SilcSKEStatus)failure;
2750 if (status == SILC_SKE_STATUS_BAD_VERSION)
2751 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2752 SILCTXT_KE_BAD_VERSION);
2753 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2754 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2755 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2756 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2757 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2758 SILCTXT_KE_UNKNOWN_GROUP);
2759 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2760 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2761 SILCTXT_KE_UNKNOWN_CIPHER);
2762 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2763 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2764 SILCTXT_KE_UNKNOWN_PKCS);
2765 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2766 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2767 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2768 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2769 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2770 SILCTXT_KE_UNKNOWN_HMAC);
2771 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2772 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2773 SILCTXT_KE_INCORRECT_SIGNATURE);
2774 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2775 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2776 SILCTXT_KE_INVALID_COOKIE);
2779 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2780 SilcUInt32 err = (SilcUInt32)failure;
2782 if (err == SILC_AUTH_FAILED)
2783 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2784 SILCTXT_AUTH_FAILED);
2788 /* Asks whether the user would like to perform the key agreement protocol.
2789 This is called after we have received an key agreement packet or an
2790 reply to our key agreement packet. This returns TRUE if the user wants
2791 the library to perform the key agreement protocol and FALSE if it is not
2792 desired (application may start it later by calling the function
2793 silc_client_perform_key_agreement). */
2795 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2796 SilcClientEntry client_entry, const char *hostname,
2797 SilcUInt16 port, SilcKeyAgreementCallback *completion,
2802 SILC_LOG_DEBUG(("Start"));
2804 /* We will just display the info on the screen and return FALSE and user
2805 will have to start the key agreement with a command. */
2808 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2811 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2812 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2814 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2815 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2816 client_entry->nickname, hostname, portstr);
2824 /* Notifies application that file transfer protocol session is being
2825 requested by the remote client indicated by the `client_entry' from
2826 the `hostname' and `port'. The `session_id' is the file transfer
2827 session and it can be used to either accept or reject the file
2828 transfer request, by calling the silc_client_file_receive or
2829 silc_client_file_close, respectively. */
2831 void silc_ftp(SilcClient client, SilcClientConnection conn,
2832 SilcClientEntry client_entry, SilcUInt32 session_id,
2833 const char *hostname, SilcUInt16 port)
2835 SILC_SERVER_REC *server;
2837 FtpSession ftp = NULL;
2839 SILC_LOG_DEBUG(("Start"));
2841 server = conn->context;
2843 silc_dlist_start(server->ftp_sessions);
2844 while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2845 if (ftp->client_entry == client_entry &&
2846 ftp->session_id == session_id) {
2847 server->current_session = ftp;
2851 if (ftp == SILC_LIST_END) {
2852 ftp = silc_calloc(1, sizeof(*ftp));
2853 ftp->client_entry = client_entry;
2854 ftp->session_id = session_id;
2857 silc_dlist_add(server->ftp_sessions, ftp);
2858 server->current_session = ftp;
2862 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2865 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2866 SILCTXT_FILE_REQUEST, client_entry->nickname);
2868 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2869 SILCTXT_FILE_REQUEST_HOST,
2870 client_entry->nickname, hostname, portstr);
2873 /* Delivers SILC session detachment data indicated by `detach_data' to the
2874 application. If application has issued SILC_COMMAND_DETACH command
2875 the client session in the SILC network is not quit. The client remains
2876 in the network but is detached. The detachment data may be used later
2877 to resume the session in the SILC Network. The appliation is
2878 responsible of saving the `detach_data', to for example in a file.
2880 The detachment data can be given as argument to the functions
2881 silc_client_connect_to_server, or silc_client_add_connection when
2882 creating connection to remote server, inside SilcClientConnectionParams
2883 structure. If it is provided the client library will attempt to resume
2884 the session in the network. After the connection is created
2885 successfully, the application is responsible of setting the user
2886 interface for user into the same state it was before detaching (showing
2887 same channels, channel modes, etc). It can do this by fetching the
2888 information (like joined channels) from the client library. */
2891 silc_detach(SilcClient client, SilcClientConnection conn,
2892 const unsigned char *detach_data, SilcUInt32 detach_data_len)
2894 SILC_SERVER_REC *server = conn->context;
2897 /* Save the detachment data to file. */
2899 file = silc_get_session_filename(server);
2900 silc_file_writefile(file, detach_data, detach_data_len);
2905 /* SILC client operations */
2906 SilcClientOperations ops = {
2908 silc_channel_message,
2909 silc_private_message,
2915 silc_get_auth_method,
2916 silc_verify_public_key,
2917 silc_ask_passphrase,