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"
32 #include "silc-cmdqueue.h"
38 #include "special-vars.h"
39 #include "fe-common/core/printtext.h"
40 #include "fe-common/core/fe-channels.h"
41 #include "fe-common/core/keyboard.h"
42 #include "fe-common/core/window-items.h"
43 #include "fe-common/silc/module-formats.h"
48 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
49 const char *name, SilcSocketType conn_type,
50 unsigned char *pk, SilcUInt32 pk_len,
51 SilcSKEPKType pk_type,
52 SilcVerifyPublicKey completion, void *context);
54 char *silc_get_session_filename(SILC_SERVER_REC *server)
56 char *file, *expanded;
58 expanded = parse_special_string(settings_get_str("session_filename"),
59 SERVER(server), NULL, "", NULL, 0);
61 file = silc_calloc(1, strlen(expanded) + 255);
62 snprintf(file, strlen(expanded) + 255, "%s/%s", get_irssi_dir(), expanded);
68 static void silc_get_umode_string(SilcUInt32 mode, char *buf,
71 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
72 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
73 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
75 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
76 "[SILC operator]" : "[unknown mode]");
78 if (mode & SILC_UMODE_GONE)
79 strcat(buf, " [away]");
80 if (mode & SILC_UMODE_INDISPOSED)
81 strcat(buf, " [indisposed]");
82 if (mode & SILC_UMODE_BUSY)
83 strcat(buf, " [busy]");
84 if (mode & SILC_UMODE_PAGE)
85 strcat(buf, " [page to reach]");
86 if (mode & SILC_UMODE_HYPER)
87 strcat(buf, " [hyper active]");
88 if (mode & SILC_UMODE_ROBOT)
89 strcat(buf, " [robot]");
90 if (mode & SILC_UMODE_ANONYMOUS)
91 strcat(buf, " [anonymous]");
92 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
93 strcat(buf, " [blocks private messages]");
94 if (mode & SILC_UMODE_DETACHED)
95 strcat(buf, " [detached]");
96 if (mode & SILC_UMODE_REJECT_WATCHING)
97 strcat(buf, " [rejects watching]");
98 if (mode & SILC_UMODE_BLOCK_INVITE)
99 strcat(buf, " [blocks invites]");
102 /* converts an utf-8 string to current locale */
103 char * silc_convert_utf8_string(const char *str)
105 int message_len = (str != NULL ? strlen(str) : 0);
106 char *message = silc_calloc(message_len + 1, sizeof(*message));
108 g_return_val_if_fail(message != NULL, NULL);
115 if (!silc_term_utf8() && silc_utf8_valid(str, message_len))
116 silc_utf8_decode(str, message_len, SILC_STRING_LANGUAGE,
117 message, message_len);
119 strcpy(message, str);
124 /* print "nick appears as" message to every channel of a server */
126 silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
127 const char *newnick, const char *oldnick,
130 if (ignore_check(SERVER(server), oldnick, address,
131 channel, newnick, MSGLEVEL_NICKS))
134 signal_emit("message silc appears", 5, server, channel,
135 oldnick, newnick, address);
139 silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
140 const char *oldnick, const char *address)
142 GSList *tmp, *windows;
144 /* Print to each channel/query where the nick is.
145 Don't print more than once to the same window. */
148 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
149 CHANNEL_REC *channel = tmp->data;
150 WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
152 if (nicklist_find(channel, newnick) == NULL ||
153 g_slist_find(windows, window) != NULL)
156 windows = g_slist_append(windows, window);
157 silc_print_nick_change_channel(server, channel->visible_name,
158 newnick, oldnick, address);
161 g_slist_free(windows);
164 void silc_say(SilcClient client, SilcClientConnection conn,
165 SilcClientMessageType type, char *msg, ...)
167 SILC_SERVER_REC *server;
172 server = conn == NULL ? NULL : conn->context;
175 str = g_strdup_vprintf(msg, va);
177 tmp = silc_convert_utf8_string(str);
178 signal_emit("message silc generic", 4, server, NULL, MSGLEVEL_CRAP, tmp);
185 void silc_say_error(char *msg, ...)
191 str = g_strdup_vprintf(msg, va);
193 tmp = silc_convert_utf8_string(str);
194 signal_emit("message silc generic", 4, NULL, NULL, MSGLEVEL_CLIENTERROR, tmp);
201 /* try to verify a message using locally stored public key data */
202 int verify_message_signature(SilcClientEntry sender,
203 SilcMessageSignedPayload sig,
204 SilcMessagePayload message)
207 char file[256], filename[256];
208 char *fingerprint, *fingerprint2;
209 unsigned char *pk_data;
210 SilcUInt32 pk_datalen;
212 int ret = SILC_MSG_SIGNED_VERIFIED, i;
215 return SILC_MSG_SIGNED_UNKNOWN;
217 /* get public key from the signature payload and compare it with the
218 one stored in the client entry */
219 pk = silc_message_signed_get_public_key(sig, &pk_data, &pk_datalen);
222 fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen);
224 if (sender->fingerprint) {
225 fingerprint2 = silc_fingerprint(sender->fingerprint,
226 sender->fingerprint_len);
227 if (strcmp(fingerprint, fingerprint2)) {
228 /* since the public key differs from the senders public key, the
229 verification _failed_ */
230 silc_pkcs_public_key_free(pk);
231 silc_free(fingerprint);
232 ret = SILC_MSG_SIGNED_UNKNOWN;
234 silc_free(fingerprint2);
236 } else if (sender->fingerprint)
237 fingerprint = silc_fingerprint(sender->fingerprint,
238 sender->fingerprint_len);
240 /* no idea, who or what signed that message ... */
241 return SILC_MSG_SIGNED_UNKNOWN;
243 /* search our local client key cache */
244 for (i = 0; i < strlen(fingerprint); i++)
245 if (fingerprint[i] == ' ')
246 fingerprint[i] = '_';
248 snprintf(file, sizeof(file) - 1, "clientkey_%s.pub", fingerprint);
249 snprintf(filename, sizeof(filename) - 1, "%s/clientkeys/%s",
250 get_irssi_dir(), file);
251 silc_free(fingerprint);
253 if (stat(filename, &st) < 0)
254 /* we don't have the public key cached ... use the one from the sig */
255 ret = SILC_MSG_SIGNED_UNKNOWN;
257 SilcPublicKey cached_pk=NULL;
259 /* try to load the file */
260 if (!silc_pkcs_load_public_key(filename, &cached_pk, SILC_PKCS_FILE_PEM) &&
261 !silc_pkcs_load_public_key(filename, &cached_pk,
262 SILC_PKCS_FILE_BIN)) {
263 /* FIXME: should we move this to fe-silc? */
264 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
265 SILCTXT_PUBKEY_COULD_NOT_LOAD, "client");
267 return SILC_MSG_SIGNED_UNKNOWN;
269 ret = SILC_MSG_SIGNED_UNKNOWN;
274 silc_pkcs_public_key_free(pk);
279 /* the public key is now in pk, our "level of trust" in ret */
280 if ((pk) && silc_message_signed_verify(sig, message, pk,
281 silc_client->sha1hash)!= SILC_AUTH_OK)
282 ret = SILC_MSG_SIGNED_FAILED;
285 silc_pkcs_public_key_free(pk);
290 char * silc_unescape_data(const char *escaped_data, SilcUInt32 *length)
293 int i = 0, j = 0, len = strlen(escaped_data);
295 data = silc_calloc(len, sizeof(char));
298 ptr = memchr(escaped_data + i, 1, len - i);
300 int inc = (ptr - escaped_data) - i;
301 memcpy(data + j, escaped_data + i, inc);
304 data[j++] = *(ptr + 1) - 1;
306 memcpy(data + j, escaped_data + i, len - i);
316 char * silc_escape_data(const char *data, SilcUInt32 len)
318 char *escaped_data, *ptr, *ptr0, *ptr1;
321 escaped_data = silc_calloc(2 * len, sizeof(char));
324 ptr0 = memchr(data + i, 0, len - i);
325 ptr1 = memchr(data + i, 1, len - i);
327 ptr = (ptr0 < ptr1 ? (ptr0 ? ptr0 : ptr1) : (ptr1 ? ptr1 : ptr0));
330 int inc = (ptr - data) - i;
332 memcpy(escaped_data + j, data + i, inc);
335 escaped_data[j++] = 1;
336 escaped_data[j++] = *(data + i++) + 1;
338 memcpy(escaped_data + j, data + i, len - i);
347 void silc_emit_mime_sig(SILC_SERVER_REC *server, WI_ITEM_REC *item,
348 const char *data, SilcUInt32 data_len, const char *nick,
353 escaped_data = silc_escape_data(data, data_len);
355 signal_emit("mime", 5, server, item, escaped_data, nick, verified);
357 silc_free(escaped_data);
361 /* Message for a channel. The `sender' is the nickname of the sender
362 received in the packet. The `channel_name' is the name of the channel. */
364 void silc_channel_message(SilcClient client, SilcClientConnection conn,
365 SilcClientEntry sender, SilcChannelEntry channel,
366 SilcMessagePayload payload,
367 SilcChannelPrivateKey key,
368 SilcMessageFlags flags, const unsigned char *message,
369 SilcUInt32 message_len)
371 SILC_SERVER_REC *server;
373 SILC_CHANNEL_REC *chanrec;
376 SILC_LOG_DEBUG(("Start"));
381 server = conn == NULL ? NULL : conn->context;
382 chanrec = silc_channel_find_entry(server, channel);
386 nick = silc_nicklist_find(chanrec, sender);
388 /* We didn't find client but it clearly exists, add it. */
389 SilcChannelUser chu = silc_client_on_channel(channel, sender);
391 nick = silc_nicklist_insert(chanrec, chu, FALSE);
394 /* If the messages is digitally signed, verify it, if possible. */
395 if (flags & SILC_MESSAGE_FLAG_SIGNED) {
396 if (!settings_get_bool("ignore_message_signatures")) {
397 SilcMessageSignedPayload sig = silc_message_get_signature(payload);
398 verified = verify_message_signature(sender, sig, payload);
400 flags &= ~SILC_MESSAGE_FLAG_SIGNED;
404 if (flags & SILC_MESSAGE_FLAG_DATA) {
405 silc_emit_mime_sig(server, (WI_ITEM_REC *)chanrec, message, message_len,
406 nick == NULL ? NULL : nick->nick,
407 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
414 if (flags & SILC_MESSAGE_FLAG_ACTION)
415 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
416 char tmp[256], *cp, *dm = NULL;
417 memset(tmp, 0, sizeof(tmp));
419 if(message_len > sizeof(tmp) - 1) {
420 dm = silc_calloc(message_len + 1, sizeof(*dm));
423 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
425 if (flags & SILC_MESSAGE_FLAG_SIGNED)
426 signal_emit("message silc signed_action", 6, server, cp, nick->nick,
427 nick->host, channel->channel_name, verified);
429 signal_emit("message silc action", 5, server, cp, nick->nick,
430 nick->host, channel->channel_name);
433 if (flags & SILC_MESSAGE_FLAG_SIGNED)
434 signal_emit("message silc signed_action", 6, server, message,
435 nick->nick, nick->host, channel->channel_name, verified);
437 signal_emit("message silc action", 5, server, message,
438 nick->nick, nick->host, channel->channel_name);
440 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
441 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
442 char tmp[256], *cp, *dm = NULL;
443 memset(tmp, 0, sizeof(tmp));
445 if(message_len > sizeof(tmp) - 1) {
446 dm = silc_calloc(message_len + 1, sizeof(*dm));
449 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
451 if (flags & SILC_MESSAGE_FLAG_SIGNED)
452 signal_emit("message silc signed_notice", 6, server, cp, nick->nick,
453 nick->host, channel->channel_name, verified);
455 signal_emit("message silc notice", 5, server, cp, nick->nick,
456 nick->host, channel->channel_name);
459 if (flags & SILC_MESSAGE_FLAG_SIGNED)
460 signal_emit("message silc signed_notice", 6, server, message,
461 nick->nick, nick->host, channel->channel_name, verified);
463 signal_emit("message silc notice", 5, server, message,
464 nick->nick, nick->host, channel->channel_name);
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 silc_emit_mime_sig(server,
540 (WI_ITEM_REC *)query_find(SERVER(server), sender->nickname) :
542 message, message_len,
543 sender->nickname ? sender->nickname : "[<unknown>]",
544 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
551 if (flags & SILC_MESSAGE_FLAG_ACTION)
552 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
553 char tmp[256], *cp, *dm = NULL;
554 memset(tmp, 0, sizeof(tmp));
556 if(message_len > sizeof(tmp) - 1) {
557 dm = silc_calloc(message_len + 1, sizeof(*dm));
560 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
562 if (flags & SILC_MESSAGE_FLAG_SIGNED)
563 signal_emit("message silc signed_private_action", 6, server, cp,
564 sender->nickname ? sender->nickname : "[<unknown>]",
565 sender->username ? userhost : NULL,
568 signal_emit("message silc private_action", 5, server, cp,
569 sender->nickname ? sender->nickname : "[<unknown>]",
570 sender->username ? userhost : NULL, NULL);
573 if (flags & SILC_MESSAGE_FLAG_SIGNED)
574 signal_emit("message silc signed_private_action", 6, server, message,
575 sender->nickname ? sender->nickname : "[<unknown>]",
576 sender->username ? userhost : NULL,
579 signal_emit("message silc private_action", 5, server, message,
580 sender->nickname ? sender->nickname : "[<unknown>]",
581 sender->username ? userhost : NULL, NULL);
583 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
584 if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
585 char tmp[256], *cp, *dm = NULL;
586 memset(tmp, 0, sizeof(tmp));
588 if(message_len > sizeof(tmp) - 1) {
589 dm = silc_calloc(message_len + 1, sizeof(*dm));
592 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
594 if (flags & SILC_MESSAGE_FLAG_SIGNED)
595 signal_emit("message silc signed_private_notice", 6, server, cp,
596 sender->nickname ? sender->nickname : "[<unknown>]",
597 sender->username ? userhost : NULL,
600 signal_emit("message silc private_notice", 5, server, cp,
601 sender->nickname ? sender->nickname : "[<unknown>]",
602 sender->username ? userhost : NULL, NULL);
605 if (flags & SILC_MESSAGE_FLAG_SIGNED)
606 signal_emit("message silc signed_private_notice", 6, server, message,
607 sender->nickname ? sender->nickname : "[<unknown>]",
608 sender->username ? userhost : NULL,
611 signal_emit("message silc private_notice", 5, server, message,
612 sender->nickname ? sender->nickname : "[<unknown>]",
613 sender->username ? userhost : NULL, NULL);
616 if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
617 char tmp[256], *cp, *dm = NULL;
619 memset(tmp, 0, sizeof(tmp));
621 if (message_len > sizeof(tmp) - 1) {
622 dm = silc_calloc(message_len + 1, sizeof(*dm));
626 silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
628 if (flags & SILC_MESSAGE_FLAG_SIGNED)
629 signal_emit("message signed_private", 5, server, cp,
630 sender->nickname ? sender->nickname : "[<unknown>]",
631 sender->username ? userhost : NULL, verified);
633 signal_emit("message private", 4, server, cp,
634 sender->nickname ? sender->nickname : "[<unknown>]",
635 sender->username ? userhost : NULL);
640 if (flags & SILC_MESSAGE_FLAG_SIGNED)
641 signal_emit("message signed_private", 5, server, message,
642 sender->nickname ? sender->nickname : "[<unknown>]",
643 sender->username ? userhost : NULL, verified);
645 signal_emit("message private", 4, server, message,
646 sender->nickname ? sender->nickname : "[<unknown>]",
647 sender->username ? userhost : NULL);
651 /* Notify message to the client. The notify arguments are sent in the
652 same order as servers sends them. The arguments are same as received
653 from the server except for ID's. If ID is received application receives
654 the corresponding entry to the ID. For example, if Client ID is received
655 application receives SilcClientEntry. Also, if the notify type is
656 for channel the channel entry is sent to application (even if server
657 does not send it). */
659 void silc_notify(SilcClient client, SilcClientConnection conn,
660 SilcNotifyType type, ...)
663 SILC_SERVER_REC *server;
664 SILC_CHANNEL_REC *chanrec;
665 SILC_NICK_REC *nickrec;
666 SilcClientEntry client_entry, client_entry2;
667 SilcChannelEntry channel;
668 SilcServerEntry server_entry;
674 GSList *list1, *list_tmp;
677 SILC_LOG_DEBUG(("Start"));
681 server = conn == NULL ? NULL : conn->context;
684 case SILC_NOTIFY_TYPE_NONE:
686 char *msg = silc_convert_utf8_string((char *)va_arg(va, char *));
688 /* Some generic notice from server */
689 signal_emit("message silc generic", 4, server, NULL, MSGLEVEL_CRAP, msg);
694 case SILC_NOTIFY_TYPE_INVITE:
696 * Invited or modified invite list.
699 SILC_LOG_DEBUG(("Notify: INVITE"));
701 channel = va_arg(va, SilcChannelEntry);
702 name = va_arg(va, char *);
703 client_entry = va_arg(va, SilcClientEntry);
705 memset(buf, 0, sizeof(buf));
706 snprintf(buf, sizeof(buf) - 1, "%s@%s",
707 client_entry->username, client_entry->hostname);
708 signal_emit("message invite", 4, server, channel ? channel->channel_name :
709 name, client_entry->nickname, buf);
712 case SILC_NOTIFY_TYPE_JOIN:
717 SILC_LOG_DEBUG(("Notify: JOIN"));
719 client_entry = va_arg(va, SilcClientEntry);
720 channel = va_arg(va, SilcChannelEntry);
722 if (client_entry == server->conn->local_entry) {
723 /* You joined to channel */
724 chanrec = silc_channel_find(server, channel->channel_name);
725 if (chanrec != NULL && !chanrec->joined)
726 chanrec->entry = channel;
728 chanrec = silc_channel_find_entry(server, channel);
729 if (chanrec != NULL) {
730 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
732 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
736 memset(buf, 0, sizeof(buf));
737 if (client_entry->username)
738 snprintf(buf, sizeof(buf) - 1, "%s@%s",
739 client_entry->username, client_entry->hostname);
740 signal_emit("message join", 4, server, channel->channel_name,
741 client_entry->nickname,
742 client_entry->username == NULL ? "" : buf);
745 case SILC_NOTIFY_TYPE_LEAVE:
750 SILC_LOG_DEBUG(("Notify: LEAVE"));
752 client_entry = va_arg(va, SilcClientEntry);
753 channel = va_arg(va, SilcChannelEntry);
755 memset(buf, 0, sizeof(buf));
756 if (client_entry->username)
757 snprintf(buf, sizeof(buf) - 1, "%s@%s",
758 client_entry->username, client_entry->hostname);
759 signal_emit("message part", 5, server, channel->channel_name,
760 client_entry->nickname, client_entry->username ?
761 buf : "", client_entry->nickname);
763 chanrec = silc_channel_find_entry(server, channel);
764 if (chanrec != NULL) {
765 nickrec = silc_nicklist_find(chanrec, client_entry);
767 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
771 case SILC_NOTIFY_TYPE_SIGNOFF:
776 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
778 client_entry = va_arg(va, SilcClientEntry);
779 tmp = silc_convert_utf8_string(va_arg(va, char *));
781 silc_server_free_ftp(server, client_entry);
783 /* Print only if we have the nickname. If this cliente has just quit
784 when we were only resolving it, it is possible we don't have the
786 if (client_entry->nickname) {
787 memset(buf, 0, sizeof(buf));
788 if (client_entry->username)
789 snprintf(buf, sizeof(buf) - 1, "%s@%s",
790 client_entry->username, client_entry->hostname);
791 signal_emit("message quit", 4, server, client_entry->nickname,
792 client_entry->username ? buf : "",
798 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
799 for (list_tmp = list1; list_tmp != NULL; list_tmp =
800 list_tmp->next->next) {
801 CHANNEL_REC *channel = list_tmp->data;
802 NICK_REC *nickrec = list_tmp->next->data;
804 nicklist_remove(channel, nickrec);
808 case SILC_NOTIFY_TYPE_TOPIC_SET:
813 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
815 idtype = va_arg(va, int);
816 entry = va_arg(va, void *);
817 tmp = va_arg(va, char *);
818 channel = va_arg(va, SilcChannelEntry);
820 tmp = silc_convert_utf8_string(tmp);
822 chanrec = silc_channel_find_entry(server, channel);
823 if (chanrec != NULL) {
825 g_free_not_null(chanrec->topic);
827 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
828 signal_emit("channel topic changed", 1, chanrec);
832 if (idtype == SILC_ID_CLIENT) {
833 client_entry = (SilcClientEntry)entry;
834 memset(buf, 0, sizeof(buf));
835 snprintf(buf, sizeof(buf) - 1, "%s@%s",
836 client_entry->username, client_entry->hostname);
837 signal_emit("message topic", 5, server, channel->channel_name,
838 tmp, client_entry->nickname, buf);
839 } else if (idtype == SILC_ID_SERVER) {
840 server_entry = (SilcServerEntry)entry;
841 signal_emit("message topic", 5, server, channel->channel_name,
842 tmp, server_entry->server_name,
843 server_entry->server_name);
844 } else if (idtype == SILC_ID_CHANNEL) {
845 channel = (SilcChannelEntry)entry;
846 signal_emit("message topic", 5, server, channel->channel_name,
847 tmp, channel->channel_name, channel->channel_name);
853 case SILC_NOTIFY_TYPE_NICK_CHANGE:
858 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
860 client_entry = va_arg(va, SilcClientEntry);
861 client_entry2 = va_arg(va, SilcClientEntry);
863 if (!strcmp(client_entry->nickname, client_entry2->nickname))
866 memset(buf, 0, sizeof(buf));
867 snprintf(buf, sizeof(buf) - 1, "%s@%s",
868 client_entry2->username, client_entry2->hostname);
869 nicklist_rename_unique(SERVER(server),
870 client_entry, client_entry->nickname,
871 client_entry2, client_entry2->nickname);
872 signal_emit("message nick", 4, server, client_entry2->nickname,
873 client_entry->nickname, buf);
876 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
878 * Changed channel mode.
881 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
883 idtype = va_arg(va, int);
884 entry = va_arg(va, void *);
885 mode = va_arg(va, SilcUInt32);
886 (void)va_arg(va, char *); /* cipher */
887 (void)va_arg(va, char *); /* hmac */
888 (void)va_arg(va, char *); /* passphrase */
889 (void)va_arg(va, SilcPublicKey); /* founder key */
890 buffer = va_arg(va, SilcBuffer); /* channel public keys */
891 channel = va_arg(va, SilcChannelEntry);
893 tmp = silc_client_chmode(mode,
894 channel->channel_key ?
895 silc_cipher_get_name(channel->channel_key) : "",
897 silc_hmac_get_name(channel->hmac) : "");
899 chanrec = silc_channel_find_entry(server, channel);
900 if (chanrec != NULL) {
901 g_free_not_null(chanrec->mode);
902 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
903 signal_emit("channel mode changed", 1, chanrec);
906 if (idtype == SILC_ID_CLIENT)
907 name = ((SilcClientEntry)entry)->nickname;
908 else if (idtype == SILC_ID_SERVER)
909 name = ((SilcServerEntry)entry)->server_name;
910 else if (idtype == SILC_ID_CHANNEL)
911 name = ((SilcChannelEntry)entry)->channel_name;
915 signal_emit("message silc cmode", 4, server, channel->channel_name,
918 /* Print the channel public key list */
920 signal_emit("message silc pubkeys", 3, server, channel, buffer);
925 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
927 * Changed user's mode on channel.
930 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
932 idtype = va_arg(va, int);
933 entry = va_arg(va, void *);
934 mode = va_arg(va, SilcUInt32);
935 client_entry2 = va_arg(va, SilcClientEntry);
936 channel = va_arg(va, SilcChannelEntry);
938 tmp = silc_client_chumode(mode);
939 chanrec = silc_channel_find_entry(server, channel);
940 if (chanrec != NULL) {
943 if (client_entry2 == server->conn->local_entry)
944 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
946 nick = silc_nicklist_find(chanrec, client_entry2);
948 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
949 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
950 signal_emit("nick mode changed", 2, chanrec, nick);
954 if (idtype == SILC_ID_CLIENT)
955 name = ((SilcClientEntry)entry)->nickname;
956 else if (idtype == SILC_ID_SERVER)
957 name = ((SilcServerEntry)entry)->server_name;
958 else if (idtype == SILC_ID_CHANNEL)
959 name = ((SilcChannelEntry)entry)->channel_name;
963 signal_emit("message silc cumode", 5, server, channel->channel_name,
964 client_entry2->nickname, tmp, name);
966 if (mode & SILC_CHANNEL_UMODE_CHANFO)
967 signal_emit("message silc founder", 3, server, channel->channel_name,
968 client_entry2->nickname);
970 if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
971 signal_emit("message silc quieted", 2, server, channel->channel_name);
976 case SILC_NOTIFY_TYPE_MOTD:
981 SILC_LOG_DEBUG(("Notify: MOTD"));
983 tmp = silc_convert_utf8_string(va_arg(va, char *));
985 if (!settings_get_bool("skip_motd"))
986 signal_emit("message silc motd", 2, server, tmp);
991 case SILC_NOTIFY_TYPE_KICKED:
993 * Someone was kicked from channel.
996 SILC_LOG_DEBUG(("Notify: KICKED"));
998 client_entry = va_arg(va, SilcClientEntry);
999 tmp = silc_convert_utf8_string(va_arg(va, char *));
1000 client_entry2 = va_arg(va, SilcClientEntry);
1001 channel = va_arg(va, SilcChannelEntry);
1003 chanrec = silc_channel_find_entry(server, channel);
1005 if (client_entry == conn->local_entry) {
1006 printformat_module("fe-common/silc", server, channel->channel_name,
1007 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
1008 channel->channel_name,
1009 client_entry ? client_entry2->nickname : "",
1012 chanrec->kicked = TRUE;
1013 channel_destroy((CHANNEL_REC *)chanrec);
1016 printformat_module("fe-common/silc", server, channel->channel_name,
1017 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
1018 client_entry->nickname, channel->channel_name,
1019 client_entry2 ? client_entry2->nickname : "",
1023 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
1024 if (nickrec != NULL)
1025 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
1031 case SILC_NOTIFY_TYPE_KILLED:
1033 * Someone was killed from the network.
1036 SILC_LOG_DEBUG(("Notify: KILLED"));
1038 client_entry = va_arg(va, SilcClientEntry);
1039 tmp = silc_convert_utf8_string(va_arg(va, char *));
1040 idtype = va_arg(va, int);
1041 entry = va_arg(va, SilcClientEntry);
1043 if (client_entry == conn->local_entry) {
1044 if (idtype == SILC_ID_CLIENT) {
1045 client_entry2 = (SilcClientEntry)entry;
1046 printformat_module("fe-common/silc", server, NULL,
1047 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1048 client_entry2 ? client_entry2->nickname : "",
1050 } else if (idtype == SILC_ID_SERVER) {
1051 server_entry = (SilcServerEntry)entry;
1052 printformat_module("fe-common/silc", server, NULL,
1053 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1054 server_entry->server_name, tmp ? tmp : "");
1055 } else if (idtype == SILC_ID_CHANNEL) {
1056 channel = (SilcChannelEntry)entry;
1057 printformat_module("fe-common/silc", server, NULL,
1058 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1059 channel->channel_name, tmp ? tmp : "");
1062 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1063 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1064 list_tmp->next->next) {
1065 CHANNEL_REC *channel = list_tmp->data;
1066 NICK_REC *nickrec = list_tmp->next->data;
1067 nicklist_remove(channel, nickrec);
1070 if (idtype == SILC_ID_CLIENT) {
1071 client_entry2 = (SilcClientEntry)entry;
1072 printformat_module("fe-common/silc", server, NULL,
1073 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1074 client_entry->nickname,
1075 client_entry2 ? client_entry2->nickname : "",
1077 } else if (idtype == SILC_ID_SERVER) {
1078 server_entry = (SilcServerEntry)entry;
1079 printformat_module("fe-common/silc", server, NULL,
1080 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1081 client_entry->nickname,
1082 server_entry->server_name, tmp ? tmp : "");
1083 } else if (idtype == SILC_ID_CHANNEL) {
1084 channel = (SilcChannelEntry)entry;
1085 printformat_module("fe-common/silc", server, NULL,
1086 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1087 client_entry->nickname,
1088 channel->channel_name, tmp ? tmp : "");
1094 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
1097 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1100 * Server has quit the network.
1103 SilcClientEntry *clients;
1104 SilcUInt32 clients_count;
1106 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1108 (void)va_arg(va, void *);
1109 clients = va_arg(va, SilcClientEntry *);
1110 clients_count = va_arg(va, SilcUInt32);
1112 for (i = 0; i < clients_count; i++) {
1113 memset(buf, 0, sizeof(buf));
1115 /* Print only if we have the nickname. If this client has just quit
1116 when we were only resolving it, it is possible we don't have the
1118 if (clients[i]->nickname) {
1119 if (clients[i]->username)
1120 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1121 clients[i]->username, clients[i]->hostname);
1122 signal_emit("message quit", 4, server, clients[i]->nickname,
1123 clients[i]->username ? buf : "",
1127 silc_server_free_ftp(server, clients[i]);
1129 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
1130 for (list_tmp = list1; list_tmp != NULL; list_tmp =
1131 list_tmp->next->next) {
1132 CHANNEL_REC *channel = list_tmp->data;
1133 NICK_REC *nickrec = list_tmp->next->data;
1134 nicklist_remove(channel, nickrec);
1140 case SILC_NOTIFY_TYPE_ERROR:
1142 SilcStatus error = va_arg(va, int);
1144 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1145 "%s", silc_get_status_message(error));
1149 case SILC_NOTIFY_TYPE_WATCH:
1151 SilcNotifyType notify;
1153 client_entry = va_arg(va, SilcClientEntry);
1154 name = va_arg(va, char *); /* Maybe NULL */
1155 mode = va_arg(va, SilcUInt32);
1156 notify = va_arg(va, int);
1158 if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1160 printformat_module("fe-common/silc", server, NULL,
1161 MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1162 client_entry->nickname, name);
1164 printformat_module("fe-common/silc", server, NULL,
1165 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1166 client_entry->nickname);
1167 } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1168 /* See if client was away and is now present */
1169 if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1170 SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1171 SILC_UMODE_DETACHED)) &&
1172 (client_entry->mode & SILC_UMODE_GONE ||
1173 client_entry->mode & SILC_UMODE_INDISPOSED ||
1174 client_entry->mode & SILC_UMODE_BUSY ||
1175 client_entry->mode & SILC_UMODE_PAGE ||
1176 client_entry->mode & SILC_UMODE_DETACHED)) {
1177 printformat_module("fe-common/silc", server, NULL,
1178 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1179 client_entry->nickname);
1183 memset(buf, 0, sizeof(buf));
1184 silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1185 printformat_module("fe-common/silc", server, NULL,
1186 MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1187 client_entry->nickname, buf);
1189 } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1190 printformat_module("fe-common/silc", server, NULL,
1191 MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1192 client_entry->nickname);
1193 } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1194 notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1195 printformat_module("fe-common/silc", server, NULL,
1196 MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1197 client_entry->nickname);
1198 } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1199 /* Client logged in to the network */
1200 printformat_module("fe-common/silc", server, NULL,
1201 MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1202 client_entry->nickname);
1208 /* Unknown notify */
1209 printformat_module("fe-common/silc", server, NULL,
1210 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1217 /* Called to indicate that connection was either successfully established
1218 or connecting failed. This is also the first time application receives
1219 the SilcClientConnection object which it should save somewhere. */
1221 void silc_connect(SilcClient client, SilcClientConnection conn,
1222 SilcClientConnectionStatus status)
1224 SILC_SERVER_REC *server = conn->context;
1226 if (!server || server->disconnected) {
1227 silc_client_close_connection(client, conn);
1232 case SILC_CLIENT_CONN_SUCCESS:
1233 /* We have successfully connected to server */
1234 if ((client->nickname != NULL) &&
1235 (strcmp(client->nickname, client->username)))
1236 silc_queue_enable(conn); /* enable queueing until we have our nick */
1237 server->connected = TRUE;
1238 signal_emit("event connected", 1, server);
1241 case SILC_CLIENT_CONN_SUCCESS_RESUME:
1242 /* We have successfully resumed old detached session */
1243 server->connected = TRUE;
1244 signal_emit("event connected", 1, server);
1246 /* If we resumed old session check whether we need to update
1248 if (strcmp(server->nick, conn->local_entry->nickname)) {
1250 old = g_strdup(server->nick);
1251 server_change_nick(SERVER(server), conn->local_entry->nickname);
1252 nicklist_rename_unique(SERVER(server),
1253 conn->local_entry, server->nick,
1254 conn->local_entry, conn->local_entry->nickname);
1255 signal_emit("message own_nick", 4, server, server->nick, old, "");
1259 /* remove the detach data now */
1263 file = silc_get_session_filename(server);
1274 file = silc_get_session_filename(server);
1276 if (silc_file_size(file) > 0)
1277 printformat_module("fe-common/silc", server, NULL,
1278 MSGLEVEL_CRAP, SILCTXT_REATTACH_FAILED, file);
1282 server->connection_lost = TRUE;
1284 server->conn->context = NULL;
1285 server_disconnect(SERVER(server));
1292 /* Called to indicate that connection was disconnected to the server. */
1294 void silc_disconnect(SilcClient client, SilcClientConnection conn,
1295 SilcStatus status, const char *message)
1297 SILC_SERVER_REC *server = conn->context;
1299 SILC_LOG_DEBUG(("Start"));
1301 if (!server || server->connection_lost)
1304 if (server->conn && server->conn->local_entry) {
1305 nicklist_rename_unique(SERVER(server),
1306 server->conn->local_entry, server->nick,
1307 server->conn->local_entry,
1308 silc_client->username);
1309 silc_change_nick(server, silc_client->username);
1313 silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
1314 "Server closed connection: %s (%d) %s",
1315 silc_get_status_message(status), status,
1316 message ? message : "");
1319 server->conn->context = NULL;
1320 server->conn = NULL;
1321 server->connection_lost = TRUE;
1322 server_disconnect(SERVER(server));
1325 /* Command handler. This function is called always in the command function.
1326 If error occurs it will be called as well. `conn' is the associated
1327 client connection. `cmd_context' is the command context that was
1328 originally sent to the command. `success' is FALSE if error occured
1329 during command. `command' is the command being processed. It must be
1330 noted that this is not reply from server. This is merely called just
1331 after application has called the command. Just to tell application
1332 that the command really was processed. */
1334 static bool cmode_list_chpks = FALSE;
1336 void silc_command(SilcClient client, SilcClientConnection conn,
1337 SilcClientCommandContext cmd_context, bool success,
1338 SilcCommand command, SilcStatus status)
1340 SILC_SERVER_REC *server = conn->context;
1342 SILC_LOG_DEBUG(("Start"));
1345 silc_say_error("%s", silc_get_status_message(status));
1351 case SILC_COMMAND_INVITE:
1352 if (cmd_context->argc > 2)
1353 printformat_module("fe-common/silc", server, NULL,
1354 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1355 cmd_context->argv[2],
1356 (cmd_context->argv[1][0] == '*' ?
1357 (char *)conn->current_channel->channel_name :
1358 (char *)cmd_context->argv[1]));
1361 case SILC_COMMAND_DETACH:
1362 server->no_reconnect = TRUE;
1365 case SILC_COMMAND_CMODE:
1366 if (cmd_context->argc == 3 &&
1367 !strcmp(cmd_context->argv[2], "+C"))
1368 cmode_list_chpks = TRUE;
1370 cmode_list_chpks = FALSE;
1379 SilcChannelEntry channel;
1383 /* Client info resolving callback when JOIN command reply is received.
1384 This will cache all users on the channel. */
1386 static void silc_client_join_get_users(SilcClient client,
1387 SilcClientConnection conn,
1388 SilcClientEntry *clients,
1389 SilcUInt32 clients_count,
1392 SilcJoinResolve r = context;
1393 SilcChannelEntry channel = r->channel;
1394 SilcHashTableList htl;
1395 SilcChannelUser chu;
1396 SILC_SERVER_REC *server = conn->context;
1397 SILC_CHANNEL_REC *chanrec;
1398 SilcClientEntry founder = NULL;
1401 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
1402 silc_hash_table_count(channel->user_list)));
1404 if (!clients && r->retry < 1) {
1405 /* Retry to resolve */
1407 silc_client_get_clients_by_channel(client, conn, channel,
1408 silc_client_join_get_users, context);
1412 chanrec = silc_channel_find(server, channel->channel_name);
1413 if (chanrec == NULL)
1416 silc_hash_table_list(channel->user_list, &htl);
1417 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1418 if (!chu->client->nickname)
1420 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1421 founder = chu->client;
1422 silc_nicklist_insert(chanrec, chu, FALSE);
1424 silc_hash_table_list_reset(&htl);
1426 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1427 nicklist_set_own(CHANNEL(chanrec), ownnick);
1428 signal_emit("channel joined", 1, chanrec);
1429 chanrec->entry = channel;
1432 printformat_module("fe-common/silc", server, channel->channel_name,
1433 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1434 channel->channel_name, chanrec->topic);
1437 if (founder == conn->local_entry) {
1438 printformat_module("fe-common/silc",
1439 server, channel->channel_name, MSGLEVEL_CRAP,
1440 SILCTXT_CHANNEL_FOUNDER_YOU,
1441 channel->channel_name);
1442 signal_emit("nick mode changed", 2, chanrec, ownnick);
1444 printformat_module("fe-common/silc",
1445 server, channel->channel_name, MSGLEVEL_CRAP,
1446 SILCTXT_CHANNEL_FOUNDER,
1447 channel->channel_name, founder->nickname);
1453 SilcClientConnection conn;
1459 void silc_getkey_cb(bool success, void *context)
1461 GetkeyContext getkey = (GetkeyContext)context;
1462 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1463 char *name = (getkey->id_type == SILC_ID_CLIENT ?
1464 ((SilcClientEntry)getkey->entry)->nickname :
1465 ((SilcServerEntry)getkey->entry)->server_name);
1468 printformat_module("fe-common/silc", NULL, NULL,
1469 MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1471 printformat_module("fe-common/silc", NULL, NULL,
1472 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1476 silc_free(getkey->fingerprint);
1480 /* Parse an invite or ban list */
1481 void silc_parse_inviteban_list(SilcClient client,
1482 SilcClientConnection conn,
1483 SILC_SERVER_REC *server,
1484 SilcChannelEntry channel,
1485 const char *list_type,
1486 SilcArgumentPayload list)
1489 SilcUInt32 type, len;
1490 SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1491 int counter=0, resolving = FALSE;
1493 if (!silc_argument_get_arg_num(list)) {
1494 printformat_module("fe-common/silc", server,
1495 (chanrec ? chanrec->visible_name : NULL),
1496 MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1497 channel->channel_name, list_type);
1501 printformat_module("fe-common/silc", server,
1502 (chanrec ? chanrec->visible_name : NULL),
1503 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1504 channel->channel_name, list_type);
1506 /* parse the list */
1507 tmp = silc_argument_get_first_arg(list, &type, &len);
1512 /* an invite string */
1516 if (tmp[len-1] == ',')
1519 list = g_strsplit(tmp, ",", -1);
1521 printformat_module("fe-common/silc", server,
1522 (chanrec ? chanrec->visible_name : NULL),
1523 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1524 ++counter, channel->channel_name, list_type,
1533 char *fingerprint, *babbleprint;
1535 /* tmp is Public Key Payload, take public key from it. */
1536 fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1537 babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1539 printformat_module("fe-common/silc", server,
1540 (chanrec ? chanrec->visible_name : NULL),
1541 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1542 ++counter, channel->channel_name, list_type,
1543 fingerprint, babbleprint);
1550 SilcClientID *client_id;
1551 SilcClientEntry client_entry;
1553 client_id = silc_id_payload_parse_id(tmp, len, NULL);
1555 if (client_id == NULL) {
1556 silc_say_error("Invalid data in %s list encountered", list_type);
1560 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1563 printformat_module("fe-common/silc", server,
1564 (chanrec ? chanrec->visible_name : NULL),
1565 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1566 ++counter, channel->channel_name, list_type,
1567 client_entry->nickname);
1570 silc_client_get_client_by_id_resolve(client, conn, client_id,
1574 silc_free(client_id);
1580 silc_say_error("Unkown type in %s list: %u (len %u)",
1581 list_type, type, len);
1583 tmp = silc_argument_get_next_arg(list, &type, &len);
1587 printformat_module("fe-common/silc", server,
1588 (chanrec ? chanrec->visible_name : NULL),
1589 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1590 list_type, channel->channel_name);
1593 /* Command reply handler. This function is called always in the command reply
1594 function. If error occurs it will be called as well. Normal scenario
1595 is that it will be called after the received command data has been parsed
1596 and processed. The function is used to pass the received command data to
1599 `conn' is the associated client connection. `cmd_payload' is the command
1600 payload data received from server and it can be ignored. It is provided
1601 if the application would like to re-parse the received command data,
1602 however, it must be noted that the data is parsed already by the library
1603 thus the payload can be ignored. `success' is FALSE if error occured.
1604 In this case arguments are not sent to the application. `command' is the
1605 command reply being processed. The function has variable argument list
1606 and each command defines the number and type of arguments it passes to the
1607 application (on error they are not sent). */
1610 silc_command_reply(SilcClient client, SilcClientConnection conn,
1611 SilcCommandPayload cmd_payload, bool success,
1612 SilcCommand command, SilcStatus status, ...)
1615 SILC_SERVER_REC *server = conn->context;
1616 SILC_CHANNEL_REC *chanrec;
1619 va_start(vp, status);
1621 SILC_LOG_DEBUG(("Start"));
1624 case SILC_COMMAND_WHOIS:
1626 char buf[1024], *nickname, *username, *realname, *nick;
1627 unsigned char *fingerprint;
1628 SilcUInt32 idle, mode;
1629 SilcBuffer channels, user_modes;
1630 SilcClientEntry client_entry;
1633 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1634 /* Print the unknown nick for user */
1635 unsigned char *tmp =
1636 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1639 silc_say_error("%s: %s", tmp,
1640 silc_get_status_message(status));
1642 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1643 /* Try to find the entry for the unknown client ID, since we
1644 might have, and print the nickname of it for user. */
1646 unsigned char *tmp =
1647 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1650 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1653 client_entry = silc_client_get_client_by_id(client, conn,
1655 if (client_entry && client_entry->nickname)
1656 silc_say_error("%s: %s", client_entry->nickname,
1657 silc_get_status_message(status));
1658 silc_free(client_id);
1662 } else if (!success) {
1663 silc_say_error("WHOIS: %s", silc_get_status_message(status));
1667 client_entry = va_arg(vp, SilcClientEntry);
1668 nickname = va_arg(vp, char *);
1669 username = va_arg(vp, char *);
1670 realname = va_arg(vp, char *);
1671 channels = va_arg(vp, SilcBuffer);
1672 mode = va_arg(vp, SilcUInt32);
1673 idle = va_arg(vp, SilcUInt32);
1674 fingerprint = va_arg(vp, unsigned char *);
1675 user_modes = va_arg(vp, SilcBuffer);
1676 attrs = va_arg(vp, SilcDList);
1678 silc_parse_userfqdn(nickname, &nick, NULL);
1679 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1680 SILCTXT_WHOIS_USERINFO, nickname,
1681 client_entry->username, client_entry->hostname,
1682 nick, client_entry->nickname);
1683 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1684 SILCTXT_WHOIS_REALNAME, realname);
1687 if (channels && user_modes) {
1689 SilcDList list = silc_channel_payload_parse_list(channels->data,
1691 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1693 SilcChannelPayload entry;
1696 memset(buf, 0, sizeof(buf));
1697 silc_dlist_start(list);
1698 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1699 SilcUInt32 name_len;
1700 char *m = silc_client_chumode_char(umodes[i++]);
1701 char *name = silc_channel_get_name(entry, &name_len);
1704 silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1705 silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1706 silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1710 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1711 SILCTXT_WHOIS_CHANNELS, buf);
1712 silc_channel_payload_list_free(list);
1718 memset(buf, 0, sizeof(buf));
1719 silc_get_umode_string(mode, buf, sizeof(buf - 1));
1720 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1721 SILCTXT_WHOIS_MODES, buf);
1724 if (idle && nickname) {
1725 memset(buf, 0, sizeof(buf));
1726 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1727 idle > 60 ? (idle / 60) : idle,
1728 idle > 60 ? "minutes" : "seconds");
1730 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1731 SILCTXT_WHOIS_IDLE, buf);
1735 fingerprint = silc_fingerprint(fingerprint, 20);
1736 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1737 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1738 silc_free(fingerprint);
1742 silc_query_attributes_print(server, silc_client, conn, attrs,
1747 case SILC_COMMAND_IDENTIFY:
1749 SilcClientEntry client_entry;
1751 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1752 /* Print the unknown nick for user */
1753 unsigned char *tmp =
1754 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1757 silc_say_error("%s: %s", tmp,
1758 silc_get_status_message(status));
1760 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1761 /* Try to find the entry for the unknown client ID, since we
1762 might have, and print the nickname of it for user. */
1764 unsigned char *tmp =
1765 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1768 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1771 client_entry = silc_client_get_client_by_id(client, conn,
1773 if (client_entry && client_entry->nickname)
1774 silc_say_error("%s: %s", client_entry->nickname,
1775 silc_get_status_message(status));
1776 silc_free(client_id);
1785 case SILC_COMMAND_WHOWAS:
1787 char *nickname, *username, *realname;
1789 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1791 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1794 silc_say_error("%s: %s", tmp,
1795 silc_get_status_message(status));
1797 } else if (!success) {
1798 silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1802 (void)va_arg(vp, SilcClientEntry);
1803 nickname = va_arg(vp, char *);
1804 username = va_arg(vp, char *);
1805 realname = va_arg(vp, char *);
1807 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1808 SILCTXT_WHOWAS_USERINFO, nickname, username,
1809 realname ? realname : "");
1813 case SILC_COMMAND_INVITE:
1815 SilcChannelEntry channel;
1817 SilcArgumentPayload invite_list;
1823 channel = va_arg(vp, SilcChannelEntry);
1824 payload = va_arg(vp, SilcBuffer);
1827 SILC_GET16_MSB(argc, payload->data);
1828 invite_list = silc_argument_payload_parse(payload->data + 2,
1829 payload->len - 2, argc);
1831 silc_parse_inviteban_list(client, conn, server, channel,
1832 "invite", invite_list);
1833 silc_argument_payload_free(invite_list);
1839 case SILC_COMMAND_JOIN:
1841 char *channel, *mode, *topic;
1843 SilcChannelEntry channel_entry;
1844 SilcBuffer client_id_list;
1845 SilcUInt32 list_count;
1850 channel = va_arg(vp, char *);
1851 channel_entry = va_arg(vp, SilcChannelEntry);
1852 modei = va_arg(vp, SilcUInt32);
1853 (void)va_arg(vp, SilcUInt32);
1854 (void)va_arg(vp, unsigned char *);
1855 (void)va_arg(vp, unsigned char *);
1856 (void)va_arg(vp, unsigned char *);
1857 topic = va_arg(vp, char *);
1858 (void)va_arg(vp, unsigned char *);
1859 list_count = va_arg(vp, SilcUInt32);
1860 client_id_list = va_arg(vp, SilcBuffer);
1862 chanrec = silc_channel_find(server, channel);
1864 chanrec = silc_channel_create(server, channel, channel, TRUE);
1867 char tmp[256], *cp, *dm = NULL;
1868 g_free_not_null(chanrec->topic);
1870 if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1871 memset(tmp, 0, sizeof(tmp));
1873 if (strlen(topic) > sizeof(tmp) - 1) {
1874 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1878 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1883 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1884 signal_emit("channel topic changed", 1, chanrec);
1889 mode = silc_client_chmode(modei,
1890 channel_entry->channel_key ?
1891 silc_cipher_get_name(channel_entry->
1893 channel_entry->hmac ?
1894 silc_hmac_get_name(channel_entry->hmac) : "");
1895 g_free_not_null(chanrec->mode);
1896 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1897 signal_emit("channel mode changed", 1, chanrec);
1899 /* Resolve the client information */
1901 SilcJoinResolve r = silc_calloc(1, sizeof(*r));
1902 r->channel = channel_entry;
1903 silc_client_get_clients_by_list(client, conn, list_count,
1905 silc_client_join_get_users, r);
1911 case SILC_COMMAND_NICK:
1914 SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1920 nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1921 if ((nicks != NULL) &&
1922 (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1924 SilcClientEntry collider, old;
1926 old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1927 collider = silc_client_get_client_by_id(client, conn,
1930 if (collider != client_entry) {
1932 memset(buf, 0, sizeof(buf));
1933 snprintf(buf, sizeof(buf) - 1, "%s@%s",
1934 collider->username, collider->hostname);
1935 nicklist_rename_unique(SERVER(server),
1937 collider, collider->nickname);
1938 silc_print_nick_change(server, collider->nickname,
1939 client_entry->nickname, buf);
1944 g_slist_free(nicks);
1946 old = g_strdup(server->nick);
1947 server_change_nick(SERVER(server), client_entry->nickname);
1948 nicklist_rename_unique(SERVER(server),
1949 server->conn->local_entry, server->nick,
1950 client_entry, client_entry->nickname);
1951 signal_emit("message own_nick", 4, server, server->nick, old, "");
1954 /* when connecting to a server, the last thing we receive
1955 is a SILC_COMMAND_LIST reply. Since we enable queueing
1956 during the connection, we can now safely disable it again */
1957 silc_queue_disable(conn);
1961 case SILC_COMMAND_LIST:
1966 char tmp[256], *cp, *dm = NULL;
1971 (void)va_arg(vp, SilcChannelEntry);
1972 name = va_arg(vp, char *);
1973 topic = va_arg(vp, char *);
1974 usercount = va_arg(vp, int);
1976 if (topic && !silc_term_utf8() &&
1977 silc_utf8_valid(topic, strlen(topic))) {
1978 memset(tmp, 0, sizeof(tmp));
1980 if (strlen(topic) > sizeof(tmp) - 1) {
1981 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1985 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1990 if (status == SILC_STATUS_LIST_START ||
1991 status == SILC_STATUS_OK)
1992 printformat_module("fe-common/silc", server, NULL,
1993 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1996 snprintf(users, sizeof(users) - 1, "N/A");
1998 snprintf(users, sizeof(users) - 1, "%d", usercount);
1999 printformat_module("fe-common/silc", server, NULL,
2000 MSGLEVEL_CRAP, SILCTXT_LIST,
2001 name, users, topic ? topic : "");
2006 case SILC_COMMAND_UMODE:
2014 mode = va_arg(vp, SilcUInt32);
2016 if (mode & SILC_UMODE_SERVER_OPERATOR &&
2017 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
2018 printformat_module("fe-common/silc", server, NULL,
2019 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
2021 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
2022 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
2023 printformat_module("fe-common/silc", server, NULL,
2024 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
2026 if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
2027 if (mode & SILC_UMODE_GONE) {
2028 if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
2029 reason = g_strdup(server->away_reason);
2031 reason = g_strdup("away");
2033 reason = g_strdup("");
2035 silc_set_away(reason, server);
2040 server->umode = mode;
2041 signal_emit("user mode changed", 2, server, NULL);
2045 case SILC_COMMAND_OPER:
2049 server->umode |= SILC_UMODE_SERVER_OPERATOR;
2050 signal_emit("user mode changed", 2, server, NULL);
2052 printformat_module("fe-common/silc", server, NULL,
2053 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
2056 case SILC_COMMAND_SILCOPER:
2060 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
2061 signal_emit("user mode changed", 2, server, NULL);
2063 printformat_module("fe-common/silc", server, NULL,
2064 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
2067 case SILC_COMMAND_USERS:
2069 SilcHashTableList htl;
2070 SilcChannelEntry channel;
2071 SilcChannelUser chu;
2076 channel = va_arg(vp, SilcChannelEntry);
2078 printformat_module("fe-common/silc", server, channel->channel_name,
2079 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
2080 channel->channel_name);
2082 silc_hash_table_list(channel->user_list, &htl);
2083 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
2084 SilcClientEntry e = chu->client;
2085 char stat[5], *mode;
2090 memset(stat, 0, sizeof(stat));
2091 mode = silc_client_chumode_char(chu->mode);
2092 if (e->mode & SILC_UMODE_GONE)
2094 else if (e->mode & SILC_UMODE_INDISPOSED)
2096 else if (e->mode & SILC_UMODE_BUSY)
2098 else if (e->mode & SILC_UMODE_PAGE)
2100 else if (e->mode & SILC_UMODE_HYPER)
2102 else if (e->mode & SILC_UMODE_ROBOT)
2104 else if (e->mode & SILC_UMODE_ANONYMOUS)
2111 printformat_module("fe-common/silc", server, channel->channel_name,
2112 MSGLEVEL_CRAP, SILCTXT_USERS,
2114 e->username ? e->username : "",
2115 e->hostname ? e->hostname : "",
2116 e->realname ? e->realname : "");
2120 silc_hash_table_list_reset(&htl);
2124 case SILC_COMMAND_BAN:
2126 SilcChannelEntry channel;
2128 SilcArgumentPayload ban_list;
2134 channel = va_arg(vp, SilcChannelEntry);
2135 payload = va_arg(vp, SilcBuffer);
2138 SILC_GET16_MSB(argc, payload->data);
2139 ban_list = silc_argument_payload_parse(payload->data + 2,
2140 payload->len - 2, argc);
2142 silc_parse_inviteban_list(client, conn, server, channel,
2144 silc_argument_payload_free(ban_list);
2150 case SILC_COMMAND_GETKEY:
2154 SilcPublicKey public_key;
2157 GetkeyContext getkey;
2163 id_type = va_arg(vp, SilcUInt32);
2164 entry = va_arg(vp, void *);
2165 public_key = va_arg(vp, SilcPublicKey);
2168 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2170 getkey = silc_calloc(1, sizeof(*getkey));
2171 getkey->entry = entry;
2172 getkey->id_type = id_type;
2173 getkey->client = client;
2174 getkey->conn = conn;
2175 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2177 name = (id_type == SILC_ID_CLIENT ?
2178 ((SilcClientEntry)entry)->nickname :
2179 ((SilcServerEntry)entry)->server_name);
2181 silc_verify_public_key_internal(client, conn, name,
2182 (id_type == SILC_ID_CLIENT ?
2183 SILC_SOCKET_TYPE_CLIENT :
2184 SILC_SOCKET_TYPE_SERVER),
2185 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
2186 silc_getkey_cb, getkey);
2189 printformat_module("fe-common/silc", server, NULL,
2190 MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2195 case SILC_COMMAND_INFO:
2197 SilcServerEntry server_entry;
2204 server_entry = va_arg(vp, SilcServerEntry);
2205 server_name = va_arg(vp, char *);
2206 server_info = va_arg(vp, char *);
2208 if (server_name && server_info )
2210 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2211 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2216 case SILC_COMMAND_TOPIC:
2218 SilcChannelEntry channel;
2220 char tmp[256], *cp, *dm = NULL;
2225 channel = va_arg(vp, SilcChannelEntry);
2226 topic = va_arg(vp, char *);
2228 if (topic && !silc_term_utf8() &&
2229 silc_utf8_valid(topic, strlen(topic))) {
2230 memset(tmp, 0, sizeof(tmp));
2232 if (strlen(topic) > sizeof(tmp) - 1) {
2233 dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2237 silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
2243 chanrec = silc_channel_find_entry(server, channel);
2245 g_free_not_null(chanrec->topic);
2246 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2247 signal_emit("channel topic changed", 1, chanrec);
2249 printformat_module("fe-common/silc", server, channel->channel_name,
2250 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2251 channel->channel_name, topic);
2253 printformat_module("fe-common/silc", server, channel->channel_name,
2254 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2255 channel->channel_name);
2261 case SILC_COMMAND_WATCH:
2264 case SILC_COMMAND_STATS:
2266 SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2267 my_router_ops, cell_clients, cell_channels, cell_servers,
2268 clients, channels, servers, routers, server_ops, router_ops;
2270 SilcBufferStruct buf;
2271 unsigned char *tmp_buf;
2273 const char *tmptime;
2274 int days, hours, mins, secs;
2279 tmp_buf = va_arg(vp, unsigned char *);
2280 buf_len = va_arg(vp, SilcUInt32);
2282 if (!tmp_buf || !buf_len) {
2283 printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2287 /* Get statistics structure */
2288 silc_buffer_set(&buf, tmp_buf, buf_len);
2289 silc_buffer_unformat(&buf,
2290 SILC_STR_UI_INT(&starttime),
2291 SILC_STR_UI_INT(&uptime),
2292 SILC_STR_UI_INT(&my_clients),
2293 SILC_STR_UI_INT(&my_channels),
2294 SILC_STR_UI_INT(&my_server_ops),
2295 SILC_STR_UI_INT(&my_router_ops),
2296 SILC_STR_UI_INT(&cell_clients),
2297 SILC_STR_UI_INT(&cell_channels),
2298 SILC_STR_UI_INT(&cell_servers),
2299 SILC_STR_UI_INT(&clients),
2300 SILC_STR_UI_INT(&channels),
2301 SILC_STR_UI_INT(&servers),
2302 SILC_STR_UI_INT(&routers),
2303 SILC_STR_UI_INT(&server_ops),
2304 SILC_STR_UI_INT(&router_ops),
2307 tmptime = silc_get_time(starttime);
2308 printformat_module("fe-common/silc", server, NULL,
2309 MSGLEVEL_CRAP, SILCTXT_STATS,
2310 "Local server start time", tmptime);
2312 days = uptime / (24 * 60 * 60);
2313 uptime -= days * (24 * 60 * 60);
2314 hours = uptime / (60 * 60);
2315 uptime -= hours * (60 * 60);
2317 uptime -= mins * 60;
2319 snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2320 days, hours, mins, secs);
2321 printformat_module("fe-common/silc", server, NULL,
2322 MSGLEVEL_CRAP, SILCTXT_STATS,
2323 "Local server uptime", tmp);
2325 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2326 printformat_module("fe-common/silc", server, NULL,
2327 MSGLEVEL_CRAP, SILCTXT_STATS,
2328 "Local server clients", tmp);
2330 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2331 printformat_module("fe-common/silc", server, NULL,
2332 MSGLEVEL_CRAP, SILCTXT_STATS,
2333 "Local server channels", tmp);
2335 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2336 printformat_module("fe-common/silc", server, NULL,
2337 MSGLEVEL_CRAP, SILCTXT_STATS,
2338 "Local server operators", tmp);
2340 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2341 printformat_module("fe-common/silc", server, NULL,
2342 MSGLEVEL_CRAP, SILCTXT_STATS,
2343 "Local router operators", tmp);
2345 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2346 printformat_module("fe-common/silc", server, NULL,
2347 MSGLEVEL_CRAP, SILCTXT_STATS,
2348 "Local cell clients", tmp);
2350 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2351 printformat_module("fe-common/silc", server, NULL,
2352 MSGLEVEL_CRAP, SILCTXT_STATS,
2353 "Local cell channels", tmp);
2355 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2356 printformat_module("fe-common/silc", server, NULL,
2357 MSGLEVEL_CRAP, SILCTXT_STATS,
2358 "Local cell servers", tmp);
2360 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2361 printformat_module("fe-common/silc", server, NULL,
2362 MSGLEVEL_CRAP, SILCTXT_STATS,
2363 "Total clients", tmp);
2365 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2366 printformat_module("fe-common/silc", server, NULL,
2367 MSGLEVEL_CRAP, SILCTXT_STATS,
2368 "Total channels", tmp);
2370 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2371 printformat_module("fe-common/silc", server, NULL,
2372 MSGLEVEL_CRAP, SILCTXT_STATS,
2373 "Total servers", tmp);
2375 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2376 printformat_module("fe-common/silc", server, NULL,
2377 MSGLEVEL_CRAP, SILCTXT_STATS,
2378 "Total routers", tmp);
2380 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2381 printformat_module("fe-common/silc", server, NULL,
2382 MSGLEVEL_CRAP, SILCTXT_STATS,
2383 "Total server operators", tmp);
2385 snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2386 printformat_module("fe-common/silc", server, NULL,
2387 MSGLEVEL_CRAP, SILCTXT_STATS,
2388 "Total router operators", tmp);
2392 case SILC_COMMAND_CMODE:
2394 SilcChannelEntry channel_entry;
2395 SilcBuffer channel_pubkeys;
2397 channel_entry = va_arg(vp, SilcChannelEntry);
2398 (void)va_arg(vp, SilcUInt32);
2399 (void)va_arg(vp, SilcPublicKey);
2400 channel_pubkeys = va_arg(vp, SilcBuffer);
2402 if (!success || !cmode_list_chpks ||
2403 !channel_entry || !channel_entry->channel_name)
2406 /* Print the channel public key list */
2407 signal_emit("message silc pubkeys", 3,
2408 server, channel_entry, channel_pubkeys);
2412 case SILC_COMMAND_LEAVE:
2414 /* we might be cycling, so disable queueing again */
2415 silc_queue_disable(conn);
2426 SilcClientConnection conn;
2432 SilcSKEPKType pk_type;
2433 SilcVerifyPublicKey completion;
2437 static void verify_public_key_completion(const char *line, void *context)
2439 PublicKeyVerify verify = (PublicKeyVerify)context;
2441 if (line[0] == 'Y' || line[0] == 'y') {
2442 /* Call the completion */
2443 if (verify->completion)
2444 verify->completion(TRUE, verify->context);
2446 /* Save the key for future checking */
2447 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
2448 verify->pk_len, SILC_PKCS_FILE_PEM);
2450 /* Call the completion */
2451 if (verify->completion)
2452 verify->completion(FALSE, verify->context);
2454 printformat_module("fe-common/silc", NULL, NULL,
2455 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2456 verify->entity_name ? verify->entity_name :
2460 silc_free(verify->filename);
2461 silc_free(verify->entity);
2462 silc_free(verify->entity_name);
2463 silc_free(verify->pk);
2467 /* Internal routine to verify public key. If the `completion' is provided
2468 it will be called to indicate whether public was verified or not. For
2469 server/router public key this will check for filename that includes the
2470 remote host's IP address and remote host's hostname. */
2473 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2474 const char *name, SilcSocketType conn_type,
2475 unsigned char *pk, SilcUInt32 pk_len,
2476 SilcSKEPKType pk_type,
2477 SilcVerifyPublicKey completion, void *context)
2480 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2481 char *fingerprint, *babbleprint, *format;
2484 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2485 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
2486 "server" : "client");
2487 PublicKeyVerify verify;
2489 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2490 printformat_module("fe-common/silc", NULL, NULL,
2491 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2494 completion(FALSE, context);
2498 pw = getpwuid(getuid());
2501 completion(FALSE, context);
2505 memset(filename, 0, sizeof(filename));
2506 memset(filename2, 0, sizeof(filename2));
2507 memset(file, 0, sizeof(file));
2509 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2510 conn_type == SILC_SOCKET_TYPE_ROUTER) {
2512 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2513 conn->sock->ip, conn->sock->port);
2514 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2515 get_irssi_dir(), entity, file);
2517 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2518 conn->sock->hostname, conn->sock->port);
2519 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2520 get_irssi_dir(), entity, file);
2525 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2526 name, conn->sock->port);
2527 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2528 get_irssi_dir(), entity, file);
2533 /* Replace all whitespaces with `_'. */
2534 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2535 for (i = 0; i < strlen(fingerprint); i++)
2536 if (fingerprint[i] == ' ')
2537 fingerprint[i] = '_';
2539 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2540 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2541 get_irssi_dir(), entity, file);
2542 silc_free(fingerprint);
2547 /* Take fingerprint of the public key */
2548 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2549 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2551 verify = silc_calloc(1, sizeof(*verify));
2552 verify->client = client;
2553 verify->conn = conn;
2554 verify->filename = strdup(ipf);
2555 verify->entity = strdup(entity);
2556 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2557 (name ? strdup(name) : strdup(conn->sock->hostname))
2559 verify->pk = silc_memdup(pk, pk_len);
2560 verify->pk_len = pk_len;
2561 verify->pk_type = pk_type;
2562 verify->completion = completion;
2563 verify->context = context;
2565 /* Check whether this key already exists */
2566 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2567 /* Key does not exist, ask user to verify the key and save it */
2569 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2570 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2571 verify->entity_name : entity);
2572 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2573 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2574 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2575 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2576 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2577 SILCTXT_PUBKEY_ACCEPT);
2578 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2581 silc_free(fingerprint);
2584 /* The key already exists, verify it. */
2585 SilcPublicKey public_key;
2586 unsigned char *encpk;
2587 SilcUInt32 encpk_len;
2589 /* Load the key file, try for both IP filename and hostname filename */
2590 if (!silc_pkcs_load_public_key(ipf, &public_key,
2591 SILC_PKCS_FILE_PEM) &&
2592 !silc_pkcs_load_public_key(ipf, &public_key,
2593 SILC_PKCS_FILE_BIN) &&
2594 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
2595 SILC_PKCS_FILE_PEM) &&
2596 !silc_pkcs_load_public_key(hostf, &public_key,
2597 SILC_PKCS_FILE_BIN)))) {
2598 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2599 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2600 verify->entity_name : entity);
2601 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2602 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2603 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2604 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2605 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2606 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2607 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2608 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2609 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2612 silc_free(fingerprint);
2616 /* Encode the key data */
2617 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2619 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2620 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2621 verify->entity_name : entity);
2622 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2623 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2624 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2625 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2626 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2627 SILCTXT_PUBKEY_MALFORMED, entity);
2628 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2629 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2630 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2633 silc_free(fingerprint);
2637 /* Compare the keys */
2638 if (memcmp(encpk, pk, encpk_len)) {
2639 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2640 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2641 verify->entity_name : entity);
2642 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2643 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2644 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2645 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2646 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2647 SILCTXT_PUBKEY_NO_MATCH, entity);
2648 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2649 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2650 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2651 SILCTXT_PUBKEY_MITM_ATTACK, entity);
2653 /* Ask user to verify the key and save it */
2654 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2655 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2656 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2659 silc_free(fingerprint);
2663 /* Local copy matched */
2665 completion(TRUE, context);
2666 silc_free(fingerprint);
2667 silc_free(verify->filename);
2668 silc_free(verify->entity);
2669 silc_free(verify->entity_name);
2670 silc_free(verify->pk);
2675 /* Verifies received public key. The `conn_type' indicates which entity
2676 (server, client etc.) has sent the public key. If user decides to trust
2677 the key may be saved as trusted public key for later use. The
2678 `completion' must be called after the public key has been verified. */
2681 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2682 SilcSocketType conn_type, unsigned char *pk,
2683 SilcUInt32 pk_len, SilcSKEPKType pk_type,
2684 SilcVerifyPublicKey completion, void *context)
2686 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2688 completion, context);
2691 /* Asks passphrase from user on the input line. */
2694 SilcAskPassphrase completion;
2698 void ask_passphrase_completion(const char *passphrase, void *context)
2700 AskPassphrase p = (AskPassphrase)context;
2701 if (passphrase && passphrase[0] == '\0')
2703 p->completion((unsigned char *)passphrase,
2704 passphrase ? strlen(passphrase) : 0, p->context);
2708 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2709 SilcAskPassphrase completion, void *context)
2711 AskPassphrase p = silc_calloc(1, sizeof(*p));
2712 p->completion = completion;
2713 p->context = context;
2715 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2716 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2720 SilcGetAuthMeth completion;
2722 } *InternalGetAuthMethod;
2724 /* Callback called when we've received the authentication method information
2725 from the server after we've requested it. This will get the authentication
2726 data from the user if needed. */
2728 static void silc_get_auth_method_callback(SilcClient client,
2729 SilcClientConnection conn,
2730 SilcAuthMethod auth_meth,
2733 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2735 SILC_LOG_DEBUG(("Start"));
2737 switch (auth_meth) {
2738 case SILC_AUTH_NONE:
2739 /* No authentication required. */
2740 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2742 case SILC_AUTH_PASSWORD:
2744 /* Check whether we find the password for this server in our
2745 configuration. If not, then don't provide so library will ask
2746 it from the user. */
2747 SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2749 if (!setup || !setup->password) {
2750 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2754 (*internal->completion)(TRUE, auth_meth, setup->password,
2755 strlen(setup->password), internal->context);
2758 case SILC_AUTH_PUBLIC_KEY:
2759 /* Do not get the authentication data now, the library will generate
2760 it using our default key, if we do not provide it here. */
2761 /* XXX In the future when we support multiple local keys and multiple
2762 local certificates we will need to ask from user which one to use. */
2763 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2767 silc_free(internal);
2770 /* Find authentication method and authentication data by hostname and
2771 port. The hostname may be IP address as well. The found authentication
2772 method and authentication data is returned to `auth_meth', `auth_data'
2773 and `auth_data_len'. The function returns TRUE if authentication method
2774 is found and FALSE if not. `conn' may be NULL. */
2776 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2777 char *hostname, SilcUInt16 port,
2778 SilcGetAuthMeth completion, void *context)
2780 InternalGetAuthMethod internal;
2782 SILC_LOG_DEBUG(("Start"));
2784 /* If we do not have this connection configured by the user in a
2785 configuration file then resolve the authentication method from the
2786 server for this session. */
2787 internal = silc_calloc(1, sizeof(*internal));
2788 internal->completion = completion;
2789 internal->context = context;
2791 silc_client_request_authentication_method(client, conn,
2792 silc_get_auth_method_callback,
2796 /* Notifies application that failure packet was received. This is called
2797 if there is some protocol active in the client. The `protocol' is the
2798 protocol context. The `failure' is opaque pointer to the failure
2799 indication. Note, that the `failure' is protocol dependant and application
2800 must explicitly cast it to correct type. Usually `failure' is 32 bit
2801 failure type (see protocol specs for all protocol failure types). */
2803 void silc_failure(SilcClient client, SilcClientConnection conn,
2804 SilcProtocol protocol, void *failure)
2806 SILC_LOG_DEBUG(("Start"));
2808 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2809 SilcSKEStatus status = (SilcSKEStatus)failure;
2811 if (status == SILC_SKE_STATUS_BAD_VERSION)
2812 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2813 SILCTXT_KE_BAD_VERSION);
2814 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2815 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2816 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2817 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2818 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2819 SILCTXT_KE_UNKNOWN_GROUP);
2820 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2821 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2822 SILCTXT_KE_UNKNOWN_CIPHER);
2823 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2824 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2825 SILCTXT_KE_UNKNOWN_PKCS);
2826 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2827 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2828 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2829 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2830 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2831 SILCTXT_KE_UNKNOWN_HMAC);
2832 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2833 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2834 SILCTXT_KE_INCORRECT_SIGNATURE);
2835 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2836 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2837 SILCTXT_KE_INVALID_COOKIE);
2840 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2841 SilcUInt32 err = (SilcUInt32)failure;
2843 if (err == SILC_AUTH_FAILED)
2844 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2845 SILCTXT_AUTH_FAILED);
2849 /* Asks whether the user would like to perform the key agreement protocol.
2850 This is called after we have received an key agreement packet or an
2851 reply to our key agreement packet. This returns TRUE if the user wants
2852 the library to perform the key agreement protocol and FALSE if it is not
2853 desired (application may start it later by calling the function
2854 silc_client_perform_key_agreement). */
2856 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2857 SilcClientEntry client_entry, const char *hostname,
2858 SilcUInt16 port, SilcKeyAgreementCallback *completion,
2863 SILC_LOG_DEBUG(("Start"));
2865 /* We will just display the info on the screen and return FALSE and user
2866 will have to start the key agreement with a command. */
2869 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2872 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2873 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2875 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2876 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2877 client_entry->nickname, hostname, portstr);
2885 /* Notifies application that file transfer protocol session is being
2886 requested by the remote client indicated by the `client_entry' from
2887 the `hostname' and `port'. The `session_id' is the file transfer
2888 session and it can be used to either accept or reject the file
2889 transfer request, by calling the silc_client_file_receive or
2890 silc_client_file_close, respectively. */
2892 void silc_ftp(SilcClient client, SilcClientConnection conn,
2893 SilcClientEntry client_entry, SilcUInt32 session_id,
2894 const char *hostname, SilcUInt16 port)
2896 SILC_SERVER_REC *server;
2898 FtpSession ftp = NULL;
2900 SILC_LOG_DEBUG(("Start"));
2902 server = conn->context;
2904 silc_dlist_start(server->ftp_sessions);
2905 while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2906 if (ftp->client_entry == client_entry &&
2907 ftp->session_id == session_id) {
2908 server->current_session = ftp;
2912 if (ftp == SILC_LIST_END) {
2913 ftp = silc_calloc(1, sizeof(*ftp));
2914 ftp->client_entry = client_entry;
2915 ftp->session_id = session_id;
2918 silc_dlist_add(server->ftp_sessions, ftp);
2919 server->current_session = ftp;
2923 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2926 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2927 SILCTXT_FILE_REQUEST, client_entry->nickname);
2929 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2930 SILCTXT_FILE_REQUEST_HOST,
2931 client_entry->nickname, hostname, portstr);
2934 /* Delivers SILC session detachment data indicated by `detach_data' to the
2935 application. If application has issued SILC_COMMAND_DETACH command
2936 the client session in the SILC network is not quit. The client remains
2937 in the network but is detached. The detachment data may be used later
2938 to resume the session in the SILC Network. The appliation is
2939 responsible of saving the `detach_data', to for example in a file.
2941 The detachment data can be given as argument to the functions
2942 silc_client_connect_to_server, or silc_client_add_connection when
2943 creating connection to remote server, inside SilcClientConnectionParams
2944 structure. If it is provided the client library will attempt to resume
2945 the session in the network. After the connection is created
2946 successfully, the application is responsible of setting the user
2947 interface for user into the same state it was before detaching (showing
2948 same channels, channel modes, etc). It can do this by fetching the
2949 information (like joined channels) from the client library. */
2952 silc_detach(SilcClient client, SilcClientConnection conn,
2953 const unsigned char *detach_data, SilcUInt32 detach_data_len)
2955 SILC_SERVER_REC *server = conn->context;
2958 /* Save the detachment data to file. */
2960 file = silc_get_session_filename(server);
2961 silc_file_writefile(file, detach_data, detach_data_len);
2966 /* SILC client operations */
2967 SilcClientOperations ops = {
2969 silc_channel_message,
2970 silc_private_message,
2976 silc_get_auth_method,
2977 silc_verify_public_key,
2978 silc_ask_passphrase,