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;
340 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
341 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
343 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
346 silc_say_error("%s: %s", tmp,
347 silc_client_command_status_message(status));
349 silc_say_error("%s", silc_client_command_status_message(status));
356 (void)va_arg(vp, SilcClientEntry);
357 nickname = va_arg(vp, char *);
358 username = va_arg(vp, char *);
359 realname = va_arg(vp, char *);
360 channels = va_arg(vp, SilcBuffer);
361 mode = va_arg(vp, uint32);
362 idle = va_arg(vp, uint32);
364 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
365 SILCTXT_WHOIS_USERINFO, nickname, username,
369 SilcDList list = silc_channel_payload_parse_list(channels);
371 SilcChannelPayload entry;
372 memset(buf, 0, sizeof(buf));
373 silc_dlist_start(list);
374 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
375 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
377 char *name = silc_channel_get_name(entry, &name_len);
380 strncat(buf, m, strlen(m));
381 strncat(buf, name, name_len);
382 strncat(buf, " ", 1);
386 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
387 SILCTXT_WHOIS_CHANNELS, buf);
388 silc_channel_payload_list_free(list);
393 memset(buf, 0, sizeof(buf));
395 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
396 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
397 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
399 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
400 "SILC Operator " : "[Unknown mode] ");
402 if (mode & SILC_UMODE_GONE)
405 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
406 SILCTXT_WHOIS_MODES, buf);
409 if (idle && nickname) {
410 memset(buf, 0, sizeof(buf));
411 snprintf(buf, sizeof(buf) - 1, "%lu %s",
412 idle > 60 ? (idle / 60) : idle,
413 idle > 60 ? "minutes" : "seconds");
415 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
416 SILCTXT_WHOIS_IDLE, buf);
421 case SILC_COMMAND_WHOWAS:
423 char *nickname, *username, *realname;
425 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
426 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
428 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
431 silc_say_error("%s: %s", tmp,
432 silc_client_command_status_message(status));
434 silc_say_error("%s", silc_client_command_status_message(status));
441 (void)va_arg(vp, SilcClientEntry);
442 nickname = va_arg(vp, char *);
443 username = va_arg(vp, char *);
444 realname = va_arg(vp, char *);
446 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
447 SILCTXT_WHOWAS_USERINFO, nickname, username,
448 realname ? realname : "");
452 case SILC_COMMAND_INVITE:
454 SilcChannelEntry channel;
456 SilcArgumentPayload args;
462 channel = va_arg(vp, SilcChannelEntry);
463 invite_list = va_arg(vp, char *);
465 args = silc_command_get_args(cmd_payload);
467 argc = silc_argument_get_arg_num(args);
470 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
471 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
474 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
475 SILCTXT_CHANNEL_NO_INVITE_LIST,
476 channel->channel_name);
480 case SILC_COMMAND_JOIN:
482 char *channel, *mode, *topic;
484 SilcChannelEntry channel_entry;
485 SilcBuffer client_id_list;
491 channel = va_arg(vp, char *);
492 channel_entry = va_arg(vp, SilcChannelEntry);
493 modei = va_arg(vp, uint32);
494 (void)va_arg(vp, uint32);
495 (void)va_arg(vp, unsigned char *);
496 (void)va_arg(vp, unsigned char *);
497 (void)va_arg(vp, unsigned char *);
498 topic = va_arg(vp, char *);
499 (void)va_arg(vp, unsigned char *);
500 list_count = va_arg(vp, uint32);
501 client_id_list = va_arg(vp, SilcBuffer);
503 chanrec = silc_channel_find(server, channel);
505 chanrec = silc_channel_create(server, channel, TRUE);
508 g_free_not_null(chanrec->topic);
509 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
510 signal_emit("channel topic changed", 1, chanrec);
513 mode = silc_client_chmode(modei,
514 channel_entry->channel_key->cipher->name,
515 channel_entry->hmac->hmac->name);
516 g_free_not_null(chanrec->mode);
517 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
518 signal_emit("channel mode changed", 1, chanrec);
520 /* Resolve the client information */
521 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
522 silc_client_join_get_users,
527 case SILC_COMMAND_NICK:
529 SilcClientEntry client = va_arg(vp, SilcClientEntry);
535 old = g_strdup(server->nick);
536 server_change_nick(SERVER(server), client->nickname);
537 nicklist_rename_unique(SERVER(server),
538 server->conn->local_entry, server->nick,
539 client, client->nickname);
541 signal_emit("message own_nick", 4, server, server->nick, old, "");
546 case SILC_COMMAND_LIST:
555 (void)va_arg(vp, SilcChannelEntry);
556 name = va_arg(vp, char *);
557 topic = va_arg(vp, char *);
558 usercount = va_arg(vp, int);
560 if (status == SILC_STATUS_LIST_START ||
561 status == SILC_STATUS_OK)
562 printformat_module("fe-common/silc", server, NULL,
563 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
565 snprintf(users, sizeof(users) - 1, "%d", usercount);
566 printformat_module("fe-common/silc", server, NULL,
567 MSGLEVEL_CRAP, SILCTXT_LIST,
568 name, users, topic ? topic : "");
572 case SILC_COMMAND_UMODE:
579 mode = va_arg(vp, uint32);
581 if (mode & SILC_UMODE_SERVER_OPERATOR)
582 printformat_module("fe-common/silc", server, NULL,
583 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
585 if (mode & SILC_UMODE_ROUTER_OPERATOR)
586 printformat_module("fe-common/silc", server, NULL,
587 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
591 case SILC_COMMAND_OPER:
595 printformat_module("fe-common/silc", server, NULL,
596 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
599 case SILC_COMMAND_SILCOPER:
603 printformat_module("fe-common/silc", server, NULL,
604 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
607 case SILC_COMMAND_USERS:
609 SilcChannelEntry channel;
615 channel = va_arg(vp, SilcChannelEntry);
617 printformat_module("fe-common/silc", server, channel->channel_name,
618 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
619 channel->channel_name);
621 silc_list_start(channel->clients);
622 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
623 SilcClientEntry e = chu->client;
626 memset(stat, 0, sizeof(stat));
627 mode = silc_client_chumode_char(chu->mode);
628 if (e->mode & SILC_UMODE_GONE)
635 printformat_module("fe-common/silc", server, channel->channel_name,
636 MSGLEVEL_CRAP, SILCTXT_USERS,
637 e->nickname, stat, e->username,
638 e->realname ? e->realname : "");
645 case SILC_COMMAND_BAN:
647 SilcChannelEntry channel;
653 channel = va_arg(vp, SilcChannelEntry);
654 ban_list = va_arg(vp, char *);
657 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
658 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
661 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
662 SILCTXT_CHANNEL_NO_BAN_LIST,
663 channel->channel_name);
667 case SILC_COMMAND_GETKEY:
671 SilcPublicKey public_key;
678 id_type = va_arg(vp, uint32);
679 entry = va_arg(vp, void *);
680 public_key = va_arg(vp, SilcPublicKey);
683 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
685 silc_verify_public_key_internal(client, conn,
686 (id_type == SILC_ID_CLIENT ?
687 SILC_SOCKET_TYPE_CLIENT :
688 SILC_SOCKET_TYPE_SERVER),
689 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
693 printformat_module("fe-common/silc", server, NULL,
694 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
699 case SILC_COMMAND_TOPIC:
701 SilcChannelEntry channel;
707 channel = va_arg(vp, SilcChannelEntry);
708 topic = va_arg(vp, char *);
711 chanrec = silc_channel_find_entry(server, channel);
713 g_free_not_null(chanrec->topic);
714 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
715 signal_emit("channel topic changed", 1, chanrec);
717 printformat_module("fe-common/silc", server, channel->channel_name,
718 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
719 channel->channel_name, topic);
721 printformat_module("fe-common/silc", server, channel->channel_name,
722 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
723 channel->channel_name);
733 /* Internal routine to verify public key. If the `completion' is provided
734 it will be called to indicate whether public was verified or not. */
738 SilcClientConnection conn;
743 SilcSKEPKType pk_type;
744 SilcVerifyPublicKey completion;
748 static void verify_public_key_completion(const char *line, void *context)
750 PublicKeyVerify verify = (PublicKeyVerify)context;
752 if (line[0] == 'Y' || line[0] == 'y') {
753 /* Call the completion */
754 if (verify->completion)
755 verify->completion(TRUE, verify->context);
757 /* Save the key for future checking */
758 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
759 verify->pk_len, SILC_PKCS_FILE_PEM);
761 /* Call the completion */
762 if (verify->completion)
763 verify->completion(FALSE, verify->context);
765 printformat_module("fe-common/silc", NULL, NULL,
766 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
769 silc_free(verify->filename);
770 silc_free(verify->entity);
771 silc_free(verify->pk);
776 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
777 SilcSocketType conn_type, unsigned char *pk,
778 uint32 pk_len, SilcSKEPKType pk_type,
779 SilcVerifyPublicKey completion, void *context)
782 char file[256], filename[256], *fingerprint, *format;
785 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
786 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
787 "server" : "client");
788 PublicKeyVerify verify;
790 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
791 printformat_module("fe-common/silc", NULL, NULL,
792 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
795 completion(FALSE, context);
799 pw = getpwuid(getuid());
802 completion(FALSE, context);
806 memset(filename, 0, sizeof(filename));
807 memset(file, 0, sizeof(file));
809 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
810 conn_type == SILC_SOCKET_TYPE_ROUTER) {
811 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
812 conn->sock->hostname, conn->sock->port);
813 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
814 pw->pw_dir, entity, file);
816 /* Replace all whitespaces with `_'. */
817 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
818 for (i = 0; i < strlen(fingerprint); i++)
819 if (fingerprint[i] == ' ')
820 fingerprint[i] = '_';
822 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
823 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
824 pw->pw_dir, entity, file);
825 silc_free(fingerprint);
828 /* Take fingerprint of the public key */
829 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
831 verify = silc_calloc(1, sizeof(*verify));
832 verify->client = client;
834 verify->filename = strdup(filename);
835 verify->entity = strdup(entity);
836 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
837 memcpy(verify->pk, pk, pk_len);
838 verify->pk_len = pk_len;
839 verify->pk_type = pk_type;
840 verify->completion = completion;
841 verify->context = context;
843 /* Check whether this key already exists */
844 if (stat(filename, &st) < 0) {
845 /* Key does not exist, ask user to verify the key and save it */
847 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
848 SILCTXT_PUBKEY_RECEIVED, entity);
849 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
850 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
851 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
852 SILCTXT_PUBKEY_ACCEPT);
853 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
856 silc_free(fingerprint);
859 /* The key already exists, verify it. */
860 SilcPublicKey public_key;
861 unsigned char *encpk;
864 /* Load the key file */
865 if (!silc_pkcs_load_public_key(filename, &public_key,
867 if (!silc_pkcs_load_public_key(filename, &public_key,
868 SILC_PKCS_FILE_BIN)) {
869 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
870 SILCTXT_PUBKEY_RECEIVED, entity);
871 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
872 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
873 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
874 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
875 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
876 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
877 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
880 silc_free(fingerprint);
884 /* Encode the key data */
885 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
887 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
888 SILCTXT_PUBKEY_RECEIVED, entity);
889 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
890 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
891 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
892 SILCTXT_PUBKEY_MALFORMED, entity);
893 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
894 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
895 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
898 silc_free(fingerprint);
902 /* Compare the keys */
903 if (memcmp(encpk, pk, encpk_len)) {
904 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
905 SILCTXT_PUBKEY_RECEIVED, entity);
906 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
907 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
908 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
909 SILCTXT_PUBKEY_NO_MATCH, entity);
910 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
911 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
912 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
913 SILCTXT_PUBKEY_MITM_ATTACK, entity);
915 /* Ask user to verify the key and save it */
916 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
917 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
918 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
921 silc_free(fingerprint);
925 /* Local copy matched */
927 completion(TRUE, context);
928 silc_free(fingerprint);
932 /* Verifies received public key. The `conn_type' indicates which entity
933 (server, client etc.) has sent the public key. If user decides to trust
934 the key may be saved as trusted public key for later use. The
935 `completion' must be called after the public key has been verified. */
938 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
939 SilcSocketType conn_type, unsigned char *pk,
940 uint32 pk_len, SilcSKEPKType pk_type,
941 SilcVerifyPublicKey completion, void *context)
943 silc_verify_public_key_internal(client, conn, conn_type, pk,
945 completion, context);
948 /* Asks passphrase from user on the input line. */
951 SilcAskPassphrase completion;
955 void ask_passphrase_completion(const char *passphrase, void *context)
957 AskPassphrase p = (AskPassphrase)context;
958 p->completion((unsigned char *)passphrase,
959 passphrase ? strlen(passphrase) : 0, p->context);
963 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
964 SilcAskPassphrase completion, void *context)
966 AskPassphrase p = silc_calloc(1, sizeof(*p));
967 p->completion = completion;
968 p->context = context;
970 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
971 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
975 SilcGetAuthMeth completion;
977 } *InternalGetAuthMethod;
979 /* Callback called when we've received the authentication method information
980 from the server after we've requested it. This will get the authentication
981 data from the user if needed. */
983 static void silc_get_auth_method_callback(SilcClient client,
984 SilcClientConnection conn,
985 SilcAuthMethod auth_meth,
988 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
992 /* No authentication required. */
993 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
995 case SILC_AUTH_PASSWORD:
996 /* Do not ask the passphrase from user, the library will ask it if
997 we do not provide it here. */
998 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1000 case SILC_AUTH_PUBLIC_KEY:
1001 /* Do not get the authentication data now, the library will generate
1002 it using our default key, if we do not provide it here. */
1003 /* XXX In the future when we support multiple local keys and multiple
1004 local certificates we will need to ask from user which one to use. */
1005 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1009 silc_free(internal);
1012 /* Find authentication method and authentication data by hostname and
1013 port. The hostname may be IP address as well. The found authentication
1014 method and authentication data is returned to `auth_meth', `auth_data'
1015 and `auth_data_len'. The function returns TRUE if authentication method
1016 is found and FALSE if not. `conn' may be NULL. */
1018 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1019 char *hostname, uint16 port,
1020 SilcGetAuthMeth completion, void *context)
1022 InternalGetAuthMethod internal;
1024 /* XXX must resolve from configuration whether this connection has
1025 any specific authentication data */
1027 /* If we do not have this connection configured by the user in a
1028 configuration file then resolve the authentication method from the
1029 server for this session. */
1030 internal = silc_calloc(1, sizeof(*internal));
1031 internal->completion = completion;
1032 internal->context = context;
1034 silc_client_request_authentication_method(client, conn,
1035 silc_get_auth_method_callback,
1039 /* Notifies application that failure packet was received. This is called
1040 if there is some protocol active in the client. The `protocol' is the
1041 protocol context. The `failure' is opaque pointer to the failure
1042 indication. Note, that the `failure' is protocol dependant and application
1043 must explicitly cast it to correct type. Usually `failure' is 32 bit
1044 failure type (see protocol specs for all protocol failure types). */
1046 void silc_failure(SilcClient client, SilcClientConnection conn,
1047 SilcProtocol protocol, void *failure)
1049 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1050 SilcSKEStatus status = (SilcSKEStatus)failure;
1052 if (status == SILC_SKE_STATUS_BAD_VERSION)
1053 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1054 SILCTXT_KE_BAD_VERSION);
1055 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1056 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1057 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1058 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1059 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1060 SILCTXT_KE_UNKNOWN_GROUP);
1061 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1062 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1063 SILCTXT_KE_UNKNOWN_CIPHER);
1064 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1065 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1066 SILCTXT_KE_UNKNOWN_PKCS);
1067 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1068 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1069 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1070 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1071 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1072 SILCTXT_KE_UNKNOWN_HMAC);
1073 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1074 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1075 SILCTXT_KE_INCORRECT_SIGNATURE);
1076 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1077 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1078 SILCTXT_KE_INVALID_COOKIE);
1081 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1082 uint32 err = (uint32)failure;
1084 if (err == SILC_AUTH_FAILED)
1085 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1086 SILCTXT_AUTH_FAILED);
1090 /* Asks whether the user would like to perform the key agreement protocol.
1091 This is called after we have received an key agreement packet or an
1092 reply to our key agreement packet. This returns TRUE if the user wants
1093 the library to perform the key agreement protocol and FALSE if it is not
1094 desired (application may start it later by calling the function
1095 silc_client_perform_key_agreement). */
1097 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1098 SilcClientEntry client_entry, char *hostname,
1100 SilcKeyAgreementCallback *completion,
1105 /* We will just display the info on the screen and return FALSE and user
1106 will have to start the key agreement with a command. */
1109 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1112 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1113 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1115 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1116 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1117 client_entry->nickname, hostname, portstr);
1125 /* SILC client operations */
1126 SilcClientOperations ops = {
1128 silc_channel_message,
1129 silc_private_message,
1135 silc_get_auth_method,
1136 silc_verify_public_key,
1137 silc_ask_passphrase,