5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2001 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"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
44 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
45 const char *name, SilcSocketType conn_type,
46 unsigned char *pk, SilcUInt32 pk_len,
47 SilcSKEPKType pk_type,
48 SilcVerifyPublicKey completion, void *context);
50 void silc_say(SilcClient client, SilcClientConnection conn,
51 SilcClientMessageType type, char *msg, ...)
53 SILC_SERVER_REC *server;
57 server = conn == NULL ? NULL : conn->context;
60 str = g_strdup_vprintf(msg, va);
61 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
66 void silc_say_error(char *msg, ...)
72 str = g_strdup_vprintf(msg, va);
73 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
79 /* Message for a channel. The `sender' is the nickname of the sender
80 received in the packet. The `channel_name' is the name of the channel. */
82 void silc_channel_message(SilcClient client, SilcClientConnection conn,
83 SilcClientEntry sender, SilcChannelEntry channel,
84 SilcMessageFlags flags, const unsigned char *message,
85 SilcUInt32 message_len)
87 SILC_SERVER_REC *server;
89 SILC_CHANNEL_REC *chanrec;
91 SILC_LOG_DEBUG(("Start"));
96 server = conn == NULL ? NULL : conn->context;
97 chanrec = silc_channel_find_entry(server, channel);
101 nick = silc_nicklist_find(chanrec, sender);
103 /* We didn't find client but it clearly exists, add it. */
104 SilcChannelUser chu = silc_client_on_channel(channel, sender);
106 nick = silc_nicklist_insert(chanrec, chu, FALSE);
109 if (flags & SILC_MESSAGE_FLAG_ACTION)
110 printformat_module("fe-common/silc", server, channel->channel_name,
111 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
112 nick == NULL ? "[<unknown>]" : nick->nick, message);
113 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
114 printformat_module("fe-common/silc", server, channel->channel_name,
115 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
116 nick == NULL ? "[<unknown>]" : nick->nick, message);
118 signal_emit("message public", 6, server, message,
119 nick == NULL ? "[<unknown>]" : nick->nick,
120 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
121 chanrec->name, nick);
124 /* Private message to the client. The `sender' is the nickname of the
125 sender received in the packet. */
127 void silc_private_message(SilcClient client, SilcClientConnection conn,
128 SilcClientEntry sender, SilcMessageFlags flags,
129 const unsigned char *message,
130 SilcUInt32 message_len)
132 SILC_SERVER_REC *server;
135 SILC_LOG_DEBUG(("Start"));
137 server = conn == NULL ? NULL : conn->context;
138 memset(userhost, 0, sizeof(userhost));
139 if (sender->username)
140 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
141 sender->username, sender->hostname);
142 signal_emit("message private", 4, server, message,
143 sender->nickname ? sender->nickname : "[<unknown>]",
144 sender->username ? userhost : NULL);
147 /* Notify message to the client. The notify arguments are sent in the
148 same order as servers sends them. The arguments are same as received
149 from the server except for ID's. If ID is received application receives
150 the corresponding entry to the ID. For example, if Client ID is received
151 application receives SilcClientEntry. Also, if the notify type is
152 for channel the channel entry is sent to application (even if server
153 does not send it). */
155 void silc_notify(SilcClient client, SilcClientConnection conn,
156 SilcNotifyType type, ...)
159 SILC_SERVER_REC *server;
160 SILC_CHANNEL_REC *chanrec;
161 SILC_NICK_REC *nickrec;
162 SilcClientEntry client_entry, client_entry2;
163 SilcChannelEntry channel, channel2;
164 SilcServerEntry server_entry;
170 GSList *list1, *list_tmp;
172 SILC_LOG_DEBUG(("Start"));
176 server = conn == NULL ? NULL : conn->context;
179 case SILC_NOTIFY_TYPE_NONE:
180 /* Some generic notice from server */
181 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
184 case SILC_NOTIFY_TYPE_INVITE:
186 * Invited or modified invite list.
189 SILC_LOG_DEBUG(("Notify: INVITE"));
191 channel = va_arg(va, SilcChannelEntry);
192 name = va_arg(va, char *);
193 client_entry = va_arg(va, SilcClientEntry);
195 memset(userhost, 0, sizeof(userhost));
196 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
197 client_entry->username, client_entry->hostname);
198 signal_emit("message invite", 4, server, channel ? channel->channel_name :
199 name, client_entry->nickname, userhost);
202 case SILC_NOTIFY_TYPE_JOIN:
207 SILC_LOG_DEBUG(("Notify: JOIN"));
209 client_entry = va_arg(va, SilcClientEntry);
210 channel = va_arg(va, SilcChannelEntry);
212 if (client_entry == server->conn->local_entry) {
213 /* You joined to channel */
214 chanrec = silc_channel_find(server, channel->channel_name);
215 if (chanrec != NULL && !chanrec->joined)
216 chanrec->entry = channel;
218 chanrec = silc_channel_find_entry(server, channel);
219 if (chanrec != NULL) {
220 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
222 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
226 memset(userhost, 0, sizeof(userhost));
227 if (client_entry->username)
228 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
229 client_entry->username, client_entry->hostname);
230 signal_emit("message join", 4, server, channel->channel_name,
231 client_entry->nickname,
232 client_entry->username == NULL ? "" : userhost);
235 case SILC_NOTIFY_TYPE_LEAVE:
240 SILC_LOG_DEBUG(("Notify: LEAVE"));
242 client_entry = va_arg(va, SilcClientEntry);
243 channel = va_arg(va, SilcChannelEntry);
245 memset(userhost, 0, sizeof(userhost));
246 if (client_entry->username)
247 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
248 client_entry->username, client_entry->hostname);
249 signal_emit("message part", 5, server, channel->channel_name,
250 client_entry->nickname, client_entry->username ?
251 userhost : "", client_entry->nickname);
253 chanrec = silc_channel_find_entry(server, channel);
254 if (chanrec != NULL) {
255 nickrec = silc_nicklist_find(chanrec, client_entry);
257 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
261 case SILC_NOTIFY_TYPE_SIGNOFF:
266 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
268 client_entry = va_arg(va, SilcClientEntry);
269 tmp = va_arg(va, char *);
271 silc_server_free_ftp(server, client_entry);
273 memset(userhost, 0, sizeof(userhost));
274 if (client_entry->username)
275 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
276 client_entry->username, client_entry->hostname);
277 signal_emit("message quit", 4, server, client_entry->nickname,
278 client_entry->username ? userhost : "",
281 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
282 for (list_tmp = list1; list_tmp != NULL; list_tmp =
283 list_tmp->next->next) {
284 CHANNEL_REC *channel = list_tmp->data;
285 NICK_REC *nickrec = list_tmp->next->data;
287 nicklist_remove(channel, nickrec);
291 case SILC_NOTIFY_TYPE_TOPIC_SET:
296 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
298 idtype = va_arg(va, int);
299 entry = va_arg(va, void *);
300 tmp = va_arg(va, char *);
301 channel = va_arg(va, SilcChannelEntry);
303 chanrec = silc_channel_find_entry(server, channel);
304 if (chanrec != NULL) {
305 g_free_not_null(chanrec->topic);
306 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
307 signal_emit("channel topic changed", 1, chanrec);
310 if (idtype == SILC_ID_CLIENT) {
311 client_entry = (SilcClientEntry)entry;
312 memset(userhost, 0, sizeof(userhost));
313 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
314 client_entry->username, client_entry->hostname);
315 signal_emit("message topic", 5, server, channel->channel_name,
316 tmp, client_entry->nickname, userhost);
317 } else if (idtype == SILC_ID_SERVER) {
318 server_entry = (SilcServerEntry)entry;
319 signal_emit("message topic", 5, server, channel->channel_name,
320 tmp, server_entry->server_name,
321 server_entry->server_name);
322 } else if (idtype == SILC_ID_CHANNEL) {
323 channel = (SilcChannelEntry)entry;
324 signal_emit("message topic", 5, server, channel->channel_name,
325 tmp, channel->channel_name, channel->channel_name);
329 case SILC_NOTIFY_TYPE_NICK_CHANGE:
334 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
336 client_entry = va_arg(va, SilcClientEntry);
337 client_entry2 = va_arg(va, SilcClientEntry);
339 memset(userhost, 0, sizeof(userhost));
340 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
341 client_entry2->username, client_entry2->hostname);
342 nicklist_rename_unique(SERVER(server),
343 client_entry, client_entry->nickname,
344 client_entry2, client_entry2->nickname);
345 signal_emit("message nick", 4, server, client_entry2->nickname,
346 client_entry->nickname, userhost);
349 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
351 * Changed channel mode.
354 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
356 idtype = va_arg(va, int);
357 entry = va_arg(va, void *);
358 mode = va_arg(va, SilcUInt32);
359 (void)va_arg(va, char *);
360 (void)va_arg(va, char *);
361 channel = va_arg(va, SilcChannelEntry);
363 tmp = silc_client_chmode(mode,
364 channel->channel_key ?
365 channel->channel_key->cipher->name : "",
367 silc_hmac_get_name(channel->hmac) : "");
369 chanrec = silc_channel_find_entry(server, channel);
370 if (chanrec != NULL) {
371 g_free_not_null(chanrec->mode);
372 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
373 signal_emit("channel mode changed", 1, chanrec);
376 if (idtype == SILC_ID_CLIENT) {
377 client_entry = (SilcClientEntry)entry;
378 printformat_module("fe-common/silc", server, channel->channel_name,
379 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
380 channel->channel_name, tmp ? tmp : "removed all",
381 client_entry->nickname);
382 } else if (idtype == SILC_ID_SERVER) {
383 server_entry = (SilcServerEntry)entry;
384 printformat_module("fe-common/silc", server, channel->channel_name,
385 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
386 channel->channel_name, tmp ? tmp : "removed all",
387 server_entry->server_name);
388 } else if (idtype == SILC_ID_CHANNEL) {
389 channel2 = (SilcChannelEntry)entry;
390 printformat_module("fe-common/silc", server, channel->channel_name,
391 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
392 channel->channel_name, tmp ? tmp : "removed all",
393 channel2->channel_name);
399 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
401 * Changed user's mode on channel.
404 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
406 idtype = va_arg(va, int);
407 entry = va_arg(va, void *);
408 mode = va_arg(va, SilcUInt32);
409 client_entry2 = va_arg(va, SilcClientEntry);
410 channel = va_arg(va, SilcChannelEntry);
412 tmp = silc_client_chumode(mode);
413 chanrec = silc_channel_find_entry(server, channel);
414 if (chanrec != NULL) {
417 if (client_entry2 == server->conn->local_entry)
418 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
420 nick = silc_nicklist_find(chanrec, client_entry2);
422 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
423 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
424 signal_emit("nick mode changed", 2, chanrec, nick);
428 if (idtype == SILC_ID_CLIENT) {
429 client_entry = (SilcClientEntry)entry;
430 printformat_module("fe-common/silc", server, channel->channel_name,
431 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
432 channel->channel_name, client_entry2->nickname,
433 tmp ? tmp : "removed all",
434 client_entry->nickname);
435 } else if (idtype == SILC_ID_SERVER) {
436 server_entry = (SilcServerEntry)entry;
437 printformat_module("fe-common/silc", server, channel->channel_name,
438 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
439 channel->channel_name, client_entry2->nickname,
440 tmp ? tmp : "removed all",
441 server_entry->server_name);
442 } else if (idtype == SILC_ID_CHANNEL) {
443 channel2 = (SilcChannelEntry)entry;
444 printformat_module("fe-common/silc", server, channel->channel_name,
445 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
446 channel->channel_name, client_entry2->nickname,
447 tmp ? tmp : "removed all",
448 channel2->channel_name);
451 if (mode & SILC_CHANNEL_UMODE_CHANFO)
452 printformat_module("fe-common/silc",
453 server, channel->channel_name, MSGLEVEL_CRAP,
454 SILCTXT_CHANNEL_FOUNDER,
455 channel->channel_name, client_entry2->nickname);
460 case SILC_NOTIFY_TYPE_MOTD:
465 SILC_LOG_DEBUG(("Notify: MOTD"));
467 tmp = va_arg(va, char *);
469 if (!settings_get_bool("skip_motd"))
470 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
473 case SILC_NOTIFY_TYPE_KICKED:
475 * Someone was kicked from channel.
478 SILC_LOG_DEBUG(("Notify: KICKED"));
480 client_entry = va_arg(va, SilcClientEntry);
481 tmp = va_arg(va, char *);
482 client_entry2 = va_arg(va, SilcClientEntry);
483 channel = va_arg(va, SilcChannelEntry);
485 chanrec = silc_channel_find_entry(server, channel);
487 if (client_entry == conn->local_entry) {
488 printformat_module("fe-common/silc", server, channel->channel_name,
489 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
490 channel->channel_name,
491 client_entry ? client_entry2->nickname : "",
494 chanrec->kicked = TRUE;
495 channel_destroy((CHANNEL_REC *)chanrec);
498 printformat_module("fe-common/silc", server, channel->channel_name,
499 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
500 client_entry->nickname, channel->channel_name,
501 client_entry2 ? client_entry2->nickname : "",
505 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
507 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
512 case SILC_NOTIFY_TYPE_KILLED:
514 * Someone was killed from the network.
517 SILC_LOG_DEBUG(("Notify: KILLED"));
519 client_entry = va_arg(va, SilcClientEntry);
520 tmp = va_arg(va, char *);
521 idtype = va_arg(va, int);
522 entry = va_arg(va, SilcClientEntry);
524 if (client_entry == conn->local_entry) {
525 if (idtype == SILC_ID_CLIENT) {
526 client_entry2 = (SilcClientEntry)entry;
527 printformat_module("fe-common/silc", server, NULL,
528 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
529 client_entry2 ? client_entry2->nickname : "",
531 } else if (idtype == SILC_ID_SERVER) {
532 server_entry = (SilcServerEntry)entry;
533 printformat_module("fe-common/silc", server, NULL,
534 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
535 server_entry->server_name, tmp ? tmp : "");
536 } else if (idtype == SILC_ID_CHANNEL) {
537 channel = (SilcChannelEntry)entry;
538 printformat_module("fe-common/silc", server, NULL,
539 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
540 channel->channel_name, tmp ? tmp : "");
543 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
544 for (list_tmp = list1; list_tmp != NULL; list_tmp =
545 list_tmp->next->next) {
546 CHANNEL_REC *channel = list_tmp->data;
547 NICK_REC *nickrec = list_tmp->next->data;
548 nicklist_remove(channel, nickrec);
551 if (idtype == SILC_ID_CLIENT) {
552 client_entry2 = (SilcClientEntry)entry;
553 printformat_module("fe-common/silc", server, NULL,
554 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
555 client_entry->nickname,
556 client_entry2 ? client_entry2->nickname : "",
558 } else if (idtype == SILC_ID_SERVER) {
559 server_entry = (SilcServerEntry)entry;
560 printformat_module("fe-common/silc", server, NULL,
561 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
562 client_entry->nickname,
563 server_entry->server_name, tmp ? tmp : "");
564 } else if (idtype == SILC_ID_CHANNEL) {
565 channel = (SilcChannelEntry)entry;
566 printformat_module("fe-common/silc", server, NULL,
567 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
568 client_entry->nickname,
569 channel->channel_name, tmp ? tmp : "");
574 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
577 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
580 * Server has quit the network.
583 SilcClientEntry *clients;
584 SilcUInt32 clients_count;
586 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
588 (void)va_arg(va, void *);
589 clients = va_arg(va, SilcClientEntry *);
590 clients_count = va_arg(va, SilcUInt32);
592 for (i = 0; i < clients_count; i++) {
593 memset(userhost, 0, sizeof(userhost));
594 if (clients[i]->username)
595 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
596 clients[i]->username, clients[i]->hostname);
597 signal_emit("message quit", 4, server, clients[i]->nickname,
598 clients[i]->username ? userhost : "",
601 silc_server_free_ftp(server, clients[i]);
603 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
604 for (list_tmp = list1; list_tmp != NULL; list_tmp =
605 list_tmp->next->next) {
606 CHANNEL_REC *channel = list_tmp->data;
607 NICK_REC *nickrec = list_tmp->next->data;
608 nicklist_remove(channel, nickrec);
616 printformat_module("fe-common/silc", server, NULL,
617 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
624 /* Called to indicate that connection was either successfully established
625 or connecting failed. This is also the first time application receives
626 the SilcClientConnection object which it should save somewhere. */
628 void silc_connect(SilcClient client, SilcClientConnection conn,
629 SilcClientConnectionStatus status)
631 SILC_SERVER_REC *server = conn->context;
633 if (!server && status == SILC_CLIENT_CONN_ERROR) {
634 silc_client_close_connection(client, conn);
639 case SILC_CLIENT_CONN_SUCCESS:
640 /* We have successfully connected to server */
641 server->connected = TRUE;
642 signal_emit("event connected", 1, server);
645 case SILC_CLIENT_CONN_SUCCESS_RESUME:
646 /* We have successfully resumed old detached session */
647 server->connected = TRUE;
648 signal_emit("event connected", 1, server);
650 /* If we resumed old session check whether we need to update
652 if (strcmp(server->nick, conn->local_entry->nickname)) {
654 old = g_strdup(server->nick);
655 server_change_nick(SERVER(server), conn->local_entry->nickname);
656 nicklist_rename_unique(SERVER(server),
657 conn->local_entry, server->nick,
658 conn->local_entry, conn->local_entry->nickname);
659 signal_emit("message own_nick", 4, server, server->nick, old, "");
665 server->connection_lost = TRUE;
667 server->conn->context = NULL;
668 server_disconnect(SERVER(server));
673 /* Called to indicate that connection was disconnected to the server. */
675 void silc_disconnect(SilcClient client, SilcClientConnection conn)
677 SILC_SERVER_REC *server = conn->context;
679 SILC_LOG_DEBUG(("Start"));
681 if (!server || server->connection_lost)
684 if (server->conn && server->conn->local_entry) {
685 nicklist_rename_unique(SERVER(server),
686 server->conn->local_entry, server->nick,
687 server->conn->local_entry,
688 silc_client->username);
689 silc_change_nick(server, silc_client->username);
692 server->conn->context = NULL;
694 server->connection_lost = TRUE;
695 server_disconnect(SERVER(server));
698 /* Command handler. This function is called always in the command function.
699 If error occurs it will be called as well. `conn' is the associated
700 client connection. `cmd_context' is the command context that was
701 originally sent to the command. `success' is FALSE if error occured
702 during command. `command' is the command being processed. It must be
703 noted that this is not reply from server. This is merely called just
704 after application has called the command. Just to tell application
705 that the command really was processed. */
707 void silc_command(SilcClient client, SilcClientConnection conn,
708 SilcClientCommandContext cmd_context, int success,
711 SILC_SERVER_REC *server = conn->context;
713 SILC_LOG_DEBUG(("Start"));
719 case SILC_COMMAND_INVITE:
720 printformat_module("fe-common/silc", server, NULL,
721 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
722 cmd_context->argv[2],
723 (cmd_context->argv[1][0] == '*' ?
724 (char *)conn->current_channel->channel_name :
725 (char *)cmd_context->argv[1]));
732 /* Client info resolving callback when JOIN command reply is received.
733 This will cache all users on the channel. */
735 static void silc_client_join_get_users(SilcClient client,
736 SilcClientConnection conn,
737 SilcClientEntry *clients,
738 SilcUInt32 clients_count,
741 SilcChannelEntry channel = (SilcChannelEntry)context;
742 SilcHashTableList htl;
744 SILC_SERVER_REC *server = conn->context;
745 SILC_CHANNEL_REC *chanrec;
746 SilcClientEntry founder = NULL;
752 chanrec = silc_channel_find(server, channel->channel_name);
756 silc_hash_table_list(channel->user_list, &htl);
757 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
758 if (!chu->client->nickname)
760 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
761 founder = chu->client;
762 silc_nicklist_insert(chanrec, chu, FALSE);
764 silc_hash_table_list_reset(&htl);
766 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
767 nicklist_set_own(CHANNEL(chanrec), ownnick);
768 signal_emit("channel joined", 1, chanrec);
769 chanrec->entry = channel;
772 printformat_module("fe-common/silc", server, channel->channel_name,
773 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
774 channel->channel_name, chanrec->topic);
776 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
779 if (founder == conn->local_entry)
780 printformat_module("fe-common/silc",
781 server, channel->channel_name, MSGLEVEL_CRAP,
782 SILCTXT_CHANNEL_FOUNDER_YOU,
783 channel->channel_name);
785 printformat_module("fe-common/silc",
786 server, channel->channel_name, MSGLEVEL_CRAP,
787 SILCTXT_CHANNEL_FOUNDER,
788 channel->channel_name, founder->nickname);
794 SilcClientConnection conn;
800 void silc_getkey_cb(bool success, void *context)
802 GetkeyContext getkey = (GetkeyContext)context;
803 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
804 char *name = (getkey->id_type == SILC_ID_CLIENT ?
805 ((SilcClientEntry)getkey->entry)->nickname :
806 ((SilcServerEntry)getkey->entry)->server_name);
809 printformat_module("fe-common/silc", NULL, NULL,
810 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
812 printformat_module("fe-common/silc", NULL, NULL,
813 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
816 silc_free(getkey->fingerprint);
820 /* Command reply handler. This function is called always in the command reply
821 function. If error occurs it will be called as well. Normal scenario
822 is that it will be called after the received command data has been parsed
823 and processed. The function is used to pass the received command data to
826 `conn' is the associated client connection. `cmd_payload' is the command
827 payload data received from server and it can be ignored. It is provided
828 if the application would like to re-parse the received command data,
829 however, it must be noted that the data is parsed already by the library
830 thus the payload can be ignored. `success' is FALSE if error occured.
831 In this case arguments are not sent to the application. `command' is the
832 command reply being processed. The function has variable argument list
833 and each command defines the number and type of arguments it passes to the
834 application (on error they are not sent). */
837 silc_command_reply(SilcClient client, SilcClientConnection conn,
838 SilcCommandPayload cmd_payload, int success,
839 SilcCommand command, SilcCommandStatus status, ...)
842 SILC_SERVER_REC *server = conn->context;
843 SILC_CHANNEL_REC *chanrec;
846 va_start(vp, status);
848 SILC_LOG_DEBUG(("Start"));
851 case SILC_COMMAND_WHOIS:
853 char buf[1024], *nickname, *username, *realname, *nick;
854 unsigned char *fingerprint;
855 SilcUInt32 idle, mode;
857 SilcClientEntry client_entry;
859 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
860 /* Print the unknown nick for user */
862 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
865 silc_say_error("%s: %s", tmp,
866 silc_client_command_status_message(status));
868 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
869 /* Try to find the entry for the unknown client ID, since we
870 might have, and print the nickname of it for user. */
873 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
876 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
879 client_entry = silc_client_get_client_by_id(client, conn,
881 if (client_entry && client_entry->nickname)
882 silc_say_error("%s: %s", client_entry->nickname,
883 silc_client_command_status_message(status));
884 silc_free(client_id);
893 client_entry = va_arg(vp, SilcClientEntry);
894 nickname = va_arg(vp, char *);
895 username = va_arg(vp, char *);
896 realname = va_arg(vp, char *);
897 channels = va_arg(vp, SilcBuffer);
898 mode = va_arg(vp, SilcUInt32);
899 idle = va_arg(vp, SilcUInt32);
900 fingerprint = va_arg(vp, unsigned char *);
902 silc_parse_userfqdn(nickname, &nick, NULL);
903 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
904 SILCTXT_WHOIS_USERINFO, nickname,
905 client_entry->username, client_entry->hostname,
906 nick, client_entry->nickname);
907 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
908 SILCTXT_WHOIS_REALNAME, realname);
912 SilcDList list = silc_channel_payload_parse_list(channels->data,
915 SilcChannelPayload entry;
916 memset(buf, 0, sizeof(buf));
917 silc_dlist_start(list);
918 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
919 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
921 char *name = silc_channel_get_name(entry, &name_len);
924 strncat(buf, m, strlen(m));
925 strncat(buf, name, name_len);
926 strncat(buf, " ", 1);
930 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
931 SILCTXT_WHOIS_CHANNELS, buf);
932 silc_channel_payload_list_free(list);
937 memset(buf, 0, sizeof(buf));
939 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
940 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
941 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
943 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
944 "SILC Operator" : "[Unknown mode]");
946 if (mode & SILC_UMODE_GONE)
947 strcat(buf, " [away]");
948 if (mode & SILC_UMODE_INDISPOSED)
949 strcat(buf, " [indisposed]");
950 if (mode & SILC_UMODE_BUSY)
951 strcat(buf, " [busy]");
952 if (mode & SILC_UMODE_PAGE)
953 strcat(buf, " [page to reach]");
954 if (mode & SILC_UMODE_HYPER)
955 strcat(buf, " [hyper active]");
956 if (mode & SILC_UMODE_ROBOT)
957 strcat(buf, " [robot]");
958 if (mode & SILC_UMODE_ANONYMOUS)
959 strcat(buf, " [anonymous]");
960 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
961 strcat(buf, " [blocks private messages]");
962 if (mode & SILC_UMODE_DETACHED)
963 strcat(buf, " [detached]");
965 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
966 SILCTXT_WHOIS_MODES, buf);
969 if (idle && nickname) {
970 memset(buf, 0, sizeof(buf));
971 snprintf(buf, sizeof(buf) - 1, "%lu %s",
972 idle > 60 ? (idle / 60) : idle,
973 idle > 60 ? "minutes" : "seconds");
975 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
976 SILCTXT_WHOIS_IDLE, buf);
980 fingerprint = silc_fingerprint(fingerprint, 20);
981 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
982 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
983 silc_free(fingerprint);
988 case SILC_COMMAND_IDENTIFY:
990 SilcClientEntry client_entry;
992 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
993 /* Print the unknown nick for user */
995 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
998 silc_say_error("%s: %s", tmp,
999 silc_client_command_status_message(status));
1001 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1002 /* Try to find the entry for the unknown client ID, since we
1003 might have, and print the nickname of it for user. */
1005 unsigned char *tmp =
1006 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1009 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1012 client_entry = silc_client_get_client_by_id(client, conn,
1014 if (client_entry && client_entry->nickname)
1015 silc_say_error("%s: %s", client_entry->nickname,
1016 silc_client_command_status_message(status));
1017 silc_free(client_id);
1026 case SILC_COMMAND_WHOWAS:
1028 char *nickname, *username, *realname;
1030 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1031 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1033 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1036 silc_say_error("%s: %s", tmp,
1037 silc_client_command_status_message(status));
1044 (void)va_arg(vp, SilcClientEntry);
1045 nickname = va_arg(vp, char *);
1046 username = va_arg(vp, char *);
1047 realname = va_arg(vp, char *);
1049 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1050 SILCTXT_WHOWAS_USERINFO, nickname, username,
1051 realname ? realname : "");
1055 case SILC_COMMAND_INVITE:
1057 SilcChannelEntry channel;
1059 SilcArgumentPayload args;
1065 channel = va_arg(vp, SilcChannelEntry);
1066 invite_list = va_arg(vp, char *);
1068 args = silc_command_get_args(cmd_payload);
1070 argc = silc_argument_get_arg_num(args);
1073 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1074 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1077 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1078 SILCTXT_CHANNEL_NO_INVITE_LIST,
1079 channel->channel_name);
1083 case SILC_COMMAND_JOIN:
1085 char *channel, *mode, *topic;
1087 SilcChannelEntry channel_entry;
1088 SilcBuffer client_id_list;
1089 SilcUInt32 list_count;
1094 channel = va_arg(vp, char *);
1095 channel_entry = va_arg(vp, SilcChannelEntry);
1096 modei = va_arg(vp, SilcUInt32);
1097 (void)va_arg(vp, SilcUInt32);
1098 (void)va_arg(vp, unsigned char *);
1099 (void)va_arg(vp, unsigned char *);
1100 (void)va_arg(vp, unsigned char *);
1101 topic = va_arg(vp, char *);
1102 (void)va_arg(vp, unsigned char *);
1103 list_count = va_arg(vp, SilcUInt32);
1104 client_id_list = va_arg(vp, SilcBuffer);
1106 chanrec = silc_channel_find(server, channel);
1108 chanrec = silc_channel_create(server, channel, TRUE);
1111 g_free_not_null(chanrec->topic);
1112 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1113 signal_emit("channel topic changed", 1, chanrec);
1116 mode = silc_client_chmode(modei,
1117 channel_entry->channel_key ?
1118 channel_entry->channel_key->cipher->name : "",
1119 channel_entry->hmac ?
1120 silc_hmac_get_name(channel_entry->hmac) : "");
1121 g_free_not_null(chanrec->mode);
1122 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1123 signal_emit("channel mode changed", 1, chanrec);
1125 /* Resolve the client information */
1126 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1127 silc_client_join_get_users,
1133 case SILC_COMMAND_NICK:
1135 SilcClientEntry client = va_arg(vp, SilcClientEntry);
1141 old = g_strdup(server->nick);
1142 server_change_nick(SERVER(server), client->nickname);
1143 nicklist_rename_unique(SERVER(server),
1144 server->conn->local_entry, server->nick,
1145 client, client->nickname);
1146 signal_emit("message own_nick", 4, server, server->nick, old, "");
1151 case SILC_COMMAND_LIST:
1160 (void)va_arg(vp, SilcChannelEntry);
1161 name = va_arg(vp, char *);
1162 topic = va_arg(vp, char *);
1163 usercount = va_arg(vp, int);
1165 if (status == SILC_STATUS_LIST_START ||
1166 status == SILC_STATUS_OK)
1167 printformat_module("fe-common/silc", server, NULL,
1168 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1171 snprintf(users, sizeof(users) - 1, "N/A");
1173 snprintf(users, sizeof(users) - 1, "%d", usercount);
1174 printformat_module("fe-common/silc", server, NULL,
1175 MSGLEVEL_CRAP, SILCTXT_LIST,
1176 name, users, topic ? topic : "");
1180 case SILC_COMMAND_UMODE:
1187 mode = va_arg(vp, SilcUInt32);
1189 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1190 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1191 printformat_module("fe-common/silc", server, NULL,
1192 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1194 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1195 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1196 printformat_module("fe-common/silc", server, NULL,
1197 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1199 server->umode = mode;
1200 signal_emit("user mode changed", 2, server, NULL);
1204 case SILC_COMMAND_OPER:
1208 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1209 signal_emit("user mode changed", 2, server, NULL);
1211 printformat_module("fe-common/silc", server, NULL,
1212 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1215 case SILC_COMMAND_SILCOPER:
1219 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1220 signal_emit("user mode changed", 2, server, NULL);
1222 printformat_module("fe-common/silc", server, NULL,
1223 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1226 case SILC_COMMAND_USERS:
1228 SilcHashTableList htl;
1229 SilcChannelEntry channel;
1230 SilcChannelUser chu;
1235 channel = va_arg(vp, SilcChannelEntry);
1237 printformat_module("fe-common/silc", server, channel->channel_name,
1238 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1239 channel->channel_name);
1241 silc_hash_table_list(channel->user_list, &htl);
1242 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1243 SilcClientEntry e = chu->client;
1244 char stat[5], *mode;
1249 memset(stat, 0, sizeof(stat));
1250 mode = silc_client_chumode_char(chu->mode);
1251 if (e->mode & SILC_UMODE_GONE)
1253 else if (e->mode & SILC_UMODE_INDISPOSED)
1255 else if (e->mode & SILC_UMODE_BUSY)
1257 else if (e->mode & SILC_UMODE_PAGE)
1259 else if (e->mode & SILC_UMODE_HYPER)
1261 else if (e->mode & SILC_UMODE_ROBOT)
1263 else if (e->mode & SILC_UMODE_ANONYMOUS)
1270 printformat_module("fe-common/silc", server, channel->channel_name,
1271 MSGLEVEL_CRAP, SILCTXT_USERS,
1273 e->username ? e->username : "",
1274 e->hostname ? e->hostname : "",
1275 e->realname ? e->realname : "");
1279 silc_hash_table_list_reset(&htl);
1283 case SILC_COMMAND_BAN:
1285 SilcChannelEntry channel;
1291 channel = va_arg(vp, SilcChannelEntry);
1292 ban_list = va_arg(vp, char *);
1295 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1296 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1299 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1300 SILCTXT_CHANNEL_NO_BAN_LIST,
1301 channel->channel_name);
1305 case SILC_COMMAND_GETKEY:
1309 SilcPublicKey public_key;
1312 GetkeyContext getkey;
1318 id_type = va_arg(vp, SilcUInt32);
1319 entry = va_arg(vp, void *);
1320 public_key = va_arg(vp, SilcPublicKey);
1323 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1325 getkey = silc_calloc(1, sizeof(*getkey));
1326 getkey->entry = entry;
1327 getkey->id_type = id_type;
1328 getkey->client = client;
1329 getkey->conn = conn;
1330 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1332 name = (id_type == SILC_ID_CLIENT ?
1333 ((SilcClientEntry)entry)->nickname :
1334 ((SilcServerEntry)entry)->server_name);
1336 silc_verify_public_key_internal(client, conn, name,
1337 (id_type == SILC_ID_CLIENT ?
1338 SILC_SOCKET_TYPE_CLIENT :
1339 SILC_SOCKET_TYPE_SERVER),
1340 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1341 silc_getkey_cb, getkey);
1344 printformat_module("fe-common/silc", server, NULL,
1345 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1350 case SILC_COMMAND_INFO:
1352 SilcServerEntry server_entry;
1359 server_entry = va_arg(vp, SilcServerEntry);
1360 server_name = va_arg(vp, char *);
1361 server_info = va_arg(vp, char *);
1363 if (server_name && server_info )
1365 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1366 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1371 case SILC_COMMAND_TOPIC:
1373 SilcChannelEntry channel;
1379 channel = va_arg(vp, SilcChannelEntry);
1380 topic = va_arg(vp, char *);
1383 chanrec = silc_channel_find_entry(server, channel);
1385 g_free_not_null(chanrec->topic);
1386 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1387 signal_emit("channel topic changed", 1, chanrec);
1389 printformat_module("fe-common/silc", server, channel->channel_name,
1390 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1391 channel->channel_name, topic);
1393 printformat_module("fe-common/silc", server, channel->channel_name,
1394 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1395 channel->channel_name);
1407 SilcClientConnection conn;
1413 SilcSKEPKType pk_type;
1414 SilcVerifyPublicKey completion;
1418 static void verify_public_key_completion(const char *line, void *context)
1420 PublicKeyVerify verify = (PublicKeyVerify)context;
1422 if (line[0] == 'Y' || line[0] == 'y') {
1423 /* Call the completion */
1424 if (verify->completion)
1425 verify->completion(TRUE, verify->context);
1427 /* Save the key for future checking */
1428 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
1429 verify->pk_len, SILC_PKCS_FILE_PEM);
1431 /* Call the completion */
1432 if (verify->completion)
1433 verify->completion(FALSE, verify->context);
1435 printformat_module("fe-common/silc", NULL, NULL,
1436 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
1437 verify->entity_name ? verify->entity_name :
1441 silc_free(verify->filename);
1442 silc_free(verify->entity);
1443 silc_free(verify->entity_name);
1444 silc_free(verify->pk);
1448 /* Internal routine to verify public key. If the `completion' is provided
1449 it will be called to indicate whether public was verified or not. For
1450 server/router public key this will check for filename that includes the
1451 remote host's IP address and remote host's hostname. */
1454 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1455 const char *name, SilcSocketType conn_type,
1456 unsigned char *pk, SilcUInt32 pk_len,
1457 SilcSKEPKType pk_type,
1458 SilcVerifyPublicKey completion, void *context)
1461 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1462 char *fingerprint, *babbleprint, *format;
1465 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1466 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1467 "server" : "client");
1468 PublicKeyVerify verify;
1470 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1471 printformat_module("fe-common/silc", NULL, NULL,
1472 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
1475 completion(FALSE, context);
1479 pw = getpwuid(getuid());
1482 completion(FALSE, context);
1486 memset(filename, 0, sizeof(filename));
1487 memset(filename2, 0, sizeof(filename2));
1488 memset(file, 0, sizeof(file));
1490 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1491 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1493 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1494 conn->sock->ip, conn->sock->port);
1495 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1496 get_irssi_dir(), entity, file);
1498 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1499 conn->sock->hostname, conn->sock->port);
1500 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
1501 get_irssi_dir(), entity, file);
1506 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1507 name, conn->sock->port);
1508 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1509 get_irssi_dir(), entity, file);
1514 /* Replace all whitespaces with `_'. */
1515 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1516 for (i = 0; i < strlen(fingerprint); i++)
1517 if (fingerprint[i] == ' ')
1518 fingerprint[i] = '_';
1520 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1521 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1522 get_irssi_dir(), entity, file);
1523 silc_free(fingerprint);
1528 /* Take fingerprint of the public key */
1529 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1530 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1532 verify = silc_calloc(1, sizeof(*verify));
1533 verify->client = client;
1534 verify->conn = conn;
1535 verify->filename = strdup(ipf);
1536 verify->entity = strdup(entity);
1537 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1538 (name ? strdup(name) : strdup(conn->sock->hostname))
1540 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1541 memcpy(verify->pk, pk, pk_len);
1542 verify->pk_len = pk_len;
1543 verify->pk_type = pk_type;
1544 verify->completion = completion;
1545 verify->context = context;
1547 /* Check whether this key already exists */
1548 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1549 /* Key does not exist, ask user to verify the key and save it */
1551 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1552 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1553 verify->entity_name : entity);
1554 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1555 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1556 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1557 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1558 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1559 SILCTXT_PUBKEY_ACCEPT);
1560 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1563 silc_free(fingerprint);
1566 /* The key already exists, verify it. */
1567 SilcPublicKey public_key;
1568 unsigned char *encpk;
1569 SilcUInt32 encpk_len;
1571 /* Load the key file, try for both IP filename and hostname filename */
1572 if (!silc_pkcs_load_public_key(ipf, &public_key,
1573 SILC_PKCS_FILE_PEM) &&
1574 !silc_pkcs_load_public_key(ipf, &public_key,
1575 SILC_PKCS_FILE_BIN) &&
1576 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
1577 SILC_PKCS_FILE_PEM) &&
1578 !silc_pkcs_load_public_key(hostf, &public_key,
1579 SILC_PKCS_FILE_BIN)))) {
1580 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1581 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1582 verify->entity_name : entity);
1583 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1584 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1585 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1586 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1587 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1588 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1589 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1590 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1591 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1594 silc_free(fingerprint);
1598 /* Encode the key data */
1599 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1601 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1602 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1603 verify->entity_name : entity);
1604 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1605 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1606 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1607 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1608 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1609 SILCTXT_PUBKEY_MALFORMED, entity);
1610 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1611 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1612 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1615 silc_free(fingerprint);
1619 /* Compare the keys */
1620 if (memcmp(encpk, pk, encpk_len)) {
1621 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1622 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1623 verify->entity_name : entity);
1624 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1625 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1626 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1627 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1628 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1629 SILCTXT_PUBKEY_NO_MATCH, entity);
1630 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1631 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1632 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1633 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1635 /* Ask user to verify the key and save it */
1636 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1637 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1638 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1641 silc_free(fingerprint);
1645 /* Local copy matched */
1647 completion(TRUE, context);
1648 silc_free(fingerprint);
1652 /* Verifies received public key. The `conn_type' indicates which entity
1653 (server, client etc.) has sent the public key. If user decides to trust
1654 the key may be saved as trusted public key for later use. The
1655 `completion' must be called after the public key has been verified. */
1658 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1659 SilcSocketType conn_type, unsigned char *pk,
1660 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1661 SilcVerifyPublicKey completion, void *context)
1663 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1665 completion, context);
1668 /* Asks passphrase from user on the input line. */
1671 SilcAskPassphrase completion;
1675 void ask_passphrase_completion(const char *passphrase, void *context)
1677 AskPassphrase p = (AskPassphrase)context;
1678 p->completion((unsigned char *)passphrase,
1679 passphrase ? strlen(passphrase) : 0, p->context);
1683 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1684 SilcAskPassphrase completion, void *context)
1686 AskPassphrase p = silc_calloc(1, sizeof(*p));
1687 p->completion = completion;
1688 p->context = context;
1690 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1691 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1695 SilcGetAuthMeth completion;
1697 } *InternalGetAuthMethod;
1699 /* Callback called when we've received the authentication method information
1700 from the server after we've requested it. This will get the authentication
1701 data from the user if needed. */
1703 static void silc_get_auth_method_callback(SilcClient client,
1704 SilcClientConnection conn,
1705 SilcAuthMethod auth_meth,
1708 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1710 SILC_LOG_DEBUG(("Start"));
1712 switch (auth_meth) {
1713 case SILC_AUTH_NONE:
1714 /* No authentication required. */
1715 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1717 case SILC_AUTH_PASSWORD:
1718 /* Do not ask the passphrase from user, the library will ask it if
1719 we do not provide it here. */
1720 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1722 case SILC_AUTH_PUBLIC_KEY:
1723 /* Do not get the authentication data now, the library will generate
1724 it using our default key, if we do not provide it here. */
1725 /* XXX In the future when we support multiple local keys and multiple
1726 local certificates we will need to ask from user which one to use. */
1727 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1731 silc_free(internal);
1734 /* Find authentication method and authentication data by hostname and
1735 port. The hostname may be IP address as well. The found authentication
1736 method and authentication data is returned to `auth_meth', `auth_data'
1737 and `auth_data_len'. The function returns TRUE if authentication method
1738 is found and FALSE if not. `conn' may be NULL. */
1740 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1741 char *hostname, SilcUInt16 port,
1742 SilcGetAuthMeth completion, void *context)
1744 InternalGetAuthMethod internal;
1746 SILC_LOG_DEBUG(("Start"));
1748 /* XXX must resolve from configuration whether this connection has
1749 any specific authentication data */
1751 /* If we do not have this connection configured by the user in a
1752 configuration file then resolve the authentication method from the
1753 server for this session. */
1754 internal = silc_calloc(1, sizeof(*internal));
1755 internal->completion = completion;
1756 internal->context = context;
1758 silc_client_request_authentication_method(client, conn,
1759 silc_get_auth_method_callback,
1763 /* Notifies application that failure packet was received. This is called
1764 if there is some protocol active in the client. The `protocol' is the
1765 protocol context. The `failure' is opaque pointer to the failure
1766 indication. Note, that the `failure' is protocol dependant and application
1767 must explicitly cast it to correct type. Usually `failure' is 32 bit
1768 failure type (see protocol specs for all protocol failure types). */
1770 void silc_failure(SilcClient client, SilcClientConnection conn,
1771 SilcProtocol protocol, void *failure)
1773 SILC_LOG_DEBUG(("Start"));
1775 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1776 SilcSKEStatus status = (SilcSKEStatus)failure;
1778 if (status == SILC_SKE_STATUS_BAD_VERSION)
1779 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1780 SILCTXT_KE_BAD_VERSION);
1781 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1782 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1783 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1784 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1785 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1786 SILCTXT_KE_UNKNOWN_GROUP);
1787 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1788 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1789 SILCTXT_KE_UNKNOWN_CIPHER);
1790 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1791 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1792 SILCTXT_KE_UNKNOWN_PKCS);
1793 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1794 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1795 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1796 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1797 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1798 SILCTXT_KE_UNKNOWN_HMAC);
1799 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1800 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1801 SILCTXT_KE_INCORRECT_SIGNATURE);
1802 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1803 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1804 SILCTXT_KE_INVALID_COOKIE);
1807 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1808 SilcUInt32 err = (SilcUInt32)failure;
1810 if (err == SILC_AUTH_FAILED)
1811 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1812 SILCTXT_AUTH_FAILED);
1816 /* Asks whether the user would like to perform the key agreement protocol.
1817 This is called after we have received an key agreement packet or an
1818 reply to our key agreement packet. This returns TRUE if the user wants
1819 the library to perform the key agreement protocol and FALSE if it is not
1820 desired (application may start it later by calling the function
1821 silc_client_perform_key_agreement). */
1823 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1824 SilcClientEntry client_entry, const char *hostname,
1825 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1830 SILC_LOG_DEBUG(("Start"));
1832 /* We will just display the info on the screen and return FALSE and user
1833 will have to start the key agreement with a command. */
1836 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1839 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1840 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1842 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1843 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1844 client_entry->nickname, hostname, portstr);
1852 /* Notifies application that file transfer protocol session is being
1853 requested by the remote client indicated by the `client_entry' from
1854 the `hostname' and `port'. The `session_id' is the file transfer
1855 session and it can be used to either accept or reject the file
1856 transfer request, by calling the silc_client_file_receive or
1857 silc_client_file_close, respectively. */
1859 void silc_ftp(SilcClient client, SilcClientConnection conn,
1860 SilcClientEntry client_entry, SilcUInt32 session_id,
1861 const char *hostname, SilcUInt16 port)
1863 SILC_SERVER_REC *server;
1865 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1867 SILC_LOG_DEBUG(("Start"));
1869 server = conn->context;
1871 ftp->client_entry = client_entry;
1872 ftp->session_id = session_id;
1875 silc_dlist_add(server->ftp_sessions, ftp);
1876 server->current_session = ftp;
1879 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1882 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1883 SILCTXT_FILE_REQUEST, client_entry->nickname);
1885 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1886 SILCTXT_FILE_REQUEST_HOST,
1887 client_entry->nickname, hostname, portstr);
1890 /* Delivers SILC session detachment data indicated by `detach_data' to the
1891 application. If application has issued SILC_COMMAND_DETACH command
1892 the client session in the SILC network is not quit. The client remains
1893 in the network but is detached. The detachment data may be used later
1894 to resume the session in the SILC Network. The appliation is
1895 responsible of saving the `detach_data', to for example in a file.
1897 The detachment data can be given as argument to the functions
1898 silc_client_connect_to_server, or silc_client_add_connection when
1899 creating connection to remote server, inside SilcClientConnectionParams
1900 structure. If it is provided the client library will attempt to resume
1901 the session in the network. After the connection is created
1902 successfully, the application is responsible of setting the user
1903 interface for user into the same state it was before detaching (showing
1904 same channels, channel modes, etc). It can do this by fetching the
1905 information (like joined channels) from the client library. */
1908 silc_detach(SilcClient client, SilcClientConnection conn,
1909 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1913 /* Save the detachment data to file. */
1915 memset(file, 0, sizeof(file));
1916 snprintf(file, sizeof(file) - 1, "%s/session.%s.%d", get_irssi_dir(),
1917 conn->remote_host, conn->remote_port);
1918 silc_file_writefile(file, detach_data, detach_data_len);
1922 /* SILC client operations */
1923 SilcClientOperations ops = {
1925 silc_channel_message,
1926 silc_private_message,
1932 silc_get_auth_method,
1933 silc_verify_public_key,
1934 silc_ask_passphrase,