5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "chat-protocols.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43 SilcSocketType conn_type, unsigned char *pk,
44 uint32 pk_len, SilcSKEPKType pk_type,
45 SilcVerifyPublicKey completion, void *context);
47 void silc_say(SilcClient client, SilcClientConnection conn,
48 SilcClientMessageType type, char *msg, ...)
50 SILC_SERVER_REC *server;
54 server = conn == NULL ? NULL : conn->context;
57 str = g_strdup_vprintf(msg, va);
58 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
63 void silc_say_error(char *msg, ...)
69 str = g_strdup_vprintf(msg, va);
70 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
76 /* Message for a channel. The `sender' is the nickname of the sender
77 received in the packet. The `channel_name' is the name of the channel. */
79 void silc_channel_message(SilcClient client, SilcClientConnection conn,
80 SilcClientEntry sender, SilcChannelEntry channel,
81 SilcMessageFlags flags, char *msg)
83 SILC_SERVER_REC *server;
85 SILC_CHANNEL_REC *chanrec;
87 server = conn == NULL ? NULL : conn->context;
88 chanrec = silc_channel_find_entry(server, channel);
92 nick = silc_nicklist_find(chanrec, sender);
94 if (flags & SILC_MESSAGE_FLAG_ACTION)
95 printformat_module("fe-common/silc", server, channel->channel_name,
96 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
97 nick == NULL ? "[<unknown>]" : nick->nick, msg);
98 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
99 printformat_module("fe-common/silc", server, channel->channel_name,
100 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
101 nick == NULL ? "[<unknown>]" : nick->nick, msg);
103 signal_emit("message public", 6, server, msg,
104 nick == NULL ? "[<unknown>]" : nick->nick,
105 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
106 chanrec->name, nick);
109 /* Private message to the client. The `sender' is the nickname of the
110 sender received in the packet. */
112 void silc_private_message(SilcClient client, SilcClientConnection conn,
113 SilcClientEntry sender, SilcMessageFlags flags,
116 SILC_SERVER_REC *server;
119 server = conn == NULL ? NULL : conn->context;
120 memset(userhost, 0, sizeof(userhost));
121 if (sender->username)
122 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
123 sender->username, sender->hostname);
124 signal_emit("message private", 4, server, msg,
125 sender->nickname ? sender->nickname : "[<unknown>]",
126 sender->username ? userhost : NULL);
129 /* Notify message to the client. The notify arguments are sent in the
130 same order as servers sends them. The arguments are same as received
131 from the server except for ID's. If ID is received application receives
132 the corresponding entry to the ID. For example, if Client ID is received
133 application receives SilcClientEntry. Also, if the notify type is
134 for channel the channel entry is sent to application (even if server
135 does not send it). */
142 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
143 static NOTIFY_REC notifies[] = {
144 { SILC_NOTIFY_TYPE_NONE, NULL },
145 { SILC_NOTIFY_TYPE_INVITE, "invite" },
146 { SILC_NOTIFY_TYPE_JOIN, "join" },
147 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
148 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
149 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
150 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
151 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
152 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
153 { SILC_NOTIFY_TYPE_MOTD, "motd" },
154 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
155 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
156 { SILC_NOTIFY_TYPE_KICKED, "kick" },
157 { SILC_NOTIFY_TYPE_KILLED, "kill" },
158 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
159 { SILC_NOTIFY_TYPE_BAN, "ban" },
162 void silc_notify(SilcClient client, SilcClientConnection conn,
163 SilcNotifyType type, ...)
165 SILC_SERVER_REC *server;
168 server = conn == NULL ? NULL : conn->context;
171 if (type == SILC_NOTIFY_TYPE_NONE) {
172 /* Some generic notice from server */
173 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
174 } else if (type < MAX_NOTIFY) {
175 /* Send signal about the notify event */
177 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
178 signal_emit(signal, 2, server, va);
181 printformat_module("fe-common/silc", server, NULL,
182 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
188 /* Called to indicate that connection was either successfully established
189 or connecting failed. This is also the first time application receives
190 the SilcClientConnection objecet which it should save somewhere. */
192 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
194 SILC_SERVER_REC *server = conn->context;
196 if (!server && !success) {
197 silc_client_close_connection(client, NULL, conn);
202 server->connected = TRUE;
203 signal_emit("event connected", 1, server);
205 server->connection_lost = TRUE;
206 server->conn->context = NULL;
207 server_disconnect(SERVER(server));
211 /* Called to indicate that connection was disconnected to the server. */
213 void silc_disconnect(SilcClient client, SilcClientConnection conn)
215 SILC_SERVER_REC *server = conn->context;
218 nicklist_rename_unique(SERVER(server),
219 server->conn->local_entry, server->nick,
220 server->conn->local_entry,
221 silc_client->username);
222 silc_change_nick(server, silc_client->username);
225 server->conn->context = NULL;
227 server->connection_lost = TRUE;
228 server_disconnect(SERVER(server));
231 /* Command handler. This function is called always in the command function.
232 If error occurs it will be called as well. `conn' is the associated
233 client connection. `cmd_context' is the command context that was
234 originally sent to the command. `success' is FALSE if error occured
235 during command. `command' is the command being processed. It must be
236 noted that this is not reply from server. This is merely called just
237 after application has called the command. Just to tell application
238 that the command really was processed. */
240 void silc_command(SilcClient client, SilcClientConnection conn,
241 SilcClientCommandContext cmd_context, int success,
244 SILC_SERVER_REC *server = conn->context;
250 case SILC_COMMAND_INVITE:
251 printformat_module("fe-common/silc", server, NULL,
252 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
253 cmd_context->argv[2],
254 (cmd_context->argv[1][0] == '*' ?
255 (char *)conn->current_channel->channel_name :
256 (char *)cmd_context->argv[1]));
263 /* Client info resolving callback when JOIN command reply is received.
264 This will cache all users on the channel. */
266 static void silc_client_join_get_users(SilcClient client,
267 SilcClientConnection conn,
268 SilcClientEntry *clients,
269 uint32 clients_count,
272 SilcChannelEntry channel = (SilcChannelEntry)context;
274 SILC_SERVER_REC *server = conn->context;
275 SILC_CHANNEL_REC *chanrec;
276 SilcClientEntry founder = NULL;
282 chanrec = silc_channel_find(server, channel->channel_name);
286 silc_list_start(channel->clients);
287 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
288 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
289 founder = chu->client;
290 silc_nicklist_insert(chanrec, chu, FALSE);
293 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
294 nicklist_set_own(CHANNEL(chanrec), ownnick);
295 signal_emit("channel joined", 1, chanrec);
298 printformat_module("fe-common/silc", server, channel->channel_name,
299 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
300 channel->channel_name, chanrec->topic);
302 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
305 if (founder == conn->local_entry)
306 printformat_module("fe-common/silc",
307 server, channel->channel_name, MSGLEVEL_CRAP,
308 SILCTXT_CHANNEL_FOUNDER_YOU,
309 channel->channel_name);
311 printformat_module("fe-common/silc",
312 server, channel->channel_name, MSGLEVEL_CRAP,
313 SILCTXT_CHANNEL_FOUNDER,
314 channel->channel_name, founder->nickname);
320 SilcClientConnection conn;
326 void silc_getkey_cb(bool success, void *context)
328 GetkeyContext getkey = (GetkeyContext)context;
329 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
330 char *name = (getkey->id_type == SILC_ID_CLIENT ?
331 ((SilcClientEntry)getkey->entry)->nickname :
332 ((SilcServerEntry)getkey->entry)->server_name);
335 printformat_module("fe-common/silc", NULL, NULL,
336 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
338 printformat_module("fe-common/silc", NULL, NULL,
339 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
342 silc_free(getkey->fingerprint);
346 /* Command reply handler. This function is called always in the command reply
347 function. If error occurs it will be called as well. Normal scenario
348 is that it will be called after the received command data has been parsed
349 and processed. The function is used to pass the received command data to
352 `conn' is the associated client connection. `cmd_payload' is the command
353 payload data received from server and it can be ignored. It is provided
354 if the application would like to re-parse the received command data,
355 however, it must be noted that the data is parsed already by the library
356 thus the payload can be ignored. `success' is FALSE if error occured.
357 In this case arguments are not sent to the application. `command' is the
358 command reply being processed. The function has variable argument list
359 and each command defines the number and type of arguments it passes to the
360 application (on error they are not sent). */
363 silc_command_reply(SilcClient client, SilcClientConnection conn,
364 SilcCommandPayload cmd_payload, int success,
365 SilcCommand command, SilcCommandStatus status, ...)
368 SILC_SERVER_REC *server = conn->context;
369 SILC_CHANNEL_REC *chanrec;
372 va_start(vp, status);
375 case SILC_COMMAND_WHOIS:
377 char buf[1024], *nickname, *username, *realname, *nick;
380 SilcClientEntry client_entry;
382 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
383 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
385 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
388 silc_say_error("%s: %s", tmp,
389 silc_client_command_status_message(status));
391 silc_say_error("%s", silc_client_command_status_message(status));
398 client_entry = va_arg(vp, SilcClientEntry);
399 nickname = va_arg(vp, char *);
400 username = va_arg(vp, char *);
401 realname = va_arg(vp, char *);
402 channels = va_arg(vp, SilcBuffer);
403 mode = va_arg(vp, uint32);
404 idle = va_arg(vp, uint32);
406 silc_parse_userfqdn(nickname, &nick, NULL);
407 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
408 SILCTXT_WHOIS_USERINFO, nickname,
409 client_entry->username, client_entry->hostname,
410 nick, client_entry->nickname);
411 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
412 SILCTXT_WHOIS_REALNAME, realname);
416 SilcDList list = silc_channel_payload_parse_list(channels);
418 SilcChannelPayload entry;
419 memset(buf, 0, sizeof(buf));
420 silc_dlist_start(list);
421 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
422 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
424 char *name = silc_channel_get_name(entry, &name_len);
427 strncat(buf, m, strlen(m));
428 strncat(buf, name, name_len);
429 strncat(buf, " ", 1);
433 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
434 SILCTXT_WHOIS_CHANNELS, buf);
435 silc_channel_payload_list_free(list);
440 memset(buf, 0, sizeof(buf));
442 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
443 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
444 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
446 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
447 "SILC Operator " : "[Unknown mode] ");
449 if (mode & SILC_UMODE_GONE)
452 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
453 SILCTXT_WHOIS_MODES, buf);
456 if (idle && nickname) {
457 memset(buf, 0, sizeof(buf));
458 snprintf(buf, sizeof(buf) - 1, "%lu %s",
459 idle > 60 ? (idle / 60) : idle,
460 idle > 60 ? "minutes" : "seconds");
462 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
463 SILCTXT_WHOIS_IDLE, buf);
468 case SILC_COMMAND_WHOWAS:
470 char *nickname, *username, *realname;
472 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
473 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
475 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
478 silc_say_error("%s: %s", tmp,
479 silc_client_command_status_message(status));
481 silc_say_error("%s", silc_client_command_status_message(status));
488 (void)va_arg(vp, SilcClientEntry);
489 nickname = va_arg(vp, char *);
490 username = va_arg(vp, char *);
491 realname = va_arg(vp, char *);
493 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
494 SILCTXT_WHOWAS_USERINFO, nickname, username,
495 realname ? realname : "");
499 case SILC_COMMAND_INVITE:
501 SilcChannelEntry channel;
503 SilcArgumentPayload args;
509 channel = va_arg(vp, SilcChannelEntry);
510 invite_list = va_arg(vp, char *);
512 args = silc_command_get_args(cmd_payload);
514 argc = silc_argument_get_arg_num(args);
517 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
518 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
521 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
522 SILCTXT_CHANNEL_NO_INVITE_LIST,
523 channel->channel_name);
527 case SILC_COMMAND_JOIN:
529 char *channel, *mode, *topic;
531 SilcChannelEntry channel_entry;
532 SilcBuffer client_id_list;
538 channel = va_arg(vp, char *);
539 channel_entry = va_arg(vp, SilcChannelEntry);
540 modei = va_arg(vp, uint32);
541 (void)va_arg(vp, uint32);
542 (void)va_arg(vp, unsigned char *);
543 (void)va_arg(vp, unsigned char *);
544 (void)va_arg(vp, unsigned char *);
545 topic = va_arg(vp, char *);
546 (void)va_arg(vp, unsigned char *);
547 list_count = va_arg(vp, uint32);
548 client_id_list = va_arg(vp, SilcBuffer);
550 chanrec = silc_channel_find(server, channel);
552 chanrec = silc_channel_create(server, channel, TRUE);
555 g_free_not_null(chanrec->topic);
556 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
557 signal_emit("channel topic changed", 1, chanrec);
560 mode = silc_client_chmode(modei,
561 channel_entry->channel_key ?
562 channel_entry->channel_key->cipher->name : "",
563 channel_entry->hmac ?
564 channel_entry->hmac->hmac->name : "");
565 g_free_not_null(chanrec->mode);
566 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
567 signal_emit("channel mode changed", 1, chanrec);
569 /* Resolve the client information */
570 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
571 silc_client_join_get_users,
576 case SILC_COMMAND_NICK:
578 SilcClientEntry client = va_arg(vp, SilcClientEntry);
584 old = g_strdup(server->nick);
585 server_change_nick(SERVER(server), client->nickname);
586 nicklist_rename_unique(SERVER(server),
587 server->conn->local_entry, server->nick,
588 client, client->nickname);
590 signal_emit("message own_nick", 4, server, server->nick, old, "");
595 case SILC_COMMAND_LIST:
604 (void)va_arg(vp, SilcChannelEntry);
605 name = va_arg(vp, char *);
606 topic = va_arg(vp, char *);
607 usercount = va_arg(vp, int);
609 if (status == SILC_STATUS_LIST_START ||
610 status == SILC_STATUS_OK)
611 printformat_module("fe-common/silc", server, NULL,
612 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
614 snprintf(users, sizeof(users) - 1, "%d", usercount);
615 printformat_module("fe-common/silc", server, NULL,
616 MSGLEVEL_CRAP, SILCTXT_LIST,
617 name, users, topic ? topic : "");
621 case SILC_COMMAND_UMODE:
628 mode = va_arg(vp, uint32);
630 if (mode & SILC_UMODE_SERVER_OPERATOR)
631 printformat_module("fe-common/silc", server, NULL,
632 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
634 if (mode & SILC_UMODE_ROUTER_OPERATOR)
635 printformat_module("fe-common/silc", server, NULL,
636 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
640 case SILC_COMMAND_OPER:
644 printformat_module("fe-common/silc", server, NULL,
645 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
648 case SILC_COMMAND_SILCOPER:
652 printformat_module("fe-common/silc", server, NULL,
653 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
656 case SILC_COMMAND_USERS:
658 SilcChannelEntry channel;
664 channel = va_arg(vp, SilcChannelEntry);
666 printformat_module("fe-common/silc", server, channel->channel_name,
667 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
668 channel->channel_name);
670 silc_list_start(channel->clients);
671 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
672 SilcClientEntry e = chu->client;
675 memset(stat, 0, sizeof(stat));
676 mode = silc_client_chumode_char(chu->mode);
677 if (e->mode & SILC_UMODE_GONE)
684 printformat_module("fe-common/silc", server, channel->channel_name,
685 MSGLEVEL_CRAP, SILCTXT_USERS,
686 e->nickname, stat, e->username,
687 e->hostname, e->realname ? e->realname : "");
694 case SILC_COMMAND_BAN:
696 SilcChannelEntry channel;
702 channel = va_arg(vp, SilcChannelEntry);
703 ban_list = va_arg(vp, char *);
706 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
707 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
710 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
711 SILCTXT_CHANNEL_NO_BAN_LIST,
712 channel->channel_name);
716 case SILC_COMMAND_GETKEY:
720 SilcPublicKey public_key;
723 GetkeyContext getkey;
728 id_type = va_arg(vp, uint32);
729 entry = va_arg(vp, void *);
730 public_key = va_arg(vp, SilcPublicKey);
733 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
735 getkey = silc_calloc(1, sizeof(*getkey));
736 getkey->entry = entry;
737 getkey->id_type = id_type;
738 getkey->client = client;
740 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
742 silc_verify_public_key_internal(client, conn,
743 (id_type == SILC_ID_CLIENT ?
744 SILC_SOCKET_TYPE_CLIENT :
745 SILC_SOCKET_TYPE_SERVER),
746 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
747 silc_getkey_cb, getkey);
750 printformat_module("fe-common/silc", server, NULL,
751 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
756 case SILC_COMMAND_TOPIC:
758 SilcChannelEntry channel;
764 channel = va_arg(vp, SilcChannelEntry);
765 topic = va_arg(vp, char *);
768 chanrec = silc_channel_find_entry(server, channel);
770 g_free_not_null(chanrec->topic);
771 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
772 signal_emit("channel topic changed", 1, chanrec);
774 printformat_module("fe-common/silc", server, channel->channel_name,
775 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
776 channel->channel_name, topic);
778 printformat_module("fe-common/silc", server, channel->channel_name,
779 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
780 channel->channel_name);
790 /* Internal routine to verify public key. If the `completion' is provided
791 it will be called to indicate whether public was verified or not. */
795 SilcClientConnection conn;
800 SilcSKEPKType pk_type;
801 SilcVerifyPublicKey completion;
805 static void verify_public_key_completion(const char *line, void *context)
807 PublicKeyVerify verify = (PublicKeyVerify)context;
809 if (line[0] == 'Y' || line[0] == 'y') {
810 /* Call the completion */
811 if (verify->completion)
812 verify->completion(TRUE, verify->context);
814 /* Save the key for future checking */
815 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
816 verify->pk_len, SILC_PKCS_FILE_PEM);
818 /* Call the completion */
819 if (verify->completion)
820 verify->completion(FALSE, verify->context);
822 printformat_module("fe-common/silc", NULL, NULL,
823 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
826 silc_free(verify->filename);
827 silc_free(verify->entity);
828 silc_free(verify->pk);
833 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
834 SilcSocketType conn_type, unsigned char *pk,
835 uint32 pk_len, SilcSKEPKType pk_type,
836 SilcVerifyPublicKey completion, void *context)
839 char file[256], filename[256], *fingerprint, *format;
842 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
843 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
844 "server" : "client");
845 PublicKeyVerify verify;
847 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
848 printformat_module("fe-common/silc", NULL, NULL,
849 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
852 completion(FALSE, context);
856 pw = getpwuid(getuid());
859 completion(FALSE, context);
863 memset(filename, 0, sizeof(filename));
864 memset(file, 0, sizeof(file));
866 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
867 conn_type == SILC_SOCKET_TYPE_ROUTER) {
868 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
869 conn->sock->hostname, conn->sock->port);
870 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
871 pw->pw_dir, entity, file);
873 /* Replace all whitespaces with `_'. */
874 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
875 for (i = 0; i < strlen(fingerprint); i++)
876 if (fingerprint[i] == ' ')
877 fingerprint[i] = '_';
879 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
880 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
881 pw->pw_dir, entity, file);
882 silc_free(fingerprint);
885 /* Take fingerprint of the public key */
886 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
888 verify = silc_calloc(1, sizeof(*verify));
889 verify->client = client;
891 verify->filename = strdup(filename);
892 verify->entity = strdup(entity);
893 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
894 memcpy(verify->pk, pk, pk_len);
895 verify->pk_len = pk_len;
896 verify->pk_type = pk_type;
897 verify->completion = completion;
898 verify->context = context;
900 /* Check whether this key already exists */
901 if (stat(filename, &st) < 0) {
902 /* Key does not exist, ask user to verify the key and save it */
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 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
909 SILCTXT_PUBKEY_ACCEPT);
910 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
913 silc_free(fingerprint);
916 /* The key already exists, verify it. */
917 SilcPublicKey public_key;
918 unsigned char *encpk;
921 /* Load the key file */
922 if (!silc_pkcs_load_public_key(filename, &public_key,
924 if (!silc_pkcs_load_public_key(filename, &public_key,
925 SILC_PKCS_FILE_BIN)) {
926 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
927 SILCTXT_PUBKEY_RECEIVED, entity);
928 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
929 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
930 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
931 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
932 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
933 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
934 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
937 silc_free(fingerprint);
941 /* Encode the key data */
942 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
944 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
945 SILCTXT_PUBKEY_RECEIVED, entity);
946 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
947 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
948 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
949 SILCTXT_PUBKEY_MALFORMED, entity);
950 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
951 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
952 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
955 silc_free(fingerprint);
959 /* Compare the keys */
960 if (memcmp(encpk, pk, encpk_len)) {
961 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
962 SILCTXT_PUBKEY_RECEIVED, entity);
963 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
964 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
965 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
966 SILCTXT_PUBKEY_NO_MATCH, entity);
967 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
968 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
969 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
970 SILCTXT_PUBKEY_MITM_ATTACK, entity);
972 /* Ask user to verify the key and save it */
973 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
974 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
975 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
978 silc_free(fingerprint);
982 /* Local copy matched */
984 completion(TRUE, context);
985 silc_free(fingerprint);
989 /* Verifies received public key. The `conn_type' indicates which entity
990 (server, client etc.) has sent the public key. If user decides to trust
991 the key may be saved as trusted public key for later use. The
992 `completion' must be called after the public key has been verified. */
995 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
996 SilcSocketType conn_type, unsigned char *pk,
997 uint32 pk_len, SilcSKEPKType pk_type,
998 SilcVerifyPublicKey completion, void *context)
1000 silc_verify_public_key_internal(client, conn, conn_type, pk,
1002 completion, context);
1005 /* Asks passphrase from user on the input line. */
1008 SilcAskPassphrase completion;
1012 void ask_passphrase_completion(const char *passphrase, void *context)
1014 AskPassphrase p = (AskPassphrase)context;
1015 p->completion((unsigned char *)passphrase,
1016 passphrase ? strlen(passphrase) : 0, p->context);
1020 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1021 SilcAskPassphrase completion, void *context)
1023 AskPassphrase p = silc_calloc(1, sizeof(*p));
1024 p->completion = completion;
1025 p->context = context;
1027 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1028 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1032 SilcGetAuthMeth completion;
1034 } *InternalGetAuthMethod;
1036 /* Callback called when we've received the authentication method information
1037 from the server after we've requested it. This will get the authentication
1038 data from the user if needed. */
1040 static void silc_get_auth_method_callback(SilcClient client,
1041 SilcClientConnection conn,
1042 SilcAuthMethod auth_meth,
1045 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1047 switch (auth_meth) {
1048 case SILC_AUTH_NONE:
1049 /* No authentication required. */
1050 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1052 case SILC_AUTH_PASSWORD:
1053 /* Do not ask the passphrase from user, the library will ask it if
1054 we do not provide it here. */
1055 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1057 case SILC_AUTH_PUBLIC_KEY:
1058 /* Do not get the authentication data now, the library will generate
1059 it using our default key, if we do not provide it here. */
1060 /* XXX In the future when we support multiple local keys and multiple
1061 local certificates we will need to ask from user which one to use. */
1062 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1066 silc_free(internal);
1069 /* Find authentication method and authentication data by hostname and
1070 port. The hostname may be IP address as well. The found authentication
1071 method and authentication data is returned to `auth_meth', `auth_data'
1072 and `auth_data_len'. The function returns TRUE if authentication method
1073 is found and FALSE if not. `conn' may be NULL. */
1075 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1076 char *hostname, uint16 port,
1077 SilcGetAuthMeth completion, void *context)
1079 InternalGetAuthMethod internal;
1081 /* XXX must resolve from configuration whether this connection has
1082 any specific authentication data */
1084 /* If we do not have this connection configured by the user in a
1085 configuration file then resolve the authentication method from the
1086 server for this session. */
1087 internal = silc_calloc(1, sizeof(*internal));
1088 internal->completion = completion;
1089 internal->context = context;
1091 silc_client_request_authentication_method(client, conn,
1092 silc_get_auth_method_callback,
1096 /* Notifies application that failure packet was received. This is called
1097 if there is some protocol active in the client. The `protocol' is the
1098 protocol context. The `failure' is opaque pointer to the failure
1099 indication. Note, that the `failure' is protocol dependant and application
1100 must explicitly cast it to correct type. Usually `failure' is 32 bit
1101 failure type (see protocol specs for all protocol failure types). */
1103 void silc_failure(SilcClient client, SilcClientConnection conn,
1104 SilcProtocol protocol, void *failure)
1106 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1107 SilcSKEStatus status = (SilcSKEStatus)failure;
1109 if (status == SILC_SKE_STATUS_BAD_VERSION)
1110 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1111 SILCTXT_KE_BAD_VERSION);
1112 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1113 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1114 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1115 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1116 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1117 SILCTXT_KE_UNKNOWN_GROUP);
1118 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1119 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1120 SILCTXT_KE_UNKNOWN_CIPHER);
1121 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1122 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1123 SILCTXT_KE_UNKNOWN_PKCS);
1124 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1125 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1126 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1127 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1128 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1129 SILCTXT_KE_UNKNOWN_HMAC);
1130 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1131 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1132 SILCTXT_KE_INCORRECT_SIGNATURE);
1133 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1134 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1135 SILCTXT_KE_INVALID_COOKIE);
1138 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1139 uint32 err = (uint32)failure;
1141 if (err == SILC_AUTH_FAILED)
1142 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1143 SILCTXT_AUTH_FAILED);
1147 /* Asks whether the user would like to perform the key agreement protocol.
1148 This is called after we have received an key agreement packet or an
1149 reply to our key agreement packet. This returns TRUE if the user wants
1150 the library to perform the key agreement protocol and FALSE if it is not
1151 desired (application may start it later by calling the function
1152 silc_client_perform_key_agreement). */
1154 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1155 SilcClientEntry client_entry, char *hostname,
1157 SilcKeyAgreementCallback *completion,
1162 /* We will just display the info on the screen and return FALSE and user
1163 will have to start the key agreement with a command. */
1166 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1169 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1170 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1172 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1173 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1174 client_entry->nickname, hostname, portstr);
1182 /* SILC client operations */
1183 SilcClientOperations ops = {
1185 silc_channel_message,
1186 silc_private_message,
1192 silc_get_auth_method,
1193 silc_verify_public_key,
1194 silc_ask_passphrase,