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 if (sender->username)
121 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
122 sender->username, sender->hostname);
123 signal_emit("message private", 4, server, msg,
124 sender->nickname ? sender->nickname : "[<unknown>]",
125 sender->username ? userhost : NULL);
128 /* Notify message to the client. The notify arguments are sent in the
129 same order as servers sends them. The arguments are same as received
130 from the server except for ID's. If ID is received application receives
131 the corresponding entry to the ID. For example, if Client ID is received
132 application receives SilcClientEntry. Also, if the notify type is
133 for channel the channel entry is sent to application (even if server
134 does not send it). */
141 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
142 static NOTIFY_REC notifies[] = {
143 { SILC_NOTIFY_TYPE_NONE, NULL },
144 { SILC_NOTIFY_TYPE_INVITE, "invite" },
145 { SILC_NOTIFY_TYPE_JOIN, "join" },
146 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
147 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
148 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
149 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
150 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
151 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
152 { SILC_NOTIFY_TYPE_MOTD, "motd" },
153 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
154 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
155 { SILC_NOTIFY_TYPE_KICKED, "kick" },
156 { SILC_NOTIFY_TYPE_KILLED, "kill" },
157 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
158 { SILC_NOTIFY_TYPE_BAN, "ban" },
161 void silc_notify(SilcClient client, SilcClientConnection conn,
162 SilcNotifyType type, ...)
164 SILC_SERVER_REC *server;
167 server = conn == NULL ? NULL : conn->context;
170 if (type == SILC_NOTIFY_TYPE_NONE) {
171 /* Some generic notice from server */
172 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
173 } else if (type < MAX_NOTIFY) {
174 /* Send signal about the notify event */
176 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
177 signal_emit(signal, 2, server, va);
180 printformat_module("fe-common/silc", server, NULL,
181 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
187 /* Called to indicate that connection was either successfully established
188 or connecting failed. This is also the first time application receives
189 the SilcClientConnection objecet which it should save somewhere. */
191 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
193 SILC_SERVER_REC *server = conn->context;
195 if (!server && !success) {
196 silc_client_close_connection(client, NULL, conn);
201 server->connected = TRUE;
202 signal_emit("event connected", 1, server);
204 server->connection_lost = TRUE;
205 server->conn->context = NULL;
206 server_disconnect(SERVER(server));
210 /* Called to indicate that connection was disconnected to the server. */
212 void silc_disconnect(SilcClient client, SilcClientConnection conn)
214 SILC_SERVER_REC *server = conn->context;
216 server->conn->context = NULL;
218 server->connection_lost = TRUE;
219 server_disconnect(SERVER(server));
222 /* Command handler. This function is called always in the command function.
223 If error occurs it will be called as well. `conn' is the associated
224 client connection. `cmd_context' is the command context that was
225 originally sent to the command. `success' is FALSE if error occured
226 during command. `command' is the command being processed. It must be
227 noted that this is not reply from server. This is merely called just
228 after application has called the command. Just to tell application
229 that the command really was processed. */
231 void silc_command(SilcClient client, SilcClientConnection conn,
232 SilcClientCommandContext cmd_context, int success,
235 SILC_SERVER_REC *server = conn->context;
241 case SILC_COMMAND_INVITE:
242 printformat_module("fe-common/silc", server, NULL,
243 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
244 cmd_context->argv[2],
245 (cmd_context->argv[1][0] == '*' ?
246 (char *)conn->current_channel->channel_name :
247 (char *)cmd_context->argv[1]));
254 /* Client info resolving callback when JOIN command reply is received.
255 This will cache all users on the channel. */
257 static void silc_client_join_get_users(SilcClient client,
258 SilcClientConnection conn,
259 SilcClientEntry *clients,
260 uint32 clients_count,
263 SilcChannelEntry channel = (SilcChannelEntry)context;
265 SILC_SERVER_REC *server = conn->context;
266 SILC_CHANNEL_REC *chanrec;
267 SilcClientEntry founder = NULL;
273 chanrec = silc_channel_find(server, channel->channel_name);
277 silc_list_start(channel->clients);
278 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
279 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
280 founder = chu->client;
281 silc_nicklist_insert(chanrec, chu, FALSE);
284 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
285 nicklist_set_own(CHANNEL(chanrec), ownnick);
286 signal_emit("channel joined", 1, chanrec);
289 printformat_module("fe-common/silc", server, channel->channel_name,
290 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
291 channel->channel_name, chanrec->topic);
293 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
296 if (founder == conn->local_entry)
297 printformat_module("fe-common/silc",
298 server, channel->channel_name, MSGLEVEL_CRAP,
299 SILCTXT_CHANNEL_FOUNDER_YOU,
300 channel->channel_name);
302 printformat_module("fe-common/silc",
303 server, channel->channel_name, MSGLEVEL_CRAP,
304 SILCTXT_CHANNEL_FOUNDER,
305 channel->channel_name, founder->nickname);
309 /* Command reply handler. This function is called always in the command reply
310 function. If error occurs it will be called as well. Normal scenario
311 is that it will be called after the received command data has been parsed
312 and processed. The function is used to pass the received command data to
315 `conn' is the associated client connection. `cmd_payload' is the command
316 payload data received from server and it can be ignored. It is provided
317 if the application would like to re-parse the received command data,
318 however, it must be noted that the data is parsed already by the library
319 thus the payload can be ignored. `success' is FALSE if error occured.
320 In this case arguments are not sent to the application. `command' is the
321 command reply being processed. The function has variable argument list
322 and each command defines the number and type of arguments it passes to the
323 application (on error they are not sent). */
326 silc_command_reply(SilcClient client, SilcClientConnection conn,
327 SilcCommandPayload cmd_payload, int success,
328 SilcCommand command, SilcCommandStatus status, ...)
331 SILC_SERVER_REC *server = conn->context;
332 SILC_CHANNEL_REC *chanrec;
335 va_start(vp, status);
338 case SILC_COMMAND_WHOIS:
340 char buf[1024], *nickname, *username, *realname, *nick;
343 SilcClientEntry client_entry;
345 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
346 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
348 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
351 silc_say_error("%s: %s", tmp,
352 silc_client_command_status_message(status));
354 silc_say_error("%s", silc_client_command_status_message(status));
361 client_entry = va_arg(vp, SilcClientEntry);
362 nickname = va_arg(vp, char *);
363 username = va_arg(vp, char *);
364 realname = va_arg(vp, char *);
365 channels = va_arg(vp, SilcBuffer);
366 mode = va_arg(vp, uint32);
367 idle = va_arg(vp, uint32);
369 silc_parse_userfqdn(nickname, &nick, NULL);
370 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
371 SILCTXT_WHOIS_USERINFO, nickname,
372 client_entry->username, client_entry->hostname,
373 nick, client_entry->nickname);
374 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
375 SILCTXT_WHOIS_REALNAME, realname);
379 SilcDList list = silc_channel_payload_parse_list(channels);
381 SilcChannelPayload entry;
382 memset(buf, 0, sizeof(buf));
383 silc_dlist_start(list);
384 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
385 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
387 char *name = silc_channel_get_name(entry, &name_len);
390 strncat(buf, m, strlen(m));
391 strncat(buf, name, name_len);
392 strncat(buf, " ", 1);
396 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
397 SILCTXT_WHOIS_CHANNELS, buf);
398 silc_channel_payload_list_free(list);
403 memset(buf, 0, sizeof(buf));
405 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
406 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
407 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
409 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
410 "SILC Operator " : "[Unknown mode] ");
412 if (mode & SILC_UMODE_GONE)
415 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
416 SILCTXT_WHOIS_MODES, buf);
419 if (idle && nickname) {
420 memset(buf, 0, sizeof(buf));
421 snprintf(buf, sizeof(buf) - 1, "%lu %s",
422 idle > 60 ? (idle / 60) : idle,
423 idle > 60 ? "minutes" : "seconds");
425 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
426 SILCTXT_WHOIS_IDLE, buf);
431 case SILC_COMMAND_WHOWAS:
433 char *nickname, *username, *realname;
435 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
436 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
438 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
441 silc_say_error("%s: %s", tmp,
442 silc_client_command_status_message(status));
444 silc_say_error("%s", silc_client_command_status_message(status));
451 (void)va_arg(vp, SilcClientEntry);
452 nickname = va_arg(vp, char *);
453 username = va_arg(vp, char *);
454 realname = va_arg(vp, char *);
456 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
457 SILCTXT_WHOWAS_USERINFO, nickname, username,
458 realname ? realname : "");
462 case SILC_COMMAND_INVITE:
464 SilcChannelEntry channel;
466 SilcArgumentPayload args;
472 channel = va_arg(vp, SilcChannelEntry);
473 invite_list = va_arg(vp, char *);
475 args = silc_command_get_args(cmd_payload);
477 argc = silc_argument_get_arg_num(args);
480 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
481 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
484 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
485 SILCTXT_CHANNEL_NO_INVITE_LIST,
486 channel->channel_name);
490 case SILC_COMMAND_JOIN:
492 char *channel, *mode, *topic;
494 SilcChannelEntry channel_entry;
495 SilcBuffer client_id_list;
501 channel = va_arg(vp, char *);
502 channel_entry = va_arg(vp, SilcChannelEntry);
503 modei = va_arg(vp, uint32);
504 (void)va_arg(vp, uint32);
505 (void)va_arg(vp, unsigned char *);
506 (void)va_arg(vp, unsigned char *);
507 (void)va_arg(vp, unsigned char *);
508 topic = va_arg(vp, char *);
509 (void)va_arg(vp, unsigned char *);
510 list_count = va_arg(vp, uint32);
511 client_id_list = va_arg(vp, SilcBuffer);
513 chanrec = silc_channel_find(server, channel);
515 chanrec = silc_channel_create(server, channel, TRUE);
518 g_free_not_null(chanrec->topic);
519 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
520 signal_emit("channel topic changed", 1, chanrec);
523 mode = silc_client_chmode(modei,
524 channel_entry->channel_key->cipher->name,
525 channel_entry->hmac->hmac->name);
526 g_free_not_null(chanrec->mode);
527 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
528 signal_emit("channel mode changed", 1, chanrec);
530 /* Resolve the client information */
531 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
532 silc_client_join_get_users,
537 case SILC_COMMAND_NICK:
539 SilcClientEntry client = va_arg(vp, SilcClientEntry);
545 old = g_strdup(server->nick);
546 server_change_nick(SERVER(server), client->nickname);
547 nicklist_rename_unique(SERVER(server),
548 server->conn->local_entry, server->nick,
549 client, client->nickname);
551 signal_emit("message own_nick", 4, server, server->nick, old, "");
556 case SILC_COMMAND_LIST:
565 (void)va_arg(vp, SilcChannelEntry);
566 name = va_arg(vp, char *);
567 topic = va_arg(vp, char *);
568 usercount = va_arg(vp, int);
570 if (status == SILC_STATUS_LIST_START ||
571 status == SILC_STATUS_OK)
572 printformat_module("fe-common/silc", server, NULL,
573 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
575 snprintf(users, sizeof(users) - 1, "%d", usercount);
576 printformat_module("fe-common/silc", server, NULL,
577 MSGLEVEL_CRAP, SILCTXT_LIST,
578 name, users, topic ? topic : "");
582 case SILC_COMMAND_UMODE:
589 mode = va_arg(vp, uint32);
591 if (mode & SILC_UMODE_SERVER_OPERATOR)
592 printformat_module("fe-common/silc", server, NULL,
593 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
595 if (mode & SILC_UMODE_ROUTER_OPERATOR)
596 printformat_module("fe-common/silc", server, NULL,
597 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
601 case SILC_COMMAND_OPER:
605 printformat_module("fe-common/silc", server, NULL,
606 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
609 case SILC_COMMAND_SILCOPER:
613 printformat_module("fe-common/silc", server, NULL,
614 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
617 case SILC_COMMAND_USERS:
619 SilcChannelEntry channel;
625 channel = va_arg(vp, SilcChannelEntry);
627 printformat_module("fe-common/silc", server, channel->channel_name,
628 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
629 channel->channel_name);
631 silc_list_start(channel->clients);
632 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
633 SilcClientEntry e = chu->client;
636 memset(stat, 0, sizeof(stat));
637 mode = silc_client_chumode_char(chu->mode);
638 if (e->mode & SILC_UMODE_GONE)
645 printformat_module("fe-common/silc", server, channel->channel_name,
646 MSGLEVEL_CRAP, SILCTXT_USERS,
647 e->nickname, stat, e->username,
648 e->hostname, e->realname ? e->realname : "");
655 case SILC_COMMAND_BAN:
657 SilcChannelEntry channel;
663 channel = va_arg(vp, SilcChannelEntry);
664 ban_list = va_arg(vp, char *);
667 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
668 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
671 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
672 SILCTXT_CHANNEL_NO_BAN_LIST,
673 channel->channel_name);
677 case SILC_COMMAND_GETKEY:
681 SilcPublicKey public_key;
688 id_type = va_arg(vp, uint32);
689 entry = va_arg(vp, void *);
690 public_key = va_arg(vp, SilcPublicKey);
693 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
695 silc_verify_public_key_internal(client, conn,
696 (id_type == SILC_ID_CLIENT ?
697 SILC_SOCKET_TYPE_CLIENT :
698 SILC_SOCKET_TYPE_SERVER),
699 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
703 printformat_module("fe-common/silc", server, NULL,
704 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
709 case SILC_COMMAND_TOPIC:
711 SilcChannelEntry channel;
717 channel = va_arg(vp, SilcChannelEntry);
718 topic = va_arg(vp, char *);
721 chanrec = silc_channel_find_entry(server, channel);
723 g_free_not_null(chanrec->topic);
724 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
725 signal_emit("channel topic changed", 1, chanrec);
727 printformat_module("fe-common/silc", server, channel->channel_name,
728 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
729 channel->channel_name, topic);
731 printformat_module("fe-common/silc", server, channel->channel_name,
732 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
733 channel->channel_name);
743 /* Internal routine to verify public key. If the `completion' is provided
744 it will be called to indicate whether public was verified or not. */
748 SilcClientConnection conn;
753 SilcSKEPKType pk_type;
754 SilcVerifyPublicKey completion;
758 static void verify_public_key_completion(const char *line, void *context)
760 PublicKeyVerify verify = (PublicKeyVerify)context;
762 if (line[0] == 'Y' || line[0] == 'y') {
763 /* Call the completion */
764 if (verify->completion)
765 verify->completion(TRUE, verify->context);
767 /* Save the key for future checking */
768 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
769 verify->pk_len, SILC_PKCS_FILE_PEM);
771 /* Call the completion */
772 if (verify->completion)
773 verify->completion(FALSE, verify->context);
775 printformat_module("fe-common/silc", NULL, NULL,
776 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
779 silc_free(verify->filename);
780 silc_free(verify->entity);
781 silc_free(verify->pk);
786 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
787 SilcSocketType conn_type, unsigned char *pk,
788 uint32 pk_len, SilcSKEPKType pk_type,
789 SilcVerifyPublicKey completion, void *context)
792 char file[256], filename[256], *fingerprint, *format;
795 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
796 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
797 "server" : "client");
798 PublicKeyVerify verify;
800 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
801 printformat_module("fe-common/silc", NULL, NULL,
802 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
805 completion(FALSE, context);
809 pw = getpwuid(getuid());
812 completion(FALSE, context);
816 memset(filename, 0, sizeof(filename));
817 memset(file, 0, sizeof(file));
819 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
820 conn_type == SILC_SOCKET_TYPE_ROUTER) {
821 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
822 conn->sock->hostname, conn->sock->port);
823 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
824 pw->pw_dir, entity, file);
826 /* Replace all whitespaces with `_'. */
827 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
828 for (i = 0; i < strlen(fingerprint); i++)
829 if (fingerprint[i] == ' ')
830 fingerprint[i] = '_';
832 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
833 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
834 pw->pw_dir, entity, file);
835 silc_free(fingerprint);
838 /* Take fingerprint of the public key */
839 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
841 verify = silc_calloc(1, sizeof(*verify));
842 verify->client = client;
844 verify->filename = strdup(filename);
845 verify->entity = strdup(entity);
846 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
847 memcpy(verify->pk, pk, pk_len);
848 verify->pk_len = pk_len;
849 verify->pk_type = pk_type;
850 verify->completion = completion;
851 verify->context = context;
853 /* Check whether this key already exists */
854 if (stat(filename, &st) < 0) {
855 /* Key does not exist, ask user to verify the key and save it */
857 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
858 SILCTXT_PUBKEY_RECEIVED, entity);
859 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
860 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
861 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
862 SILCTXT_PUBKEY_ACCEPT);
863 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
866 silc_free(fingerprint);
869 /* The key already exists, verify it. */
870 SilcPublicKey public_key;
871 unsigned char *encpk;
874 /* Load the key file */
875 if (!silc_pkcs_load_public_key(filename, &public_key,
877 if (!silc_pkcs_load_public_key(filename, &public_key,
878 SILC_PKCS_FILE_BIN)) {
879 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
880 SILCTXT_PUBKEY_RECEIVED, entity);
881 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
882 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
883 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
884 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
885 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
886 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
887 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
890 silc_free(fingerprint);
894 /* Encode the key data */
895 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
897 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
898 SILCTXT_PUBKEY_RECEIVED, entity);
899 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
900 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
901 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
902 SILCTXT_PUBKEY_MALFORMED, entity);
903 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
904 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
905 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
908 silc_free(fingerprint);
912 /* Compare the keys */
913 if (memcmp(encpk, pk, encpk_len)) {
914 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
915 SILCTXT_PUBKEY_RECEIVED, entity);
916 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
917 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
918 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
919 SILCTXT_PUBKEY_NO_MATCH, entity);
920 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
921 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
922 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
923 SILCTXT_PUBKEY_MITM_ATTACK, entity);
925 /* Ask user to verify the key and save it */
926 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
927 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
928 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
931 silc_free(fingerprint);
935 /* Local copy matched */
937 completion(TRUE, context);
938 silc_free(fingerprint);
942 /* Verifies received public key. The `conn_type' indicates which entity
943 (server, client etc.) has sent the public key. If user decides to trust
944 the key may be saved as trusted public key for later use. The
945 `completion' must be called after the public key has been verified. */
948 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
949 SilcSocketType conn_type, unsigned char *pk,
950 uint32 pk_len, SilcSKEPKType pk_type,
951 SilcVerifyPublicKey completion, void *context)
953 silc_verify_public_key_internal(client, conn, conn_type, pk,
955 completion, context);
958 /* Asks passphrase from user on the input line. */
961 SilcAskPassphrase completion;
965 void ask_passphrase_completion(const char *passphrase, void *context)
967 AskPassphrase p = (AskPassphrase)context;
968 p->completion((unsigned char *)passphrase,
969 passphrase ? strlen(passphrase) : 0, p->context);
973 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
974 SilcAskPassphrase completion, void *context)
976 AskPassphrase p = silc_calloc(1, sizeof(*p));
977 p->completion = completion;
978 p->context = context;
980 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
981 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
985 SilcGetAuthMeth completion;
987 } *InternalGetAuthMethod;
989 /* Callback called when we've received the authentication method information
990 from the server after we've requested it. This will get the authentication
991 data from the user if needed. */
993 static void silc_get_auth_method_callback(SilcClient client,
994 SilcClientConnection conn,
995 SilcAuthMethod auth_meth,
998 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1000 switch (auth_meth) {
1001 case SILC_AUTH_NONE:
1002 /* No authentication required. */
1003 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1005 case SILC_AUTH_PASSWORD:
1006 /* Do not ask the passphrase from user, the library will ask it if
1007 we do not provide it here. */
1008 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1010 case SILC_AUTH_PUBLIC_KEY:
1011 /* Do not get the authentication data now, the library will generate
1012 it using our default key, if we do not provide it here. */
1013 /* XXX In the future when we support multiple local keys and multiple
1014 local certificates we will need to ask from user which one to use. */
1015 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1019 silc_free(internal);
1022 /* Find authentication method and authentication data by hostname and
1023 port. The hostname may be IP address as well. The found authentication
1024 method and authentication data is returned to `auth_meth', `auth_data'
1025 and `auth_data_len'. The function returns TRUE if authentication method
1026 is found and FALSE if not. `conn' may be NULL. */
1028 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1029 char *hostname, uint16 port,
1030 SilcGetAuthMeth completion, void *context)
1032 InternalGetAuthMethod internal;
1034 /* XXX must resolve from configuration whether this connection has
1035 any specific authentication data */
1037 /* If we do not have this connection configured by the user in a
1038 configuration file then resolve the authentication method from the
1039 server for this session. */
1040 internal = silc_calloc(1, sizeof(*internal));
1041 internal->completion = completion;
1042 internal->context = context;
1044 silc_client_request_authentication_method(client, conn,
1045 silc_get_auth_method_callback,
1049 /* Notifies application that failure packet was received. This is called
1050 if there is some protocol active in the client. The `protocol' is the
1051 protocol context. The `failure' is opaque pointer to the failure
1052 indication. Note, that the `failure' is protocol dependant and application
1053 must explicitly cast it to correct type. Usually `failure' is 32 bit
1054 failure type (see protocol specs for all protocol failure types). */
1056 void silc_failure(SilcClient client, SilcClientConnection conn,
1057 SilcProtocol protocol, void *failure)
1059 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1060 SilcSKEStatus status = (SilcSKEStatus)failure;
1062 if (status == SILC_SKE_STATUS_BAD_VERSION)
1063 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1064 SILCTXT_KE_BAD_VERSION);
1065 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1066 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1067 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1068 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1069 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1070 SILCTXT_KE_UNKNOWN_GROUP);
1071 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1072 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1073 SILCTXT_KE_UNKNOWN_CIPHER);
1074 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1075 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1076 SILCTXT_KE_UNKNOWN_PKCS);
1077 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1078 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1079 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1080 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1081 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1082 SILCTXT_KE_UNKNOWN_HMAC);
1083 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1084 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1085 SILCTXT_KE_INCORRECT_SIGNATURE);
1086 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1087 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1088 SILCTXT_KE_INVALID_COOKIE);
1091 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1092 uint32 err = (uint32)failure;
1094 if (err == SILC_AUTH_FAILED)
1095 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1096 SILCTXT_AUTH_FAILED);
1100 /* Asks whether the user would like to perform the key agreement protocol.
1101 This is called after we have received an key agreement packet or an
1102 reply to our key agreement packet. This returns TRUE if the user wants
1103 the library to perform the key agreement protocol and FALSE if it is not
1104 desired (application may start it later by calling the function
1105 silc_client_perform_key_agreement). */
1107 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1108 SilcClientEntry client_entry, char *hostname,
1110 SilcKeyAgreementCallback *completion,
1115 /* We will just display the info on the screen and return FALSE and user
1116 will have to start the key agreement with a command. */
1119 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1122 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1123 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1125 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1126 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1127 client_entry->nickname, hostname, portstr);
1135 /* SILC client operations */
1136 SilcClientOperations ops = {
1138 silc_channel_message,
1139 silc_private_message,
1145 silc_get_auth_method,
1146 silc_verify_public_key,
1147 silc_ask_passphrase,