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 if (!strcmp(client_entry->nickname, client_entry2->nickname))
342 memset(userhost, 0, sizeof(userhost));
343 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
344 client_entry2->username, client_entry2->hostname);
345 nicklist_rename_unique(SERVER(server),
346 client_entry, client_entry->nickname,
347 client_entry2, client_entry2->nickname);
348 signal_emit("message nick", 4, server, client_entry2->nickname,
349 client_entry->nickname, userhost);
352 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
354 * Changed channel mode.
357 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
359 idtype = va_arg(va, int);
360 entry = va_arg(va, void *);
361 mode = va_arg(va, SilcUInt32);
362 (void)va_arg(va, char *);
363 (void)va_arg(va, char *);
364 channel = va_arg(va, SilcChannelEntry);
366 tmp = silc_client_chmode(mode,
367 channel->channel_key ?
368 channel->channel_key->cipher->name : "",
370 silc_hmac_get_name(channel->hmac) : "");
372 chanrec = silc_channel_find_entry(server, channel);
373 if (chanrec != NULL) {
374 g_free_not_null(chanrec->mode);
375 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
376 signal_emit("channel mode changed", 1, chanrec);
379 if (idtype == SILC_ID_CLIENT) {
380 client_entry = (SilcClientEntry)entry;
381 printformat_module("fe-common/silc", server, channel->channel_name,
382 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
383 channel->channel_name, tmp ? tmp : "removed all",
384 client_entry->nickname);
385 } else if (idtype == SILC_ID_SERVER) {
386 server_entry = (SilcServerEntry)entry;
387 printformat_module("fe-common/silc", server, channel->channel_name,
388 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
389 channel->channel_name, tmp ? tmp : "removed all",
390 server_entry->server_name);
391 } else if (idtype == SILC_ID_CHANNEL) {
392 channel2 = (SilcChannelEntry)entry;
393 printformat_module("fe-common/silc", server, channel->channel_name,
394 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
395 channel->channel_name, tmp ? tmp : "removed all",
396 channel2->channel_name);
402 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
404 * Changed user's mode on channel.
407 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
409 idtype = va_arg(va, int);
410 entry = va_arg(va, void *);
411 mode = va_arg(va, SilcUInt32);
412 client_entry2 = va_arg(va, SilcClientEntry);
413 channel = va_arg(va, SilcChannelEntry);
415 tmp = silc_client_chumode(mode);
416 chanrec = silc_channel_find_entry(server, channel);
417 if (chanrec != NULL) {
420 if (client_entry2 == server->conn->local_entry)
421 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
423 nick = silc_nicklist_find(chanrec, client_entry2);
425 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
426 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
427 signal_emit("nick mode changed", 2, chanrec, nick);
431 if (idtype == SILC_ID_CLIENT) {
432 client_entry = (SilcClientEntry)entry;
433 printformat_module("fe-common/silc", server, channel->channel_name,
434 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
435 channel->channel_name, client_entry2->nickname,
436 tmp ? tmp : "removed all",
437 client_entry->nickname);
438 } else if (idtype == SILC_ID_SERVER) {
439 server_entry = (SilcServerEntry)entry;
440 printformat_module("fe-common/silc", server, channel->channel_name,
441 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
442 channel->channel_name, client_entry2->nickname,
443 tmp ? tmp : "removed all",
444 server_entry->server_name);
445 } else if (idtype == SILC_ID_CHANNEL) {
446 channel2 = (SilcChannelEntry)entry;
447 printformat_module("fe-common/silc", server, channel->channel_name,
448 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
449 channel->channel_name, client_entry2->nickname,
450 tmp ? tmp : "removed all",
451 channel2->channel_name);
454 if (mode & SILC_CHANNEL_UMODE_CHANFO)
455 printformat_module("fe-common/silc",
456 server, channel->channel_name, MSGLEVEL_CRAP,
457 SILCTXT_CHANNEL_FOUNDER,
458 channel->channel_name, client_entry2->nickname);
463 case SILC_NOTIFY_TYPE_MOTD:
468 SILC_LOG_DEBUG(("Notify: MOTD"));
470 tmp = va_arg(va, char *);
472 if (!settings_get_bool("skip_motd"))
473 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
476 case SILC_NOTIFY_TYPE_KICKED:
478 * Someone was kicked from channel.
481 SILC_LOG_DEBUG(("Notify: KICKED"));
483 client_entry = va_arg(va, SilcClientEntry);
484 tmp = va_arg(va, char *);
485 client_entry2 = va_arg(va, SilcClientEntry);
486 channel = va_arg(va, SilcChannelEntry);
488 chanrec = silc_channel_find_entry(server, channel);
490 if (client_entry == conn->local_entry) {
491 printformat_module("fe-common/silc", server, channel->channel_name,
492 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
493 channel->channel_name,
494 client_entry ? client_entry2->nickname : "",
497 chanrec->kicked = TRUE;
498 channel_destroy((CHANNEL_REC *)chanrec);
501 printformat_module("fe-common/silc", server, channel->channel_name,
502 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
503 client_entry->nickname, channel->channel_name,
504 client_entry2 ? client_entry2->nickname : "",
508 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
510 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
515 case SILC_NOTIFY_TYPE_KILLED:
517 * Someone was killed from the network.
520 SILC_LOG_DEBUG(("Notify: KILLED"));
522 client_entry = va_arg(va, SilcClientEntry);
523 tmp = va_arg(va, char *);
524 idtype = va_arg(va, int);
525 entry = va_arg(va, SilcClientEntry);
527 if (client_entry == conn->local_entry) {
528 if (idtype == SILC_ID_CLIENT) {
529 client_entry2 = (SilcClientEntry)entry;
530 printformat_module("fe-common/silc", server, NULL,
531 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
532 client_entry2 ? client_entry2->nickname : "",
534 } else if (idtype == SILC_ID_SERVER) {
535 server_entry = (SilcServerEntry)entry;
536 printformat_module("fe-common/silc", server, NULL,
537 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
538 server_entry->server_name, tmp ? tmp : "");
539 } else if (idtype == SILC_ID_CHANNEL) {
540 channel = (SilcChannelEntry)entry;
541 printformat_module("fe-common/silc", server, NULL,
542 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
543 channel->channel_name, tmp ? tmp : "");
546 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
547 for (list_tmp = list1; list_tmp != NULL; list_tmp =
548 list_tmp->next->next) {
549 CHANNEL_REC *channel = list_tmp->data;
550 NICK_REC *nickrec = list_tmp->next->data;
551 nicklist_remove(channel, nickrec);
554 if (idtype == SILC_ID_CLIENT) {
555 client_entry2 = (SilcClientEntry)entry;
556 printformat_module("fe-common/silc", server, NULL,
557 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
558 client_entry->nickname,
559 client_entry2 ? client_entry2->nickname : "",
561 } else if (idtype == SILC_ID_SERVER) {
562 server_entry = (SilcServerEntry)entry;
563 printformat_module("fe-common/silc", server, NULL,
564 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
565 client_entry->nickname,
566 server_entry->server_name, tmp ? tmp : "");
567 } else if (idtype == SILC_ID_CHANNEL) {
568 channel = (SilcChannelEntry)entry;
569 printformat_module("fe-common/silc", server, NULL,
570 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
571 client_entry->nickname,
572 channel->channel_name, tmp ? tmp : "");
577 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
580 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
583 * Server has quit the network.
586 SilcClientEntry *clients;
587 SilcUInt32 clients_count;
589 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
591 (void)va_arg(va, void *);
592 clients = va_arg(va, SilcClientEntry *);
593 clients_count = va_arg(va, SilcUInt32);
595 for (i = 0; i < clients_count; i++) {
596 memset(userhost, 0, sizeof(userhost));
597 if (clients[i]->username)
598 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
599 clients[i]->username, clients[i]->hostname);
600 signal_emit("message quit", 4, server, clients[i]->nickname,
601 clients[i]->username ? userhost : "",
604 silc_server_free_ftp(server, clients[i]);
606 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
607 for (list_tmp = list1; list_tmp != NULL; list_tmp =
608 list_tmp->next->next) {
609 CHANNEL_REC *channel = list_tmp->data;
610 NICK_REC *nickrec = list_tmp->next->data;
611 nicklist_remove(channel, nickrec);
619 printformat_module("fe-common/silc", server, NULL,
620 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
627 /* Called to indicate that connection was either successfully established
628 or connecting failed. This is also the first time application receives
629 the SilcClientConnection object which it should save somewhere. */
631 void silc_connect(SilcClient client, SilcClientConnection conn,
632 SilcClientConnectionStatus status)
634 SILC_SERVER_REC *server = conn->context;
637 silc_client_close_connection(client, conn);
642 case SILC_CLIENT_CONN_SUCCESS:
643 /* We have successfully connected to server */
644 server->connected = TRUE;
645 signal_emit("event connected", 1, server);
648 case SILC_CLIENT_CONN_SUCCESS_RESUME:
649 /* We have successfully resumed old detached session */
650 server->connected = TRUE;
651 signal_emit("event connected", 1, server);
653 /* If we resumed old session check whether we need to update
655 if (strcmp(server->nick, conn->local_entry->nickname)) {
657 old = g_strdup(server->nick);
658 server_change_nick(SERVER(server), conn->local_entry->nickname);
659 nicklist_rename_unique(SERVER(server),
660 conn->local_entry, server->nick,
661 conn->local_entry, conn->local_entry->nickname);
662 signal_emit("message own_nick", 4, server, server->nick, old, "");
668 server->connection_lost = TRUE;
670 server->conn->context = NULL;
671 server_disconnect(SERVER(server));
676 /* Called to indicate that connection was disconnected to the server. */
678 void silc_disconnect(SilcClient client, SilcClientConnection conn)
680 SILC_SERVER_REC *server = conn->context;
682 SILC_LOG_DEBUG(("Start"));
684 if (!server || server->connection_lost)
687 if (server->conn && server->conn->local_entry) {
688 nicklist_rename_unique(SERVER(server),
689 server->conn->local_entry, server->nick,
690 server->conn->local_entry,
691 silc_client->username);
692 silc_change_nick(server, silc_client->username);
695 server->conn->context = NULL;
697 server->connection_lost = TRUE;
698 server_disconnect(SERVER(server));
701 /* Command handler. This function is called always in the command function.
702 If error occurs it will be called as well. `conn' is the associated
703 client connection. `cmd_context' is the command context that was
704 originally sent to the command. `success' is FALSE if error occured
705 during command. `command' is the command being processed. It must be
706 noted that this is not reply from server. This is merely called just
707 after application has called the command. Just to tell application
708 that the command really was processed. */
710 void silc_command(SilcClient client, SilcClientConnection conn,
711 SilcClientCommandContext cmd_context, int success,
714 SILC_SERVER_REC *server = conn->context;
716 SILC_LOG_DEBUG(("Start"));
722 case SILC_COMMAND_INVITE:
723 printformat_module("fe-common/silc", server, NULL,
724 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
725 cmd_context->argv[2],
726 (cmd_context->argv[1][0] == '*' ?
727 (char *)conn->current_channel->channel_name :
728 (char *)cmd_context->argv[1]));
735 /* Client info resolving callback when JOIN command reply is received.
736 This will cache all users on the channel. */
738 static void silc_client_join_get_users(SilcClient client,
739 SilcClientConnection conn,
740 SilcClientEntry *clients,
741 SilcUInt32 clients_count,
744 SilcChannelEntry channel = (SilcChannelEntry)context;
745 SilcHashTableList htl;
747 SILC_SERVER_REC *server = conn->context;
748 SILC_CHANNEL_REC *chanrec;
749 SilcClientEntry founder = NULL;
752 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
753 silc_hash_table_count(channel->user_list)));
758 chanrec = silc_channel_find(server, channel->channel_name);
762 silc_hash_table_list(channel->user_list, &htl);
763 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
764 if (!chu->client->nickname)
766 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
767 founder = chu->client;
768 silc_nicklist_insert(chanrec, chu, FALSE);
770 silc_hash_table_list_reset(&htl);
772 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
773 nicklist_set_own(CHANNEL(chanrec), ownnick);
774 signal_emit("channel joined", 1, chanrec);
775 chanrec->entry = channel;
778 printformat_module("fe-common/silc", server, channel->channel_name,
779 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
780 channel->channel_name, chanrec->topic);
782 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
785 if (founder == conn->local_entry)
786 printformat_module("fe-common/silc",
787 server, channel->channel_name, MSGLEVEL_CRAP,
788 SILCTXT_CHANNEL_FOUNDER_YOU,
789 channel->channel_name);
791 printformat_module("fe-common/silc",
792 server, channel->channel_name, MSGLEVEL_CRAP,
793 SILCTXT_CHANNEL_FOUNDER,
794 channel->channel_name, founder->nickname);
800 SilcClientConnection conn;
806 void silc_getkey_cb(bool success, void *context)
808 GetkeyContext getkey = (GetkeyContext)context;
809 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
810 char *name = (getkey->id_type == SILC_ID_CLIENT ?
811 ((SilcClientEntry)getkey->entry)->nickname :
812 ((SilcServerEntry)getkey->entry)->server_name);
815 printformat_module("fe-common/silc", NULL, NULL,
816 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
818 printformat_module("fe-common/silc", NULL, NULL,
819 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
822 silc_free(getkey->fingerprint);
826 /* Command reply handler. This function is called always in the command reply
827 function. If error occurs it will be called as well. Normal scenario
828 is that it will be called after the received command data has been parsed
829 and processed. The function is used to pass the received command data to
832 `conn' is the associated client connection. `cmd_payload' is the command
833 payload data received from server and it can be ignored. It is provided
834 if the application would like to re-parse the received command data,
835 however, it must be noted that the data is parsed already by the library
836 thus the payload can be ignored. `success' is FALSE if error occured.
837 In this case arguments are not sent to the application. `command' is the
838 command reply being processed. The function has variable argument list
839 and each command defines the number and type of arguments it passes to the
840 application (on error they are not sent). */
843 silc_command_reply(SilcClient client, SilcClientConnection conn,
844 SilcCommandPayload cmd_payload, int success,
845 SilcCommand command, SilcCommandStatus status, ...)
848 SILC_SERVER_REC *server = conn->context;
849 SILC_CHANNEL_REC *chanrec;
852 va_start(vp, status);
854 SILC_LOG_DEBUG(("Start"));
857 case SILC_COMMAND_WHOIS:
859 char buf[1024], *nickname, *username, *realname, *nick;
860 unsigned char *fingerprint;
861 SilcUInt32 idle, mode;
862 SilcBuffer channels, user_modes;
863 SilcClientEntry client_entry;
865 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
866 /* Print the unknown nick for user */
868 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
871 silc_say_error("%s: %s", tmp,
872 silc_client_command_status_message(status));
874 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
875 /* Try to find the entry for the unknown client ID, since we
876 might have, and print the nickname of it for user. */
879 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
882 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
885 client_entry = silc_client_get_client_by_id(client, conn,
887 if (client_entry && client_entry->nickname)
888 silc_say_error("%s: %s", client_entry->nickname,
889 silc_client_command_status_message(status));
890 silc_free(client_id);
899 client_entry = va_arg(vp, SilcClientEntry);
900 nickname = va_arg(vp, char *);
901 username = va_arg(vp, char *);
902 realname = va_arg(vp, char *);
903 channels = va_arg(vp, SilcBuffer);
904 mode = va_arg(vp, SilcUInt32);
905 idle = va_arg(vp, SilcUInt32);
906 fingerprint = va_arg(vp, unsigned char *);
907 user_modes = va_arg(vp, SilcBuffer);
909 silc_parse_userfqdn(nickname, &nick, NULL);
910 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
911 SILCTXT_WHOIS_USERINFO, nickname,
912 client_entry->username, client_entry->hostname,
913 nick, client_entry->nickname);
914 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
915 SILCTXT_WHOIS_REALNAME, realname);
918 if (channels && user_modes) {
920 SilcDList list = silc_channel_payload_parse_list(channels->data,
922 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
924 SilcChannelPayload entry;
927 memset(buf, 0, sizeof(buf));
928 silc_dlist_start(list);
929 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
931 char *m = silc_client_chumode_char(umodes[i++]);
932 char *name = silc_channel_get_name(entry, &name_len);
935 strncat(buf, m, strlen(m));
936 strncat(buf, name, name_len);
937 strncat(buf, " ", 1);
941 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
942 SILCTXT_WHOIS_CHANNELS, buf);
943 silc_channel_payload_list_free(list);
949 memset(buf, 0, sizeof(buf));
951 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
952 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
953 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
955 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
956 "SILC Operator" : "[Unknown mode]");
958 if (mode & SILC_UMODE_GONE)
959 strcat(buf, " [away]");
960 if (mode & SILC_UMODE_INDISPOSED)
961 strcat(buf, " [indisposed]");
962 if (mode & SILC_UMODE_BUSY)
963 strcat(buf, " [busy]");
964 if (mode & SILC_UMODE_PAGE)
965 strcat(buf, " [page to reach]");
966 if (mode & SILC_UMODE_HYPER)
967 strcat(buf, " [hyper active]");
968 if (mode & SILC_UMODE_ROBOT)
969 strcat(buf, " [robot]");
970 if (mode & SILC_UMODE_ANONYMOUS)
971 strcat(buf, " [anonymous]");
972 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
973 strcat(buf, " [blocks private messages]");
974 if (mode & SILC_UMODE_DETACHED)
975 strcat(buf, " [detached]");
977 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
978 SILCTXT_WHOIS_MODES, buf);
981 if (idle && nickname) {
982 memset(buf, 0, sizeof(buf));
983 snprintf(buf, sizeof(buf) - 1, "%lu %s",
984 idle > 60 ? (idle / 60) : idle,
985 idle > 60 ? "minutes" : "seconds");
987 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
988 SILCTXT_WHOIS_IDLE, buf);
992 fingerprint = silc_fingerprint(fingerprint, 20);
993 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
994 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
995 silc_free(fingerprint);
1000 case SILC_COMMAND_IDENTIFY:
1002 SilcClientEntry client_entry;
1004 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1005 /* Print the unknown nick for user */
1006 unsigned char *tmp =
1007 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1010 silc_say_error("%s: %s", tmp,
1011 silc_client_command_status_message(status));
1013 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1014 /* Try to find the entry for the unknown client ID, since we
1015 might have, and print the nickname of it for user. */
1017 unsigned char *tmp =
1018 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1021 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1024 client_entry = silc_client_get_client_by_id(client, conn,
1026 if (client_entry && client_entry->nickname)
1027 silc_say_error("%s: %s", client_entry->nickname,
1028 silc_client_command_status_message(status));
1029 silc_free(client_id);
1038 case SILC_COMMAND_WHOWAS:
1040 char *nickname, *username, *realname;
1042 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1043 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1045 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1048 silc_say_error("%s: %s", tmp,
1049 silc_client_command_status_message(status));
1056 (void)va_arg(vp, SilcClientEntry);
1057 nickname = va_arg(vp, char *);
1058 username = va_arg(vp, char *);
1059 realname = va_arg(vp, char *);
1061 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1062 SILCTXT_WHOWAS_USERINFO, nickname, username,
1063 realname ? realname : "");
1067 case SILC_COMMAND_INVITE:
1069 SilcChannelEntry channel;
1071 SilcArgumentPayload args;
1077 channel = va_arg(vp, SilcChannelEntry);
1078 invite_list = va_arg(vp, char *);
1080 args = silc_command_get_args(cmd_payload);
1082 argc = silc_argument_get_arg_num(args);
1085 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1086 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1089 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1090 SILCTXT_CHANNEL_NO_INVITE_LIST,
1091 channel->channel_name);
1095 case SILC_COMMAND_JOIN:
1097 char *channel, *mode, *topic;
1099 SilcChannelEntry channel_entry;
1100 SilcBuffer client_id_list;
1101 SilcUInt32 list_count;
1106 channel = va_arg(vp, char *);
1107 channel_entry = va_arg(vp, SilcChannelEntry);
1108 modei = va_arg(vp, SilcUInt32);
1109 (void)va_arg(vp, SilcUInt32);
1110 (void)va_arg(vp, unsigned char *);
1111 (void)va_arg(vp, unsigned char *);
1112 (void)va_arg(vp, unsigned char *);
1113 topic = va_arg(vp, char *);
1114 (void)va_arg(vp, unsigned char *);
1115 list_count = va_arg(vp, SilcUInt32);
1116 client_id_list = va_arg(vp, SilcBuffer);
1118 chanrec = silc_channel_find(server, channel);
1120 chanrec = silc_channel_create(server, channel, TRUE);
1123 g_free_not_null(chanrec->topic);
1124 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1125 signal_emit("channel topic changed", 1, chanrec);
1128 mode = silc_client_chmode(modei,
1129 channel_entry->channel_key ?
1130 channel_entry->channel_key->cipher->name : "",
1131 channel_entry->hmac ?
1132 silc_hmac_get_name(channel_entry->hmac) : "");
1133 g_free_not_null(chanrec->mode);
1134 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1135 signal_emit("channel mode changed", 1, chanrec);
1137 /* Resolve the client information */
1138 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1139 silc_client_join_get_users,
1145 case SILC_COMMAND_NICK:
1147 SilcClientEntry client = va_arg(vp, SilcClientEntry);
1153 old = g_strdup(server->nick);
1154 server_change_nick(SERVER(server), client->nickname);
1155 nicklist_rename_unique(SERVER(server),
1156 server->conn->local_entry, server->nick,
1157 client, client->nickname);
1158 signal_emit("message own_nick", 4, server, server->nick, old, "");
1163 case SILC_COMMAND_LIST:
1172 (void)va_arg(vp, SilcChannelEntry);
1173 name = va_arg(vp, char *);
1174 topic = va_arg(vp, char *);
1175 usercount = va_arg(vp, int);
1177 if (status == SILC_STATUS_LIST_START ||
1178 status == SILC_STATUS_OK)
1179 printformat_module("fe-common/silc", server, NULL,
1180 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1183 snprintf(users, sizeof(users) - 1, "N/A");
1185 snprintf(users, sizeof(users) - 1, "%d", usercount);
1186 printformat_module("fe-common/silc", server, NULL,
1187 MSGLEVEL_CRAP, SILCTXT_LIST,
1188 name, users, topic ? topic : "");
1192 case SILC_COMMAND_UMODE:
1199 mode = va_arg(vp, SilcUInt32);
1201 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1202 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1203 printformat_module("fe-common/silc", server, NULL,
1204 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1206 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1207 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1208 printformat_module("fe-common/silc", server, NULL,
1209 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1211 server->umode = mode;
1212 signal_emit("user mode changed", 2, server, NULL);
1216 case SILC_COMMAND_OPER:
1220 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1221 signal_emit("user mode changed", 2, server, NULL);
1223 printformat_module("fe-common/silc", server, NULL,
1224 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1227 case SILC_COMMAND_SILCOPER:
1231 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1232 signal_emit("user mode changed", 2, server, NULL);
1234 printformat_module("fe-common/silc", server, NULL,
1235 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1238 case SILC_COMMAND_USERS:
1240 SilcHashTableList htl;
1241 SilcChannelEntry channel;
1242 SilcChannelUser chu;
1247 channel = va_arg(vp, SilcChannelEntry);
1249 printformat_module("fe-common/silc", server, channel->channel_name,
1250 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1251 channel->channel_name);
1253 silc_hash_table_list(channel->user_list, &htl);
1254 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1255 SilcClientEntry e = chu->client;
1256 char stat[5], *mode;
1261 memset(stat, 0, sizeof(stat));
1262 mode = silc_client_chumode_char(chu->mode);
1263 if (e->mode & SILC_UMODE_GONE)
1265 else if (e->mode & SILC_UMODE_INDISPOSED)
1267 else if (e->mode & SILC_UMODE_BUSY)
1269 else if (e->mode & SILC_UMODE_PAGE)
1271 else if (e->mode & SILC_UMODE_HYPER)
1273 else if (e->mode & SILC_UMODE_ROBOT)
1275 else if (e->mode & SILC_UMODE_ANONYMOUS)
1282 printformat_module("fe-common/silc", server, channel->channel_name,
1283 MSGLEVEL_CRAP, SILCTXT_USERS,
1285 e->username ? e->username : "",
1286 e->hostname ? e->hostname : "",
1287 e->realname ? e->realname : "");
1291 silc_hash_table_list_reset(&htl);
1295 case SILC_COMMAND_BAN:
1297 SilcChannelEntry channel;
1303 channel = va_arg(vp, SilcChannelEntry);
1304 ban_list = va_arg(vp, char *);
1307 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1308 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1311 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1312 SILCTXT_CHANNEL_NO_BAN_LIST,
1313 channel->channel_name);
1317 case SILC_COMMAND_GETKEY:
1321 SilcPublicKey public_key;
1324 GetkeyContext getkey;
1330 id_type = va_arg(vp, SilcUInt32);
1331 entry = va_arg(vp, void *);
1332 public_key = va_arg(vp, SilcPublicKey);
1335 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1337 getkey = silc_calloc(1, sizeof(*getkey));
1338 getkey->entry = entry;
1339 getkey->id_type = id_type;
1340 getkey->client = client;
1341 getkey->conn = conn;
1342 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1344 name = (id_type == SILC_ID_CLIENT ?
1345 ((SilcClientEntry)entry)->nickname :
1346 ((SilcServerEntry)entry)->server_name);
1348 silc_verify_public_key_internal(client, conn, name,
1349 (id_type == SILC_ID_CLIENT ?
1350 SILC_SOCKET_TYPE_CLIENT :
1351 SILC_SOCKET_TYPE_SERVER),
1352 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1353 silc_getkey_cb, getkey);
1356 printformat_module("fe-common/silc", server, NULL,
1357 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1362 case SILC_COMMAND_INFO:
1364 SilcServerEntry server_entry;
1371 server_entry = va_arg(vp, SilcServerEntry);
1372 server_name = va_arg(vp, char *);
1373 server_info = va_arg(vp, char *);
1375 if (server_name && server_info )
1377 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1378 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1383 case SILC_COMMAND_TOPIC:
1385 SilcChannelEntry channel;
1391 channel = va_arg(vp, SilcChannelEntry);
1392 topic = va_arg(vp, char *);
1395 chanrec = silc_channel_find_entry(server, channel);
1397 g_free_not_null(chanrec->topic);
1398 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1399 signal_emit("channel topic changed", 1, chanrec);
1401 printformat_module("fe-common/silc", server, channel->channel_name,
1402 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1403 channel->channel_name, topic);
1405 printformat_module("fe-common/silc", server, channel->channel_name,
1406 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1407 channel->channel_name);
1419 SilcClientConnection conn;
1425 SilcSKEPKType pk_type;
1426 SilcVerifyPublicKey completion;
1430 static void verify_public_key_completion(const char *line, void *context)
1432 PublicKeyVerify verify = (PublicKeyVerify)context;
1434 if (line[0] == 'Y' || line[0] == 'y') {
1435 /* Call the completion */
1436 if (verify->completion)
1437 verify->completion(TRUE, verify->context);
1439 /* Save the key for future checking */
1440 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
1441 verify->pk_len, SILC_PKCS_FILE_PEM);
1443 /* Call the completion */
1444 if (verify->completion)
1445 verify->completion(FALSE, verify->context);
1447 printformat_module("fe-common/silc", NULL, NULL,
1448 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
1449 verify->entity_name ? verify->entity_name :
1453 silc_free(verify->filename);
1454 silc_free(verify->entity);
1455 silc_free(verify->entity_name);
1456 silc_free(verify->pk);
1460 /* Internal routine to verify public key. If the `completion' is provided
1461 it will be called to indicate whether public was verified or not. For
1462 server/router public key this will check for filename that includes the
1463 remote host's IP address and remote host's hostname. */
1466 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1467 const char *name, SilcSocketType conn_type,
1468 unsigned char *pk, SilcUInt32 pk_len,
1469 SilcSKEPKType pk_type,
1470 SilcVerifyPublicKey completion, void *context)
1473 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1474 char *fingerprint, *babbleprint, *format;
1477 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1478 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1479 "server" : "client");
1480 PublicKeyVerify verify;
1482 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1483 printformat_module("fe-common/silc", NULL, NULL,
1484 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
1487 completion(FALSE, context);
1491 pw = getpwuid(getuid());
1494 completion(FALSE, context);
1498 memset(filename, 0, sizeof(filename));
1499 memset(filename2, 0, sizeof(filename2));
1500 memset(file, 0, sizeof(file));
1502 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1503 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1505 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1506 conn->sock->ip, conn->sock->port);
1507 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1508 get_irssi_dir(), entity, file);
1510 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1511 conn->sock->hostname, conn->sock->port);
1512 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
1513 get_irssi_dir(), entity, file);
1518 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1519 name, conn->sock->port);
1520 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1521 get_irssi_dir(), entity, file);
1526 /* Replace all whitespaces with `_'. */
1527 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1528 for (i = 0; i < strlen(fingerprint); i++)
1529 if (fingerprint[i] == ' ')
1530 fingerprint[i] = '_';
1532 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1533 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1534 get_irssi_dir(), entity, file);
1535 silc_free(fingerprint);
1540 /* Take fingerprint of the public key */
1541 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1542 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1544 verify = silc_calloc(1, sizeof(*verify));
1545 verify->client = client;
1546 verify->conn = conn;
1547 verify->filename = strdup(ipf);
1548 verify->entity = strdup(entity);
1549 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1550 (name ? strdup(name) : strdup(conn->sock->hostname))
1552 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1553 memcpy(verify->pk, pk, pk_len);
1554 verify->pk_len = pk_len;
1555 verify->pk_type = pk_type;
1556 verify->completion = completion;
1557 verify->context = context;
1559 /* Check whether this key already exists */
1560 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1561 /* Key does not exist, ask user to verify the key and save it */
1563 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1564 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1565 verify->entity_name : entity);
1566 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1567 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1568 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1569 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1570 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1571 SILCTXT_PUBKEY_ACCEPT);
1572 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1575 silc_free(fingerprint);
1578 /* The key already exists, verify it. */
1579 SilcPublicKey public_key;
1580 unsigned char *encpk;
1581 SilcUInt32 encpk_len;
1583 /* Load the key file, try for both IP filename and hostname filename */
1584 if (!silc_pkcs_load_public_key(ipf, &public_key,
1585 SILC_PKCS_FILE_PEM) &&
1586 !silc_pkcs_load_public_key(ipf, &public_key,
1587 SILC_PKCS_FILE_BIN) &&
1588 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
1589 SILC_PKCS_FILE_PEM) &&
1590 !silc_pkcs_load_public_key(hostf, &public_key,
1591 SILC_PKCS_FILE_BIN)))) {
1592 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1593 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1594 verify->entity_name : entity);
1595 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1596 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1597 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1598 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1599 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1600 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1601 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1602 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1603 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1606 silc_free(fingerprint);
1610 /* Encode the key data */
1611 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1613 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1614 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1615 verify->entity_name : entity);
1616 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1617 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1618 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1619 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1620 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1621 SILCTXT_PUBKEY_MALFORMED, entity);
1622 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1623 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1624 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1627 silc_free(fingerprint);
1631 /* Compare the keys */
1632 if (memcmp(encpk, pk, encpk_len)) {
1633 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1634 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1635 verify->entity_name : entity);
1636 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1637 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1638 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1639 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1640 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1641 SILCTXT_PUBKEY_NO_MATCH, entity);
1642 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1643 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1644 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1645 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1647 /* Ask user to verify the key and save it */
1648 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1649 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1650 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1653 silc_free(fingerprint);
1657 /* Local copy matched */
1659 completion(TRUE, context);
1660 silc_free(fingerprint);
1664 /* Verifies received public key. The `conn_type' indicates which entity
1665 (server, client etc.) has sent the public key. If user decides to trust
1666 the key may be saved as trusted public key for later use. The
1667 `completion' must be called after the public key has been verified. */
1670 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1671 SilcSocketType conn_type, unsigned char *pk,
1672 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1673 SilcVerifyPublicKey completion, void *context)
1675 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1677 completion, context);
1680 /* Asks passphrase from user on the input line. */
1683 SilcAskPassphrase completion;
1687 void ask_passphrase_completion(const char *passphrase, void *context)
1689 AskPassphrase p = (AskPassphrase)context;
1690 p->completion((unsigned char *)passphrase,
1691 passphrase ? strlen(passphrase) : 0, p->context);
1695 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1696 SilcAskPassphrase completion, void *context)
1698 AskPassphrase p = silc_calloc(1, sizeof(*p));
1699 p->completion = completion;
1700 p->context = context;
1702 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1703 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1707 SilcGetAuthMeth completion;
1709 } *InternalGetAuthMethod;
1711 /* Callback called when we've received the authentication method information
1712 from the server after we've requested it. This will get the authentication
1713 data from the user if needed. */
1715 static void silc_get_auth_method_callback(SilcClient client,
1716 SilcClientConnection conn,
1717 SilcAuthMethod auth_meth,
1720 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1722 SILC_LOG_DEBUG(("Start"));
1724 switch (auth_meth) {
1725 case SILC_AUTH_NONE:
1726 /* No authentication required. */
1727 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1729 case SILC_AUTH_PASSWORD:
1730 /* Do not ask the passphrase from user, the library will ask it if
1731 we do not provide it here. */
1732 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1734 case SILC_AUTH_PUBLIC_KEY:
1735 /* Do not get the authentication data now, the library will generate
1736 it using our default key, if we do not provide it here. */
1737 /* XXX In the future when we support multiple local keys and multiple
1738 local certificates we will need to ask from user which one to use. */
1739 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1743 silc_free(internal);
1746 /* Find authentication method and authentication data by hostname and
1747 port. The hostname may be IP address as well. The found authentication
1748 method and authentication data is returned to `auth_meth', `auth_data'
1749 and `auth_data_len'. The function returns TRUE if authentication method
1750 is found and FALSE if not. `conn' may be NULL. */
1752 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1753 char *hostname, SilcUInt16 port,
1754 SilcGetAuthMeth completion, void *context)
1756 InternalGetAuthMethod internal;
1758 SILC_LOG_DEBUG(("Start"));
1760 /* XXX must resolve from configuration whether this connection has
1761 any specific authentication data */
1763 /* If we do not have this connection configured by the user in a
1764 configuration file then resolve the authentication method from the
1765 server for this session. */
1766 internal = silc_calloc(1, sizeof(*internal));
1767 internal->completion = completion;
1768 internal->context = context;
1770 silc_client_request_authentication_method(client, conn,
1771 silc_get_auth_method_callback,
1775 /* Notifies application that failure packet was received. This is called
1776 if there is some protocol active in the client. The `protocol' is the
1777 protocol context. The `failure' is opaque pointer to the failure
1778 indication. Note, that the `failure' is protocol dependant and application
1779 must explicitly cast it to correct type. Usually `failure' is 32 bit
1780 failure type (see protocol specs for all protocol failure types). */
1782 void silc_failure(SilcClient client, SilcClientConnection conn,
1783 SilcProtocol protocol, void *failure)
1785 SILC_LOG_DEBUG(("Start"));
1787 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1788 SilcSKEStatus status = (SilcSKEStatus)failure;
1790 if (status == SILC_SKE_STATUS_BAD_VERSION)
1791 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1792 SILCTXT_KE_BAD_VERSION);
1793 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1794 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1795 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1796 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1797 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1798 SILCTXT_KE_UNKNOWN_GROUP);
1799 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1800 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1801 SILCTXT_KE_UNKNOWN_CIPHER);
1802 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1803 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1804 SILCTXT_KE_UNKNOWN_PKCS);
1805 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1806 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1807 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1808 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1809 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1810 SILCTXT_KE_UNKNOWN_HMAC);
1811 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1812 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1813 SILCTXT_KE_INCORRECT_SIGNATURE);
1814 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1815 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1816 SILCTXT_KE_INVALID_COOKIE);
1819 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1820 SilcUInt32 err = (SilcUInt32)failure;
1822 if (err == SILC_AUTH_FAILED)
1823 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1824 SILCTXT_AUTH_FAILED);
1828 /* Asks whether the user would like to perform the key agreement protocol.
1829 This is called after we have received an key agreement packet or an
1830 reply to our key agreement packet. This returns TRUE if the user wants
1831 the library to perform the key agreement protocol and FALSE if it is not
1832 desired (application may start it later by calling the function
1833 silc_client_perform_key_agreement). */
1835 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1836 SilcClientEntry client_entry, const char *hostname,
1837 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1842 SILC_LOG_DEBUG(("Start"));
1844 /* We will just display the info on the screen and return FALSE and user
1845 will have to start the key agreement with a command. */
1848 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1851 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1852 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1854 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1855 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1856 client_entry->nickname, hostname, portstr);
1864 /* Notifies application that file transfer protocol session is being
1865 requested by the remote client indicated by the `client_entry' from
1866 the `hostname' and `port'. The `session_id' is the file transfer
1867 session and it can be used to either accept or reject the file
1868 transfer request, by calling the silc_client_file_receive or
1869 silc_client_file_close, respectively. */
1871 void silc_ftp(SilcClient client, SilcClientConnection conn,
1872 SilcClientEntry client_entry, SilcUInt32 session_id,
1873 const char *hostname, SilcUInt16 port)
1875 SILC_SERVER_REC *server;
1877 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1879 SILC_LOG_DEBUG(("Start"));
1881 server = conn->context;
1883 ftp->client_entry = client_entry;
1884 ftp->session_id = session_id;
1887 silc_dlist_add(server->ftp_sessions, ftp);
1888 server->current_session = ftp;
1891 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1894 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1895 SILCTXT_FILE_REQUEST, client_entry->nickname);
1897 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1898 SILCTXT_FILE_REQUEST_HOST,
1899 client_entry->nickname, hostname, portstr);
1902 /* Delivers SILC session detachment data indicated by `detach_data' to the
1903 application. If application has issued SILC_COMMAND_DETACH command
1904 the client session in the SILC network is not quit. The client remains
1905 in the network but is detached. The detachment data may be used later
1906 to resume the session in the SILC Network. The appliation is
1907 responsible of saving the `detach_data', to for example in a file.
1909 The detachment data can be given as argument to the functions
1910 silc_client_connect_to_server, or silc_client_add_connection when
1911 creating connection to remote server, inside SilcClientConnectionParams
1912 structure. If it is provided the client library will attempt to resume
1913 the session in the network. After the connection is created
1914 successfully, the application is responsible of setting the user
1915 interface for user into the same state it was before detaching (showing
1916 same channels, channel modes, etc). It can do this by fetching the
1917 information (like joined channels) from the client library. */
1920 silc_detach(SilcClient client, SilcClientConnection conn,
1921 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1925 /* Save the detachment data to file. */
1927 memset(file, 0, sizeof(file));
1928 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
1929 silc_file_writefile(file, detach_data, detach_data_len);
1933 /* SILC client operations */
1934 SilcClientOperations ops = {
1936 silc_channel_message,
1937 silc_private_message,
1943 silc_get_auth_method,
1944 silc_verify_public_key,
1945 silc_ask_passphrase,