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;
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 *);
905 silc_parse_userfqdn(nickname, &nick, NULL);
906 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
907 SILCTXT_WHOIS_USERINFO, nickname,
908 client_entry->username, client_entry->hostname,
909 nick, client_entry->nickname);
910 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
911 SILCTXT_WHOIS_REALNAME, realname);
915 SilcDList list = silc_channel_payload_parse_list(channels->data,
918 SilcChannelPayload entry;
919 memset(buf, 0, sizeof(buf));
920 silc_dlist_start(list);
921 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
922 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
924 char *name = silc_channel_get_name(entry, &name_len);
927 strncat(buf, m, strlen(m));
928 strncat(buf, name, name_len);
929 strncat(buf, " ", 1);
933 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
934 SILCTXT_WHOIS_CHANNELS, buf);
935 silc_channel_payload_list_free(list);
940 memset(buf, 0, sizeof(buf));
942 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
943 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
944 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
946 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
947 "SILC Operator" : "[Unknown mode]");
949 if (mode & SILC_UMODE_GONE)
950 strcat(buf, " [away]");
951 if (mode & SILC_UMODE_INDISPOSED)
952 strcat(buf, " [indisposed]");
953 if (mode & SILC_UMODE_BUSY)
954 strcat(buf, " [busy]");
955 if (mode & SILC_UMODE_PAGE)
956 strcat(buf, " [page to reach]");
957 if (mode & SILC_UMODE_HYPER)
958 strcat(buf, " [hyper active]");
959 if (mode & SILC_UMODE_ROBOT)
960 strcat(buf, " [robot]");
961 if (mode & SILC_UMODE_ANONYMOUS)
962 strcat(buf, " [anonymous]");
963 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
964 strcat(buf, " [blocks private messages]");
965 if (mode & SILC_UMODE_DETACHED)
966 strcat(buf, " [detached]");
968 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
969 SILCTXT_WHOIS_MODES, buf);
972 if (idle && nickname) {
973 memset(buf, 0, sizeof(buf));
974 snprintf(buf, sizeof(buf) - 1, "%lu %s",
975 idle > 60 ? (idle / 60) : idle,
976 idle > 60 ? "minutes" : "seconds");
978 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
979 SILCTXT_WHOIS_IDLE, buf);
983 fingerprint = silc_fingerprint(fingerprint, 20);
984 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
985 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
986 silc_free(fingerprint);
991 case SILC_COMMAND_IDENTIFY:
993 SilcClientEntry client_entry;
995 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
996 /* Print the unknown nick for user */
998 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1001 silc_say_error("%s: %s", tmp,
1002 silc_client_command_status_message(status));
1004 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1005 /* Try to find the entry for the unknown client ID, since we
1006 might have, and print the nickname of it for user. */
1008 unsigned char *tmp =
1009 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1012 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1015 client_entry = silc_client_get_client_by_id(client, conn,
1017 if (client_entry && client_entry->nickname)
1018 silc_say_error("%s: %s", client_entry->nickname,
1019 silc_client_command_status_message(status));
1020 silc_free(client_id);
1029 case SILC_COMMAND_WHOWAS:
1031 char *nickname, *username, *realname;
1033 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1034 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1036 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1039 silc_say_error("%s: %s", tmp,
1040 silc_client_command_status_message(status));
1047 (void)va_arg(vp, SilcClientEntry);
1048 nickname = va_arg(vp, char *);
1049 username = va_arg(vp, char *);
1050 realname = va_arg(vp, char *);
1052 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1053 SILCTXT_WHOWAS_USERINFO, nickname, username,
1054 realname ? realname : "");
1058 case SILC_COMMAND_INVITE:
1060 SilcChannelEntry channel;
1062 SilcArgumentPayload args;
1068 channel = va_arg(vp, SilcChannelEntry);
1069 invite_list = va_arg(vp, char *);
1071 args = silc_command_get_args(cmd_payload);
1073 argc = silc_argument_get_arg_num(args);
1076 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1077 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1080 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1081 SILCTXT_CHANNEL_NO_INVITE_LIST,
1082 channel->channel_name);
1086 case SILC_COMMAND_JOIN:
1088 char *channel, *mode, *topic;
1090 SilcChannelEntry channel_entry;
1091 SilcBuffer client_id_list;
1092 SilcUInt32 list_count;
1097 channel = va_arg(vp, char *);
1098 channel_entry = va_arg(vp, SilcChannelEntry);
1099 modei = va_arg(vp, SilcUInt32);
1100 (void)va_arg(vp, SilcUInt32);
1101 (void)va_arg(vp, unsigned char *);
1102 (void)va_arg(vp, unsigned char *);
1103 (void)va_arg(vp, unsigned char *);
1104 topic = va_arg(vp, char *);
1105 (void)va_arg(vp, unsigned char *);
1106 list_count = va_arg(vp, SilcUInt32);
1107 client_id_list = va_arg(vp, SilcBuffer);
1109 chanrec = silc_channel_find(server, channel);
1111 chanrec = silc_channel_create(server, channel, TRUE);
1114 g_free_not_null(chanrec->topic);
1115 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1116 signal_emit("channel topic changed", 1, chanrec);
1119 mode = silc_client_chmode(modei,
1120 channel_entry->channel_key ?
1121 channel_entry->channel_key->cipher->name : "",
1122 channel_entry->hmac ?
1123 silc_hmac_get_name(channel_entry->hmac) : "");
1124 g_free_not_null(chanrec->mode);
1125 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1126 signal_emit("channel mode changed", 1, chanrec);
1128 /* Resolve the client information */
1129 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1130 silc_client_join_get_users,
1136 case SILC_COMMAND_NICK:
1138 SilcClientEntry client = va_arg(vp, SilcClientEntry);
1144 old = g_strdup(server->nick);
1145 server_change_nick(SERVER(server), client->nickname);
1146 nicklist_rename_unique(SERVER(server),
1147 server->conn->local_entry, server->nick,
1148 client, client->nickname);
1149 signal_emit("message own_nick", 4, server, server->nick, old, "");
1154 case SILC_COMMAND_LIST:
1163 (void)va_arg(vp, SilcChannelEntry);
1164 name = va_arg(vp, char *);
1165 topic = va_arg(vp, char *);
1166 usercount = va_arg(vp, int);
1168 if (status == SILC_STATUS_LIST_START ||
1169 status == SILC_STATUS_OK)
1170 printformat_module("fe-common/silc", server, NULL,
1171 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1174 snprintf(users, sizeof(users) - 1, "N/A");
1176 snprintf(users, sizeof(users) - 1, "%d", usercount);
1177 printformat_module("fe-common/silc", server, NULL,
1178 MSGLEVEL_CRAP, SILCTXT_LIST,
1179 name, users, topic ? topic : "");
1183 case SILC_COMMAND_UMODE:
1190 mode = va_arg(vp, SilcUInt32);
1192 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1193 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1194 printformat_module("fe-common/silc", server, NULL,
1195 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1197 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1198 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1199 printformat_module("fe-common/silc", server, NULL,
1200 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1202 server->umode = mode;
1203 signal_emit("user mode changed", 2, server, NULL);
1207 case SILC_COMMAND_OPER:
1211 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1212 signal_emit("user mode changed", 2, server, NULL);
1214 printformat_module("fe-common/silc", server, NULL,
1215 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1218 case SILC_COMMAND_SILCOPER:
1222 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1223 signal_emit("user mode changed", 2, server, NULL);
1225 printformat_module("fe-common/silc", server, NULL,
1226 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1229 case SILC_COMMAND_USERS:
1231 SilcHashTableList htl;
1232 SilcChannelEntry channel;
1233 SilcChannelUser chu;
1238 channel = va_arg(vp, SilcChannelEntry);
1240 printformat_module("fe-common/silc", server, channel->channel_name,
1241 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1242 channel->channel_name);
1244 silc_hash_table_list(channel->user_list, &htl);
1245 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1246 SilcClientEntry e = chu->client;
1247 char stat[5], *mode;
1252 memset(stat, 0, sizeof(stat));
1253 mode = silc_client_chumode_char(chu->mode);
1254 if (e->mode & SILC_UMODE_GONE)
1256 else if (e->mode & SILC_UMODE_INDISPOSED)
1258 else if (e->mode & SILC_UMODE_BUSY)
1260 else if (e->mode & SILC_UMODE_PAGE)
1262 else if (e->mode & SILC_UMODE_HYPER)
1264 else if (e->mode & SILC_UMODE_ROBOT)
1266 else if (e->mode & SILC_UMODE_ANONYMOUS)
1273 printformat_module("fe-common/silc", server, channel->channel_name,
1274 MSGLEVEL_CRAP, SILCTXT_USERS,
1276 e->username ? e->username : "",
1277 e->hostname ? e->hostname : "",
1278 e->realname ? e->realname : "");
1282 silc_hash_table_list_reset(&htl);
1286 case SILC_COMMAND_BAN:
1288 SilcChannelEntry channel;
1294 channel = va_arg(vp, SilcChannelEntry);
1295 ban_list = va_arg(vp, char *);
1298 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1299 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1302 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1303 SILCTXT_CHANNEL_NO_BAN_LIST,
1304 channel->channel_name);
1308 case SILC_COMMAND_GETKEY:
1312 SilcPublicKey public_key;
1315 GetkeyContext getkey;
1321 id_type = va_arg(vp, SilcUInt32);
1322 entry = va_arg(vp, void *);
1323 public_key = va_arg(vp, SilcPublicKey);
1326 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1328 getkey = silc_calloc(1, sizeof(*getkey));
1329 getkey->entry = entry;
1330 getkey->id_type = id_type;
1331 getkey->client = client;
1332 getkey->conn = conn;
1333 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1335 name = (id_type == SILC_ID_CLIENT ?
1336 ((SilcClientEntry)entry)->nickname :
1337 ((SilcServerEntry)entry)->server_name);
1339 silc_verify_public_key_internal(client, conn, name,
1340 (id_type == SILC_ID_CLIENT ?
1341 SILC_SOCKET_TYPE_CLIENT :
1342 SILC_SOCKET_TYPE_SERVER),
1343 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1344 silc_getkey_cb, getkey);
1347 printformat_module("fe-common/silc", server, NULL,
1348 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1353 case SILC_COMMAND_INFO:
1355 SilcServerEntry server_entry;
1362 server_entry = va_arg(vp, SilcServerEntry);
1363 server_name = va_arg(vp, char *);
1364 server_info = va_arg(vp, char *);
1366 if (server_name && server_info )
1368 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1369 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1374 case SILC_COMMAND_TOPIC:
1376 SilcChannelEntry channel;
1382 channel = va_arg(vp, SilcChannelEntry);
1383 topic = va_arg(vp, char *);
1386 chanrec = silc_channel_find_entry(server, channel);
1388 g_free_not_null(chanrec->topic);
1389 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1390 signal_emit("channel topic changed", 1, chanrec);
1392 printformat_module("fe-common/silc", server, channel->channel_name,
1393 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1394 channel->channel_name, topic);
1396 printformat_module("fe-common/silc", server, channel->channel_name,
1397 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1398 channel->channel_name);
1410 SilcClientConnection conn;
1416 SilcSKEPKType pk_type;
1417 SilcVerifyPublicKey completion;
1421 static void verify_public_key_completion(const char *line, void *context)
1423 PublicKeyVerify verify = (PublicKeyVerify)context;
1425 if (line[0] == 'Y' || line[0] == 'y') {
1426 /* Call the completion */
1427 if (verify->completion)
1428 verify->completion(TRUE, verify->context);
1430 /* Save the key for future checking */
1431 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
1432 verify->pk_len, SILC_PKCS_FILE_PEM);
1434 /* Call the completion */
1435 if (verify->completion)
1436 verify->completion(FALSE, verify->context);
1438 printformat_module("fe-common/silc", NULL, NULL,
1439 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
1440 verify->entity_name ? verify->entity_name :
1444 silc_free(verify->filename);
1445 silc_free(verify->entity);
1446 silc_free(verify->entity_name);
1447 silc_free(verify->pk);
1451 /* Internal routine to verify public key. If the `completion' is provided
1452 it will be called to indicate whether public was verified or not. For
1453 server/router public key this will check for filename that includes the
1454 remote host's IP address and remote host's hostname. */
1457 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1458 const char *name, SilcSocketType conn_type,
1459 unsigned char *pk, SilcUInt32 pk_len,
1460 SilcSKEPKType pk_type,
1461 SilcVerifyPublicKey completion, void *context)
1464 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1465 char *fingerprint, *babbleprint, *format;
1468 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1469 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1470 "server" : "client");
1471 PublicKeyVerify verify;
1473 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1474 printformat_module("fe-common/silc", NULL, NULL,
1475 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
1478 completion(FALSE, context);
1482 pw = getpwuid(getuid());
1485 completion(FALSE, context);
1489 memset(filename, 0, sizeof(filename));
1490 memset(filename2, 0, sizeof(filename2));
1491 memset(file, 0, sizeof(file));
1493 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1494 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1496 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1497 conn->sock->ip, conn->sock->port);
1498 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1499 get_irssi_dir(), entity, file);
1501 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1502 conn->sock->hostname, conn->sock->port);
1503 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
1504 get_irssi_dir(), entity, file);
1509 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1510 name, conn->sock->port);
1511 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1512 get_irssi_dir(), entity, file);
1517 /* Replace all whitespaces with `_'. */
1518 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1519 for (i = 0; i < strlen(fingerprint); i++)
1520 if (fingerprint[i] == ' ')
1521 fingerprint[i] = '_';
1523 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1524 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1525 get_irssi_dir(), entity, file);
1526 silc_free(fingerprint);
1531 /* Take fingerprint of the public key */
1532 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1533 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1535 verify = silc_calloc(1, sizeof(*verify));
1536 verify->client = client;
1537 verify->conn = conn;
1538 verify->filename = strdup(ipf);
1539 verify->entity = strdup(entity);
1540 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1541 (name ? strdup(name) : strdup(conn->sock->hostname))
1543 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1544 memcpy(verify->pk, pk, pk_len);
1545 verify->pk_len = pk_len;
1546 verify->pk_type = pk_type;
1547 verify->completion = completion;
1548 verify->context = context;
1550 /* Check whether this key already exists */
1551 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1552 /* Key does not exist, ask user to verify the key and save it */
1554 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1555 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1556 verify->entity_name : entity);
1557 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1558 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1559 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1560 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1561 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1562 SILCTXT_PUBKEY_ACCEPT);
1563 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1566 silc_free(fingerprint);
1569 /* The key already exists, verify it. */
1570 SilcPublicKey public_key;
1571 unsigned char *encpk;
1572 SilcUInt32 encpk_len;
1574 /* Load the key file, try for both IP filename and hostname filename */
1575 if (!silc_pkcs_load_public_key(ipf, &public_key,
1576 SILC_PKCS_FILE_PEM) &&
1577 !silc_pkcs_load_public_key(ipf, &public_key,
1578 SILC_PKCS_FILE_BIN) &&
1579 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
1580 SILC_PKCS_FILE_PEM) &&
1581 !silc_pkcs_load_public_key(hostf, &public_key,
1582 SILC_PKCS_FILE_BIN)))) {
1583 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1584 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1585 verify->entity_name : entity);
1586 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1587 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1588 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1589 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1590 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1591 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1592 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1593 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1594 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1597 silc_free(fingerprint);
1601 /* Encode the key data */
1602 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1604 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1605 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1606 verify->entity_name : entity);
1607 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1608 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1609 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1610 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1611 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1612 SILCTXT_PUBKEY_MALFORMED, entity);
1613 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1614 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1615 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1618 silc_free(fingerprint);
1622 /* Compare the keys */
1623 if (memcmp(encpk, pk, encpk_len)) {
1624 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1625 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1626 verify->entity_name : entity);
1627 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1628 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1629 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1630 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1631 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1632 SILCTXT_PUBKEY_NO_MATCH, entity);
1633 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1634 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1635 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1636 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1638 /* Ask user to verify the key and save it */
1639 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1640 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1641 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1644 silc_free(fingerprint);
1648 /* Local copy matched */
1650 completion(TRUE, context);
1651 silc_free(fingerprint);
1655 /* Verifies received public key. The `conn_type' indicates which entity
1656 (server, client etc.) has sent the public key. If user decides to trust
1657 the key may be saved as trusted public key for later use. The
1658 `completion' must be called after the public key has been verified. */
1661 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1662 SilcSocketType conn_type, unsigned char *pk,
1663 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1664 SilcVerifyPublicKey completion, void *context)
1666 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1668 completion, context);
1671 /* Asks passphrase from user on the input line. */
1674 SilcAskPassphrase completion;
1678 void ask_passphrase_completion(const char *passphrase, void *context)
1680 AskPassphrase p = (AskPassphrase)context;
1681 p->completion((unsigned char *)passphrase,
1682 passphrase ? strlen(passphrase) : 0, p->context);
1686 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1687 SilcAskPassphrase completion, void *context)
1689 AskPassphrase p = silc_calloc(1, sizeof(*p));
1690 p->completion = completion;
1691 p->context = context;
1693 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1694 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1698 SilcGetAuthMeth completion;
1700 } *InternalGetAuthMethod;
1702 /* Callback called when we've received the authentication method information
1703 from the server after we've requested it. This will get the authentication
1704 data from the user if needed. */
1706 static void silc_get_auth_method_callback(SilcClient client,
1707 SilcClientConnection conn,
1708 SilcAuthMethod auth_meth,
1711 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1713 SILC_LOG_DEBUG(("Start"));
1715 switch (auth_meth) {
1716 case SILC_AUTH_NONE:
1717 /* No authentication required. */
1718 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1720 case SILC_AUTH_PASSWORD:
1721 /* Do not ask the passphrase from user, the library will ask it if
1722 we do not provide it here. */
1723 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1725 case SILC_AUTH_PUBLIC_KEY:
1726 /* Do not get the authentication data now, the library will generate
1727 it using our default key, if we do not provide it here. */
1728 /* XXX In the future when we support multiple local keys and multiple
1729 local certificates we will need to ask from user which one to use. */
1730 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1734 silc_free(internal);
1737 /* Find authentication method and authentication data by hostname and
1738 port. The hostname may be IP address as well. The found authentication
1739 method and authentication data is returned to `auth_meth', `auth_data'
1740 and `auth_data_len'. The function returns TRUE if authentication method
1741 is found and FALSE if not. `conn' may be NULL. */
1743 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1744 char *hostname, SilcUInt16 port,
1745 SilcGetAuthMeth completion, void *context)
1747 InternalGetAuthMethod internal;
1749 SILC_LOG_DEBUG(("Start"));
1751 /* XXX must resolve from configuration whether this connection has
1752 any specific authentication data */
1754 /* If we do not have this connection configured by the user in a
1755 configuration file then resolve the authentication method from the
1756 server for this session. */
1757 internal = silc_calloc(1, sizeof(*internal));
1758 internal->completion = completion;
1759 internal->context = context;
1761 silc_client_request_authentication_method(client, conn,
1762 silc_get_auth_method_callback,
1766 /* Notifies application that failure packet was received. This is called
1767 if there is some protocol active in the client. The `protocol' is the
1768 protocol context. The `failure' is opaque pointer to the failure
1769 indication. Note, that the `failure' is protocol dependant and application
1770 must explicitly cast it to correct type. Usually `failure' is 32 bit
1771 failure type (see protocol specs for all protocol failure types). */
1773 void silc_failure(SilcClient client, SilcClientConnection conn,
1774 SilcProtocol protocol, void *failure)
1776 SILC_LOG_DEBUG(("Start"));
1778 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1779 SilcSKEStatus status = (SilcSKEStatus)failure;
1781 if (status == SILC_SKE_STATUS_BAD_VERSION)
1782 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1783 SILCTXT_KE_BAD_VERSION);
1784 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1785 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1786 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1787 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1788 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1789 SILCTXT_KE_UNKNOWN_GROUP);
1790 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1791 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1792 SILCTXT_KE_UNKNOWN_CIPHER);
1793 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1794 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1795 SILCTXT_KE_UNKNOWN_PKCS);
1796 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1797 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1798 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1799 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1800 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1801 SILCTXT_KE_UNKNOWN_HMAC);
1802 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1803 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1804 SILCTXT_KE_INCORRECT_SIGNATURE);
1805 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1806 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1807 SILCTXT_KE_INVALID_COOKIE);
1810 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1811 SilcUInt32 err = (SilcUInt32)failure;
1813 if (err == SILC_AUTH_FAILED)
1814 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1815 SILCTXT_AUTH_FAILED);
1819 /* Asks whether the user would like to perform the key agreement protocol.
1820 This is called after we have received an key agreement packet or an
1821 reply to our key agreement packet. This returns TRUE if the user wants
1822 the library to perform the key agreement protocol and FALSE if it is not
1823 desired (application may start it later by calling the function
1824 silc_client_perform_key_agreement). */
1826 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1827 SilcClientEntry client_entry, const char *hostname,
1828 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1833 SILC_LOG_DEBUG(("Start"));
1835 /* We will just display the info on the screen and return FALSE and user
1836 will have to start the key agreement with a command. */
1839 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1842 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1843 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1845 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1846 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1847 client_entry->nickname, hostname, portstr);
1855 /* Notifies application that file transfer protocol session is being
1856 requested by the remote client indicated by the `client_entry' from
1857 the `hostname' and `port'. The `session_id' is the file transfer
1858 session and it can be used to either accept or reject the file
1859 transfer request, by calling the silc_client_file_receive or
1860 silc_client_file_close, respectively. */
1862 void silc_ftp(SilcClient client, SilcClientConnection conn,
1863 SilcClientEntry client_entry, SilcUInt32 session_id,
1864 const char *hostname, SilcUInt16 port)
1866 SILC_SERVER_REC *server;
1868 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1870 SILC_LOG_DEBUG(("Start"));
1872 server = conn->context;
1874 ftp->client_entry = client_entry;
1875 ftp->session_id = session_id;
1878 silc_dlist_add(server->ftp_sessions, ftp);
1879 server->current_session = ftp;
1882 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1885 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1886 SILCTXT_FILE_REQUEST, client_entry->nickname);
1888 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1889 SILCTXT_FILE_REQUEST_HOST,
1890 client_entry->nickname, hostname, portstr);
1893 /* Delivers SILC session detachment data indicated by `detach_data' to the
1894 application. If application has issued SILC_COMMAND_DETACH command
1895 the client session in the SILC network is not quit. The client remains
1896 in the network but is detached. The detachment data may be used later
1897 to resume the session in the SILC Network. The appliation is
1898 responsible of saving the `detach_data', to for example in a file.
1900 The detachment data can be given as argument to the functions
1901 silc_client_connect_to_server, or silc_client_add_connection when
1902 creating connection to remote server, inside SilcClientConnectionParams
1903 structure. If it is provided the client library will attempt to resume
1904 the session in the network. After the connection is created
1905 successfully, the application is responsible of setting the user
1906 interface for user into the same state it was before detaching (showing
1907 same channels, channel modes, etc). It can do this by fetching the
1908 information (like joined channels) from the client library. */
1911 silc_detach(SilcClient client, SilcClientConnection conn,
1912 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1916 /* Save the detachment data to file. */
1918 memset(file, 0, sizeof(file));
1919 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
1920 silc_file_writefile(file, detach_data, detach_data_len);
1924 /* SILC client operations */
1925 SilcClientOperations ops = {
1927 silc_channel_message,
1928 silc_private_message,
1934 silc_get_auth_method,
1935 silc_verify_public_key,
1936 silc_ask_passphrase,