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 ?
526 channel_entry->channel_key->cipher->name : "",
527 channel_entry->hmac ?
528 channel_entry->hmac->hmac->name : "");
529 g_free_not_null(chanrec->mode);
530 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
531 signal_emit("channel mode changed", 1, chanrec);
533 /* Resolve the client information */
534 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
535 silc_client_join_get_users,
540 case SILC_COMMAND_NICK:
542 SilcClientEntry client = va_arg(vp, SilcClientEntry);
548 old = g_strdup(server->nick);
549 server_change_nick(SERVER(server), client->nickname);
550 nicklist_rename_unique(SERVER(server),
551 server->conn->local_entry, server->nick,
552 client, client->nickname);
554 signal_emit("message own_nick", 4, server, server->nick, old, "");
559 case SILC_COMMAND_LIST:
568 (void)va_arg(vp, SilcChannelEntry);
569 name = va_arg(vp, char *);
570 topic = va_arg(vp, char *);
571 usercount = va_arg(vp, int);
573 if (status == SILC_STATUS_LIST_START ||
574 status == SILC_STATUS_OK)
575 printformat_module("fe-common/silc", server, NULL,
576 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
578 snprintf(users, sizeof(users) - 1, "%d", usercount);
579 printformat_module("fe-common/silc", server, NULL,
580 MSGLEVEL_CRAP, SILCTXT_LIST,
581 name, users, topic ? topic : "");
585 case SILC_COMMAND_UMODE:
592 mode = va_arg(vp, uint32);
594 if (mode & SILC_UMODE_SERVER_OPERATOR)
595 printformat_module("fe-common/silc", server, NULL,
596 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
598 if (mode & SILC_UMODE_ROUTER_OPERATOR)
599 printformat_module("fe-common/silc", server, NULL,
600 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
604 case SILC_COMMAND_OPER:
608 printformat_module("fe-common/silc", server, NULL,
609 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
612 case SILC_COMMAND_SILCOPER:
616 printformat_module("fe-common/silc", server, NULL,
617 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
620 case SILC_COMMAND_USERS:
622 SilcChannelEntry channel;
628 channel = va_arg(vp, SilcChannelEntry);
630 printformat_module("fe-common/silc", server, channel->channel_name,
631 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
632 channel->channel_name);
634 silc_list_start(channel->clients);
635 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
636 SilcClientEntry e = chu->client;
639 memset(stat, 0, sizeof(stat));
640 mode = silc_client_chumode_char(chu->mode);
641 if (e->mode & SILC_UMODE_GONE)
648 printformat_module("fe-common/silc", server, channel->channel_name,
649 MSGLEVEL_CRAP, SILCTXT_USERS,
650 e->nickname, stat, e->username,
651 e->hostname, e->realname ? e->realname : "");
658 case SILC_COMMAND_BAN:
660 SilcChannelEntry channel;
666 channel = va_arg(vp, SilcChannelEntry);
667 ban_list = va_arg(vp, char *);
670 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
671 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
674 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
675 SILCTXT_CHANNEL_NO_BAN_LIST,
676 channel->channel_name);
680 case SILC_COMMAND_GETKEY:
684 SilcPublicKey public_key;
691 id_type = va_arg(vp, uint32);
692 entry = va_arg(vp, void *);
693 public_key = va_arg(vp, SilcPublicKey);
696 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
698 silc_verify_public_key_internal(client, conn,
699 (id_type == SILC_ID_CLIENT ?
700 SILC_SOCKET_TYPE_CLIENT :
701 SILC_SOCKET_TYPE_SERVER),
702 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
706 printformat_module("fe-common/silc", server, NULL,
707 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
712 case SILC_COMMAND_TOPIC:
714 SilcChannelEntry channel;
720 channel = va_arg(vp, SilcChannelEntry);
721 topic = va_arg(vp, char *);
724 chanrec = silc_channel_find_entry(server, channel);
726 g_free_not_null(chanrec->topic);
727 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
728 signal_emit("channel topic changed", 1, chanrec);
730 printformat_module("fe-common/silc", server, channel->channel_name,
731 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
732 channel->channel_name, topic);
734 printformat_module("fe-common/silc", server, channel->channel_name,
735 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
736 channel->channel_name);
746 /* Internal routine to verify public key. If the `completion' is provided
747 it will be called to indicate whether public was verified or not. */
751 SilcClientConnection conn;
756 SilcSKEPKType pk_type;
757 SilcVerifyPublicKey completion;
761 static void verify_public_key_completion(const char *line, void *context)
763 PublicKeyVerify verify = (PublicKeyVerify)context;
765 if (line[0] == 'Y' || line[0] == 'y') {
766 /* Call the completion */
767 if (verify->completion)
768 verify->completion(TRUE, verify->context);
770 /* Save the key for future checking */
771 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
772 verify->pk_len, SILC_PKCS_FILE_PEM);
774 /* Call the completion */
775 if (verify->completion)
776 verify->completion(FALSE, verify->context);
778 printformat_module("fe-common/silc", NULL, NULL,
779 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
782 silc_free(verify->filename);
783 silc_free(verify->entity);
784 silc_free(verify->pk);
789 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
790 SilcSocketType conn_type, unsigned char *pk,
791 uint32 pk_len, SilcSKEPKType pk_type,
792 SilcVerifyPublicKey completion, void *context)
795 char file[256], filename[256], *fingerprint, *format;
798 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
799 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
800 "server" : "client");
801 PublicKeyVerify verify;
803 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
804 printformat_module("fe-common/silc", NULL, NULL,
805 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
808 completion(FALSE, context);
812 pw = getpwuid(getuid());
815 completion(FALSE, context);
819 memset(filename, 0, sizeof(filename));
820 memset(file, 0, sizeof(file));
822 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
823 conn_type == SILC_SOCKET_TYPE_ROUTER) {
824 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
825 conn->sock->hostname, conn->sock->port);
826 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
827 pw->pw_dir, entity, file);
829 /* Replace all whitespaces with `_'. */
830 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
831 for (i = 0; i < strlen(fingerprint); i++)
832 if (fingerprint[i] == ' ')
833 fingerprint[i] = '_';
835 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
836 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
837 pw->pw_dir, entity, file);
838 silc_free(fingerprint);
841 /* Take fingerprint of the public key */
842 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
844 verify = silc_calloc(1, sizeof(*verify));
845 verify->client = client;
847 verify->filename = strdup(filename);
848 verify->entity = strdup(entity);
849 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
850 memcpy(verify->pk, pk, pk_len);
851 verify->pk_len = pk_len;
852 verify->pk_type = pk_type;
853 verify->completion = completion;
854 verify->context = context;
856 /* Check whether this key already exists */
857 if (stat(filename, &st) < 0) {
858 /* Key does not exist, ask user to verify the key and save it */
860 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
861 SILCTXT_PUBKEY_RECEIVED, entity);
862 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
863 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
864 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
865 SILCTXT_PUBKEY_ACCEPT);
866 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
869 silc_free(fingerprint);
872 /* The key already exists, verify it. */
873 SilcPublicKey public_key;
874 unsigned char *encpk;
877 /* Load the key file */
878 if (!silc_pkcs_load_public_key(filename, &public_key,
880 if (!silc_pkcs_load_public_key(filename, &public_key,
881 SILC_PKCS_FILE_BIN)) {
882 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
883 SILCTXT_PUBKEY_RECEIVED, entity);
884 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
885 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
886 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
887 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
888 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
889 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
890 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
893 silc_free(fingerprint);
897 /* Encode the key data */
898 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
900 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
901 SILCTXT_PUBKEY_RECEIVED, entity);
902 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
903 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
904 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
905 SILCTXT_PUBKEY_MALFORMED, entity);
906 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
907 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
908 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
911 silc_free(fingerprint);
915 /* Compare the keys */
916 if (memcmp(encpk, pk, encpk_len)) {
917 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
918 SILCTXT_PUBKEY_RECEIVED, entity);
919 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
920 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
921 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
922 SILCTXT_PUBKEY_NO_MATCH, entity);
923 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
924 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
925 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
926 SILCTXT_PUBKEY_MITM_ATTACK, entity);
928 /* Ask user to verify the key and save it */
929 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
930 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
931 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
934 silc_free(fingerprint);
938 /* Local copy matched */
940 completion(TRUE, context);
941 silc_free(fingerprint);
945 /* Verifies received public key. The `conn_type' indicates which entity
946 (server, client etc.) has sent the public key. If user decides to trust
947 the key may be saved as trusted public key for later use. The
948 `completion' must be called after the public key has been verified. */
951 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
952 SilcSocketType conn_type, unsigned char *pk,
953 uint32 pk_len, SilcSKEPKType pk_type,
954 SilcVerifyPublicKey completion, void *context)
956 silc_verify_public_key_internal(client, conn, conn_type, pk,
958 completion, context);
961 /* Asks passphrase from user on the input line. */
964 SilcAskPassphrase completion;
968 void ask_passphrase_completion(const char *passphrase, void *context)
970 AskPassphrase p = (AskPassphrase)context;
971 p->completion((unsigned char *)passphrase,
972 passphrase ? strlen(passphrase) : 0, p->context);
976 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
977 SilcAskPassphrase completion, void *context)
979 AskPassphrase p = silc_calloc(1, sizeof(*p));
980 p->completion = completion;
981 p->context = context;
983 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
984 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
988 SilcGetAuthMeth completion;
990 } *InternalGetAuthMethod;
992 /* Callback called when we've received the authentication method information
993 from the server after we've requested it. This will get the authentication
994 data from the user if needed. */
996 static void silc_get_auth_method_callback(SilcClient client,
997 SilcClientConnection conn,
998 SilcAuthMethod auth_meth,
1001 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1003 switch (auth_meth) {
1004 case SILC_AUTH_NONE:
1005 /* No authentication required. */
1006 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1008 case SILC_AUTH_PASSWORD:
1009 /* Do not ask the passphrase from user, the library will ask it if
1010 we do not provide it here. */
1011 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1013 case SILC_AUTH_PUBLIC_KEY:
1014 /* Do not get the authentication data now, the library will generate
1015 it using our default key, if we do not provide it here. */
1016 /* XXX In the future when we support multiple local keys and multiple
1017 local certificates we will need to ask from user which one to use. */
1018 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1022 silc_free(internal);
1025 /* Find authentication method and authentication data by hostname and
1026 port. The hostname may be IP address as well. The found authentication
1027 method and authentication data is returned to `auth_meth', `auth_data'
1028 and `auth_data_len'. The function returns TRUE if authentication method
1029 is found and FALSE if not. `conn' may be NULL. */
1031 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1032 char *hostname, uint16 port,
1033 SilcGetAuthMeth completion, void *context)
1035 InternalGetAuthMethod internal;
1037 /* XXX must resolve from configuration whether this connection has
1038 any specific authentication data */
1040 /* If we do not have this connection configured by the user in a
1041 configuration file then resolve the authentication method from the
1042 server for this session. */
1043 internal = silc_calloc(1, sizeof(*internal));
1044 internal->completion = completion;
1045 internal->context = context;
1047 silc_client_request_authentication_method(client, conn,
1048 silc_get_auth_method_callback,
1052 /* Notifies application that failure packet was received. This is called
1053 if there is some protocol active in the client. The `protocol' is the
1054 protocol context. The `failure' is opaque pointer to the failure
1055 indication. Note, that the `failure' is protocol dependant and application
1056 must explicitly cast it to correct type. Usually `failure' is 32 bit
1057 failure type (see protocol specs for all protocol failure types). */
1059 void silc_failure(SilcClient client, SilcClientConnection conn,
1060 SilcProtocol protocol, void *failure)
1062 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1063 SilcSKEStatus status = (SilcSKEStatus)failure;
1065 if (status == SILC_SKE_STATUS_BAD_VERSION)
1066 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1067 SILCTXT_KE_BAD_VERSION);
1068 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1069 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1070 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1071 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1072 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1073 SILCTXT_KE_UNKNOWN_GROUP);
1074 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1075 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1076 SILCTXT_KE_UNKNOWN_CIPHER);
1077 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1078 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1079 SILCTXT_KE_UNKNOWN_PKCS);
1080 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1081 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1082 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1083 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1084 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1085 SILCTXT_KE_UNKNOWN_HMAC);
1086 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1087 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1088 SILCTXT_KE_INCORRECT_SIGNATURE);
1089 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1090 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1091 SILCTXT_KE_INVALID_COOKIE);
1094 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1095 uint32 err = (uint32)failure;
1097 if (err == SILC_AUTH_FAILED)
1098 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1099 SILCTXT_AUTH_FAILED);
1103 /* Asks whether the user would like to perform the key agreement protocol.
1104 This is called after we have received an key agreement packet or an
1105 reply to our key agreement packet. This returns TRUE if the user wants
1106 the library to perform the key agreement protocol and FALSE if it is not
1107 desired (application may start it later by calling the function
1108 silc_client_perform_key_agreement). */
1110 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1111 SilcClientEntry client_entry, char *hostname,
1113 SilcKeyAgreementCallback *completion,
1118 /* We will just display the info on the screen and return FALSE and user
1119 will have to start the key agreement with a command. */
1122 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1125 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1126 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1128 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1129 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1130 client_entry->nickname, hostname, portstr);
1138 /* SILC client operations */
1139 SilcClientOperations ops = {
1141 silc_channel_message,
1142 silc_private_message,
1148 silc_get_auth_method,
1149 silc_verify_public_key,
1150 silc_ask_passphrase,