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;
636 if (!server || status == SILC_CLIENT_CONN_ERROR) {
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;
755 chanrec = silc_channel_find(server, channel->channel_name);
759 silc_hash_table_list(channel->user_list, &htl);
760 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
761 if (!chu->client->nickname)
763 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
764 founder = chu->client;
765 silc_nicklist_insert(chanrec, chu, FALSE);
767 silc_hash_table_list_reset(&htl);
769 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
770 nicklist_set_own(CHANNEL(chanrec), ownnick);
771 signal_emit("channel joined", 1, chanrec);
772 chanrec->entry = channel;
775 printformat_module("fe-common/silc", server, channel->channel_name,
776 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
777 channel->channel_name, chanrec->topic);
779 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
782 if (founder == conn->local_entry)
783 printformat_module("fe-common/silc",
784 server, channel->channel_name, MSGLEVEL_CRAP,
785 SILCTXT_CHANNEL_FOUNDER_YOU,
786 channel->channel_name);
788 printformat_module("fe-common/silc",
789 server, channel->channel_name, MSGLEVEL_CRAP,
790 SILCTXT_CHANNEL_FOUNDER,
791 channel->channel_name, founder->nickname);
797 SilcClientConnection conn;
803 void silc_getkey_cb(bool success, void *context)
805 GetkeyContext getkey = (GetkeyContext)context;
806 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
807 char *name = (getkey->id_type == SILC_ID_CLIENT ?
808 ((SilcClientEntry)getkey->entry)->nickname :
809 ((SilcServerEntry)getkey->entry)->server_name);
812 printformat_module("fe-common/silc", NULL, NULL,
813 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
815 printformat_module("fe-common/silc", NULL, NULL,
816 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
819 silc_free(getkey->fingerprint);
823 /* Command reply handler. This function is called always in the command reply
824 function. If error occurs it will be called as well. Normal scenario
825 is that it will be called after the received command data has been parsed
826 and processed. The function is used to pass the received command data to
829 `conn' is the associated client connection. `cmd_payload' is the command
830 payload data received from server and it can be ignored. It is provided
831 if the application would like to re-parse the received command data,
832 however, it must be noted that the data is parsed already by the library
833 thus the payload can be ignored. `success' is FALSE if error occured.
834 In this case arguments are not sent to the application. `command' is the
835 command reply being processed. The function has variable argument list
836 and each command defines the number and type of arguments it passes to the
837 application (on error they are not sent). */
840 silc_command_reply(SilcClient client, SilcClientConnection conn,
841 SilcCommandPayload cmd_payload, int success,
842 SilcCommand command, SilcCommandStatus status, ...)
845 SILC_SERVER_REC *server = conn->context;
846 SILC_CHANNEL_REC *chanrec;
849 va_start(vp, status);
851 SILC_LOG_DEBUG(("Start"));
854 case SILC_COMMAND_WHOIS:
856 char buf[1024], *nickname, *username, *realname, *nick;
857 unsigned char *fingerprint;
858 SilcUInt32 idle, mode;
859 SilcBuffer channels, user_modes;
860 SilcClientEntry client_entry;
862 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
863 /* Print the unknown nick for user */
865 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
868 silc_say_error("%s: %s", tmp,
869 silc_client_command_status_message(status));
871 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
872 /* Try to find the entry for the unknown client ID, since we
873 might have, and print the nickname of it for user. */
876 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
879 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
882 client_entry = silc_client_get_client_by_id(client, conn,
884 if (client_entry && client_entry->nickname)
885 silc_say_error("%s: %s", client_entry->nickname,
886 silc_client_command_status_message(status));
887 silc_free(client_id);
896 client_entry = va_arg(vp, SilcClientEntry);
897 nickname = va_arg(vp, char *);
898 username = va_arg(vp, char *);
899 realname = va_arg(vp, char *);
900 channels = va_arg(vp, SilcBuffer);
901 mode = va_arg(vp, SilcUInt32);
902 idle = va_arg(vp, SilcUInt32);
903 fingerprint = va_arg(vp, unsigned char *);
904 user_modes = va_arg(vp, SilcBuffer);
906 silc_parse_userfqdn(nickname, &nick, NULL);
907 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
908 SILCTXT_WHOIS_USERINFO, nickname,
909 client_entry->username, client_entry->hostname,
910 nick, client_entry->nickname);
911 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
912 SILCTXT_WHOIS_REALNAME, realname);
915 if (channels && user_modes) {
917 SilcDList list = silc_channel_payload_parse_list(channels->data,
919 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
921 SilcChannelPayload entry;
924 memset(buf, 0, sizeof(buf));
925 silc_dlist_start(list);
926 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
928 char *m = silc_client_chumode_char(umodes[i++]);
929 char *name = silc_channel_get_name(entry, &name_len);
932 strncat(buf, m, strlen(m));
933 strncat(buf, name, name_len);
934 strncat(buf, " ", 1);
938 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
939 SILCTXT_WHOIS_CHANNELS, buf);
940 silc_channel_payload_list_free(list);
946 memset(buf, 0, sizeof(buf));
948 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
949 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
950 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
952 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
953 "SILC Operator" : "[Unknown mode]");
955 if (mode & SILC_UMODE_GONE)
956 strcat(buf, " [away]");
957 if (mode & SILC_UMODE_INDISPOSED)
958 strcat(buf, " [indisposed]");
959 if (mode & SILC_UMODE_BUSY)
960 strcat(buf, " [busy]");
961 if (mode & SILC_UMODE_PAGE)
962 strcat(buf, " [page to reach]");
963 if (mode & SILC_UMODE_HYPER)
964 strcat(buf, " [hyper active]");
965 if (mode & SILC_UMODE_ROBOT)
966 strcat(buf, " [robot]");
967 if (mode & SILC_UMODE_ANONYMOUS)
968 strcat(buf, " [anonymous]");
969 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
970 strcat(buf, " [blocks private messages]");
971 if (mode & SILC_UMODE_DETACHED)
972 strcat(buf, " [detached]");
974 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
975 SILCTXT_WHOIS_MODES, buf);
978 if (idle && nickname) {
979 memset(buf, 0, sizeof(buf));
980 snprintf(buf, sizeof(buf) - 1, "%lu %s",
981 idle > 60 ? (idle / 60) : idle,
982 idle > 60 ? "minutes" : "seconds");
984 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
985 SILCTXT_WHOIS_IDLE, buf);
989 fingerprint = silc_fingerprint(fingerprint, 20);
990 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
991 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
992 silc_free(fingerprint);
997 case SILC_COMMAND_IDENTIFY:
999 SilcClientEntry client_entry;
1001 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1002 /* Print the unknown nick for user */
1003 unsigned char *tmp =
1004 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1007 silc_say_error("%s: %s", tmp,
1008 silc_client_command_status_message(status));
1010 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1011 /* Try to find the entry for the unknown client ID, since we
1012 might have, and print the nickname of it for user. */
1014 unsigned char *tmp =
1015 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1018 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1021 client_entry = silc_client_get_client_by_id(client, conn,
1023 if (client_entry && client_entry->nickname)
1024 silc_say_error("%s: %s", client_entry->nickname,
1025 silc_client_command_status_message(status));
1026 silc_free(client_id);
1035 case SILC_COMMAND_WHOWAS:
1037 char *nickname, *username, *realname;
1039 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1040 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1042 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1045 silc_say_error("%s: %s", tmp,
1046 silc_client_command_status_message(status));
1053 (void)va_arg(vp, SilcClientEntry);
1054 nickname = va_arg(vp, char *);
1055 username = va_arg(vp, char *);
1056 realname = va_arg(vp, char *);
1058 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1059 SILCTXT_WHOWAS_USERINFO, nickname, username,
1060 realname ? realname : "");
1064 case SILC_COMMAND_INVITE:
1066 SilcChannelEntry channel;
1068 SilcArgumentPayload args;
1074 channel = va_arg(vp, SilcChannelEntry);
1075 invite_list = va_arg(vp, char *);
1077 args = silc_command_get_args(cmd_payload);
1079 argc = silc_argument_get_arg_num(args);
1082 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1083 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1086 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1087 SILCTXT_CHANNEL_NO_INVITE_LIST,
1088 channel->channel_name);
1092 case SILC_COMMAND_JOIN:
1094 char *channel, *mode, *topic;
1096 SilcChannelEntry channel_entry;
1097 SilcBuffer client_id_list;
1098 SilcUInt32 list_count;
1103 channel = va_arg(vp, char *);
1104 channel_entry = va_arg(vp, SilcChannelEntry);
1105 modei = va_arg(vp, SilcUInt32);
1106 (void)va_arg(vp, SilcUInt32);
1107 (void)va_arg(vp, unsigned char *);
1108 (void)va_arg(vp, unsigned char *);
1109 (void)va_arg(vp, unsigned char *);
1110 topic = va_arg(vp, char *);
1111 (void)va_arg(vp, unsigned char *);
1112 list_count = va_arg(vp, SilcUInt32);
1113 client_id_list = va_arg(vp, SilcBuffer);
1115 chanrec = silc_channel_find(server, channel);
1117 chanrec = silc_channel_create(server, channel, TRUE);
1120 g_free_not_null(chanrec->topic);
1121 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1122 signal_emit("channel topic changed", 1, chanrec);
1125 mode = silc_client_chmode(modei,
1126 channel_entry->channel_key ?
1127 channel_entry->channel_key->cipher->name : "",
1128 channel_entry->hmac ?
1129 silc_hmac_get_name(channel_entry->hmac) : "");
1130 g_free_not_null(chanrec->mode);
1131 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1132 signal_emit("channel mode changed", 1, chanrec);
1134 /* Resolve the client information */
1135 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1136 silc_client_join_get_users,
1142 case SILC_COMMAND_NICK:
1144 SilcClientEntry client = va_arg(vp, SilcClientEntry);
1150 old = g_strdup(server->nick);
1151 server_change_nick(SERVER(server), client->nickname);
1152 nicklist_rename_unique(SERVER(server),
1153 server->conn->local_entry, server->nick,
1154 client, client->nickname);
1155 signal_emit("message own_nick", 4, server, server->nick, old, "");
1160 case SILC_COMMAND_LIST:
1169 (void)va_arg(vp, SilcChannelEntry);
1170 name = va_arg(vp, char *);
1171 topic = va_arg(vp, char *);
1172 usercount = va_arg(vp, int);
1174 if (status == SILC_STATUS_LIST_START ||
1175 status == SILC_STATUS_OK)
1176 printformat_module("fe-common/silc", server, NULL,
1177 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1180 snprintf(users, sizeof(users) - 1, "N/A");
1182 snprintf(users, sizeof(users) - 1, "%d", usercount);
1183 printformat_module("fe-common/silc", server, NULL,
1184 MSGLEVEL_CRAP, SILCTXT_LIST,
1185 name, users, topic ? topic : "");
1189 case SILC_COMMAND_UMODE:
1196 mode = va_arg(vp, SilcUInt32);
1198 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1199 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1200 printformat_module("fe-common/silc", server, NULL,
1201 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1203 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1204 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1205 printformat_module("fe-common/silc", server, NULL,
1206 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1208 server->umode = mode;
1209 signal_emit("user mode changed", 2, server, NULL);
1213 case SILC_COMMAND_OPER:
1217 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1218 signal_emit("user mode changed", 2, server, NULL);
1220 printformat_module("fe-common/silc", server, NULL,
1221 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1224 case SILC_COMMAND_SILCOPER:
1228 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1229 signal_emit("user mode changed", 2, server, NULL);
1231 printformat_module("fe-common/silc", server, NULL,
1232 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1235 case SILC_COMMAND_USERS:
1237 SilcHashTableList htl;
1238 SilcChannelEntry channel;
1239 SilcChannelUser chu;
1244 channel = va_arg(vp, SilcChannelEntry);
1246 printformat_module("fe-common/silc", server, channel->channel_name,
1247 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1248 channel->channel_name);
1250 silc_hash_table_list(channel->user_list, &htl);
1251 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1252 SilcClientEntry e = chu->client;
1253 char stat[5], *mode;
1258 memset(stat, 0, sizeof(stat));
1259 mode = silc_client_chumode_char(chu->mode);
1260 if (e->mode & SILC_UMODE_GONE)
1262 else if (e->mode & SILC_UMODE_INDISPOSED)
1264 else if (e->mode & SILC_UMODE_BUSY)
1266 else if (e->mode & SILC_UMODE_PAGE)
1268 else if (e->mode & SILC_UMODE_HYPER)
1270 else if (e->mode & SILC_UMODE_ROBOT)
1272 else if (e->mode & SILC_UMODE_ANONYMOUS)
1279 printformat_module("fe-common/silc", server, channel->channel_name,
1280 MSGLEVEL_CRAP, SILCTXT_USERS,
1282 e->username ? e->username : "",
1283 e->hostname ? e->hostname : "",
1284 e->realname ? e->realname : "");
1288 silc_hash_table_list_reset(&htl);
1292 case SILC_COMMAND_BAN:
1294 SilcChannelEntry channel;
1300 channel = va_arg(vp, SilcChannelEntry);
1301 ban_list = va_arg(vp, char *);
1304 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1305 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1308 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1309 SILCTXT_CHANNEL_NO_BAN_LIST,
1310 channel->channel_name);
1314 case SILC_COMMAND_GETKEY:
1318 SilcPublicKey public_key;
1321 GetkeyContext getkey;
1327 id_type = va_arg(vp, SilcUInt32);
1328 entry = va_arg(vp, void *);
1329 public_key = va_arg(vp, SilcPublicKey);
1332 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1334 getkey = silc_calloc(1, sizeof(*getkey));
1335 getkey->entry = entry;
1336 getkey->id_type = id_type;
1337 getkey->client = client;
1338 getkey->conn = conn;
1339 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1341 name = (id_type == SILC_ID_CLIENT ?
1342 ((SilcClientEntry)entry)->nickname :
1343 ((SilcServerEntry)entry)->server_name);
1345 silc_verify_public_key_internal(client, conn, name,
1346 (id_type == SILC_ID_CLIENT ?
1347 SILC_SOCKET_TYPE_CLIENT :
1348 SILC_SOCKET_TYPE_SERVER),
1349 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1350 silc_getkey_cb, getkey);
1353 printformat_module("fe-common/silc", server, NULL,
1354 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1359 case SILC_COMMAND_INFO:
1361 SilcServerEntry server_entry;
1368 server_entry = va_arg(vp, SilcServerEntry);
1369 server_name = va_arg(vp, char *);
1370 server_info = va_arg(vp, char *);
1372 if (server_name && server_info )
1374 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1375 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1380 case SILC_COMMAND_TOPIC:
1382 SilcChannelEntry channel;
1388 channel = va_arg(vp, SilcChannelEntry);
1389 topic = va_arg(vp, char *);
1392 chanrec = silc_channel_find_entry(server, channel);
1394 g_free_not_null(chanrec->topic);
1395 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1396 signal_emit("channel topic changed", 1, chanrec);
1398 printformat_module("fe-common/silc", server, channel->channel_name,
1399 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1400 channel->channel_name, topic);
1402 printformat_module("fe-common/silc", server, channel->channel_name,
1403 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1404 channel->channel_name);
1416 SilcClientConnection conn;
1422 SilcSKEPKType pk_type;
1423 SilcVerifyPublicKey completion;
1427 static void verify_public_key_completion(const char *line, void *context)
1429 PublicKeyVerify verify = (PublicKeyVerify)context;
1431 if (line[0] == 'Y' || line[0] == 'y') {
1432 /* Call the completion */
1433 if (verify->completion)
1434 verify->completion(TRUE, verify->context);
1436 /* Save the key for future checking */
1437 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
1438 verify->pk_len, SILC_PKCS_FILE_PEM);
1440 /* Call the completion */
1441 if (verify->completion)
1442 verify->completion(FALSE, verify->context);
1444 printformat_module("fe-common/silc", NULL, NULL,
1445 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
1446 verify->entity_name ? verify->entity_name :
1450 silc_free(verify->filename);
1451 silc_free(verify->entity);
1452 silc_free(verify->entity_name);
1453 silc_free(verify->pk);
1457 /* Internal routine to verify public key. If the `completion' is provided
1458 it will be called to indicate whether public was verified or not. For
1459 server/router public key this will check for filename that includes the
1460 remote host's IP address and remote host's hostname. */
1463 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1464 const char *name, SilcSocketType conn_type,
1465 unsigned char *pk, SilcUInt32 pk_len,
1466 SilcSKEPKType pk_type,
1467 SilcVerifyPublicKey completion, void *context)
1470 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1471 char *fingerprint, *babbleprint, *format;
1474 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1475 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1476 "server" : "client");
1477 PublicKeyVerify verify;
1479 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1480 printformat_module("fe-common/silc", NULL, NULL,
1481 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
1484 completion(FALSE, context);
1488 pw = getpwuid(getuid());
1491 completion(FALSE, context);
1495 memset(filename, 0, sizeof(filename));
1496 memset(filename2, 0, sizeof(filename2));
1497 memset(file, 0, sizeof(file));
1499 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1500 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1502 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1503 conn->sock->ip, conn->sock->port);
1504 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1505 get_irssi_dir(), entity, file);
1507 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1508 conn->sock->hostname, conn->sock->port);
1509 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
1510 get_irssi_dir(), entity, file);
1515 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1516 name, conn->sock->port);
1517 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1518 get_irssi_dir(), entity, file);
1523 /* Replace all whitespaces with `_'. */
1524 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1525 for (i = 0; i < strlen(fingerprint); i++)
1526 if (fingerprint[i] == ' ')
1527 fingerprint[i] = '_';
1529 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1530 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1531 get_irssi_dir(), entity, file);
1532 silc_free(fingerprint);
1537 /* Take fingerprint of the public key */
1538 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1539 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1541 verify = silc_calloc(1, sizeof(*verify));
1542 verify->client = client;
1543 verify->conn = conn;
1544 verify->filename = strdup(ipf);
1545 verify->entity = strdup(entity);
1546 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1547 (name ? strdup(name) : strdup(conn->sock->hostname))
1549 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1550 memcpy(verify->pk, pk, pk_len);
1551 verify->pk_len = pk_len;
1552 verify->pk_type = pk_type;
1553 verify->completion = completion;
1554 verify->context = context;
1556 /* Check whether this key already exists */
1557 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1558 /* Key does not exist, ask user to verify the key and save it */
1560 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1561 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1562 verify->entity_name : entity);
1563 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1564 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1565 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1566 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1567 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1568 SILCTXT_PUBKEY_ACCEPT);
1569 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1572 silc_free(fingerprint);
1575 /* The key already exists, verify it. */
1576 SilcPublicKey public_key;
1577 unsigned char *encpk;
1578 SilcUInt32 encpk_len;
1580 /* Load the key file, try for both IP filename and hostname filename */
1581 if (!silc_pkcs_load_public_key(ipf, &public_key,
1582 SILC_PKCS_FILE_PEM) &&
1583 !silc_pkcs_load_public_key(ipf, &public_key,
1584 SILC_PKCS_FILE_BIN) &&
1585 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
1586 SILC_PKCS_FILE_PEM) &&
1587 !silc_pkcs_load_public_key(hostf, &public_key,
1588 SILC_PKCS_FILE_BIN)))) {
1589 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1590 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1591 verify->entity_name : entity);
1592 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1593 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1594 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1595 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1596 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1597 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1598 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1599 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1600 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1603 silc_free(fingerprint);
1607 /* Encode the key data */
1608 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1610 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1611 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1612 verify->entity_name : entity);
1613 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1614 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1615 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1616 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1617 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1618 SILCTXT_PUBKEY_MALFORMED, entity);
1619 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1620 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1621 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1624 silc_free(fingerprint);
1628 /* Compare the keys */
1629 if (memcmp(encpk, pk, encpk_len)) {
1630 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1631 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1632 verify->entity_name : entity);
1633 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1634 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1635 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1636 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1637 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1638 SILCTXT_PUBKEY_NO_MATCH, entity);
1639 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1640 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1641 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1642 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1644 /* Ask user to verify the key and save it */
1645 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1646 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1647 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1650 silc_free(fingerprint);
1654 /* Local copy matched */
1656 completion(TRUE, context);
1657 silc_free(fingerprint);
1661 /* Verifies received public key. The `conn_type' indicates which entity
1662 (server, client etc.) has sent the public key. If user decides to trust
1663 the key may be saved as trusted public key for later use. The
1664 `completion' must be called after the public key has been verified. */
1667 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1668 SilcSocketType conn_type, unsigned char *pk,
1669 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1670 SilcVerifyPublicKey completion, void *context)
1672 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1674 completion, context);
1677 /* Asks passphrase from user on the input line. */
1680 SilcAskPassphrase completion;
1684 void ask_passphrase_completion(const char *passphrase, void *context)
1686 AskPassphrase p = (AskPassphrase)context;
1687 p->completion((unsigned char *)passphrase,
1688 passphrase ? strlen(passphrase) : 0, p->context);
1692 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1693 SilcAskPassphrase completion, void *context)
1695 AskPassphrase p = silc_calloc(1, sizeof(*p));
1696 p->completion = completion;
1697 p->context = context;
1699 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1700 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1704 SilcGetAuthMeth completion;
1706 } *InternalGetAuthMethod;
1708 /* Callback called when we've received the authentication method information
1709 from the server after we've requested it. This will get the authentication
1710 data from the user if needed. */
1712 static void silc_get_auth_method_callback(SilcClient client,
1713 SilcClientConnection conn,
1714 SilcAuthMethod auth_meth,
1717 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1719 SILC_LOG_DEBUG(("Start"));
1721 switch (auth_meth) {
1722 case SILC_AUTH_NONE:
1723 /* No authentication required. */
1724 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1726 case SILC_AUTH_PASSWORD:
1727 /* Do not ask the passphrase from user, the library will ask it if
1728 we do not provide it here. */
1729 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1731 case SILC_AUTH_PUBLIC_KEY:
1732 /* Do not get the authentication data now, the library will generate
1733 it using our default key, if we do not provide it here. */
1734 /* XXX In the future when we support multiple local keys and multiple
1735 local certificates we will need to ask from user which one to use. */
1736 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1740 silc_free(internal);
1743 /* Find authentication method and authentication data by hostname and
1744 port. The hostname may be IP address as well. The found authentication
1745 method and authentication data is returned to `auth_meth', `auth_data'
1746 and `auth_data_len'. The function returns TRUE if authentication method
1747 is found and FALSE if not. `conn' may be NULL. */
1749 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1750 char *hostname, SilcUInt16 port,
1751 SilcGetAuthMeth completion, void *context)
1753 InternalGetAuthMethod internal;
1755 SILC_LOG_DEBUG(("Start"));
1757 /* XXX must resolve from configuration whether this connection has
1758 any specific authentication data */
1760 /* If we do not have this connection configured by the user in a
1761 configuration file then resolve the authentication method from the
1762 server for this session. */
1763 internal = silc_calloc(1, sizeof(*internal));
1764 internal->completion = completion;
1765 internal->context = context;
1767 silc_client_request_authentication_method(client, conn,
1768 silc_get_auth_method_callback,
1772 /* Notifies application that failure packet was received. This is called
1773 if there is some protocol active in the client. The `protocol' is the
1774 protocol context. The `failure' is opaque pointer to the failure
1775 indication. Note, that the `failure' is protocol dependant and application
1776 must explicitly cast it to correct type. Usually `failure' is 32 bit
1777 failure type (see protocol specs for all protocol failure types). */
1779 void silc_failure(SilcClient client, SilcClientConnection conn,
1780 SilcProtocol protocol, void *failure)
1782 SILC_LOG_DEBUG(("Start"));
1784 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1785 SilcSKEStatus status = (SilcSKEStatus)failure;
1787 if (status == SILC_SKE_STATUS_BAD_VERSION)
1788 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1789 SILCTXT_KE_BAD_VERSION);
1790 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1791 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1792 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1793 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1794 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1795 SILCTXT_KE_UNKNOWN_GROUP);
1796 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1797 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1798 SILCTXT_KE_UNKNOWN_CIPHER);
1799 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1800 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1801 SILCTXT_KE_UNKNOWN_PKCS);
1802 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1803 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1804 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1805 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1806 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1807 SILCTXT_KE_UNKNOWN_HMAC);
1808 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1809 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1810 SILCTXT_KE_INCORRECT_SIGNATURE);
1811 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1812 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1813 SILCTXT_KE_INVALID_COOKIE);
1816 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1817 SilcUInt32 err = (SilcUInt32)failure;
1819 if (err == SILC_AUTH_FAILED)
1820 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1821 SILCTXT_AUTH_FAILED);
1825 /* Asks whether the user would like to perform the key agreement protocol.
1826 This is called after we have received an key agreement packet or an
1827 reply to our key agreement packet. This returns TRUE if the user wants
1828 the library to perform the key agreement protocol and FALSE if it is not
1829 desired (application may start it later by calling the function
1830 silc_client_perform_key_agreement). */
1832 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1833 SilcClientEntry client_entry, const char *hostname,
1834 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1839 SILC_LOG_DEBUG(("Start"));
1841 /* We will just display the info on the screen and return FALSE and user
1842 will have to start the key agreement with a command. */
1845 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1848 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1849 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1851 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1852 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1853 client_entry->nickname, hostname, portstr);
1861 /* Notifies application that file transfer protocol session is being
1862 requested by the remote client indicated by the `client_entry' from
1863 the `hostname' and `port'. The `session_id' is the file transfer
1864 session and it can be used to either accept or reject the file
1865 transfer request, by calling the silc_client_file_receive or
1866 silc_client_file_close, respectively. */
1868 void silc_ftp(SilcClient client, SilcClientConnection conn,
1869 SilcClientEntry client_entry, SilcUInt32 session_id,
1870 const char *hostname, SilcUInt16 port)
1872 SILC_SERVER_REC *server;
1874 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1876 SILC_LOG_DEBUG(("Start"));
1878 server = conn->context;
1880 ftp->client_entry = client_entry;
1881 ftp->session_id = session_id;
1884 silc_dlist_add(server->ftp_sessions, ftp);
1885 server->current_session = ftp;
1888 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1891 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1892 SILCTXT_FILE_REQUEST, client_entry->nickname);
1894 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1895 SILCTXT_FILE_REQUEST_HOST,
1896 client_entry->nickname, hostname, portstr);
1899 /* Delivers SILC session detachment data indicated by `detach_data' to the
1900 application. If application has issued SILC_COMMAND_DETACH command
1901 the client session in the SILC network is not quit. The client remains
1902 in the network but is detached. The detachment data may be used later
1903 to resume the session in the SILC Network. The appliation is
1904 responsible of saving the `detach_data', to for example in a file.
1906 The detachment data can be given as argument to the functions
1907 silc_client_connect_to_server, or silc_client_add_connection when
1908 creating connection to remote server, inside SilcClientConnectionParams
1909 structure. If it is provided the client library will attempt to resume
1910 the session in the network. After the connection is created
1911 successfully, the application is responsible of setting the user
1912 interface for user into the same state it was before detaching (showing
1913 same channels, channel modes, etc). It can do this by fetching the
1914 information (like joined channels) from the client library. */
1917 silc_detach(SilcClient client, SilcClientConnection conn,
1918 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1922 /* Save the detachment data to file. */
1924 memset(file, 0, sizeof(file));
1925 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
1926 silc_file_writefile(file, detach_data, detach_data_len);
1930 /* SILC client operations */
1931 SilcClientOperations ops = {
1933 silc_channel_message,
1934 silc_private_message,
1940 silc_get_auth_method,
1941 silc_verify_public_key,
1942 silc_ask_passphrase,