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);
310 /* Command reply handler. This function is called always in the command reply
311 function. If error occurs it will be called as well. Normal scenario
312 is that it will be called after the received command data has been parsed
313 and processed. The function is used to pass the received command data to
316 `conn' is the associated client connection. `cmd_payload' is the command
317 payload data received from server and it can be ignored. It is provided
318 if the application would like to re-parse the received command data,
319 however, it must be noted that the data is parsed already by the library
320 thus the payload can be ignored. `success' is FALSE if error occured.
321 In this case arguments are not sent to the application. `command' is the
322 command reply being processed. The function has variable argument list
323 and each command defines the number and type of arguments it passes to the
324 application (on error they are not sent). */
327 silc_command_reply(SilcClient client, SilcClientConnection conn,
328 SilcCommandPayload cmd_payload, int success,
329 SilcCommand command, SilcCommandStatus status, ...)
332 SILC_SERVER_REC *server = conn->context;
333 SILC_CHANNEL_REC *chanrec;
336 va_start(vp, status);
339 case SILC_COMMAND_WHOIS:
341 char buf[1024], *nickname, *username, *realname, *nick;
344 SilcClientEntry client_entry;
346 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
347 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
349 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
352 silc_say_error("%s: %s", tmp,
353 silc_client_command_status_message(status));
355 silc_say_error("%s", silc_client_command_status_message(status));
362 client_entry = va_arg(vp, SilcClientEntry);
363 nickname = va_arg(vp, char *);
364 username = va_arg(vp, char *);
365 realname = va_arg(vp, char *);
366 channels = va_arg(vp, SilcBuffer);
367 mode = va_arg(vp, uint32);
368 idle = va_arg(vp, uint32);
370 silc_parse_userfqdn(nickname, &nick, NULL);
371 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
372 SILCTXT_WHOIS_USERINFO, nickname,
373 client_entry->username, client_entry->hostname,
374 nick, client_entry->nickname);
375 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
376 SILCTXT_WHOIS_REALNAME, realname);
380 SilcDList list = silc_channel_payload_parse_list(channels);
382 SilcChannelPayload entry;
383 memset(buf, 0, sizeof(buf));
384 silc_dlist_start(list);
385 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
386 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
388 char *name = silc_channel_get_name(entry, &name_len);
391 strncat(buf, m, strlen(m));
392 strncat(buf, name, name_len);
393 strncat(buf, " ", 1);
397 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
398 SILCTXT_WHOIS_CHANNELS, buf);
399 silc_channel_payload_list_free(list);
404 memset(buf, 0, sizeof(buf));
406 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
407 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
408 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
410 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
411 "SILC Operator " : "[Unknown mode] ");
413 if (mode & SILC_UMODE_GONE)
416 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
417 SILCTXT_WHOIS_MODES, buf);
420 if (idle && nickname) {
421 memset(buf, 0, sizeof(buf));
422 snprintf(buf, sizeof(buf) - 1, "%lu %s",
423 idle > 60 ? (idle / 60) : idle,
424 idle > 60 ? "minutes" : "seconds");
426 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
427 SILCTXT_WHOIS_IDLE, buf);
432 case SILC_COMMAND_WHOWAS:
434 char *nickname, *username, *realname;
436 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
437 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
439 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
442 silc_say_error("%s: %s", tmp,
443 silc_client_command_status_message(status));
445 silc_say_error("%s", silc_client_command_status_message(status));
452 (void)va_arg(vp, SilcClientEntry);
453 nickname = va_arg(vp, char *);
454 username = va_arg(vp, char *);
455 realname = va_arg(vp, char *);
457 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
458 SILCTXT_WHOWAS_USERINFO, nickname, username,
459 realname ? realname : "");
463 case SILC_COMMAND_INVITE:
465 SilcChannelEntry channel;
467 SilcArgumentPayload args;
473 channel = va_arg(vp, SilcChannelEntry);
474 invite_list = va_arg(vp, char *);
476 args = silc_command_get_args(cmd_payload);
478 argc = silc_argument_get_arg_num(args);
481 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
482 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
485 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
486 SILCTXT_CHANNEL_NO_INVITE_LIST,
487 channel->channel_name);
491 case SILC_COMMAND_JOIN:
493 char *channel, *mode, *topic;
495 SilcChannelEntry channel_entry;
496 SilcBuffer client_id_list;
502 channel = va_arg(vp, char *);
503 channel_entry = va_arg(vp, SilcChannelEntry);
504 modei = va_arg(vp, uint32);
505 (void)va_arg(vp, uint32);
506 (void)va_arg(vp, unsigned char *);
507 (void)va_arg(vp, unsigned char *);
508 (void)va_arg(vp, unsigned char *);
509 topic = va_arg(vp, char *);
510 (void)va_arg(vp, unsigned char *);
511 list_count = va_arg(vp, uint32);
512 client_id_list = va_arg(vp, SilcBuffer);
514 chanrec = silc_channel_find(server, channel);
516 chanrec = silc_channel_create(server, channel, TRUE);
519 g_free_not_null(chanrec->topic);
520 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
521 signal_emit("channel topic changed", 1, chanrec);
524 mode = silc_client_chmode(modei,
525 channel_entry->channel_key->cipher->name,
526 channel_entry->hmac->hmac->name);
527 g_free_not_null(chanrec->mode);
528 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
529 signal_emit("channel mode changed", 1, chanrec);
531 /* Resolve the client information */
532 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
533 silc_client_join_get_users,
538 case SILC_COMMAND_NICK:
540 SilcClientEntry client = va_arg(vp, SilcClientEntry);
546 old = g_strdup(server->nick);
547 server_change_nick(SERVER(server), client->nickname);
548 nicklist_rename_unique(SERVER(server),
549 server->conn->local_entry, server->nick,
550 client, client->nickname);
552 signal_emit("message own_nick", 4, server, server->nick, old, "");
557 case SILC_COMMAND_LIST:
566 (void)va_arg(vp, SilcChannelEntry);
567 name = va_arg(vp, char *);
568 topic = va_arg(vp, char *);
569 usercount = va_arg(vp, int);
571 if (status == SILC_STATUS_LIST_START ||
572 status == SILC_STATUS_OK)
573 printformat_module("fe-common/silc", server, NULL,
574 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
576 snprintf(users, sizeof(users) - 1, "%d", usercount);
577 printformat_module("fe-common/silc", server, NULL,
578 MSGLEVEL_CRAP, SILCTXT_LIST,
579 name, users, topic ? topic : "");
583 case SILC_COMMAND_UMODE:
590 mode = va_arg(vp, uint32);
592 if (mode & SILC_UMODE_SERVER_OPERATOR)
593 printformat_module("fe-common/silc", server, NULL,
594 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
596 if (mode & SILC_UMODE_ROUTER_OPERATOR)
597 printformat_module("fe-common/silc", server, NULL,
598 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
602 case SILC_COMMAND_OPER:
606 printformat_module("fe-common/silc", server, NULL,
607 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
610 case SILC_COMMAND_SILCOPER:
614 printformat_module("fe-common/silc", server, NULL,
615 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
618 case SILC_COMMAND_USERS:
620 SilcChannelEntry channel;
626 channel = va_arg(vp, SilcChannelEntry);
628 printformat_module("fe-common/silc", server, channel->channel_name,
629 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
630 channel->channel_name);
632 silc_list_start(channel->clients);
633 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
634 SilcClientEntry e = chu->client;
637 memset(stat, 0, sizeof(stat));
638 mode = silc_client_chumode_char(chu->mode);
639 if (e->mode & SILC_UMODE_GONE)
646 printformat_module("fe-common/silc", server, channel->channel_name,
647 MSGLEVEL_CRAP, SILCTXT_USERS,
648 e->nickname, stat, e->username,
649 e->hostname, e->realname ? e->realname : "");
656 case SILC_COMMAND_BAN:
658 SilcChannelEntry channel;
664 channel = va_arg(vp, SilcChannelEntry);
665 ban_list = va_arg(vp, char *);
668 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
669 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
672 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
673 SILCTXT_CHANNEL_NO_BAN_LIST,
674 channel->channel_name);
678 case SILC_COMMAND_GETKEY:
682 SilcPublicKey public_key;
689 id_type = va_arg(vp, uint32);
690 entry = va_arg(vp, void *);
691 public_key = va_arg(vp, SilcPublicKey);
694 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
696 silc_verify_public_key_internal(client, conn,
697 (id_type == SILC_ID_CLIENT ?
698 SILC_SOCKET_TYPE_CLIENT :
699 SILC_SOCKET_TYPE_SERVER),
700 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
704 printformat_module("fe-common/silc", server, NULL,
705 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
710 case SILC_COMMAND_TOPIC:
712 SilcChannelEntry channel;
718 channel = va_arg(vp, SilcChannelEntry);
719 topic = va_arg(vp, char *);
722 chanrec = silc_channel_find_entry(server, channel);
724 g_free_not_null(chanrec->topic);
725 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
726 signal_emit("channel topic changed", 1, chanrec);
728 printformat_module("fe-common/silc", server, channel->channel_name,
729 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
730 channel->channel_name, topic);
732 printformat_module("fe-common/silc", server, channel->channel_name,
733 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
734 channel->channel_name);
744 /* Internal routine to verify public key. If the `completion' is provided
745 it will be called to indicate whether public was verified or not. */
749 SilcClientConnection conn;
754 SilcSKEPKType pk_type;
755 SilcVerifyPublicKey completion;
759 static void verify_public_key_completion(const char *line, void *context)
761 PublicKeyVerify verify = (PublicKeyVerify)context;
763 if (line[0] == 'Y' || line[0] == 'y') {
764 /* Call the completion */
765 if (verify->completion)
766 verify->completion(TRUE, verify->context);
768 /* Save the key for future checking */
769 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
770 verify->pk_len, SILC_PKCS_FILE_PEM);
772 /* Call the completion */
773 if (verify->completion)
774 verify->completion(FALSE, verify->context);
776 printformat_module("fe-common/silc", NULL, NULL,
777 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
780 silc_free(verify->filename);
781 silc_free(verify->entity);
782 silc_free(verify->pk);
787 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
788 SilcSocketType conn_type, unsigned char *pk,
789 uint32 pk_len, SilcSKEPKType pk_type,
790 SilcVerifyPublicKey completion, void *context)
793 char file[256], filename[256], *fingerprint, *format;
796 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
797 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
798 "server" : "client");
799 PublicKeyVerify verify;
801 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
802 printformat_module("fe-common/silc", NULL, NULL,
803 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
806 completion(FALSE, context);
810 pw = getpwuid(getuid());
813 completion(FALSE, context);
817 memset(filename, 0, sizeof(filename));
818 memset(file, 0, sizeof(file));
820 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
821 conn_type == SILC_SOCKET_TYPE_ROUTER) {
822 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
823 conn->sock->hostname, conn->sock->port);
824 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
825 pw->pw_dir, entity, file);
827 /* Replace all whitespaces with `_'. */
828 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
829 for (i = 0; i < strlen(fingerprint); i++)
830 if (fingerprint[i] == ' ')
831 fingerprint[i] = '_';
833 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
834 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
835 pw->pw_dir, entity, file);
836 silc_free(fingerprint);
839 /* Take fingerprint of the public key */
840 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
842 verify = silc_calloc(1, sizeof(*verify));
843 verify->client = client;
845 verify->filename = strdup(filename);
846 verify->entity = strdup(entity);
847 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
848 memcpy(verify->pk, pk, pk_len);
849 verify->pk_len = pk_len;
850 verify->pk_type = pk_type;
851 verify->completion = completion;
852 verify->context = context;
854 /* Check whether this key already exists */
855 if (stat(filename, &st) < 0) {
856 /* Key does not exist, ask user to verify the key and save it */
858 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
859 SILCTXT_PUBKEY_RECEIVED, entity);
860 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
861 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
862 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
863 SILCTXT_PUBKEY_ACCEPT);
864 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
867 silc_free(fingerprint);
870 /* The key already exists, verify it. */
871 SilcPublicKey public_key;
872 unsigned char *encpk;
875 /* Load the key file */
876 if (!silc_pkcs_load_public_key(filename, &public_key,
878 if (!silc_pkcs_load_public_key(filename, &public_key,
879 SILC_PKCS_FILE_BIN)) {
880 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
881 SILCTXT_PUBKEY_RECEIVED, entity);
882 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
883 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
884 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
885 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
886 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
887 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
888 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
891 silc_free(fingerprint);
895 /* Encode the key data */
896 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
898 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
899 SILCTXT_PUBKEY_RECEIVED, entity);
900 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
901 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
902 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
903 SILCTXT_PUBKEY_MALFORMED, entity);
904 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
905 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
906 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
909 silc_free(fingerprint);
913 /* Compare the keys */
914 if (memcmp(encpk, pk, encpk_len)) {
915 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
916 SILCTXT_PUBKEY_RECEIVED, entity);
917 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
918 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
919 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
920 SILCTXT_PUBKEY_NO_MATCH, entity);
921 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
922 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
923 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
924 SILCTXT_PUBKEY_MITM_ATTACK, entity);
926 /* Ask user to verify the key and save it */
927 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
928 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
929 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
932 silc_free(fingerprint);
936 /* Local copy matched */
938 completion(TRUE, context);
939 silc_free(fingerprint);
943 /* Verifies received public key. The `conn_type' indicates which entity
944 (server, client etc.) has sent the public key. If user decides to trust
945 the key may be saved as trusted public key for later use. The
946 `completion' must be called after the public key has been verified. */
949 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
950 SilcSocketType conn_type, unsigned char *pk,
951 uint32 pk_len, SilcSKEPKType pk_type,
952 SilcVerifyPublicKey completion, void *context)
954 silc_verify_public_key_internal(client, conn, conn_type, pk,
956 completion, context);
959 /* Asks passphrase from user on the input line. */
962 SilcAskPassphrase completion;
966 void ask_passphrase_completion(const char *passphrase, void *context)
968 AskPassphrase p = (AskPassphrase)context;
969 p->completion((unsigned char *)passphrase,
970 passphrase ? strlen(passphrase) : 0, p->context);
974 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
975 SilcAskPassphrase completion, void *context)
977 AskPassphrase p = silc_calloc(1, sizeof(*p));
978 p->completion = completion;
979 p->context = context;
981 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
982 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
986 SilcGetAuthMeth completion;
988 } *InternalGetAuthMethod;
990 /* Callback called when we've received the authentication method information
991 from the server after we've requested it. This will get the authentication
992 data from the user if needed. */
994 static void silc_get_auth_method_callback(SilcClient client,
995 SilcClientConnection conn,
996 SilcAuthMethod auth_meth,
999 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1001 switch (auth_meth) {
1002 case SILC_AUTH_NONE:
1003 /* No authentication required. */
1004 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1006 case SILC_AUTH_PASSWORD:
1007 /* Do not ask the passphrase from user, the library will ask it if
1008 we do not provide it here. */
1009 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1011 case SILC_AUTH_PUBLIC_KEY:
1012 /* Do not get the authentication data now, the library will generate
1013 it using our default key, if we do not provide it here. */
1014 /* XXX In the future when we support multiple local keys and multiple
1015 local certificates we will need to ask from user which one to use. */
1016 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1020 silc_free(internal);
1023 /* Find authentication method and authentication data by hostname and
1024 port. The hostname may be IP address as well. The found authentication
1025 method and authentication data is returned to `auth_meth', `auth_data'
1026 and `auth_data_len'. The function returns TRUE if authentication method
1027 is found and FALSE if not. `conn' may be NULL. */
1029 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1030 char *hostname, uint16 port,
1031 SilcGetAuthMeth completion, void *context)
1033 InternalGetAuthMethod internal;
1035 /* XXX must resolve from configuration whether this connection has
1036 any specific authentication data */
1038 /* If we do not have this connection configured by the user in a
1039 configuration file then resolve the authentication method from the
1040 server for this session. */
1041 internal = silc_calloc(1, sizeof(*internal));
1042 internal->completion = completion;
1043 internal->context = context;
1045 silc_client_request_authentication_method(client, conn,
1046 silc_get_auth_method_callback,
1050 /* Notifies application that failure packet was received. This is called
1051 if there is some protocol active in the client. The `protocol' is the
1052 protocol context. The `failure' is opaque pointer to the failure
1053 indication. Note, that the `failure' is protocol dependant and application
1054 must explicitly cast it to correct type. Usually `failure' is 32 bit
1055 failure type (see protocol specs for all protocol failure types). */
1057 void silc_failure(SilcClient client, SilcClientConnection conn,
1058 SilcProtocol protocol, void *failure)
1060 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1061 SilcSKEStatus status = (SilcSKEStatus)failure;
1063 if (status == SILC_SKE_STATUS_BAD_VERSION)
1064 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1065 SILCTXT_KE_BAD_VERSION);
1066 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1067 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1068 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1069 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1070 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1071 SILCTXT_KE_UNKNOWN_GROUP);
1072 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1073 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1074 SILCTXT_KE_UNKNOWN_CIPHER);
1075 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1076 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1077 SILCTXT_KE_UNKNOWN_PKCS);
1078 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1079 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1080 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1081 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1082 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1083 SILCTXT_KE_UNKNOWN_HMAC);
1084 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1085 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1086 SILCTXT_KE_INCORRECT_SIGNATURE);
1087 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1088 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1089 SILCTXT_KE_INVALID_COOKIE);
1092 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1093 uint32 err = (uint32)failure;
1095 if (err == SILC_AUTH_FAILED)
1096 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1097 SILCTXT_AUTH_FAILED);
1101 /* Asks whether the user would like to perform the key agreement protocol.
1102 This is called after we have received an key agreement packet or an
1103 reply to our key agreement packet. This returns TRUE if the user wants
1104 the library to perform the key agreement protocol and FALSE if it is not
1105 desired (application may start it later by calling the function
1106 silc_client_perform_key_agreement). */
1108 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1109 SilcClientEntry client_entry, char *hostname,
1111 SilcKeyAgreementCallback *completion,
1116 /* We will just display the info on the screen and return FALSE and user
1117 will have to start the key agreement with a command. */
1120 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1123 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1124 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1126 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1127 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1128 client_entry->nickname, hostname, portstr);
1136 /* SILC client operations */
1137 SilcClientOperations ops = {
1139 silc_channel_message,
1140 silc_private_message,
1146 silc_get_auth_method,
1147 silc_verify_public_key,
1148 silc_ask_passphrase,