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;
118 server = conn == NULL ? NULL : conn->context;
119 signal_emit("message private", 4, server, msg,
120 sender->nickname ? sender->nickname : "[<unknown>]",
121 sender->username ? sender->username : NULL);
124 /* Notify message to the client. The notify arguments are sent in the
125 same order as servers sends them. The arguments are same as received
126 from the server except for ID's. If ID is received application receives
127 the corresponding entry to the ID. For example, if Client ID is received
128 application receives SilcClientEntry. Also, if the notify type is
129 for channel the channel entry is sent to application (even if server
130 does not send it). */
137 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
138 static NOTIFY_REC notifies[] = {
139 { SILC_NOTIFY_TYPE_NONE, NULL },
140 { SILC_NOTIFY_TYPE_INVITE, "invite" },
141 { SILC_NOTIFY_TYPE_JOIN, "join" },
142 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
143 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
144 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
145 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
146 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
147 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
148 { SILC_NOTIFY_TYPE_MOTD, "motd" },
149 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
150 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
151 { SILC_NOTIFY_TYPE_KICKED, "kick" },
152 { SILC_NOTIFY_TYPE_KILLED, "kill" },
153 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
154 { SILC_NOTIFY_TYPE_BAN, "ban" },
157 void silc_notify(SilcClient client, SilcClientConnection conn,
158 SilcNotifyType type, ...)
160 SILC_SERVER_REC *server;
163 server = conn == NULL ? NULL : conn->context;
166 if (type == SILC_NOTIFY_TYPE_NONE) {
167 /* Some generic notice from server */
168 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
169 } else if (type < MAX_NOTIFY) {
170 /* Send signal about the notify event */
172 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
173 signal_emit(signal, 2, server, va);
176 printformat_module("fe-common/silc", server, NULL,
177 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
183 /* Called to indicate that connection was either successfully established
184 or connecting failed. This is also the first time application receives
185 the SilcClientConnection objecet which it should save somewhere. */
187 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
189 SILC_SERVER_REC *server = conn->context;
191 if (!server && !success) {
192 silc_client_close_connection(client, NULL, conn);
197 server->connected = TRUE;
198 signal_emit("event connected", 1, server);
200 server->connection_lost = TRUE;
201 server->conn->context = NULL;
202 server_disconnect(SERVER(server));
206 /* Called to indicate that connection was disconnected to the server. */
208 void silc_disconnect(SilcClient client, SilcClientConnection conn)
210 SILC_SERVER_REC *server = conn->context;
212 server->conn->context = NULL;
214 server->connection_lost = TRUE;
215 server_disconnect(SERVER(server));
218 /* Command handler. This function is called always in the command function.
219 If error occurs it will be called as well. `conn' is the associated
220 client connection. `cmd_context' is the command context that was
221 originally sent to the command. `success' is FALSE if error occured
222 during command. `command' is the command being processed. It must be
223 noted that this is not reply from server. This is merely called just
224 after application has called the command. Just to tell application
225 that the command really was processed. */
227 void silc_command(SilcClient client, SilcClientConnection conn,
228 SilcClientCommandContext cmd_context, int success,
231 SILC_SERVER_REC *server = conn->context;
237 case SILC_COMMAND_INVITE:
238 printformat_module("fe-common/silc", server, NULL,
239 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
240 cmd_context->argv[2],
241 (cmd_context->argv[1][0] == '*' ?
242 (char *)conn->current_channel->channel_name :
243 (char *)cmd_context->argv[1]));
250 /* Client info resolving callback when JOIN command reply is received.
251 This will cache all users on the channel. */
253 static void silc_client_join_get_users(SilcClient client,
254 SilcClientConnection conn,
255 SilcClientEntry *clients,
256 uint32 clients_count,
259 SilcChannelEntry channel = (SilcChannelEntry)context;
261 SILC_SERVER_REC *server = conn->context;
262 SILC_CHANNEL_REC *chanrec;
263 SilcClientEntry founder = NULL;
269 chanrec = silc_channel_find(server, channel->channel_name);
273 silc_list_start(channel->clients);
274 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
275 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
276 founder = chu->client;
277 silc_nicklist_insert(chanrec, chu, FALSE);
280 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
281 nicklist_set_own(CHANNEL(chanrec), ownnick);
282 signal_emit("channel joined", 1, chanrec);
285 printformat_module("fe-common/silc", server, channel->channel_name,
286 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
287 channel->channel_name, chanrec->topic);
289 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
292 if (founder == conn->local_entry)
293 printformat_module("fe-common/silc",
294 server, channel->channel_name, MSGLEVEL_CRAP,
295 SILCTXT_CHANNEL_FOUNDER_YOU,
296 channel->channel_name);
298 printformat_module("fe-common/silc",
299 server, channel->channel_name, MSGLEVEL_CRAP,
300 SILCTXT_CHANNEL_FOUNDER,
301 channel->channel_name, founder->nickname);
305 /* Command reply handler. This function is called always in the command reply
306 function. If error occurs it will be called as well. Normal scenario
307 is that it will be called after the received command data has been parsed
308 and processed. The function is used to pass the received command data to
311 `conn' is the associated client connection. `cmd_payload' is the command
312 payload data received from server and it can be ignored. It is provided
313 if the application would like to re-parse the received command data,
314 however, it must be noted that the data is parsed already by the library
315 thus the payload can be ignored. `success' is FALSE if error occured.
316 In this case arguments are not sent to the application. `command' is the
317 command reply being processed. The function has variable argument list
318 and each command defines the number and type of arguments it passes to the
319 application (on error they are not sent). */
322 silc_command_reply(SilcClient client, SilcClientConnection conn,
323 SilcCommandPayload cmd_payload, int success,
324 SilcCommand command, SilcCommandStatus status, ...)
327 SILC_SERVER_REC *server = conn->context;
328 SILC_CHANNEL_REC *chanrec;
331 va_start(vp, status);
334 case SILC_COMMAND_WHOIS:
336 char buf[1024], *nickname, *username, *realname, *nick;
339 SilcClientEntry client_entry;
341 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
342 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
344 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
347 silc_say_error("%s: %s", tmp,
348 silc_client_command_status_message(status));
350 silc_say_error("%s", silc_client_command_status_message(status));
357 client_entry = va_arg(vp, SilcClientEntry);
358 nickname = va_arg(vp, char *);
359 username = va_arg(vp, char *);
360 realname = va_arg(vp, char *);
361 channels = va_arg(vp, SilcBuffer);
362 mode = va_arg(vp, uint32);
363 idle = va_arg(vp, uint32);
365 silc_parse_userfqdn(nickname, &nick, NULL);
366 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
367 SILCTXT_WHOIS_USERINFO, nickname,
368 client_entry->username, client_entry->hostname,
369 nick, client_entry->nickname);
370 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
371 SILCTXT_WHOIS_REALNAME, realname);
375 SilcDList list = silc_channel_payload_parse_list(channels);
377 SilcChannelPayload entry;
378 memset(buf, 0, sizeof(buf));
379 silc_dlist_start(list);
380 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
381 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
383 char *name = silc_channel_get_name(entry, &name_len);
386 strncat(buf, m, strlen(m));
387 strncat(buf, name, name_len);
388 strncat(buf, " ", 1);
392 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
393 SILCTXT_WHOIS_CHANNELS, buf);
394 silc_channel_payload_list_free(list);
399 memset(buf, 0, sizeof(buf));
401 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
402 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
403 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
405 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
406 "SILC Operator " : "[Unknown mode] ");
408 if (mode & SILC_UMODE_GONE)
411 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
412 SILCTXT_WHOIS_MODES, buf);
415 if (idle && nickname) {
416 memset(buf, 0, sizeof(buf));
417 snprintf(buf, sizeof(buf) - 1, "%lu %s",
418 idle > 60 ? (idle / 60) : idle,
419 idle > 60 ? "minutes" : "seconds");
421 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
422 SILCTXT_WHOIS_IDLE, buf);
427 case SILC_COMMAND_WHOWAS:
429 char *nickname, *username, *realname;
431 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
432 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
434 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
437 silc_say_error("%s: %s", tmp,
438 silc_client_command_status_message(status));
440 silc_say_error("%s", silc_client_command_status_message(status));
447 (void)va_arg(vp, SilcClientEntry);
448 nickname = va_arg(vp, char *);
449 username = va_arg(vp, char *);
450 realname = va_arg(vp, char *);
452 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
453 SILCTXT_WHOWAS_USERINFO, nickname, username,
454 realname ? realname : "");
458 case SILC_COMMAND_INVITE:
460 SilcChannelEntry channel;
462 SilcArgumentPayload args;
468 channel = va_arg(vp, SilcChannelEntry);
469 invite_list = va_arg(vp, char *);
471 args = silc_command_get_args(cmd_payload);
473 argc = silc_argument_get_arg_num(args);
476 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
477 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
480 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
481 SILCTXT_CHANNEL_NO_INVITE_LIST,
482 channel->channel_name);
486 case SILC_COMMAND_JOIN:
488 char *channel, *mode, *topic;
490 SilcChannelEntry channel_entry;
491 SilcBuffer client_id_list;
497 channel = va_arg(vp, char *);
498 channel_entry = va_arg(vp, SilcChannelEntry);
499 modei = va_arg(vp, uint32);
500 (void)va_arg(vp, uint32);
501 (void)va_arg(vp, unsigned char *);
502 (void)va_arg(vp, unsigned char *);
503 (void)va_arg(vp, unsigned char *);
504 topic = va_arg(vp, char *);
505 (void)va_arg(vp, unsigned char *);
506 list_count = va_arg(vp, uint32);
507 client_id_list = va_arg(vp, SilcBuffer);
509 chanrec = silc_channel_find(server, channel);
511 chanrec = silc_channel_create(server, channel, TRUE);
514 g_free_not_null(chanrec->topic);
515 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
516 signal_emit("channel topic changed", 1, chanrec);
519 mode = silc_client_chmode(modei,
520 channel_entry->channel_key->cipher->name,
521 channel_entry->hmac->hmac->name);
522 g_free_not_null(chanrec->mode);
523 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
524 signal_emit("channel mode changed", 1, chanrec);
526 /* Resolve the client information */
527 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
528 silc_client_join_get_users,
533 case SILC_COMMAND_NICK:
535 SilcClientEntry client = va_arg(vp, SilcClientEntry);
541 old = g_strdup(server->nick);
542 server_change_nick(SERVER(server), client->nickname);
543 nicklist_rename_unique(SERVER(server),
544 server->conn->local_entry, server->nick,
545 client, client->nickname);
547 signal_emit("message own_nick", 4, server, server->nick, old, "");
552 case SILC_COMMAND_LIST:
561 (void)va_arg(vp, SilcChannelEntry);
562 name = va_arg(vp, char *);
563 topic = va_arg(vp, char *);
564 usercount = va_arg(vp, int);
566 if (status == SILC_STATUS_LIST_START ||
567 status == SILC_STATUS_OK)
568 printformat_module("fe-common/silc", server, NULL,
569 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
571 snprintf(users, sizeof(users) - 1, "%d", usercount);
572 printformat_module("fe-common/silc", server, NULL,
573 MSGLEVEL_CRAP, SILCTXT_LIST,
574 name, users, topic ? topic : "");
578 case SILC_COMMAND_UMODE:
585 mode = va_arg(vp, uint32);
587 if (mode & SILC_UMODE_SERVER_OPERATOR)
588 printformat_module("fe-common/silc", server, NULL,
589 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
591 if (mode & SILC_UMODE_ROUTER_OPERATOR)
592 printformat_module("fe-common/silc", server, NULL,
593 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
597 case SILC_COMMAND_OPER:
601 printformat_module("fe-common/silc", server, NULL,
602 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
605 case SILC_COMMAND_SILCOPER:
609 printformat_module("fe-common/silc", server, NULL,
610 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
613 case SILC_COMMAND_USERS:
615 SilcChannelEntry channel;
621 channel = va_arg(vp, SilcChannelEntry);
623 printformat_module("fe-common/silc", server, channel->channel_name,
624 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
625 channel->channel_name);
627 silc_list_start(channel->clients);
628 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
629 SilcClientEntry e = chu->client;
632 memset(stat, 0, sizeof(stat));
633 mode = silc_client_chumode_char(chu->mode);
634 if (e->mode & SILC_UMODE_GONE)
641 printformat_module("fe-common/silc", server, channel->channel_name,
642 MSGLEVEL_CRAP, SILCTXT_USERS,
643 e->nickname, stat, e->username,
644 e->realname ? e->realname : "");
651 case SILC_COMMAND_BAN:
653 SilcChannelEntry channel;
659 channel = va_arg(vp, SilcChannelEntry);
660 ban_list = va_arg(vp, char *);
663 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
664 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
667 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
668 SILCTXT_CHANNEL_NO_BAN_LIST,
669 channel->channel_name);
673 case SILC_COMMAND_GETKEY:
677 SilcPublicKey public_key;
684 id_type = va_arg(vp, uint32);
685 entry = va_arg(vp, void *);
686 public_key = va_arg(vp, SilcPublicKey);
689 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
691 silc_verify_public_key_internal(client, conn,
692 (id_type == SILC_ID_CLIENT ?
693 SILC_SOCKET_TYPE_CLIENT :
694 SILC_SOCKET_TYPE_SERVER),
695 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
699 printformat_module("fe-common/silc", server, NULL,
700 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
705 case SILC_COMMAND_TOPIC:
707 SilcChannelEntry channel;
713 channel = va_arg(vp, SilcChannelEntry);
714 topic = va_arg(vp, char *);
717 chanrec = silc_channel_find_entry(server, channel);
719 g_free_not_null(chanrec->topic);
720 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
721 signal_emit("channel topic changed", 1, chanrec);
723 printformat_module("fe-common/silc", server, channel->channel_name,
724 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
725 channel->channel_name, topic);
727 printformat_module("fe-common/silc", server, channel->channel_name,
728 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
729 channel->channel_name);
739 /* Internal routine to verify public key. If the `completion' is provided
740 it will be called to indicate whether public was verified or not. */
744 SilcClientConnection conn;
749 SilcSKEPKType pk_type;
750 SilcVerifyPublicKey completion;
754 static void verify_public_key_completion(const char *line, void *context)
756 PublicKeyVerify verify = (PublicKeyVerify)context;
758 if (line[0] == 'Y' || line[0] == 'y') {
759 /* Call the completion */
760 if (verify->completion)
761 verify->completion(TRUE, verify->context);
763 /* Save the key for future checking */
764 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
765 verify->pk_len, SILC_PKCS_FILE_PEM);
767 /* Call the completion */
768 if (verify->completion)
769 verify->completion(FALSE, verify->context);
771 printformat_module("fe-common/silc", NULL, NULL,
772 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
775 silc_free(verify->filename);
776 silc_free(verify->entity);
777 silc_free(verify->pk);
782 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
783 SilcSocketType conn_type, unsigned char *pk,
784 uint32 pk_len, SilcSKEPKType pk_type,
785 SilcVerifyPublicKey completion, void *context)
788 char file[256], filename[256], *fingerprint, *format;
791 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
792 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
793 "server" : "client");
794 PublicKeyVerify verify;
796 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
797 printformat_module("fe-common/silc", NULL, NULL,
798 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
801 completion(FALSE, context);
805 pw = getpwuid(getuid());
808 completion(FALSE, context);
812 memset(filename, 0, sizeof(filename));
813 memset(file, 0, sizeof(file));
815 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
816 conn_type == SILC_SOCKET_TYPE_ROUTER) {
817 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
818 conn->sock->hostname, conn->sock->port);
819 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
820 pw->pw_dir, entity, file);
822 /* Replace all whitespaces with `_'. */
823 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
824 for (i = 0; i < strlen(fingerprint); i++)
825 if (fingerprint[i] == ' ')
826 fingerprint[i] = '_';
828 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
829 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
830 pw->pw_dir, entity, file);
831 silc_free(fingerprint);
834 /* Take fingerprint of the public key */
835 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
837 verify = silc_calloc(1, sizeof(*verify));
838 verify->client = client;
840 verify->filename = strdup(filename);
841 verify->entity = strdup(entity);
842 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
843 memcpy(verify->pk, pk, pk_len);
844 verify->pk_len = pk_len;
845 verify->pk_type = pk_type;
846 verify->completion = completion;
847 verify->context = context;
849 /* Check whether this key already exists */
850 if (stat(filename, &st) < 0) {
851 /* Key does not exist, ask user to verify the key and save it */
853 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
854 SILCTXT_PUBKEY_RECEIVED, entity);
855 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
856 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
857 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
858 SILCTXT_PUBKEY_ACCEPT);
859 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
862 silc_free(fingerprint);
865 /* The key already exists, verify it. */
866 SilcPublicKey public_key;
867 unsigned char *encpk;
870 /* Load the key file */
871 if (!silc_pkcs_load_public_key(filename, &public_key,
873 if (!silc_pkcs_load_public_key(filename, &public_key,
874 SILC_PKCS_FILE_BIN)) {
875 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
876 SILCTXT_PUBKEY_RECEIVED, entity);
877 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
878 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
879 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
880 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
881 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
882 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
883 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
886 silc_free(fingerprint);
890 /* Encode the key data */
891 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
893 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
894 SILCTXT_PUBKEY_RECEIVED, entity);
895 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
896 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
897 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
898 SILCTXT_PUBKEY_MALFORMED, entity);
899 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
900 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
901 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
904 silc_free(fingerprint);
908 /* Compare the keys */
909 if (memcmp(encpk, pk, encpk_len)) {
910 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
911 SILCTXT_PUBKEY_RECEIVED, entity);
912 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
913 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
914 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
915 SILCTXT_PUBKEY_NO_MATCH, entity);
916 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
917 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
918 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
919 SILCTXT_PUBKEY_MITM_ATTACK, entity);
921 /* Ask user to verify the key and save it */
922 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
923 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
924 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
927 silc_free(fingerprint);
931 /* Local copy matched */
933 completion(TRUE, context);
934 silc_free(fingerprint);
938 /* Verifies received public key. The `conn_type' indicates which entity
939 (server, client etc.) has sent the public key. If user decides to trust
940 the key may be saved as trusted public key for later use. The
941 `completion' must be called after the public key has been verified. */
944 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
945 SilcSocketType conn_type, unsigned char *pk,
946 uint32 pk_len, SilcSKEPKType pk_type,
947 SilcVerifyPublicKey completion, void *context)
949 silc_verify_public_key_internal(client, conn, conn_type, pk,
951 completion, context);
954 /* Asks passphrase from user on the input line. */
957 SilcAskPassphrase completion;
961 void ask_passphrase_completion(const char *passphrase, void *context)
963 AskPassphrase p = (AskPassphrase)context;
964 p->completion((unsigned char *)passphrase,
965 passphrase ? strlen(passphrase) : 0, p->context);
969 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
970 SilcAskPassphrase completion, void *context)
972 AskPassphrase p = silc_calloc(1, sizeof(*p));
973 p->completion = completion;
974 p->context = context;
976 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
977 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
981 SilcGetAuthMeth completion;
983 } *InternalGetAuthMethod;
985 /* Callback called when we've received the authentication method information
986 from the server after we've requested it. This will get the authentication
987 data from the user if needed. */
989 static void silc_get_auth_method_callback(SilcClient client,
990 SilcClientConnection conn,
991 SilcAuthMethod auth_meth,
994 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
998 /* No authentication required. */
999 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1001 case SILC_AUTH_PASSWORD:
1002 /* Do not ask the passphrase from user, the library will ask it if
1003 we do not provide it here. */
1004 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1006 case SILC_AUTH_PUBLIC_KEY:
1007 /* Do not get the authentication data now, the library will generate
1008 it using our default key, if we do not provide it here. */
1009 /* XXX In the future when we support multiple local keys and multiple
1010 local certificates we will need to ask from user which one to use. */
1011 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1015 silc_free(internal);
1018 /* Find authentication method and authentication data by hostname and
1019 port. The hostname may be IP address as well. The found authentication
1020 method and authentication data is returned to `auth_meth', `auth_data'
1021 and `auth_data_len'. The function returns TRUE if authentication method
1022 is found and FALSE if not. `conn' may be NULL. */
1024 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1025 char *hostname, uint16 port,
1026 SilcGetAuthMeth completion, void *context)
1028 InternalGetAuthMethod internal;
1030 /* XXX must resolve from configuration whether this connection has
1031 any specific authentication data */
1033 /* If we do not have this connection configured by the user in a
1034 configuration file then resolve the authentication method from the
1035 server for this session. */
1036 internal = silc_calloc(1, sizeof(*internal));
1037 internal->completion = completion;
1038 internal->context = context;
1040 silc_client_request_authentication_method(client, conn,
1041 silc_get_auth_method_callback,
1045 /* Notifies application that failure packet was received. This is called
1046 if there is some protocol active in the client. The `protocol' is the
1047 protocol context. The `failure' is opaque pointer to the failure
1048 indication. Note, that the `failure' is protocol dependant and application
1049 must explicitly cast it to correct type. Usually `failure' is 32 bit
1050 failure type (see protocol specs for all protocol failure types). */
1052 void silc_failure(SilcClient client, SilcClientConnection conn,
1053 SilcProtocol protocol, void *failure)
1055 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1056 SilcSKEStatus status = (SilcSKEStatus)failure;
1058 if (status == SILC_SKE_STATUS_BAD_VERSION)
1059 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1060 SILCTXT_KE_BAD_VERSION);
1061 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1062 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1063 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1064 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1065 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1066 SILCTXT_KE_UNKNOWN_GROUP);
1067 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1068 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1069 SILCTXT_KE_UNKNOWN_CIPHER);
1070 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1071 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1072 SILCTXT_KE_UNKNOWN_PKCS);
1073 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1074 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1075 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1076 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1077 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1078 SILCTXT_KE_UNKNOWN_HMAC);
1079 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1080 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1081 SILCTXT_KE_INCORRECT_SIGNATURE);
1082 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1083 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1084 SILCTXT_KE_INVALID_COOKIE);
1087 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1088 uint32 err = (uint32)failure;
1090 if (err == SILC_AUTH_FAILED)
1091 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1092 SILCTXT_AUTH_FAILED);
1096 /* Asks whether the user would like to perform the key agreement protocol.
1097 This is called after we have received an key agreement packet or an
1098 reply to our key agreement packet. This returns TRUE if the user wants
1099 the library to perform the key agreement protocol and FALSE if it is not
1100 desired (application may start it later by calling the function
1101 silc_client_perform_key_agreement). */
1103 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1104 SilcClientEntry client_entry, char *hostname,
1106 SilcKeyAgreementCallback *completion,
1111 /* We will just display the info on the screen and return FALSE and user
1112 will have to start the key agreement with a command. */
1115 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1118 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1119 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1121 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1122 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1123 client_entry->nickname, hostname, portstr);
1131 /* SILC client operations */
1132 SilcClientOperations ops = {
1134 silc_channel_message,
1135 silc_private_message,
1141 silc_get_auth_method,
1142 silc_verify_public_key,
1143 silc_ask_passphrase,