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 /* We didn't find client but it clearly exists, add it. */
97 silc_list_start(channel->clients);
98 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
99 if (chu->client == sender) {
100 nick = silc_nicklist_insert(chanrec, chu, FALSE);
106 if (flags & SILC_MESSAGE_FLAG_ACTION)
107 printformat_module("fe-common/silc", server, channel->channel_name,
108 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
109 nick == NULL ? "[<unknown>]" : nick->nick, msg);
110 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
111 printformat_module("fe-common/silc", server, channel->channel_name,
112 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
113 nick == NULL ? "[<unknown>]" : nick->nick, msg);
115 signal_emit("message public", 6, server, msg,
116 nick == NULL ? "[<unknown>]" : nick->nick,
117 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
118 chanrec->name, nick);
121 /* Private message to the client. The `sender' is the nickname of the
122 sender received in the packet. */
124 void silc_private_message(SilcClient client, SilcClientConnection conn,
125 SilcClientEntry sender, SilcMessageFlags flags,
128 SILC_SERVER_REC *server;
131 server = conn == NULL ? NULL : conn->context;
132 memset(userhost, 0, sizeof(userhost));
133 if (sender->username)
134 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
135 sender->username, sender->hostname);
136 signal_emit("message private", 4, server, msg,
137 sender->nickname ? sender->nickname : "[<unknown>]",
138 sender->username ? userhost : NULL);
141 /* Notify message to the client. The notify arguments are sent in the
142 same order as servers sends them. The arguments are same as received
143 from the server except for ID's. If ID is received application receives
144 the corresponding entry to the ID. For example, if Client ID is received
145 application receives SilcClientEntry. Also, if the notify type is
146 for channel the channel entry is sent to application (even if server
147 does not send it). */
154 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
155 static NOTIFY_REC notifies[] = {
156 { SILC_NOTIFY_TYPE_NONE, NULL },
157 { SILC_NOTIFY_TYPE_INVITE, "invite" },
158 { SILC_NOTIFY_TYPE_JOIN, "join" },
159 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
160 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
161 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
162 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
163 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
164 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
165 { SILC_NOTIFY_TYPE_MOTD, "motd" },
166 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
167 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
168 { SILC_NOTIFY_TYPE_KICKED, "kick" },
169 { SILC_NOTIFY_TYPE_KILLED, "kill" },
170 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
171 { SILC_NOTIFY_TYPE_BAN, "ban" },
174 void silc_notify(SilcClient client, SilcClientConnection conn,
175 SilcNotifyType type, ...)
177 SILC_SERVER_REC *server;
180 server = conn == NULL ? NULL : conn->context;
183 if (type == SILC_NOTIFY_TYPE_NONE) {
184 /* Some generic notice from server */
185 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
186 } else if (type < MAX_NOTIFY) {
187 /* Send signal about the notify event */
189 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
190 signal_emit(signal, 2, server, va);
193 printformat_module("fe-common/silc", server, NULL,
194 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
200 /* Called to indicate that connection was either successfully established
201 or connecting failed. This is also the first time application receives
202 the SilcClientConnection objecet which it should save somewhere. */
204 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
206 SILC_SERVER_REC *server = conn->context;
208 if (!server && !success) {
209 silc_client_close_connection(client, NULL, conn);
214 server->connected = TRUE;
215 signal_emit("event connected", 1, server);
217 server->connection_lost = TRUE;
218 server->conn->context = NULL;
219 server_disconnect(SERVER(server));
223 /* Called to indicate that connection was disconnected to the server. */
225 void silc_disconnect(SilcClient client, SilcClientConnection conn)
227 SILC_SERVER_REC *server = conn->context;
230 nicklist_rename_unique(SERVER(server),
231 server->conn->local_entry, server->nick,
232 server->conn->local_entry,
233 silc_client->username);
234 silc_change_nick(server, silc_client->username);
237 server->conn->context = NULL;
239 server->connection_lost = TRUE;
240 server_disconnect(SERVER(server));
243 /* Command handler. This function is called always in the command function.
244 If error occurs it will be called as well. `conn' is the associated
245 client connection. `cmd_context' is the command context that was
246 originally sent to the command. `success' is FALSE if error occured
247 during command. `command' is the command being processed. It must be
248 noted that this is not reply from server. This is merely called just
249 after application has called the command. Just to tell application
250 that the command really was processed. */
252 void silc_command(SilcClient client, SilcClientConnection conn,
253 SilcClientCommandContext cmd_context, int success,
256 SILC_SERVER_REC *server = conn->context;
262 case SILC_COMMAND_INVITE:
263 printformat_module("fe-common/silc", server, NULL,
264 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
265 cmd_context->argv[2],
266 (cmd_context->argv[1][0] == '*' ?
267 (char *)conn->current_channel->channel_name :
268 (char *)cmd_context->argv[1]));
275 /* Client info resolving callback when JOIN command reply is received.
276 This will cache all users on the channel. */
278 static void silc_client_join_get_users(SilcClient client,
279 SilcClientConnection conn,
280 SilcClientEntry *clients,
281 uint32 clients_count,
284 SilcChannelEntry channel = (SilcChannelEntry)context;
286 SILC_SERVER_REC *server = conn->context;
287 SILC_CHANNEL_REC *chanrec;
288 SilcClientEntry founder = NULL;
294 chanrec = silc_channel_find(server, channel->channel_name);
298 silc_list_start(channel->clients);
299 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
300 if (!chu->client->nickname)
302 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
303 founder = chu->client;
304 silc_nicklist_insert(chanrec, chu, FALSE);
307 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
308 nicklist_set_own(CHANNEL(chanrec), ownnick);
309 signal_emit("channel joined", 1, chanrec);
312 printformat_module("fe-common/silc", server, channel->channel_name,
313 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
314 channel->channel_name, chanrec->topic);
316 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
319 if (founder == conn->local_entry)
320 printformat_module("fe-common/silc",
321 server, channel->channel_name, MSGLEVEL_CRAP,
322 SILCTXT_CHANNEL_FOUNDER_YOU,
323 channel->channel_name);
325 printformat_module("fe-common/silc",
326 server, channel->channel_name, MSGLEVEL_CRAP,
327 SILCTXT_CHANNEL_FOUNDER,
328 channel->channel_name, founder->nickname);
334 SilcClientConnection conn;
340 void silc_getkey_cb(bool success, void *context)
342 GetkeyContext getkey = (GetkeyContext)context;
343 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
344 char *name = (getkey->id_type == SILC_ID_CLIENT ?
345 ((SilcClientEntry)getkey->entry)->nickname :
346 ((SilcServerEntry)getkey->entry)->server_name);
349 printformat_module("fe-common/silc", NULL, NULL,
350 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
352 printformat_module("fe-common/silc", NULL, NULL,
353 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
356 silc_free(getkey->fingerprint);
360 /* Command reply handler. This function is called always in the command reply
361 function. If error occurs it will be called as well. Normal scenario
362 is that it will be called after the received command data has been parsed
363 and processed. The function is used to pass the received command data to
366 `conn' is the associated client connection. `cmd_payload' is the command
367 payload data received from server and it can be ignored. It is provided
368 if the application would like to re-parse the received command data,
369 however, it must be noted that the data is parsed already by the library
370 thus the payload can be ignored. `success' is FALSE if error occured.
371 In this case arguments are not sent to the application. `command' is the
372 command reply being processed. The function has variable argument list
373 and each command defines the number and type of arguments it passes to the
374 application (on error they are not sent). */
377 silc_command_reply(SilcClient client, SilcClientConnection conn,
378 SilcCommandPayload cmd_payload, int success,
379 SilcCommand command, SilcCommandStatus status, ...)
382 SILC_SERVER_REC *server = conn->context;
383 SILC_CHANNEL_REC *chanrec;
386 va_start(vp, status);
389 case SILC_COMMAND_WHOIS:
391 char buf[1024], *nickname, *username, *realname, *nick;
394 SilcClientEntry client_entry;
396 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
397 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
399 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
402 silc_say_error("%s: %s", tmp,
403 silc_client_command_status_message(status));
405 silc_say_error("%s", silc_client_command_status_message(status));
412 client_entry = va_arg(vp, SilcClientEntry);
413 nickname = va_arg(vp, char *);
414 username = va_arg(vp, char *);
415 realname = va_arg(vp, char *);
416 channels = va_arg(vp, SilcBuffer);
417 mode = va_arg(vp, uint32);
418 idle = va_arg(vp, uint32);
420 silc_parse_userfqdn(nickname, &nick, NULL);
421 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
422 SILCTXT_WHOIS_USERINFO, nickname,
423 client_entry->username, client_entry->hostname,
424 nick, client_entry->nickname);
425 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
426 SILCTXT_WHOIS_REALNAME, realname);
430 SilcDList list = silc_channel_payload_parse_list(channels);
432 SilcChannelPayload entry;
433 memset(buf, 0, sizeof(buf));
434 silc_dlist_start(list);
435 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
436 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
438 char *name = silc_channel_get_name(entry, &name_len);
441 strncat(buf, m, strlen(m));
442 strncat(buf, name, name_len);
443 strncat(buf, " ", 1);
447 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
448 SILCTXT_WHOIS_CHANNELS, buf);
449 silc_channel_payload_list_free(list);
454 memset(buf, 0, sizeof(buf));
456 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
457 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
458 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
460 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
461 "SILC Operator " : "[Unknown mode] ");
463 if (mode & SILC_UMODE_GONE)
466 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
467 SILCTXT_WHOIS_MODES, buf);
470 if (idle && nickname) {
471 memset(buf, 0, sizeof(buf));
472 snprintf(buf, sizeof(buf) - 1, "%lu %s",
473 idle > 60 ? (idle / 60) : idle,
474 idle > 60 ? "minutes" : "seconds");
476 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
477 SILCTXT_WHOIS_IDLE, buf);
482 case SILC_COMMAND_WHOWAS:
484 char *nickname, *username, *realname;
486 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
487 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
489 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
492 silc_say_error("%s: %s", tmp,
493 silc_client_command_status_message(status));
495 silc_say_error("%s", silc_client_command_status_message(status));
502 (void)va_arg(vp, SilcClientEntry);
503 nickname = va_arg(vp, char *);
504 username = va_arg(vp, char *);
505 realname = va_arg(vp, char *);
507 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
508 SILCTXT_WHOWAS_USERINFO, nickname, username,
509 realname ? realname : "");
513 case SILC_COMMAND_INVITE:
515 SilcChannelEntry channel;
517 SilcArgumentPayload args;
523 channel = va_arg(vp, SilcChannelEntry);
524 invite_list = va_arg(vp, char *);
526 args = silc_command_get_args(cmd_payload);
528 argc = silc_argument_get_arg_num(args);
531 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
532 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
535 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
536 SILCTXT_CHANNEL_NO_INVITE_LIST,
537 channel->channel_name);
541 case SILC_COMMAND_JOIN:
543 char *channel, *mode, *topic;
545 SilcChannelEntry channel_entry;
546 SilcBuffer client_id_list;
552 channel = va_arg(vp, char *);
553 channel_entry = va_arg(vp, SilcChannelEntry);
554 modei = va_arg(vp, uint32);
555 (void)va_arg(vp, uint32);
556 (void)va_arg(vp, unsigned char *);
557 (void)va_arg(vp, unsigned char *);
558 (void)va_arg(vp, unsigned char *);
559 topic = va_arg(vp, char *);
560 (void)va_arg(vp, unsigned char *);
561 list_count = va_arg(vp, uint32);
562 client_id_list = va_arg(vp, SilcBuffer);
564 chanrec = silc_channel_find(server, channel);
566 chanrec = silc_channel_create(server, channel, TRUE);
569 g_free_not_null(chanrec->topic);
570 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
571 signal_emit("channel topic changed", 1, chanrec);
574 mode = silc_client_chmode(modei,
575 channel_entry->channel_key ?
576 channel_entry->channel_key->cipher->name : "",
577 channel_entry->hmac ?
578 silc_hmac_get_name(channel_entry->hmac) : "");
579 g_free_not_null(chanrec->mode);
580 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
581 signal_emit("channel mode changed", 1, chanrec);
583 /* Resolve the client information */
584 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
585 silc_client_join_get_users,
590 case SILC_COMMAND_NICK:
592 SilcClientEntry client = va_arg(vp, SilcClientEntry);
598 old = g_strdup(server->nick);
599 server_change_nick(SERVER(server), client->nickname);
600 nicklist_rename_unique(SERVER(server),
601 server->conn->local_entry, server->nick,
602 client, client->nickname);
604 signal_emit("message own_nick", 4, server, server->nick, old, "");
609 case SILC_COMMAND_LIST:
618 (void)va_arg(vp, SilcChannelEntry);
619 name = va_arg(vp, char *);
620 topic = va_arg(vp, char *);
621 usercount = va_arg(vp, int);
623 if (status == SILC_STATUS_LIST_START ||
624 status == SILC_STATUS_OK)
625 printformat_module("fe-common/silc", server, NULL,
626 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
628 snprintf(users, sizeof(users) - 1, "%d", usercount);
629 printformat_module("fe-common/silc", server, NULL,
630 MSGLEVEL_CRAP, SILCTXT_LIST,
631 name, users, topic ? topic : "");
635 case SILC_COMMAND_UMODE:
642 mode = va_arg(vp, uint32);
644 if (mode & SILC_UMODE_SERVER_OPERATOR)
645 printformat_module("fe-common/silc", server, NULL,
646 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
648 if (mode & SILC_UMODE_ROUTER_OPERATOR)
649 printformat_module("fe-common/silc", server, NULL,
650 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
654 case SILC_COMMAND_OPER:
658 printformat_module("fe-common/silc", server, NULL,
659 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
662 case SILC_COMMAND_SILCOPER:
666 printformat_module("fe-common/silc", server, NULL,
667 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
670 case SILC_COMMAND_USERS:
672 SilcChannelEntry channel;
678 channel = va_arg(vp, SilcChannelEntry);
680 printformat_module("fe-common/silc", server, channel->channel_name,
681 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
682 channel->channel_name);
684 silc_list_start(channel->clients);
685 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
686 SilcClientEntry e = chu->client;
692 memset(stat, 0, sizeof(stat));
693 mode = silc_client_chumode_char(chu->mode);
694 if (e->mode & SILC_UMODE_GONE)
701 printformat_module("fe-common/silc", server, channel->channel_name,
702 MSGLEVEL_CRAP, SILCTXT_USERS,
704 e->username ? e->username : "",
705 e->hostname ? e->hostname : "",
706 e->realname ? e->realname : "");
713 case SILC_COMMAND_BAN:
715 SilcChannelEntry channel;
721 channel = va_arg(vp, SilcChannelEntry);
722 ban_list = va_arg(vp, char *);
725 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
726 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
729 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
730 SILCTXT_CHANNEL_NO_BAN_LIST,
731 channel->channel_name);
735 case SILC_COMMAND_GETKEY:
739 SilcPublicKey public_key;
742 GetkeyContext getkey;
747 id_type = va_arg(vp, uint32);
748 entry = va_arg(vp, void *);
749 public_key = va_arg(vp, SilcPublicKey);
752 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
754 getkey = silc_calloc(1, sizeof(*getkey));
755 getkey->entry = entry;
756 getkey->id_type = id_type;
757 getkey->client = client;
759 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
761 silc_verify_public_key_internal(client, conn,
762 (id_type == SILC_ID_CLIENT ?
763 SILC_SOCKET_TYPE_CLIENT :
764 SILC_SOCKET_TYPE_SERVER),
765 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
766 silc_getkey_cb, getkey);
769 printformat_module("fe-common/silc", server, NULL,
770 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
775 case SILC_COMMAND_TOPIC:
777 SilcChannelEntry channel;
783 channel = va_arg(vp, SilcChannelEntry);
784 topic = va_arg(vp, char *);
787 chanrec = silc_channel_find_entry(server, channel);
789 g_free_not_null(chanrec->topic);
790 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
791 signal_emit("channel topic changed", 1, chanrec);
793 printformat_module("fe-common/silc", server, channel->channel_name,
794 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
795 channel->channel_name, topic);
797 printformat_module("fe-common/silc", server, channel->channel_name,
798 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
799 channel->channel_name);
809 /* Internal routine to verify public key. If the `completion' is provided
810 it will be called to indicate whether public was verified or not. */
814 SilcClientConnection conn;
819 SilcSKEPKType pk_type;
820 SilcVerifyPublicKey completion;
824 static void verify_public_key_completion(const char *line, void *context)
826 PublicKeyVerify verify = (PublicKeyVerify)context;
828 if (line[0] == 'Y' || line[0] == 'y') {
829 /* Call the completion */
830 if (verify->completion)
831 verify->completion(TRUE, verify->context);
833 /* Save the key for future checking */
834 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
835 verify->pk_len, SILC_PKCS_FILE_PEM);
837 /* Call the completion */
838 if (verify->completion)
839 verify->completion(FALSE, verify->context);
841 printformat_module("fe-common/silc", NULL, NULL,
842 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
845 silc_free(verify->filename);
846 silc_free(verify->entity);
847 silc_free(verify->pk);
852 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
853 SilcSocketType conn_type, unsigned char *pk,
854 uint32 pk_len, SilcSKEPKType pk_type,
855 SilcVerifyPublicKey completion, void *context)
858 char file[256], filename[256], *fingerprint, *format;
861 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
862 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
863 "server" : "client");
864 PublicKeyVerify verify;
866 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
867 printformat_module("fe-common/silc", NULL, NULL,
868 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
871 completion(FALSE, context);
875 pw = getpwuid(getuid());
878 completion(FALSE, context);
882 memset(filename, 0, sizeof(filename));
883 memset(file, 0, sizeof(file));
885 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
886 conn_type == SILC_SOCKET_TYPE_ROUTER) {
887 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
888 conn->sock->hostname, conn->sock->port);
889 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
890 pw->pw_dir, entity, file);
892 /* Replace all whitespaces with `_'. */
893 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
894 for (i = 0; i < strlen(fingerprint); i++)
895 if (fingerprint[i] == ' ')
896 fingerprint[i] = '_';
898 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
899 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
900 pw->pw_dir, entity, file);
901 silc_free(fingerprint);
904 /* Take fingerprint of the public key */
905 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
907 verify = silc_calloc(1, sizeof(*verify));
908 verify->client = client;
910 verify->filename = strdup(filename);
911 verify->entity = strdup(entity);
912 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
913 memcpy(verify->pk, pk, pk_len);
914 verify->pk_len = pk_len;
915 verify->pk_type = pk_type;
916 verify->completion = completion;
917 verify->context = context;
919 /* Check whether this key already exists */
920 if (stat(filename, &st) < 0) {
921 /* Key does not exist, ask user to verify the key and save it */
923 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
924 SILCTXT_PUBKEY_RECEIVED, entity);
925 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
926 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
927 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
928 SILCTXT_PUBKEY_ACCEPT);
929 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
932 silc_free(fingerprint);
935 /* The key already exists, verify it. */
936 SilcPublicKey public_key;
937 unsigned char *encpk;
940 /* Load the key file */
941 if (!silc_pkcs_load_public_key(filename, &public_key,
943 if (!silc_pkcs_load_public_key(filename, &public_key,
944 SILC_PKCS_FILE_BIN)) {
945 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
946 SILCTXT_PUBKEY_RECEIVED, entity);
947 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
948 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
949 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
950 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
951 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
952 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
953 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
956 silc_free(fingerprint);
960 /* Encode the key data */
961 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
963 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
964 SILCTXT_PUBKEY_RECEIVED, entity);
965 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
966 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
967 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
968 SILCTXT_PUBKEY_MALFORMED, entity);
969 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
970 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
971 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
974 silc_free(fingerprint);
978 /* Compare the keys */
979 if (memcmp(encpk, pk, encpk_len)) {
980 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
981 SILCTXT_PUBKEY_RECEIVED, entity);
982 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
983 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
984 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
985 SILCTXT_PUBKEY_NO_MATCH, entity);
986 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
987 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
988 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
989 SILCTXT_PUBKEY_MITM_ATTACK, entity);
991 /* Ask user to verify the key and save it */
992 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
993 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
994 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
997 silc_free(fingerprint);
1001 /* Local copy matched */
1003 completion(TRUE, context);
1004 silc_free(fingerprint);
1008 /* Verifies received public key. The `conn_type' indicates which entity
1009 (server, client etc.) has sent the public key. If user decides to trust
1010 the key may be saved as trusted public key for later use. The
1011 `completion' must be called after the public key has been verified. */
1014 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1015 SilcSocketType conn_type, unsigned char *pk,
1016 uint32 pk_len, SilcSKEPKType pk_type,
1017 SilcVerifyPublicKey completion, void *context)
1019 silc_verify_public_key_internal(client, conn, conn_type, pk,
1021 completion, context);
1024 /* Asks passphrase from user on the input line. */
1027 SilcAskPassphrase completion;
1031 void ask_passphrase_completion(const char *passphrase, void *context)
1033 AskPassphrase p = (AskPassphrase)context;
1034 p->completion((unsigned char *)passphrase,
1035 passphrase ? strlen(passphrase) : 0, p->context);
1039 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1040 SilcAskPassphrase completion, void *context)
1042 AskPassphrase p = silc_calloc(1, sizeof(*p));
1043 p->completion = completion;
1044 p->context = context;
1046 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1047 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1051 SilcGetAuthMeth completion;
1053 } *InternalGetAuthMethod;
1055 /* Callback called when we've received the authentication method information
1056 from the server after we've requested it. This will get the authentication
1057 data from the user if needed. */
1059 static void silc_get_auth_method_callback(SilcClient client,
1060 SilcClientConnection conn,
1061 SilcAuthMethod auth_meth,
1064 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1066 switch (auth_meth) {
1067 case SILC_AUTH_NONE:
1068 /* No authentication required. */
1069 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1071 case SILC_AUTH_PASSWORD:
1072 /* Do not ask the passphrase from user, the library will ask it if
1073 we do not provide it here. */
1074 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1076 case SILC_AUTH_PUBLIC_KEY:
1077 /* Do not get the authentication data now, the library will generate
1078 it using our default key, if we do not provide it here. */
1079 /* XXX In the future when we support multiple local keys and multiple
1080 local certificates we will need to ask from user which one to use. */
1081 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1085 silc_free(internal);
1088 /* Find authentication method and authentication data by hostname and
1089 port. The hostname may be IP address as well. The found authentication
1090 method and authentication data is returned to `auth_meth', `auth_data'
1091 and `auth_data_len'. The function returns TRUE if authentication method
1092 is found and FALSE if not. `conn' may be NULL. */
1094 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1095 char *hostname, uint16 port,
1096 SilcGetAuthMeth completion, void *context)
1098 InternalGetAuthMethod internal;
1100 /* XXX must resolve from configuration whether this connection has
1101 any specific authentication data */
1103 /* If we do not have this connection configured by the user in a
1104 configuration file then resolve the authentication method from the
1105 server for this session. */
1106 internal = silc_calloc(1, sizeof(*internal));
1107 internal->completion = completion;
1108 internal->context = context;
1110 silc_client_request_authentication_method(client, conn,
1111 silc_get_auth_method_callback,
1115 /* Notifies application that failure packet was received. This is called
1116 if there is some protocol active in the client. The `protocol' is the
1117 protocol context. The `failure' is opaque pointer to the failure
1118 indication. Note, that the `failure' is protocol dependant and application
1119 must explicitly cast it to correct type. Usually `failure' is 32 bit
1120 failure type (see protocol specs for all protocol failure types). */
1122 void silc_failure(SilcClient client, SilcClientConnection conn,
1123 SilcProtocol protocol, void *failure)
1125 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1126 SilcSKEStatus status = (SilcSKEStatus)failure;
1128 if (status == SILC_SKE_STATUS_BAD_VERSION)
1129 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1130 SILCTXT_KE_BAD_VERSION);
1131 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1132 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1133 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1134 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1135 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1136 SILCTXT_KE_UNKNOWN_GROUP);
1137 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1138 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1139 SILCTXT_KE_UNKNOWN_CIPHER);
1140 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1141 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1142 SILCTXT_KE_UNKNOWN_PKCS);
1143 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1144 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1145 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1146 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1147 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1148 SILCTXT_KE_UNKNOWN_HMAC);
1149 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1150 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1151 SILCTXT_KE_INCORRECT_SIGNATURE);
1152 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1153 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1154 SILCTXT_KE_INVALID_COOKIE);
1157 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1158 uint32 err = (uint32)failure;
1160 if (err == SILC_AUTH_FAILED)
1161 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1162 SILCTXT_AUTH_FAILED);
1166 /* Asks whether the user would like to perform the key agreement protocol.
1167 This is called after we have received an key agreement packet or an
1168 reply to our key agreement packet. This returns TRUE if the user wants
1169 the library to perform the key agreement protocol and FALSE if it is not
1170 desired (application may start it later by calling the function
1171 silc_client_perform_key_agreement). */
1173 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1174 SilcClientEntry client_entry, char *hostname,
1176 SilcKeyAgreementCallback *completion,
1181 /* We will just display the info on the screen and return FALSE and user
1182 will have to start the key agreement with a command. */
1185 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1188 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1189 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1191 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1192 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1193 client_entry->nickname, hostname, portstr);
1201 /* SILC client operations */
1202 SilcClientOperations ops = {
1204 silc_channel_message,
1205 silc_private_message,
1211 silc_get_auth_method,
1212 silc_verify_public_key,
1213 silc_ask_passphrase,