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"
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43 SilcSocketType conn_type, unsigned char *pk,
44 uint32 pk_len, SilcSKEPKType pk_type,
45 SilcVerifyPublicKey completion, void *context);
47 void silc_say(SilcClient client, SilcClientConnection conn,
48 SilcClientMessageType type, char *msg, ...)
50 SILC_SERVER_REC *server;
54 server = conn == NULL ? NULL : conn->context;
57 str = g_strdup_vprintf(msg, va);
58 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
63 void silc_say_error(char *msg, ...)
69 str = g_strdup_vprintf(msg, va);
70 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
76 /* Message for a channel. The `sender' is the nickname of the sender
77 received in the packet. The `channel_name' is the name of the channel. */
79 void silc_channel_message(SilcClient client, SilcClientConnection conn,
80 SilcClientEntry sender, SilcChannelEntry channel,
81 SilcMessageFlags flags, char *msg)
83 SILC_SERVER_REC *server;
85 SILC_CHANNEL_REC *chanrec;
87 server = conn == NULL ? NULL : conn->context;
88 chanrec = silc_channel_find_entry(server, channel);
92 nick = silc_nicklist_find(chanrec, sender);
94 if (flags & SILC_MESSAGE_FLAG_ACTION)
95 printformat_module("fe-common/silc", server, channel->channel_name,
96 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
97 nick == NULL ? "[<unknown>]" : nick->nick, msg);
98 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
99 printformat_module("fe-common/silc", server, channel->channel_name,
100 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
101 nick == NULL ? "[<unknown>]" : nick->nick, msg);
103 signal_emit("message public", 6, server, msg,
104 nick == NULL ? "[<unknown>]" : nick->nick,
105 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
106 chanrec->name, nick);
109 /* Private message to the client. The `sender' is the nickname of the
110 sender received in the packet. */
112 void silc_private_message(SilcClient client, SilcClientConnection conn,
113 SilcClientEntry sender, SilcMessageFlags flags,
116 SILC_SERVER_REC *server;
119 server = conn == NULL ? NULL : conn->context;
120 memset(userhost, 0, sizeof(userhost));
121 if (sender->username)
122 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
123 sender->username, sender->hostname);
124 signal_emit("message private", 4, server, msg,
125 sender->nickname ? sender->nickname : "[<unknown>]",
126 sender->username ? userhost : NULL);
129 /* Notify message to the client. The notify arguments are sent in the
130 same order as servers sends them. The arguments are same as received
131 from the server except for ID's. If ID is received application receives
132 the corresponding entry to the ID. For example, if Client ID is received
133 application receives SilcClientEntry. Also, if the notify type is
134 for channel the channel entry is sent to application (even if server
135 does not send it). */
142 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
143 static NOTIFY_REC notifies[] = {
144 { SILC_NOTIFY_TYPE_NONE, NULL },
145 { SILC_NOTIFY_TYPE_INVITE, "invite" },
146 { SILC_NOTIFY_TYPE_JOIN, "join" },
147 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
148 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
149 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
150 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
151 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
152 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
153 { SILC_NOTIFY_TYPE_MOTD, "motd" },
154 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
155 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
156 { SILC_NOTIFY_TYPE_KICKED, "kick" },
157 { SILC_NOTIFY_TYPE_KILLED, "kill" },
158 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
159 { SILC_NOTIFY_TYPE_BAN, "ban" },
162 void silc_notify(SilcClient client, SilcClientConnection conn,
163 SilcNotifyType type, ...)
165 SILC_SERVER_REC *server;
168 server = conn == NULL ? NULL : conn->context;
171 if (type == SILC_NOTIFY_TYPE_NONE) {
172 /* Some generic notice from server */
173 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
174 } else if (type < MAX_NOTIFY) {
175 /* Send signal about the notify event */
177 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
178 signal_emit(signal, 2, server, va);
181 printformat_module("fe-common/silc", server, NULL,
182 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
188 /* Called to indicate that connection was either successfully established
189 or connecting failed. This is also the first time application receives
190 the SilcClientConnection objecet which it should save somewhere. */
192 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
194 SILC_SERVER_REC *server = conn->context;
196 if (!server && !success) {
197 silc_client_close_connection(client, NULL, conn);
202 server->connected = TRUE;
203 signal_emit("event connected", 1, server);
205 server->connection_lost = TRUE;
206 server->conn->context = NULL;
207 server_disconnect(SERVER(server));
211 /* Called to indicate that connection was disconnected to the server. */
213 void silc_disconnect(SilcClient client, SilcClientConnection conn)
215 SILC_SERVER_REC *server = conn->context;
217 server->conn->context = NULL;
219 server->connection_lost = TRUE;
220 server_disconnect(SERVER(server));
223 /* Command handler. This function is called always in the command function.
224 If error occurs it will be called as well. `conn' is the associated
225 client connection. `cmd_context' is the command context that was
226 originally sent to the command. `success' is FALSE if error occured
227 during command. `command' is the command being processed. It must be
228 noted that this is not reply from server. This is merely called just
229 after application has called the command. Just to tell application
230 that the command really was processed. */
232 void silc_command(SilcClient client, SilcClientConnection conn,
233 SilcClientCommandContext cmd_context, int success,
236 SILC_SERVER_REC *server = conn->context;
242 case SILC_COMMAND_INVITE:
243 printformat_module("fe-common/silc", server, NULL,
244 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
245 cmd_context->argv[2],
246 (cmd_context->argv[1][0] == '*' ?
247 (char *)conn->current_channel->channel_name :
248 (char *)cmd_context->argv[1]));
255 /* Client info resolving callback when JOIN command reply is received.
256 This will cache all users on the channel. */
258 static void silc_client_join_get_users(SilcClient client,
259 SilcClientConnection conn,
260 SilcClientEntry *clients,
261 uint32 clients_count,
264 SilcChannelEntry channel = (SilcChannelEntry)context;
266 SILC_SERVER_REC *server = conn->context;
267 SILC_CHANNEL_REC *chanrec;
268 SilcClientEntry founder = NULL;
274 chanrec = silc_channel_find(server, channel->channel_name);
278 silc_list_start(channel->clients);
279 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
280 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
281 founder = chu->client;
282 silc_nicklist_insert(chanrec, chu, FALSE);
285 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
286 nicklist_set_own(CHANNEL(chanrec), ownnick);
287 signal_emit("channel joined", 1, chanrec);
290 printformat_module("fe-common/silc", server, channel->channel_name,
291 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
292 channel->channel_name, chanrec->topic);
294 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
297 if (founder == conn->local_entry)
298 printformat_module("fe-common/silc",
299 server, channel->channel_name, MSGLEVEL_CRAP,
300 SILCTXT_CHANNEL_FOUNDER_YOU,
301 channel->channel_name);
303 printformat_module("fe-common/silc",
304 server, channel->channel_name, MSGLEVEL_CRAP,
305 SILCTXT_CHANNEL_FOUNDER,
306 channel->channel_name, founder->nickname);
312 SilcClientConnection conn;
318 void silc_getkey_cb(bool success, void *context)
320 GetkeyContext getkey = (GetkeyContext)context;
321 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
322 char *name = (getkey->id_type == SILC_ID_CLIENT ?
323 ((SilcClientEntry)getkey->entry)->nickname :
324 ((SilcServerEntry)getkey->entry)->server_name);
327 printformat_module("fe-common/silc", NULL, NULL,
328 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
330 printformat_module("fe-common/silc", NULL, NULL,
331 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
334 silc_free(getkey->fingerprint);
338 /* Command reply handler. This function is called always in the command reply
339 function. If error occurs it will be called as well. Normal scenario
340 is that it will be called after the received command data has been parsed
341 and processed. The function is used to pass the received command data to
344 `conn' is the associated client connection. `cmd_payload' is the command
345 payload data received from server and it can be ignored. It is provided
346 if the application would like to re-parse the received command data,
347 however, it must be noted that the data is parsed already by the library
348 thus the payload can be ignored. `success' is FALSE if error occured.
349 In this case arguments are not sent to the application. `command' is the
350 command reply being processed. The function has variable argument list
351 and each command defines the number and type of arguments it passes to the
352 application (on error they are not sent). */
355 silc_command_reply(SilcClient client, SilcClientConnection conn,
356 SilcCommandPayload cmd_payload, int success,
357 SilcCommand command, SilcCommandStatus status, ...)
360 SILC_SERVER_REC *server = conn->context;
361 SILC_CHANNEL_REC *chanrec;
364 va_start(vp, status);
367 case SILC_COMMAND_WHOIS:
369 char buf[1024], *nickname, *username, *realname, *nick;
372 SilcClientEntry client_entry;
374 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
375 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
377 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
380 silc_say_error("%s: %s", tmp,
381 silc_client_command_status_message(status));
383 silc_say_error("%s", silc_client_command_status_message(status));
390 client_entry = va_arg(vp, SilcClientEntry);
391 nickname = va_arg(vp, char *);
392 username = va_arg(vp, char *);
393 realname = va_arg(vp, char *);
394 channels = va_arg(vp, SilcBuffer);
395 mode = va_arg(vp, uint32);
396 idle = va_arg(vp, uint32);
398 silc_parse_userfqdn(nickname, &nick, NULL);
399 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
400 SILCTXT_WHOIS_USERINFO, nickname,
401 client_entry->username, client_entry->hostname,
402 nick, client_entry->nickname);
403 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
404 SILCTXT_WHOIS_REALNAME, realname);
408 SilcDList list = silc_channel_payload_parse_list(channels);
410 SilcChannelPayload entry;
411 memset(buf, 0, sizeof(buf));
412 silc_dlist_start(list);
413 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
414 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
416 char *name = silc_channel_get_name(entry, &name_len);
419 strncat(buf, m, strlen(m));
420 strncat(buf, name, name_len);
421 strncat(buf, " ", 1);
425 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
426 SILCTXT_WHOIS_CHANNELS, buf);
427 silc_channel_payload_list_free(list);
432 memset(buf, 0, sizeof(buf));
434 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
435 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
436 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
438 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
439 "SILC Operator " : "[Unknown mode] ");
441 if (mode & SILC_UMODE_GONE)
444 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
445 SILCTXT_WHOIS_MODES, buf);
448 if (idle && nickname) {
449 memset(buf, 0, sizeof(buf));
450 snprintf(buf, sizeof(buf) - 1, "%lu %s",
451 idle > 60 ? (idle / 60) : idle,
452 idle > 60 ? "minutes" : "seconds");
454 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
455 SILCTXT_WHOIS_IDLE, buf);
460 case SILC_COMMAND_WHOWAS:
462 char *nickname, *username, *realname;
464 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
465 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
467 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
470 silc_say_error("%s: %s", tmp,
471 silc_client_command_status_message(status));
473 silc_say_error("%s", silc_client_command_status_message(status));
480 (void)va_arg(vp, SilcClientEntry);
481 nickname = va_arg(vp, char *);
482 username = va_arg(vp, char *);
483 realname = va_arg(vp, char *);
485 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
486 SILCTXT_WHOWAS_USERINFO, nickname, username,
487 realname ? realname : "");
491 case SILC_COMMAND_INVITE:
493 SilcChannelEntry channel;
495 SilcArgumentPayload args;
501 channel = va_arg(vp, SilcChannelEntry);
502 invite_list = va_arg(vp, char *);
504 args = silc_command_get_args(cmd_payload);
506 argc = silc_argument_get_arg_num(args);
509 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
510 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
513 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
514 SILCTXT_CHANNEL_NO_INVITE_LIST,
515 channel->channel_name);
519 case SILC_COMMAND_JOIN:
521 char *channel, *mode, *topic;
523 SilcChannelEntry channel_entry;
524 SilcBuffer client_id_list;
530 channel = va_arg(vp, char *);
531 channel_entry = va_arg(vp, SilcChannelEntry);
532 modei = va_arg(vp, uint32);
533 (void)va_arg(vp, uint32);
534 (void)va_arg(vp, unsigned char *);
535 (void)va_arg(vp, unsigned char *);
536 (void)va_arg(vp, unsigned char *);
537 topic = va_arg(vp, char *);
538 (void)va_arg(vp, unsigned char *);
539 list_count = va_arg(vp, uint32);
540 client_id_list = va_arg(vp, SilcBuffer);
542 chanrec = silc_channel_find(server, channel);
544 chanrec = silc_channel_create(server, channel, TRUE);
547 g_free_not_null(chanrec->topic);
548 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
549 signal_emit("channel topic changed", 1, chanrec);
552 mode = silc_client_chmode(modei,
553 channel_entry->channel_key ?
554 channel_entry->channel_key->cipher->name : "",
555 channel_entry->hmac ?
556 channel_entry->hmac->hmac->name : "");
557 g_free_not_null(chanrec->mode);
558 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
559 signal_emit("channel mode changed", 1, chanrec);
561 /* Resolve the client information */
562 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
563 silc_client_join_get_users,
568 case SILC_COMMAND_NICK:
570 SilcClientEntry client = va_arg(vp, SilcClientEntry);
576 old = g_strdup(server->nick);
577 server_change_nick(SERVER(server), client->nickname);
578 nicklist_rename_unique(SERVER(server),
579 server->conn->local_entry, server->nick,
580 client, client->nickname);
582 signal_emit("message own_nick", 4, server, server->nick, old, "");
587 case SILC_COMMAND_LIST:
596 (void)va_arg(vp, SilcChannelEntry);
597 name = va_arg(vp, char *);
598 topic = va_arg(vp, char *);
599 usercount = va_arg(vp, int);
601 if (status == SILC_STATUS_LIST_START ||
602 status == SILC_STATUS_OK)
603 printformat_module("fe-common/silc", server, NULL,
604 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
606 snprintf(users, sizeof(users) - 1, "%d", usercount);
607 printformat_module("fe-common/silc", server, NULL,
608 MSGLEVEL_CRAP, SILCTXT_LIST,
609 name, users, topic ? topic : "");
613 case SILC_COMMAND_UMODE:
620 mode = va_arg(vp, uint32);
622 if (mode & SILC_UMODE_SERVER_OPERATOR)
623 printformat_module("fe-common/silc", server, NULL,
624 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
626 if (mode & SILC_UMODE_ROUTER_OPERATOR)
627 printformat_module("fe-common/silc", server, NULL,
628 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
632 case SILC_COMMAND_OPER:
636 printformat_module("fe-common/silc", server, NULL,
637 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
640 case SILC_COMMAND_SILCOPER:
644 printformat_module("fe-common/silc", server, NULL,
645 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
648 case SILC_COMMAND_USERS:
650 SilcChannelEntry channel;
656 channel = va_arg(vp, SilcChannelEntry);
658 printformat_module("fe-common/silc", server, channel->channel_name,
659 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
660 channel->channel_name);
662 silc_list_start(channel->clients);
663 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
664 SilcClientEntry e = chu->client;
667 memset(stat, 0, sizeof(stat));
668 mode = silc_client_chumode_char(chu->mode);
669 if (e->mode & SILC_UMODE_GONE)
676 printformat_module("fe-common/silc", server, channel->channel_name,
677 MSGLEVEL_CRAP, SILCTXT_USERS,
678 e->nickname, stat, e->username,
679 e->hostname, e->realname ? e->realname : "");
686 case SILC_COMMAND_BAN:
688 SilcChannelEntry channel;
694 channel = va_arg(vp, SilcChannelEntry);
695 ban_list = va_arg(vp, char *);
698 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
699 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
702 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
703 SILCTXT_CHANNEL_NO_BAN_LIST,
704 channel->channel_name);
708 case SILC_COMMAND_GETKEY:
712 SilcPublicKey public_key;
715 GetkeyContext getkey;
720 id_type = va_arg(vp, uint32);
721 entry = va_arg(vp, void *);
722 public_key = va_arg(vp, SilcPublicKey);
725 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
727 getkey = silc_calloc(1, sizeof(*getkey));
728 getkey->entry = entry;
729 getkey->id_type = id_type;
730 getkey->client = client;
732 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
734 silc_verify_public_key_internal(client, conn,
735 (id_type == SILC_ID_CLIENT ?
736 SILC_SOCKET_TYPE_CLIENT :
737 SILC_SOCKET_TYPE_SERVER),
738 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
739 silc_getkey_cb, getkey);
742 printformat_module("fe-common/silc", server, NULL,
743 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
748 case SILC_COMMAND_TOPIC:
750 SilcChannelEntry channel;
756 channel = va_arg(vp, SilcChannelEntry);
757 topic = va_arg(vp, char *);
760 chanrec = silc_channel_find_entry(server, channel);
762 g_free_not_null(chanrec->topic);
763 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
764 signal_emit("channel topic changed", 1, chanrec);
766 printformat_module("fe-common/silc", server, channel->channel_name,
767 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
768 channel->channel_name, topic);
770 printformat_module("fe-common/silc", server, channel->channel_name,
771 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
772 channel->channel_name);
782 /* Internal routine to verify public key. If the `completion' is provided
783 it will be called to indicate whether public was verified or not. */
787 SilcClientConnection conn;
792 SilcSKEPKType pk_type;
793 SilcVerifyPublicKey completion;
797 static void verify_public_key_completion(const char *line, void *context)
799 PublicKeyVerify verify = (PublicKeyVerify)context;
801 if (line[0] == 'Y' || line[0] == 'y') {
802 /* Call the completion */
803 if (verify->completion)
804 verify->completion(TRUE, verify->context);
806 /* Save the key for future checking */
807 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
808 verify->pk_len, SILC_PKCS_FILE_PEM);
810 /* Call the completion */
811 if (verify->completion)
812 verify->completion(FALSE, verify->context);
814 printformat_module("fe-common/silc", NULL, NULL,
815 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
818 silc_free(verify->filename);
819 silc_free(verify->entity);
820 silc_free(verify->pk);
825 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
826 SilcSocketType conn_type, unsigned char *pk,
827 uint32 pk_len, SilcSKEPKType pk_type,
828 SilcVerifyPublicKey completion, void *context)
831 char file[256], filename[256], *fingerprint, *format;
834 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
835 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
836 "server" : "client");
837 PublicKeyVerify verify;
839 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
840 printformat_module("fe-common/silc", NULL, NULL,
841 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
844 completion(FALSE, context);
848 pw = getpwuid(getuid());
851 completion(FALSE, context);
855 memset(filename, 0, sizeof(filename));
856 memset(file, 0, sizeof(file));
858 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
859 conn_type == SILC_SOCKET_TYPE_ROUTER) {
860 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
861 conn->sock->hostname, conn->sock->port);
862 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
863 pw->pw_dir, entity, file);
865 /* Replace all whitespaces with `_'. */
866 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
867 for (i = 0; i < strlen(fingerprint); i++)
868 if (fingerprint[i] == ' ')
869 fingerprint[i] = '_';
871 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
872 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
873 pw->pw_dir, entity, file);
874 silc_free(fingerprint);
877 /* Take fingerprint of the public key */
878 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
880 verify = silc_calloc(1, sizeof(*verify));
881 verify->client = client;
883 verify->filename = strdup(filename);
884 verify->entity = strdup(entity);
885 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
886 memcpy(verify->pk, pk, pk_len);
887 verify->pk_len = pk_len;
888 verify->pk_type = pk_type;
889 verify->completion = completion;
890 verify->context = context;
892 /* Check whether this key already exists */
893 if (stat(filename, &st) < 0) {
894 /* Key does not exist, ask user to verify the key and save it */
896 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
897 SILCTXT_PUBKEY_RECEIVED, entity);
898 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
899 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
900 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
901 SILCTXT_PUBKEY_ACCEPT);
902 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
905 silc_free(fingerprint);
908 /* The key already exists, verify it. */
909 SilcPublicKey public_key;
910 unsigned char *encpk;
913 /* Load the key file */
914 if (!silc_pkcs_load_public_key(filename, &public_key,
916 if (!silc_pkcs_load_public_key(filename, &public_key,
917 SILC_PKCS_FILE_BIN)) {
918 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
919 SILCTXT_PUBKEY_RECEIVED, entity);
920 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
921 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
922 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
923 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
924 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
925 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
926 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
929 silc_free(fingerprint);
933 /* Encode the key data */
934 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
936 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
937 SILCTXT_PUBKEY_RECEIVED, entity);
938 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
939 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
940 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
941 SILCTXT_PUBKEY_MALFORMED, entity);
942 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
943 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
944 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
947 silc_free(fingerprint);
951 /* Compare the keys */
952 if (memcmp(encpk, pk, encpk_len)) {
953 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
954 SILCTXT_PUBKEY_RECEIVED, entity);
955 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
956 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
957 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
958 SILCTXT_PUBKEY_NO_MATCH, entity);
959 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
960 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
961 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
962 SILCTXT_PUBKEY_MITM_ATTACK, entity);
964 /* Ask user to verify the key and save it */
965 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
966 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
967 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
970 silc_free(fingerprint);
974 /* Local copy matched */
976 completion(TRUE, context);
977 silc_free(fingerprint);
981 /* Verifies received public key. The `conn_type' indicates which entity
982 (server, client etc.) has sent the public key. If user decides to trust
983 the key may be saved as trusted public key for later use. The
984 `completion' must be called after the public key has been verified. */
987 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
988 SilcSocketType conn_type, unsigned char *pk,
989 uint32 pk_len, SilcSKEPKType pk_type,
990 SilcVerifyPublicKey completion, void *context)
992 silc_verify_public_key_internal(client, conn, conn_type, pk,
994 completion, context);
997 /* Asks passphrase from user on the input line. */
1000 SilcAskPassphrase completion;
1004 void ask_passphrase_completion(const char *passphrase, void *context)
1006 AskPassphrase p = (AskPassphrase)context;
1007 p->completion((unsigned char *)passphrase,
1008 passphrase ? strlen(passphrase) : 0, p->context);
1012 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1013 SilcAskPassphrase completion, void *context)
1015 AskPassphrase p = silc_calloc(1, sizeof(*p));
1016 p->completion = completion;
1017 p->context = context;
1019 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1020 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1024 SilcGetAuthMeth completion;
1026 } *InternalGetAuthMethod;
1028 /* Callback called when we've received the authentication method information
1029 from the server after we've requested it. This will get the authentication
1030 data from the user if needed. */
1032 static void silc_get_auth_method_callback(SilcClient client,
1033 SilcClientConnection conn,
1034 SilcAuthMethod auth_meth,
1037 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1039 switch (auth_meth) {
1040 case SILC_AUTH_NONE:
1041 /* No authentication required. */
1042 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1044 case SILC_AUTH_PASSWORD:
1045 /* Do not ask the passphrase from user, the library will ask it if
1046 we do not provide it here. */
1047 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1049 case SILC_AUTH_PUBLIC_KEY:
1050 /* Do not get the authentication data now, the library will generate
1051 it using our default key, if we do not provide it here. */
1052 /* XXX In the future when we support multiple local keys and multiple
1053 local certificates we will need to ask from user which one to use. */
1054 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1058 silc_free(internal);
1061 /* Find authentication method and authentication data by hostname and
1062 port. The hostname may be IP address as well. The found authentication
1063 method and authentication data is returned to `auth_meth', `auth_data'
1064 and `auth_data_len'. The function returns TRUE if authentication method
1065 is found and FALSE if not. `conn' may be NULL. */
1067 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1068 char *hostname, uint16 port,
1069 SilcGetAuthMeth completion, void *context)
1071 InternalGetAuthMethod internal;
1073 /* XXX must resolve from configuration whether this connection has
1074 any specific authentication data */
1076 /* If we do not have this connection configured by the user in a
1077 configuration file then resolve the authentication method from the
1078 server for this session. */
1079 internal = silc_calloc(1, sizeof(*internal));
1080 internal->completion = completion;
1081 internal->context = context;
1083 silc_client_request_authentication_method(client, conn,
1084 silc_get_auth_method_callback,
1088 /* Notifies application that failure packet was received. This is called
1089 if there is some protocol active in the client. The `protocol' is the
1090 protocol context. The `failure' is opaque pointer to the failure
1091 indication. Note, that the `failure' is protocol dependant and application
1092 must explicitly cast it to correct type. Usually `failure' is 32 bit
1093 failure type (see protocol specs for all protocol failure types). */
1095 void silc_failure(SilcClient client, SilcClientConnection conn,
1096 SilcProtocol protocol, void *failure)
1098 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1099 SilcSKEStatus status = (SilcSKEStatus)failure;
1101 if (status == SILC_SKE_STATUS_BAD_VERSION)
1102 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1103 SILCTXT_KE_BAD_VERSION);
1104 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1105 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1106 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1107 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1108 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1109 SILCTXT_KE_UNKNOWN_GROUP);
1110 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1111 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1112 SILCTXT_KE_UNKNOWN_CIPHER);
1113 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1114 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1115 SILCTXT_KE_UNKNOWN_PKCS);
1116 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1117 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1118 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1119 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1120 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1121 SILCTXT_KE_UNKNOWN_HMAC);
1122 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1123 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1124 SILCTXT_KE_INCORRECT_SIGNATURE);
1125 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1126 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1127 SILCTXT_KE_INVALID_COOKIE);
1130 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1131 uint32 err = (uint32)failure;
1133 if (err == SILC_AUTH_FAILED)
1134 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1135 SILCTXT_AUTH_FAILED);
1139 /* Asks whether the user would like to perform the key agreement protocol.
1140 This is called after we have received an key agreement packet or an
1141 reply to our key agreement packet. This returns TRUE if the user wants
1142 the library to perform the key agreement protocol and FALSE if it is not
1143 desired (application may start it later by calling the function
1144 silc_client_perform_key_agreement). */
1146 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1147 SilcClientEntry client_entry, char *hostname,
1149 SilcKeyAgreementCallback *completion,
1154 /* We will just display the info on the screen and return FALSE and user
1155 will have to start the key agreement with a command. */
1158 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1161 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1162 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1164 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1165 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1166 client_entry->nickname, hostname, portstr);
1174 /* SILC client operations */
1175 SilcClientOperations ops = {
1177 silc_channel_message,
1178 silc_private_message,
1184 silc_get_auth_method,
1185 silc_verify_public_key,
1186 silc_ask_passphrase,