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"
32 #include "version_internal.h"
38 #include "fe-common/core/printtext.h"
39 #include "fe-common/core/fe-channels.h"
40 #include "fe-common/core/keyboard.h"
41 #include "fe-common/silc/module-formats.h"
43 /* Command line option variables */
44 static bool opt_create_keypair = FALSE;
45 static bool opt_debug = FALSE;
46 static char *opt_pkcs = NULL;
47 static char *opt_keyfile = NULL;
48 static int opt_bits = 0;
52 SilcClient silc_client = NULL;
53 SilcClientConfig silc_config = NULL;
54 extern SilcClientOperations ops;
55 extern int silc_debug;
57 /* SIM (SILC Module) table */
58 SilcSimContext **sims = NULL;
59 uint32 sims_count = 0;
62 static void silc_say(SilcClient client, SilcClientConnection conn,
65 silc_channel_message(SilcClient client, SilcClientConnection conn,
66 SilcClientEntry sender, SilcChannelEntry channel,
67 SilcMessageFlags flags, char *msg);
69 silc_private_message(SilcClient client, SilcClientConnection conn,
70 SilcClientEntry sender, SilcMessageFlags flags,
72 static void silc_notify(SilcClient client, SilcClientConnection conn,
73 SilcNotifyType type, ...);
75 silc_connect(SilcClient client, SilcClientConnection conn, int success);
77 silc_disconnect(SilcClient client, SilcClientConnection conn);
79 silc_command(SilcClient client, SilcClientConnection conn,
80 SilcClientCommandContext cmd_context, int success,
83 silc_command_reply(SilcClient client, SilcClientConnection conn,
84 SilcCommandPayload cmd_payload, int success,
85 SilcCommand command, SilcCommandStatus status, ...);
87 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
88 SilcSocketType conn_type, unsigned char *pk,
89 uint32 pk_len, SilcSKEPKType pk_type,
90 SilcVerifyPublicKey completion, void *context);
92 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
93 SilcSocketType conn_type, unsigned char *pk,
94 uint32 pk_len, SilcSKEPKType pk_type,
95 SilcVerifyPublicKey completion, void *context);
96 static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
97 SilcAskPassphrase completion, void *context);
99 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
100 char *hostname, uint16 port,
101 SilcProtocolAuthMeth *auth_meth,
102 unsigned char **auth_data,
103 uint32 *auth_data_len);
105 silc_failure(SilcClient client, SilcClientConnection conn,
106 SilcProtocol protocol, void *failure);
108 silc_key_agreement(SilcClient client, SilcClientConnection conn,
109 SilcClientEntry client_entry, char *hostname,
111 SilcKeyAgreementCallback *completion,
114 static void silc_say(SilcClient client, SilcClientConnection conn,
117 SILC_SERVER_REC *server;
121 server = conn == NULL ? NULL : conn->context;
124 str = g_strdup_vprintf(msg, va);
125 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
130 static void silc_say_error(char *msg, ...)
136 str = g_strdup_vprintf(msg, va);
137 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
143 /* Message for a channel. The `sender' is the nickname of the sender
144 received in the packet. The `channel_name' is the name of the channel. */
147 silc_channel_message(SilcClient client, SilcClientConnection conn,
148 SilcClientEntry sender, SilcChannelEntry channel,
149 SilcMessageFlags flags, char *msg)
151 SILC_SERVER_REC *server;
153 SILC_CHANNEL_REC *chanrec;
155 server = conn == NULL ? NULL : conn->context;
156 chanrec = silc_channel_find_entry(server, channel);
158 nick = silc_nicklist_find(chanrec, sender);
160 if (flags & SILC_MESSAGE_FLAG_ACTION)
161 printformat_module("fe-common/silc", server, channel->channel_name,
162 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, msg);
163 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
164 printformat_module("fe-common/silc", server, channel->channel_name,
165 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, msg);
167 signal_emit("message public", 6, server, msg,
168 nick == NULL ? "[<unknown>]" : nick->nick,
169 nick == NULL ? NULL : nick->host,
170 chanrec->name, nick);
173 /* Private message to the client. The `sender' is the nickname of the
174 sender received in the packet. */
177 silc_private_message(SilcClient client, SilcClientConnection conn,
178 SilcClientEntry sender, SilcMessageFlags flags,
181 SILC_SERVER_REC *server;
183 server = conn == NULL ? NULL : conn->context;
184 signal_emit("message private", 4, server, msg,
185 sender->nickname ? sender->nickname : "[<unknown>]",
186 sender->username ? sender->username : NULL);
189 /* Notify message to the client. The notify arguments are sent in the
190 same order as servers sends them. The arguments are same as received
191 from the server except for ID's. If ID is received application receives
192 the corresponding entry to the ID. For example, if Client ID is received
193 application receives SilcClientEntry. Also, if the notify type is
194 for channel the channel entry is sent to application (even if server
195 does not send it). */
202 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
203 static NOTIFY_REC notifies[] = {
204 { SILC_NOTIFY_TYPE_NONE, NULL },
205 { SILC_NOTIFY_TYPE_INVITE, "invite" },
206 { SILC_NOTIFY_TYPE_JOIN, "join" },
207 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
208 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
209 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
210 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
211 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
212 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
213 { SILC_NOTIFY_TYPE_MOTD, "motd" },
214 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
215 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
216 { SILC_NOTIFY_TYPE_KICKED, "kick" },
217 { SILC_NOTIFY_TYPE_KILLED, "kill" },
218 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
219 { SILC_NOTIFY_TYPE_BAN, "ban" },
222 static void silc_notify(SilcClient client, SilcClientConnection conn,
223 SilcNotifyType type, ...)
225 SILC_SERVER_REC *server;
228 server = conn == NULL ? NULL : conn->context;
231 if (type == SILC_NOTIFY_TYPE_NONE) {
232 /* Some generic notice from server */
233 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
234 } else if (type < MAX_NOTIFY) {
235 /* Send signal about the notify event */
237 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
238 signal_emit(signal, 2, server, va);
241 printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
247 /* Called to indicate that connection was either successfully established
248 or connecting failed. This is also the first time application receives
249 the SilcClientConnection objecet which it should save somewhere. */
252 silc_connect(SilcClient client, SilcClientConnection conn, int success)
254 SILC_SERVER_REC *server = conn->context;
257 server->connected = TRUE;
258 signal_emit("event connected", 1, server);
260 server->connection_lost = TRUE;
261 server->conn->context = NULL;
262 server_disconnect(SERVER(server));
266 /* Called to indicate that connection was disconnected to the server. */
269 silc_disconnect(SilcClient client, SilcClientConnection conn)
271 SILC_SERVER_REC *server = conn->context;
273 server->conn->context = NULL;
275 server->connection_lost = TRUE;
276 server_disconnect(SERVER(server));
279 /* Command handler. This function is called always in the command function.
280 If error occurs it will be called as well. `conn' is the associated
281 client connection. `cmd_context' is the command context that was
282 originally sent to the command. `success' is FALSE if error occured
283 during command. `command' is the command being processed. It must be
284 noted that this is not reply from server. This is merely called just
285 after application has called the command. Just to tell application
286 that the command really was processed. */
289 silc_command(SilcClient client, SilcClientConnection conn,
290 SilcClientCommandContext cmd_context, int success,
295 /* Client info resolving callback when JOIN command reply is received.
296 This will cache all users on the channel. */
298 void silc_client_join_get_users(SilcClient client,
299 SilcClientConnection conn,
300 SilcClientEntry *clients,
301 uint32 clients_count,
304 SilcChannelEntry channel = (SilcChannelEntry)context;
306 SILC_SERVER_REC *server = conn->context;
307 SILC_CHANNEL_REC *chanrec;
308 SilcClientEntry founder = NULL;
314 chanrec = silc_channel_find(server, channel->channel_name);
318 silc_list_start(channel->clients);
319 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
320 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
321 founder = chu->client;
322 silc_nicklist_insert(chanrec, chu, FALSE);
325 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
326 nicklist_set_own(CHANNEL(chanrec), ownnick);
327 signal_emit("channel joined", 1, chanrec);
330 printformat_module("fe-common/silc", server, channel->channel_name,
331 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
332 channel->channel_name, chanrec->topic);
334 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
337 if (founder == conn->local_entry)
338 printformat_module("fe-common/silc",
339 server, channel->channel_name, MSGLEVEL_CRAP,
340 SILCTXT_CHANNEL_FOUNDER_YOU,
341 channel->channel_name);
343 printformat_module("fe-common/silc",
344 server, channel->channel_name, MSGLEVEL_CRAP,
345 SILCTXT_CHANNEL_FOUNDER,
346 channel->channel_name, founder->nickname);
350 /* Command reply handler. This function is called always in the command reply
351 function. If error occurs it will be called as well. Normal scenario
352 is that it will be called after the received command data has been parsed
353 and processed. The function is used to pass the received command data to
356 `conn' is the associated client connection. `cmd_payload' is the command
357 payload data received from server and it can be ignored. It is provided
358 if the application would like to re-parse the received command data,
359 however, it must be noted that the data is parsed already by the library
360 thus the payload can be ignored. `success' is FALSE if error occured.
361 In this case arguments are not sent to the application. `command' is the
362 command reply being processed. The function has variable argument list
363 and each command defines the number and type of arguments it passes to the
364 application (on error they are not sent). */
367 silc_command_reply(SilcClient client, SilcClientConnection conn,
368 SilcCommandPayload cmd_payload, int success,
369 SilcCommand command, SilcCommandStatus status, ...)
372 SILC_SERVER_REC *server = conn->context;
373 SILC_CHANNEL_REC *chanrec;
376 va_start(vp, status);
379 case SILC_COMMAND_WHOIS:
381 char buf[1024], *nickname, *username, *realname;
385 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
386 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
388 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
391 silc_say_error("%s: %s", tmp,
392 silc_client_command_status_message(status));
394 silc_say_error("%s", silc_client_command_status_message(status));
401 (void)va_arg(vp, SilcClientEntry);
402 nickname = va_arg(vp, char *);
403 username = va_arg(vp, char *);
404 realname = va_arg(vp, char *);
405 channels = va_arg(vp, SilcBuffer);
406 mode = va_arg(vp, uint32);
407 idle = va_arg(vp, uint32);
409 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
410 SILCTXT_WHOIS_USERINFO, nickname, username,
414 SilcDList list = silc_channel_payload_parse_list(channels);
416 SilcChannelPayload entry;
417 memset(buf, 0, sizeof(buf));
418 silc_dlist_start(list);
419 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
420 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
422 char *name = silc_channel_get_name(entry, &name_len);
425 strncat(buf, m, strlen(m));
426 strncat(buf, name, name_len);
427 strncat(buf, " ", 1);
431 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
432 SILCTXT_WHOIS_CHANNELS, buf);
433 silc_channel_payload_list_free(list);
438 memset(buf, 0, sizeof(buf));
440 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
441 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
442 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
444 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
445 "SILC Operator " : "[Unknown mode] ");
447 if (mode & SILC_UMODE_GONE)
450 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
451 SILCTXT_WHOIS_MODES, buf);
454 if (idle && nickname) {
455 memset(buf, 0, sizeof(buf));
456 snprintf(buf, sizeof(buf) - 1, "%lu %s",
457 idle > 60 ? (idle / 60) : idle,
458 idle > 60 ? "minutes" : "seconds");
460 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
461 SILCTXT_WHOIS_IDLE, buf);
466 case SILC_COMMAND_WHOWAS:
468 char *nickname, *username, *realname;
470 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
471 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
473 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
476 silc_say_error("%s: %s", tmp,
477 silc_client_command_status_message(status));
479 silc_say_error("%s", silc_client_command_status_message(status));
486 (void)va_arg(vp, SilcClientEntry);
487 nickname = va_arg(vp, char *);
488 username = va_arg(vp, char *);
489 realname = va_arg(vp, char *);
491 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
492 SILCTXT_WHOWAS_USERINFO, nickname, username,
493 realname ? realname : "");
497 case SILC_COMMAND_INVITE:
499 SilcChannelEntry channel;
505 /* XXX should use irssi routines */
507 channel = va_arg(vp, SilcChannelEntry);
508 invite_list = va_arg(vp, char *);
511 silc_say(client, conn, "%s invite list: %s", channel->channel_name,
514 silc_say(client, conn, "%s invite list not set",
515 channel->channel_name);
519 case SILC_COMMAND_JOIN:
521 char *channel, *mode, *topic;
523 SilcChannelEntry channel_entry;
524 SilcBuffer client_id_list;
527 channel = va_arg(vp, char *);
528 channel_entry = va_arg(vp, SilcChannelEntry);
529 modei = va_arg(vp, uint32);
530 (void)va_arg(vp, uint32);
531 (void)va_arg(vp, unsigned char *);
532 (void)va_arg(vp, unsigned char *);
533 (void)va_arg(vp, unsigned char *);
534 topic = va_arg(vp, char *);
535 (void)va_arg(vp, unsigned char *);
536 list_count = va_arg(vp, uint32);
537 client_id_list = va_arg(vp, SilcBuffer);
542 chanrec = silc_channel_find(server, channel);
543 if (chanrec != NULL && !success)
544 channel_destroy(CHANNEL(chanrec));
545 else if (chanrec == NULL && success)
546 chanrec = silc_channel_create(server, channel, TRUE);
549 g_free_not_null(chanrec->topic);
550 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
551 signal_emit("channel topic changed", 1, chanrec);
554 mode = silc_client_chmode(modei, channel_entry);
555 g_free_not_null(chanrec->mode);
556 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
557 signal_emit("channel mode changed", 1, chanrec);
559 /* Resolve the client information */
560 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
561 silc_client_join_get_users,
566 case SILC_COMMAND_NICK:
568 SilcClientEntry client = va_arg(vp, SilcClientEntry);
574 old = g_strdup(server->nick);
575 server_change_nick(SERVER(server), client->nickname);
576 nicklist_rename_unique(SERVER(server),
577 server->conn->local_entry, server->nick,
578 client, client->nickname);
580 signal_emit("message own_nick", 4, server, server->nick, old, "");
585 case SILC_COMMAND_LIST:
589 unsigned char buf[256], tmp[16];
595 /* XXX should use irssi routines */
597 (void)va_arg(vp, SilcChannelEntry);
598 name = va_arg(vp, char *);
599 topic = va_arg(vp, char *);
600 usercount = va_arg(vp, int);
602 if (status == SILC_STATUS_LIST_START ||
603 status == SILC_STATUS_OK)
604 silc_say(client, conn,
605 " Channel Users Topic");
607 memset(buf, 0, sizeof(buf));
608 strncat(buf, " ", 2);
610 strncat(buf, name, len > 40 ? 40 : len);
612 for (i = 0; i < 40 - len; i++)
616 memset(tmp, 0, sizeof(tmp));
618 snprintf(tmp, sizeof(tmp), "%d", usercount);
623 for (i = 0; i < 10 - len; i++)
629 strncat(buf, topic, len);
632 silc_say(client, conn, "%s", buf);
636 case SILC_COMMAND_UMODE:
643 mode = va_arg(vp, uint32);
649 case SILC_COMMAND_OPER:
650 silc_say(client, conn, "You are now server operator");
653 case SILC_COMMAND_SILCOPER:
654 silc_say(client, conn, "You are now SILC operator");
657 case SILC_COMMAND_USERS:
659 SilcChannelEntry channel;
667 channel = va_arg(vp, SilcChannelEntry);
669 /* There are two ways to do this, either parse the list (that
670 the command_reply sends (just take it with va_arg()) or just
671 traverse the channel's client list. I'll do the latter. See
672 JOIN command reply for example for the list. */
674 silc_say(client, conn, "Users on %s", channel->channel_name);
676 line = silc_calloc(1024, sizeof(*line));
678 silc_list_start(channel->clients);
679 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
680 SilcClientEntry e = chu->client;
684 memset(line, 0, line_len);
686 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
688 line_len += strlen(e->nickname) + strlen(e->server) + 100;
689 line = silc_calloc(line_len, sizeof(*line));
692 memset(tmp, 0, sizeof(tmp));
693 m = silc_client_chumode_char(chu->mode);
695 strncat(line, " ", 1);
696 strncat(line, e->nickname, strlen(e->nickname));
697 strncat(line, e->server ? "@" : "", 1);
701 len1 = strlen(e->server);
702 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
706 memset(&line[29], 0, len1 - 29);
708 for (i = 0; i < 30 - len1 - 1; i++)
712 if (e->mode & SILC_UMODE_GONE)
716 strcat(tmp, m ? m : "");
717 strncat(line, tmp, strlen(tmp));
720 for (i = 0; i < 5 - strlen(tmp); i++)
723 strcat(line, e->username ? e->username : "");
725 silc_say(client, conn, "%s", line);
735 case SILC_COMMAND_BAN:
737 SilcChannelEntry channel;
743 /* XXX should use irssi routines */
745 channel = va_arg(vp, SilcChannelEntry);
746 ban_list = va_arg(vp, char *);
749 silc_say(client, conn, "%s ban list: %s", channel->channel_name,
752 silc_say(client, conn, "%s ban list not set", channel->channel_name);
756 case SILC_COMMAND_GETKEY:
760 SilcPublicKey public_key;
764 id_type = va_arg(vp, uint32);
765 entry = va_arg(vp, void *);
766 public_key = va_arg(vp, SilcPublicKey);
768 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
770 if (id_type == SILC_ID_CLIENT) {
771 silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
772 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
779 case SILC_COMMAND_TOPIC:
781 SilcChannelEntry channel;
787 channel = va_arg(vp, SilcChannelEntry);
788 topic = va_arg(vp, char *);
791 chanrec = silc_channel_find_entry(server, channel);
793 g_free_not_null(chanrec->topic);
794 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
795 signal_emit("channel topic changed", 1, chanrec);
797 printformat_module("fe-common/silc", server, channel->channel_name,
798 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
799 channel->channel_name, topic);
808 /* Internal routine to verify public key. If the `completion' is provided
809 it will be called to indicate whether public was verified or not. */
813 SilcClientConnection conn;
818 SilcSKEPKType pk_type;
819 SilcVerifyPublicKey completion;
823 static void verify_public_key_completion(const char *line, void *context)
825 PublicKeyVerify verify = (PublicKeyVerify)context;
827 if (line[0] == 'Y' || line[0] == 'y') {
828 /* Call the completion */
829 if (verify->completion)
830 verify->completion(TRUE, verify->context);
832 /* Save the key for future checking */
833 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
834 verify->pk_len, SILC_PKCS_FILE_PEM);
836 /* Call the completion */
837 if (verify->completion)
838 verify->completion(FALSE, verify->context);
840 silc_say(verify->client,
841 verify->conn, "Will not accept the %s key", verify->entity);
844 silc_free(verify->filename);
845 silc_free(verify->entity);
850 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
851 SilcSocketType conn_type, unsigned char *pk,
852 uint32 pk_len, SilcSKEPKType pk_type,
853 SilcVerifyPublicKey completion, void *context)
856 char file[256], filename[256], *fingerprint;
859 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
860 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
861 "server" : "client");
862 PublicKeyVerify verify;
864 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
865 silc_say(client, conn, "We don't support %s public key type %d",
868 completion(FALSE, context);
872 pw = getpwuid(getuid());
875 completion(FALSE, context);
879 memset(filename, 0, sizeof(filename));
880 memset(file, 0, sizeof(file));
882 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
883 conn_type == SILC_SOCKET_TYPE_ROUTER) {
884 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
885 conn->sock->hostname, conn->sock->port);
886 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
887 pw->pw_dir, entity, file);
889 /* Replace all whitespaces with `_'. */
890 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
891 for (i = 0; i < strlen(fingerprint); i++)
892 if (fingerprint[i] == ' ')
893 fingerprint[i] = '_';
895 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
896 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
897 pw->pw_dir, entity, file);
898 silc_free(fingerprint);
901 /* Take fingerprint of the public key */
902 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
904 verify = silc_calloc(1, sizeof(*verify));
905 verify->client = client;
907 verify->filename = strdup(filename);
908 verify->entity = strdup(entity);
910 verify->pk_len = pk_len;
911 verify->pk_type = pk_type;
912 verify->completion = completion;
913 verify->context = context;
915 /* Check whether this key already exists */
916 if (stat(filename, &st) < 0) {
917 /* Key does not exist, ask user to verify the key and save it */
919 silc_say(client, conn, "Received %s public key", entity);
920 silc_say(client, conn, "Fingerprint for the %s key is", entity);
921 silc_say(client, conn, "%s", fingerprint);
923 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
924 "Would you like to accept the key (y/n)? ", 0,
926 silc_free(fingerprint);
929 /* The key already exists, verify it. */
930 SilcPublicKey public_key;
931 unsigned char *encpk;
934 /* Load the key file */
935 if (!silc_pkcs_load_public_key(filename, &public_key,
937 if (!silc_pkcs_load_public_key(filename, &public_key,
938 SILC_PKCS_FILE_BIN)) {
939 silc_say(client, conn, "Received %s public key", entity);
940 silc_say(client, conn, "Fingerprint for the %s key is", entity);
941 silc_say(client, conn, "%s", fingerprint);
942 silc_say(client, conn, "Could not load your local copy of the %s key",
944 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
945 "Would you like to accept the key "
948 silc_free(fingerprint);
952 /* Encode the key data */
953 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
955 silc_say(client, conn, "Received %s public key", entity);
956 silc_say(client, conn, "Fingerprint for the %s key is", entity);
957 silc_say(client, conn, "%s", fingerprint);
958 silc_say(client, conn, "Your local copy of the %s key is malformed",
960 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
961 "Would you like to accept the key "
964 silc_free(fingerprint);
968 /* Compare the keys */
969 if (memcmp(encpk, pk, encpk_len)) {
970 silc_say(client, conn, "Received %s public key", entity);
971 silc_say(client, conn, "Fingerprint for the %s key is", entity);
972 silc_say(client, conn, "%s", fingerprint);
973 silc_say(client, conn, "%s key does not match with your local copy",
975 silc_say(client, conn,
976 "It is possible that the key has expired or changed");
977 silc_say(client, conn, "It is also possible that some one is performing "
978 "man-in-the-middle attack");
980 /* Ask user to verify the key and save it */
981 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
982 "Would you like to accept the key "
985 silc_free(fingerprint);
989 /* Local copy matched */
991 completion(TRUE, context);
992 silc_free(fingerprint);
996 /* Verifies received public key. The `conn_type' indicates which entity
997 (server, client etc.) has sent the public key. If user decides to trust
998 the key may be saved as trusted public key for later use. The
999 `completion' must be called after the public key has been verified. */
1002 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1003 SilcSocketType conn_type, unsigned char *pk,
1004 uint32 pk_len, SilcSKEPKType pk_type,
1005 SilcVerifyPublicKey completion, void *context)
1007 silc_verify_public_key_internal(client, conn, conn_type, pk,
1009 completion, context);
1012 /* Asks passphrase from user on the input line. */
1015 SilcAskPassphrase completion;
1019 static void ask_passphrase_completion(const char *passphrase, void *context)
1021 AskPassphrase p = (AskPassphrase)context;
1022 p->completion((unsigned char *)passphrase,
1023 passphrase ? strlen(passphrase) : 0, p->context);
1027 static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1028 SilcAskPassphrase completion, void *context)
1030 AskPassphrase p = silc_calloc(1, sizeof(*p));
1031 p->completion = completion;
1032 p->context = context;
1034 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1035 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1038 /* Find authentication method and authentication data by hostname and
1039 port. The hostname may be IP address as well. The found authentication
1040 method and authentication data is returned to `auth_meth', `auth_data'
1041 and `auth_data_len'. The function returns TRUE if authentication method
1042 is found and FALSE if not. `conn' may be NULL. */
1045 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1046 char *hostname, uint16 port,
1047 SilcProtocolAuthMeth *auth_meth,
1048 unsigned char **auth_data,
1049 uint32 *auth_data_len)
1052 /* XXX must resolve from configuration whether this connection has
1053 any specific authentication data */
1055 *auth_meth = SILC_AUTH_NONE;
1062 /* Notifies application that failure packet was received. This is called
1063 if there is some protocol active in the client. The `protocol' is the
1064 protocol context. The `failure' is opaque pointer to the failure
1065 indication. Note, that the `failure' is protocol dependant and application
1066 must explicitly cast it to correct type. Usually `failure' is 32 bit
1067 failure type (see protocol specs for all protocol failure types). */
1070 silc_failure(SilcClient client, SilcClientConnection conn,
1071 SilcProtocol protocol, void *failure)
1073 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1074 SilcSKEStatus status = (SilcSKEStatus)failure;
1076 if (status == SILC_SKE_STATUS_BAD_VERSION)
1077 silc_say_error("You are running incompatible client version (it may be "
1078 "too old or too new)");
1079 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1080 silc_say_error("Server does not support your public key type");
1081 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1082 silc_say_error("Server does not support one of your proposed KE group");
1083 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1084 silc_say_error("Server does not support one of your proposed cipher");
1085 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1086 silc_say_error("Server does not support one of your proposed PKCS");
1087 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1088 silc_say_error("Server does not support one of your proposed "
1090 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1091 silc_say_error("Server does not support one of your proposed HMAC");
1092 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1093 silc_say_error("Incorrect signature");
1096 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1097 uint32 err = (uint32)failure;
1099 if (err == SILC_AUTH_FAILED)
1100 silc_say(client, conn, "Authentication failed");
1104 /* Asks whether the user would like to perform the key agreement protocol.
1105 This is called after we have received an key agreement packet or an
1106 reply to our key agreement packet. This returns TRUE if the user wants
1107 the library to perform the key agreement protocol and FALSE if it is not
1108 desired (application may start it later by calling the function
1109 silc_client_perform_key_agreement). */
1112 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1113 SilcClientEntry client_entry, char *hostname,
1115 SilcKeyAgreementCallback *completion,
1120 /* We will just display the info on the screen and return FALSE and user
1121 will have to start the key agreement with a command. */
1124 memset(host, 0, sizeof(host));
1125 snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port);
1128 silc_say(client, conn, "%s wants to perform key agreement %s",
1129 client_entry->nickname, hostname ? host : "");
1137 /* SILC client operations */
1138 SilcClientOperations ops = {
1140 silc_channel_message,
1141 silc_private_message,
1147 silc_get_auth_method,
1148 silc_verify_public_key,
1149 silc_ask_passphrase,
1154 static int my_silc_scheduler(void)
1156 silc_schedule_one(0);
1160 static CHATNET_REC *create_chatnet(void)
1162 return g_malloc0(sizeof(CHATNET_REC));
1165 static SERVER_SETUP_REC *create_server_setup(void)
1167 return g_malloc0(sizeof(SERVER_SETUP_REC));
1170 static CHANNEL_SETUP_REC *create_channel_setup(void)
1172 return g_malloc0(sizeof(CHANNEL_SETUP_REC));
1175 static SERVER_CONNECT_REC *create_server_connect(void)
1177 return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
1180 /* Checks user information and saves them to the config file it they
1181 do not exist there already. */
1183 static void silc_init_userinfo(void)
1185 const char *set, *nick, *user_name;
1188 /* check if nick/username/realname wasn't read from setup.. */
1189 set = settings_get_str("real_name");
1190 if (set == NULL || *set == '\0') {
1191 str = g_getenv("SILCNAME");
1193 str = g_getenv("IRCNAME");
1194 settings_set_str("real_name",
1195 str != NULL ? str : g_get_real_name());
1199 user_name = settings_get_str("user_name");
1200 if (user_name == NULL || *user_name == '\0') {
1201 str = g_getenv("SILCUSER");
1203 str = g_getenv("IRCUSER");
1204 settings_set_str("user_name",
1205 str != NULL ? str : g_get_user_name());
1207 user_name = settings_get_str("user_name");
1211 nick = settings_get_str("nick");
1212 if (nick == NULL || *nick == '\0') {
1213 str = g_getenv("SILCNICK");
1215 str = g_getenv("IRCNICK");
1216 settings_set_str("nick", str != NULL ? str : user_name);
1218 nick = settings_get_str("nick");
1221 /* alternate nick */
1222 set = settings_get_str("alternate_nick");
1223 if (set == NULL || *set == '\0') {
1224 if (strlen(nick) < 9)
1225 str = g_strconcat(nick, "_", NULL);
1227 str = g_strdup(nick);
1228 str[strlen(str)-1] = '_';
1230 settings_set_str("alternate_nick", str);
1235 set = settings_get_str("hostname");
1236 if (set == NULL || *set == '\0') {
1237 str = g_getenv("SILCHOST");
1239 str = g_getenv("IRCHOST");
1241 settings_set_str("hostname", str);
1247 static void silc_log_info(char *message)
1249 fprintf(stderr, "%s\n", message);
1252 static void silc_log_warning(char *message)
1254 fprintf(stderr, "%s\n", message);
1257 static void silc_log_error(char *message)
1259 fprintf(stderr, "%s\n", message);
1262 /* Init SILC. Called from src/fe-text/silc.c */
1264 void silc_core_init(void)
1266 static struct poptOption options[] = {
1267 { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0,
1268 "Create new public key pair", NULL },
1269 { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0,
1270 "Set the PKCS of the public key pair", "PKCS" },
1271 { "bits", 0, POPT_ARG_INT, &opt_bits, 0,
1272 "Set the length of the public key pair", "VALUE" },
1273 { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0,
1274 "Show the contents of the public key", "FILE" },
1275 { "debug", 'd', POPT_ARG_NONE, &opt_debug, 0,
1276 "Enable debugging", NULL },
1277 { NULL, '\0', 0, NULL }
1280 args_register(options);
1283 /* Finalize init. Called from src/fe-text/silc.c */
1285 void silc_core_init_finish(void)
1287 CHAT_PROTOCOL_REC *rec;
1289 if (opt_create_keypair == TRUE) {
1290 /* Create new key pair and exit */
1291 silc_cipher_register_default();
1292 silc_pkcs_register_default();
1293 silc_hash_register_default();
1294 silc_hmac_register_default();
1295 silc_client_create_key_pair(opt_pkcs, opt_bits,
1296 NULL, NULL, NULL, NULL, NULL);
1302 silc_cipher_register_default();
1303 silc_pkcs_register_default();
1304 silc_hash_register_default();
1305 silc_hmac_register_default();
1306 silc_client_show_key(opt_keyfile);
1310 silc_debug = opt_debug;
1311 silc_log_set_callbacks(silc_log_info, silc_log_warning,
1312 silc_log_error, NULL);
1314 /* Do some irssi initializing */
1315 settings_add_bool("server", "skip_motd", FALSE);
1316 settings_add_str("server", "alternate_nick", NULL);
1317 silc_init_userinfo();
1319 /* Allocate SILC client */
1320 silc_client = silc_client_alloc(&ops, NULL);
1322 /* Load local config file */
1323 silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
1325 /* Get user information */
1326 silc_client->username = g_strdup(settings_get_str("user_name"));
1327 silc_client->hostname = silc_net_localhost();
1328 silc_client->realname = g_strdup(settings_get_str("real_name"));
1330 /* Register all configured ciphers, PKCS and hash functions. */
1332 silc_config->client = silc_client;
1333 if (!silc_client_config_register_ciphers(silc_config))
1334 silc_cipher_register_default();
1335 if (!silc_client_config_register_pkcs(silc_config))
1336 silc_pkcs_register_default();
1337 if (!silc_client_config_register_hashfuncs(silc_config))
1338 silc_hash_register_default();
1339 if (!silc_client_config_register_hmacs(silc_config))
1340 silc_hmac_register_default();
1342 /* Register default ciphers, pkcs, hash funtions and hmacs. */
1343 silc_cipher_register_default();
1344 silc_pkcs_register_default();
1345 silc_hash_register_default();
1346 silc_hmac_register_default();
1349 /* Check ~/.silc directory and public and private keys */
1350 if (silc_client_check_silc_dir() == FALSE) {
1355 /* Load public and private key */
1356 if (silc_client_load_keys(silc_client) == FALSE) {
1361 /* Initialize the SILC client */
1362 if (!silc_client_init(silc_client)) {
1367 /* Register SILC to the irssi */
1368 rec = g_new0(CHAT_PROTOCOL_REC, 1);
1370 rec->fullname = "Secure Internet Live Conferencing";
1371 rec->chatnet = "silcnet";
1372 rec->create_chatnet = create_chatnet;
1373 rec->create_server_setup = create_server_setup;
1374 rec->create_channel_setup = create_channel_setup;
1375 rec->create_server_connect = create_server_connect;
1376 rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
1377 silc_server_connect;
1378 rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
1379 silc_channel_create;
1380 rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
1383 chat_protocol_register(rec);
1387 silc_channels_init();
1388 silc_queries_init();
1390 idletag = g_timeout_add(50, (GSourceFunc) my_silc_scheduler, NULL);
1393 /* Deinit SILC. Called from src/fe-text/silc.c */
1395 void silc_core_deinit(void)
1397 if (idletag != -1) {
1398 signal_emit("chat protocol deinit", 1,
1399 chat_protocol_find("SILC"));
1401 silc_server_deinit();
1402 silc_channels_deinit();
1403 silc_queries_deinit();
1405 chat_protocol_unregister("SILC");
1407 g_source_remove(idletag);
1410 g_free(silc_client->username);
1411 g_free(silc_client->realname);
1412 silc_client_free(silc_client);