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,
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. */
80 silc_channel_message(SilcClient client, SilcClientConnection conn,
81 SilcClientEntry sender, SilcChannelEntry channel,
82 SilcMessageFlags flags, char *msg)
84 SILC_SERVER_REC *server;
86 SILC_CHANNEL_REC *chanrec;
88 server = conn == NULL ? NULL : conn->context;
89 chanrec = silc_channel_find_entry(server, channel);
91 nick = silc_nicklist_find(chanrec, sender);
93 if (flags & SILC_MESSAGE_FLAG_ACTION)
94 printformat_module("fe-common/silc", server, channel->channel_name,
95 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, msg);
96 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
97 printformat_module("fe-common/silc", server, channel->channel_name,
98 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, msg);
100 signal_emit("message public", 6, server, msg,
101 nick == NULL ? "[<unknown>]" : nick->nick,
102 nick == NULL ? NULL : nick->host,
103 chanrec->name, nick);
106 /* Private message to the client. The `sender' is the nickname of the
107 sender received in the packet. */
110 silc_private_message(SilcClient client, SilcClientConnection conn,
111 SilcClientEntry sender, SilcMessageFlags flags,
114 SILC_SERVER_REC *server;
116 server = conn == NULL ? NULL : conn->context;
117 signal_emit("message private", 4, server, msg,
118 sender->nickname ? sender->nickname : "[<unknown>]",
119 sender->username ? sender->username : NULL);
122 /* Notify message to the client. The notify arguments are sent in the
123 same order as servers sends them. The arguments are same as received
124 from the server except for ID's. If ID is received application receives
125 the corresponding entry to the ID. For example, if Client ID is received
126 application receives SilcClientEntry. Also, if the notify type is
127 for channel the channel entry is sent to application (even if server
128 does not send it). */
135 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
136 static NOTIFY_REC notifies[] = {
137 { SILC_NOTIFY_TYPE_NONE, NULL },
138 { SILC_NOTIFY_TYPE_INVITE, "invite" },
139 { SILC_NOTIFY_TYPE_JOIN, "join" },
140 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
141 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
142 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
143 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
144 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
145 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
146 { SILC_NOTIFY_TYPE_MOTD, "motd" },
147 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
148 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
149 { SILC_NOTIFY_TYPE_KICKED, "kick" },
150 { SILC_NOTIFY_TYPE_KILLED, "kill" },
151 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
152 { SILC_NOTIFY_TYPE_BAN, "ban" },
155 void silc_notify(SilcClient client, SilcClientConnection conn,
156 SilcNotifyType type, ...)
158 SILC_SERVER_REC *server;
161 server = conn == NULL ? NULL : conn->context;
164 if (type == SILC_NOTIFY_TYPE_NONE) {
165 /* Some generic notice from server */
166 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
167 } else if (type < MAX_NOTIFY) {
168 /* Send signal about the notify event */
170 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
171 signal_emit(signal, 2, server, va);
174 printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
180 /* Called to indicate that connection was either successfully established
181 or connecting failed. This is also the first time application receives
182 the SilcClientConnection objecet which it should save somewhere. */
185 silc_connect(SilcClient client, SilcClientConnection conn, int success)
187 SILC_SERVER_REC *server = conn->context;
190 server->connected = TRUE;
191 signal_emit("event connected", 1, server);
193 server->connection_lost = TRUE;
194 server->conn->context = NULL;
195 server_disconnect(SERVER(server));
199 /* Called to indicate that connection was disconnected to the server. */
202 silc_disconnect(SilcClient client, SilcClientConnection conn)
204 SILC_SERVER_REC *server = conn->context;
206 server->conn->context = NULL;
208 server->connection_lost = TRUE;
209 server_disconnect(SERVER(server));
212 /* Command handler. This function is called always in the command function.
213 If error occurs it will be called as well. `conn' is the associated
214 client connection. `cmd_context' is the command context that was
215 originally sent to the command. `success' is FALSE if error occured
216 during command. `command' is the command being processed. It must be
217 noted that this is not reply from server. This is merely called just
218 after application has called the command. Just to tell application
219 that the command really was processed. */
222 silc_command(SilcClient client, SilcClientConnection conn,
223 SilcClientCommandContext cmd_context, int success,
228 /* Client info resolving callback when JOIN command reply is received.
229 This will cache all users on the channel. */
231 void silc_client_join_get_users(SilcClient client,
232 SilcClientConnection conn,
233 SilcClientEntry *clients,
234 uint32 clients_count,
237 SilcChannelEntry channel = (SilcChannelEntry)context;
239 SILC_SERVER_REC *server = conn->context;
240 SILC_CHANNEL_REC *chanrec;
241 SilcClientEntry founder = NULL;
247 chanrec = silc_channel_find(server, channel->channel_name);
251 silc_list_start(channel->clients);
252 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
253 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
254 founder = chu->client;
255 silc_nicklist_insert(chanrec, chu, FALSE);
258 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
259 nicklist_set_own(CHANNEL(chanrec), ownnick);
260 signal_emit("channel joined", 1, chanrec);
263 printformat_module("fe-common/silc", server, channel->channel_name,
264 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
265 channel->channel_name, chanrec->topic);
267 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
270 if (founder == conn->local_entry)
271 printformat_module("fe-common/silc",
272 server, channel->channel_name, MSGLEVEL_CRAP,
273 SILCTXT_CHANNEL_FOUNDER_YOU,
274 channel->channel_name);
276 printformat_module("fe-common/silc",
277 server, channel->channel_name, MSGLEVEL_CRAP,
278 SILCTXT_CHANNEL_FOUNDER,
279 channel->channel_name, founder->nickname);
283 /* Command reply handler. This function is called always in the command reply
284 function. If error occurs it will be called as well. Normal scenario
285 is that it will be called after the received command data has been parsed
286 and processed. The function is used to pass the received command data to
289 `conn' is the associated client connection. `cmd_payload' is the command
290 payload data received from server and it can be ignored. It is provided
291 if the application would like to re-parse the received command data,
292 however, it must be noted that the data is parsed already by the library
293 thus the payload can be ignored. `success' is FALSE if error occured.
294 In this case arguments are not sent to the application. `command' is the
295 command reply being processed. The function has variable argument list
296 and each command defines the number and type of arguments it passes to the
297 application (on error they are not sent). */
300 silc_command_reply(SilcClient client, SilcClientConnection conn,
301 SilcCommandPayload cmd_payload, int success,
302 SilcCommand command, SilcCommandStatus status, ...)
305 SILC_SERVER_REC *server = conn->context;
306 SILC_CHANNEL_REC *chanrec;
309 va_start(vp, status);
312 case SILC_COMMAND_WHOIS:
314 char buf[1024], *nickname, *username, *realname;
318 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
319 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
321 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
324 silc_say_error("%s: %s", tmp,
325 silc_client_command_status_message(status));
327 silc_say_error("%s", silc_client_command_status_message(status));
334 (void)va_arg(vp, SilcClientEntry);
335 nickname = va_arg(vp, char *);
336 username = va_arg(vp, char *);
337 realname = va_arg(vp, char *);
338 channels = va_arg(vp, SilcBuffer);
339 mode = va_arg(vp, uint32);
340 idle = va_arg(vp, uint32);
342 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
343 SILCTXT_WHOIS_USERINFO, nickname, username,
347 SilcDList list = silc_channel_payload_parse_list(channels);
349 SilcChannelPayload entry;
350 memset(buf, 0, sizeof(buf));
351 silc_dlist_start(list);
352 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
353 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
355 char *name = silc_channel_get_name(entry, &name_len);
358 strncat(buf, m, strlen(m));
359 strncat(buf, name, name_len);
360 strncat(buf, " ", 1);
364 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
365 SILCTXT_WHOIS_CHANNELS, buf);
366 silc_channel_payload_list_free(list);
371 memset(buf, 0, sizeof(buf));
373 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
374 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
375 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
377 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
378 "SILC Operator " : "[Unknown mode] ");
380 if (mode & SILC_UMODE_GONE)
383 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
384 SILCTXT_WHOIS_MODES, buf);
387 if (idle && nickname) {
388 memset(buf, 0, sizeof(buf));
389 snprintf(buf, sizeof(buf) - 1, "%lu %s",
390 idle > 60 ? (idle / 60) : idle,
391 idle > 60 ? "minutes" : "seconds");
393 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
394 SILCTXT_WHOIS_IDLE, buf);
399 case SILC_COMMAND_WHOWAS:
401 char *nickname, *username, *realname;
403 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
404 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
406 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
409 silc_say_error("%s: %s", tmp,
410 silc_client_command_status_message(status));
412 silc_say_error("%s", silc_client_command_status_message(status));
419 (void)va_arg(vp, SilcClientEntry);
420 nickname = va_arg(vp, char *);
421 username = va_arg(vp, char *);
422 realname = va_arg(vp, char *);
424 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
425 SILCTXT_WHOWAS_USERINFO, nickname, username,
426 realname ? realname : "");
430 case SILC_COMMAND_INVITE:
432 SilcChannelEntry channel;
438 /* XXX should use irssi routines */
440 channel = va_arg(vp, SilcChannelEntry);
441 invite_list = va_arg(vp, char *);
444 silc_say(client, conn, "%s invite list: %s", channel->channel_name,
447 silc_say(client, conn, "%s invite list not set",
448 channel->channel_name);
452 case SILC_COMMAND_JOIN:
454 char *channel, *mode, *topic;
456 SilcChannelEntry channel_entry;
457 SilcBuffer client_id_list;
460 channel = va_arg(vp, char *);
461 channel_entry = va_arg(vp, SilcChannelEntry);
462 modei = va_arg(vp, uint32);
463 (void)va_arg(vp, uint32);
464 (void)va_arg(vp, unsigned char *);
465 (void)va_arg(vp, unsigned char *);
466 (void)va_arg(vp, unsigned char *);
467 topic = va_arg(vp, char *);
468 (void)va_arg(vp, unsigned char *);
469 list_count = va_arg(vp, uint32);
470 client_id_list = va_arg(vp, SilcBuffer);
475 chanrec = silc_channel_find(server, channel);
476 if (chanrec != NULL && !success)
477 channel_destroy(CHANNEL(chanrec));
478 else if (chanrec == NULL && success)
479 chanrec = silc_channel_create(server, channel, TRUE);
482 g_free_not_null(chanrec->topic);
483 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
484 signal_emit("channel topic changed", 1, chanrec);
487 mode = silc_client_chmode(modei, channel_entry);
488 g_free_not_null(chanrec->mode);
489 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
490 signal_emit("channel mode changed", 1, chanrec);
492 /* Resolve the client information */
493 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
494 silc_client_join_get_users,
499 case SILC_COMMAND_NICK:
501 SilcClientEntry client = va_arg(vp, SilcClientEntry);
507 old = g_strdup(server->nick);
508 server_change_nick(SERVER(server), client->nickname);
509 nicklist_rename_unique(SERVER(server),
510 server->conn->local_entry, server->nick,
511 client, client->nickname);
513 signal_emit("message own_nick", 4, server, server->nick, old, "");
518 case SILC_COMMAND_LIST:
522 unsigned char buf[256], tmp[16];
528 /* XXX should use irssi routines */
530 (void)va_arg(vp, SilcChannelEntry);
531 name = va_arg(vp, char *);
532 topic = va_arg(vp, char *);
533 usercount = va_arg(vp, int);
535 if (status == SILC_STATUS_LIST_START ||
536 status == SILC_STATUS_OK)
537 silc_say(client, conn,
538 " Channel Users Topic");
540 memset(buf, 0, sizeof(buf));
541 strncat(buf, " ", 2);
543 strncat(buf, name, len > 40 ? 40 : len);
545 for (i = 0; i < 40 - len; i++)
549 memset(tmp, 0, sizeof(tmp));
551 snprintf(tmp, sizeof(tmp), "%d", usercount);
556 for (i = 0; i < 10 - len; i++)
562 strncat(buf, topic, len);
565 silc_say(client, conn, "%s", buf);
569 case SILC_COMMAND_UMODE:
576 mode = va_arg(vp, uint32);
582 case SILC_COMMAND_OPER:
583 silc_say(client, conn, "You are now server operator");
586 case SILC_COMMAND_SILCOPER:
587 silc_say(client, conn, "You are now SILC operator");
590 case SILC_COMMAND_USERS:
592 SilcChannelEntry channel;
600 channel = va_arg(vp, SilcChannelEntry);
602 /* There are two ways to do this, either parse the list (that
603 the command_reply sends (just take it with va_arg()) or just
604 traverse the channel's client list. I'll do the latter. See
605 JOIN command reply for example for the list. */
607 silc_say(client, conn, "Users on %s", channel->channel_name);
609 line = silc_calloc(1024, sizeof(*line));
611 silc_list_start(channel->clients);
612 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
613 SilcClientEntry e = chu->client;
617 memset(line, 0, line_len);
619 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
621 line_len += strlen(e->nickname) + strlen(e->server) + 100;
622 line = silc_calloc(line_len, sizeof(*line));
625 memset(tmp, 0, sizeof(tmp));
626 m = silc_client_chumode_char(chu->mode);
628 strncat(line, " ", 1);
629 strncat(line, e->nickname, strlen(e->nickname));
630 strncat(line, e->server ? "@" : "", 1);
634 len1 = strlen(e->server);
635 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
639 memset(&line[29], 0, len1 - 29);
641 for (i = 0; i < 30 - len1 - 1; i++)
645 if (e->mode & SILC_UMODE_GONE)
649 strcat(tmp, m ? m : "");
650 strncat(line, tmp, strlen(tmp));
653 for (i = 0; i < 5 - strlen(tmp); i++)
656 strcat(line, e->username ? e->username : "");
658 silc_say(client, conn, "%s", line);
668 case SILC_COMMAND_BAN:
670 SilcChannelEntry channel;
676 /* XXX should use irssi routines */
678 channel = va_arg(vp, SilcChannelEntry);
679 ban_list = va_arg(vp, char *);
682 silc_say(client, conn, "%s ban list: %s", channel->channel_name,
685 silc_say(client, conn, "%s ban list not set", channel->channel_name);
689 case SILC_COMMAND_GETKEY:
693 SilcPublicKey public_key;
697 id_type = va_arg(vp, uint32);
698 entry = va_arg(vp, void *);
699 public_key = va_arg(vp, SilcPublicKey);
701 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
703 if (id_type == SILC_ID_CLIENT) {
704 silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
705 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
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);
741 /* Internal routine to verify public key. If the `completion' is provided
742 it will be called to indicate whether public was verified or not. */
746 SilcClientConnection conn;
751 SilcSKEPKType pk_type;
752 SilcVerifyPublicKey completion;
756 static void verify_public_key_completion(const char *line, void *context)
758 PublicKeyVerify verify = (PublicKeyVerify)context;
760 if (line[0] == 'Y' || line[0] == 'y') {
761 /* Call the completion */
762 if (verify->completion)
763 verify->completion(TRUE, verify->context);
765 /* Save the key for future checking */
766 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
767 verify->pk_len, SILC_PKCS_FILE_PEM);
769 /* Call the completion */
770 if (verify->completion)
771 verify->completion(FALSE, verify->context);
773 silc_say(verify->client,
774 verify->conn, "Will not accept the %s key", verify->entity);
777 silc_free(verify->filename);
778 silc_free(verify->entity);
783 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
784 SilcSocketType conn_type, unsigned char *pk,
785 uint32 pk_len, SilcSKEPKType pk_type,
786 SilcVerifyPublicKey completion, void *context)
789 char file[256], filename[256], *fingerprint;
792 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
793 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
794 "server" : "client");
795 PublicKeyVerify verify;
797 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
798 silc_say(client, conn, "We don't support %s public key type %d",
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);
843 verify->pk_len = pk_len;
844 verify->pk_type = pk_type;
845 verify->completion = completion;
846 verify->context = context;
848 /* Check whether this key already exists */
849 if (stat(filename, &st) < 0) {
850 /* Key does not exist, ask user to verify the key and save it */
852 silc_say(client, conn, "Received %s public key", entity);
853 silc_say(client, conn, "Fingerprint for the %s key is", entity);
854 silc_say(client, conn, "%s", fingerprint);
856 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
857 "Would you like to accept the key (y/n)? ", 0,
859 silc_free(fingerprint);
862 /* The key already exists, verify it. */
863 SilcPublicKey public_key;
864 unsigned char *encpk;
867 /* Load the key file */
868 if (!silc_pkcs_load_public_key(filename, &public_key,
870 if (!silc_pkcs_load_public_key(filename, &public_key,
871 SILC_PKCS_FILE_BIN)) {
872 silc_say(client, conn, "Received %s public key", entity);
873 silc_say(client, conn, "Fingerprint for the %s key is", entity);
874 silc_say(client, conn, "%s", fingerprint);
875 silc_say(client, conn, "Could not load your local copy of the %s key",
877 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
878 "Would you like to accept the key "
881 silc_free(fingerprint);
885 /* Encode the key data */
886 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
888 silc_say(client, conn, "Received %s public key", entity);
889 silc_say(client, conn, "Fingerprint for the %s key is", entity);
890 silc_say(client, conn, "%s", fingerprint);
891 silc_say(client, conn, "Your local copy of the %s key is malformed",
893 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
894 "Would you like to accept the key "
897 silc_free(fingerprint);
901 /* Compare the keys */
902 if (memcmp(encpk, pk, encpk_len)) {
903 silc_say(client, conn, "Received %s public key", entity);
904 silc_say(client, conn, "Fingerprint for the %s key is", entity);
905 silc_say(client, conn, "%s", fingerprint);
906 silc_say(client, conn, "%s key does not match with your local copy",
908 silc_say(client, conn,
909 "It is possible that the key has expired or changed");
910 silc_say(client, conn, "It is also possible that some one is performing "
911 "man-in-the-middle attack");
913 /* Ask user to verify the key and save it */
914 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
915 "Would you like to accept the key "
918 silc_free(fingerprint);
922 /* Local copy matched */
924 completion(TRUE, context);
925 silc_free(fingerprint);
929 /* Verifies received public key. The `conn_type' indicates which entity
930 (server, client etc.) has sent the public key. If user decides to trust
931 the key may be saved as trusted public key for later use. The
932 `completion' must be called after the public key has been verified. */
935 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
936 SilcSocketType conn_type, unsigned char *pk,
937 uint32 pk_len, SilcSKEPKType pk_type,
938 SilcVerifyPublicKey completion, void *context)
940 silc_verify_public_key_internal(client, conn, conn_type, pk,
942 completion, context);
945 /* Asks passphrase from user on the input line. */
948 SilcAskPassphrase completion;
952 void ask_passphrase_completion(const char *passphrase, void *context)
954 AskPassphrase p = (AskPassphrase)context;
955 p->completion((unsigned char *)passphrase,
956 passphrase ? strlen(passphrase) : 0, p->context);
960 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
961 SilcAskPassphrase completion, void *context)
963 AskPassphrase p = silc_calloc(1, sizeof(*p));
964 p->completion = completion;
965 p->context = context;
967 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
968 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
971 /* Find authentication method and authentication data by hostname and
972 port. The hostname may be IP address as well. The found authentication
973 method and authentication data is returned to `auth_meth', `auth_data'
974 and `auth_data_len'. The function returns TRUE if authentication method
975 is found and FALSE if not. `conn' may be NULL. */
977 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
978 char *hostname, uint16 port,
979 SilcProtocolAuthMeth *auth_meth,
980 unsigned char **auth_data,
981 uint32 *auth_data_len)
984 /* XXX must resolve from configuration whether this connection has
985 any specific authentication data */
987 *auth_meth = SILC_AUTH_NONE;
994 /* Notifies application that failure packet was received. This is called
995 if there is some protocol active in the client. The `protocol' is the
996 protocol context. The `failure' is opaque pointer to the failure
997 indication. Note, that the `failure' is protocol dependant and application
998 must explicitly cast it to correct type. Usually `failure' is 32 bit
999 failure type (see protocol specs for all protocol failure types). */
1002 silc_failure(SilcClient client, SilcClientConnection conn,
1003 SilcProtocol protocol, void *failure)
1005 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1006 SilcSKEStatus status = (SilcSKEStatus)failure;
1008 if (status == SILC_SKE_STATUS_BAD_VERSION)
1009 silc_say_error("You are running incompatible client version (it may be "
1010 "too old or too new)");
1011 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1012 silc_say_error("Server does not support your public key type");
1013 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1014 silc_say_error("Server does not support one of your proposed KE group");
1015 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1016 silc_say_error("Server does not support one of your proposed cipher");
1017 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1018 silc_say_error("Server does not support one of your proposed PKCS");
1019 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1020 silc_say_error("Server does not support one of your proposed "
1022 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1023 silc_say_error("Server does not support one of your proposed HMAC");
1024 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1025 silc_say_error("Incorrect signature");
1028 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1029 uint32 err = (uint32)failure;
1031 if (err == SILC_AUTH_FAILED)
1032 silc_say(client, conn, "Authentication failed");
1036 /* Asks whether the user would like to perform the key agreement protocol.
1037 This is called after we have received an key agreement packet or an
1038 reply to our key agreement packet. This returns TRUE if the user wants
1039 the library to perform the key agreement protocol and FALSE if it is not
1040 desired (application may start it later by calling the function
1041 silc_client_perform_key_agreement). */
1043 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1044 SilcClientEntry client_entry, char *hostname,
1046 SilcKeyAgreementCallback *completion,
1051 /* We will just display the info on the screen and return FALSE and user
1052 will have to start the key agreement with a command. */
1055 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1058 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1059 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1061 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1062 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1063 client_entry->nickname, hostname, portstr);
1071 /* SILC client operations */
1072 SilcClientOperations ops = {
1074 silc_channel_message,
1075 silc_private_message,
1081 silc_get_auth_method,
1082 silc_verify_public_key,
1083 silc_ask_passphrase,