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)
162 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
165 signal_emit("message public", 6, server, msg,
166 nick == NULL ? "[<unknown>]" : nick->nick,
167 nick == NULL ? NULL : nick->host,
168 chanrec->name, nick);
171 /* Private message to the client. The `sender' is the nickname of the
172 sender received in the packet. */
175 silc_private_message(SilcClient client, SilcClientConnection conn,
176 SilcClientEntry sender, SilcMessageFlags flags,
179 SILC_SERVER_REC *server;
181 server = conn == NULL ? NULL : conn->context;
182 signal_emit("message private", 4, server, msg,
183 sender->nickname ? sender->nickname : "[<unknown>]",
184 sender->username ? sender->username : NULL);
187 /* Notify message to the client. The notify arguments are sent in the
188 same order as servers sends them. The arguments are same as received
189 from the server except for ID's. If ID is received application receives
190 the corresponding entry to the ID. For example, if Client ID is received
191 application receives SilcClientEntry. Also, if the notify type is
192 for channel the channel entry is sent to application (even if server
193 does not send it). */
200 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
201 static NOTIFY_REC notifies[] = {
202 { SILC_NOTIFY_TYPE_NONE, NULL },
203 { SILC_NOTIFY_TYPE_INVITE, "invite" },
204 { SILC_NOTIFY_TYPE_JOIN, "join" },
205 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
206 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
207 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
208 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
209 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
210 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
211 { SILC_NOTIFY_TYPE_MOTD, "motd" },
212 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
213 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
214 { SILC_NOTIFY_TYPE_KICKED, "kick" },
215 { SILC_NOTIFY_TYPE_KILLED, "kill" },
216 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
217 { SILC_NOTIFY_TYPE_BAN, "ban" },
220 static void silc_notify(SilcClient client, SilcClientConnection conn,
221 SilcNotifyType type, ...)
223 SILC_SERVER_REC *server;
226 server = conn == NULL ? NULL : conn->context;
229 if (type == SILC_NOTIFY_TYPE_NONE) {
230 /* Some generic notice from server */
231 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
232 } else if (type < MAX_NOTIFY) {
233 /* Send signal about the notify event */
235 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
236 signal_emit(signal, 2, server, va);
239 printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
245 /* Called to indicate that connection was either successfully established
246 or connecting failed. This is also the first time application receives
247 the SilcClientConnection objecet which it should save somewhere. */
250 silc_connect(SilcClient client, SilcClientConnection conn, int success)
252 SILC_SERVER_REC *server = conn->context;
255 server->connected = TRUE;
256 signal_emit("event connected", 1, server);
258 server->connection_lost = TRUE;
259 server->conn->context = NULL;
260 server_disconnect(SERVER(server));
264 /* Called to indicate that connection was disconnected to the server. */
267 silc_disconnect(SilcClient client, SilcClientConnection conn)
269 SILC_SERVER_REC *server = conn->context;
271 server->conn->context = NULL;
273 server->connection_lost = TRUE;
274 server_disconnect(SERVER(server));
277 /* Command handler. This function is called always in the command function.
278 If error occurs it will be called as well. `conn' is the associated
279 client connection. `cmd_context' is the command context that was
280 originally sent to the command. `success' is FALSE if error occured
281 during command. `command' is the command being processed. It must be
282 noted that this is not reply from server. This is merely called just
283 after application has called the command. Just to tell application
284 that the command really was processed. */
287 silc_command(SilcClient client, SilcClientConnection conn,
288 SilcClientCommandContext cmd_context, int success,
293 /* Client info resolving callback when JOIN command reply is received.
294 This will cache all users on the channel. */
296 void silc_client_join_get_users(SilcClient client,
297 SilcClientConnection conn,
298 SilcClientEntry *clients,
299 uint32 clients_count,
302 SilcChannelEntry channel = (SilcChannelEntry)context;
304 SILC_SERVER_REC *server = conn->context;
305 SILC_CHANNEL_REC *chanrec;
306 SilcClientEntry founder = NULL;
312 chanrec = silc_channel_find(server, channel->channel_name);
316 silc_list_start(channel->clients);
317 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
318 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
319 founder = chu->client;
320 silc_nicklist_insert(chanrec, chu, FALSE);
323 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
324 nicklist_set_own(CHANNEL(chanrec), ownnick);
325 signal_emit("channel joined", 1, chanrec);
328 printformat_module("fe-common/silc", server, channel->channel_name,
329 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
330 channel->channel_name, chanrec->topic);
332 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
335 if (founder == conn->local_entry)
336 printformat_module("fe-common/silc",
337 server, channel->channel_name, MSGLEVEL_CRAP,
338 SILCTXT_CHANNEL_FOUNDER_YOU,
339 channel->channel_name);
341 printformat_module("fe-common/silc",
342 server, channel->channel_name, MSGLEVEL_CRAP,
343 SILCTXT_CHANNEL_FOUNDER,
344 channel->channel_name, founder->nickname);
348 /* Command reply handler. This function is called always in the command reply
349 function. If error occurs it will be called as well. Normal scenario
350 is that it will be called after the received command data has been parsed
351 and processed. The function is used to pass the received command data to
354 `conn' is the associated client connection. `cmd_payload' is the command
355 payload data received from server and it can be ignored. It is provided
356 if the application would like to re-parse the received command data,
357 however, it must be noted that the data is parsed already by the library
358 thus the payload can be ignored. `success' is FALSE if error occured.
359 In this case arguments are not sent to the application. `command' is the
360 command reply being processed. The function has variable argument list
361 and each command defines the number and type of arguments it passes to the
362 application (on error they are not sent). */
365 silc_command_reply(SilcClient client, SilcClientConnection conn,
366 SilcCommandPayload cmd_payload, int success,
367 SilcCommand command, SilcCommandStatus status, ...)
370 SILC_SERVER_REC *server = conn->context;
371 SILC_CHANNEL_REC *chanrec;
374 va_start(vp, status);
377 case SILC_COMMAND_WHOIS:
379 char buf[1024], *nickname, *username, *realname;
384 /* XXX should use irssi routines */
386 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
387 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
389 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
392 client->ops->say(client, conn, "%s: %s", tmp,
393 silc_client_command_status_message(status));
395 client->ops->say(client, conn, "%s",
396 silc_client_command_status_message(status));
403 (void)va_arg(vp, SilcClientEntry);
404 nickname = va_arg(vp, char *);
405 username = va_arg(vp, char *);
406 realname = va_arg(vp, char *);
407 channels = va_arg(vp, SilcBuffer);
408 mode = va_arg(vp, uint32);
409 idle = va_arg(vp, uint32);
411 memset(buf, 0, sizeof(buf));
414 len = strlen(nickname);
415 strncat(buf, nickname, len);
416 strncat(buf, " is ", 4);
420 strncat(buf, username, strlen(username));
424 strncat(buf, " (", 2);
425 strncat(buf, realname, strlen(realname));
426 strncat(buf, ")", 1);
429 client->ops->say(client, conn, "%s", buf);
432 SilcDList list = silc_channel_payload_parse_list(channels);
434 SilcChannelPayload entry;
436 memset(buf, 0, sizeof(buf));
437 strcat(buf, "on channels: ");
439 silc_dlist_start(list);
440 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
441 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
443 char *name = silc_channel_get_name(entry, &name_len);
446 strncat(buf, m, strlen(m));
447 strncat(buf, name, name_len);
448 strncat(buf, " ", 1);
452 client->ops->say(client, conn, "%s", buf);
453 silc_channel_payload_list_free(list);
458 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
459 (mode & SILC_UMODE_ROUTER_OPERATOR))
460 client->ops->say(client, conn, "%s is %s", nickname,
461 (mode & SILC_UMODE_SERVER_OPERATOR) ?
463 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
464 "SILC Operator" : "[Unknown mode]");
466 if (mode & SILC_UMODE_GONE)
467 client->ops->say(client, conn, "%s is gone", nickname);
470 if (idle && nickname)
471 client->ops->say(client, conn, "%s has been idle %d %s",
473 idle > 60 ? (idle / 60) : idle,
474 idle > 60 ? "minutes" : "seconds");
478 case SILC_COMMAND_WHOWAS:
480 char buf[1024], *nickname, *username, *realname;
483 /* XXX should use irssi routines */
485 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
486 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
488 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
491 client->ops->say(client, conn, "%s: %s", tmp,
492 silc_client_command_status_message(status));
494 client->ops->say(client, conn, "%s",
495 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 memset(buf, 0, sizeof(buf));
510 len = strlen(nickname);
511 strncat(buf, nickname, len);
512 strncat(buf, " was ", 5);
516 strncat(buf, username, strlen(nickname));
520 strncat(buf, " (", 2);
521 strncat(buf, realname, strlen(realname));
522 strncat(buf, ")", 1);
525 client->ops->say(client, conn, "%s", buf);
529 case SILC_COMMAND_INVITE:
531 SilcChannelEntry channel;
537 /* XXX should use irssi routines */
539 channel = va_arg(vp, SilcChannelEntry);
540 invite_list = va_arg(vp, char *);
543 silc_say(client, conn, "%s invite list: %s", channel->channel_name,
546 silc_say(client, conn, "%s invite list not set",
547 channel->channel_name);
551 case SILC_COMMAND_JOIN:
553 char *channel, *mode, *topic;
555 SilcChannelEntry channel_entry;
556 SilcBuffer client_id_list;
559 channel = va_arg(vp, char *);
560 channel_entry = va_arg(vp, SilcChannelEntry);
561 modei = va_arg(vp, uint32);
562 (void)va_arg(vp, uint32);
563 (void)va_arg(vp, unsigned char *);
564 (void)va_arg(vp, unsigned char *);
565 (void)va_arg(vp, unsigned char *);
566 topic = va_arg(vp, char *);
567 (void)va_arg(vp, unsigned char *);
568 list_count = va_arg(vp, uint32);
569 client_id_list = va_arg(vp, SilcBuffer);
574 chanrec = silc_channel_find(server, channel);
575 if (chanrec != NULL && !success)
576 channel_destroy(CHANNEL(chanrec));
577 else if (chanrec == NULL && success)
578 chanrec = silc_channel_create(server, channel, TRUE);
581 g_free_not_null(chanrec->topic);
582 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
583 signal_emit("channel topic changed", 1, chanrec);
586 mode = silc_client_chmode(modei, channel_entry);
587 g_free_not_null(chanrec->mode);
588 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
589 signal_emit("channel mode changed", 1, chanrec);
591 /* Resolve the client information */
592 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
593 silc_client_join_get_users,
598 case SILC_COMMAND_NICK:
600 SilcClientEntry client = va_arg(vp, SilcClientEntry);
606 old = g_strdup(server->nick);
607 server_change_nick(SERVER(server), client->nickname);
608 nicklist_rename_unique(SERVER(server),
609 server->conn->local_entry, server->nick,
610 client, client->nickname);
612 signal_emit("message own_nick", 4, server, server->nick, old, "");
617 case SILC_COMMAND_LIST:
621 unsigned char buf[256], tmp[16];
627 /* XXX should use irssi routines */
629 (void)va_arg(vp, SilcChannelEntry);
630 name = va_arg(vp, char *);
631 topic = va_arg(vp, char *);
632 usercount = va_arg(vp, int);
634 if (status == SILC_STATUS_LIST_START ||
635 status == SILC_STATUS_OK)
636 silc_say(client, conn,
637 " Channel Users Topic");
639 memset(buf, 0, sizeof(buf));
640 strncat(buf, " ", 2);
642 strncat(buf, name, len > 40 ? 40 : len);
644 for (i = 0; i < 40 - len; i++)
648 memset(tmp, 0, sizeof(tmp));
650 snprintf(tmp, sizeof(tmp), "%d", usercount);
655 for (i = 0; i < 10 - len; i++)
661 strncat(buf, topic, len);
664 silc_say(client, conn, "%s", buf);
668 case SILC_COMMAND_UMODE:
675 mode = va_arg(vp, uint32);
681 case SILC_COMMAND_OPER:
683 if (status == SILC_STATUS_OK) {
684 conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
685 if (app->screen->bottom_line->umode)
686 silc_free(app->screen->bottom_line->umode);
687 app->screen->bottom_line->umode = strdup("Server Operator");;
688 silc_screen_print_bottom_line(app->screen, 0);
693 case SILC_COMMAND_SILCOPER:
695 if (status == SILC_STATUS_OK) {
696 conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
697 if (app->screen->bottom_line->umode)
698 silc_free(app->screen->bottom_line->umode);
699 app->screen->bottom_line->umode = strdup("SILC Operator");;
700 silc_screen_print_bottom_line(app->screen, 0);
705 case SILC_COMMAND_USERS:
707 SilcChannelEntry channel;
715 channel = va_arg(vp, SilcChannelEntry);
717 /* There are two ways to do this, either parse the list (that
718 the command_reply sends (just take it with va_arg()) or just
719 traverse the channel's client list. I'll do the latter. See
720 JOIN command reply for example for the list. */
722 silc_say(client, conn, "Users on %s", channel->channel_name);
724 line = silc_calloc(1024, sizeof(*line));
726 silc_list_start(channel->clients);
727 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
728 SilcClientEntry e = chu->client;
732 memset(line, 0, line_len);
734 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
736 line_len += strlen(e->nickname) + strlen(e->server) + 100;
737 line = silc_calloc(line_len, sizeof(*line));
740 memset(tmp, 0, sizeof(tmp));
741 m = silc_client_chumode_char(chu->mode);
743 strncat(line, " ", 1);
744 strncat(line, e->nickname, strlen(e->nickname));
745 strncat(line, e->server ? "@" : "", 1);
749 len1 = strlen(e->server);
750 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
754 memset(&line[29], 0, len1 - 29);
756 for (i = 0; i < 30 - len1 - 1; i++)
760 if (e->mode & SILC_UMODE_GONE)
764 strcat(tmp, m ? m : "");
765 strncat(line, tmp, strlen(tmp));
768 for (i = 0; i < 5 - strlen(tmp); i++)
771 strcat(line, e->username ? e->username : "");
773 silc_say(client, conn, "%s", line);
783 case SILC_COMMAND_BAN:
785 SilcChannelEntry channel;
791 /* XXX should use irssi routines */
793 channel = va_arg(vp, SilcChannelEntry);
794 ban_list = va_arg(vp, char *);
797 silc_say(client, conn, "%s ban list: %s", channel->channel_name,
800 silc_say(client, conn, "%s ban list not set", channel->channel_name);
804 case SILC_COMMAND_GETKEY:
808 SilcPublicKey public_key;
812 id_type = va_arg(vp, uint32);
813 entry = va_arg(vp, void *);
814 public_key = va_arg(vp, SilcPublicKey);
816 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
818 if (id_type == SILC_ID_CLIENT) {
819 silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
820 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
827 case SILC_COMMAND_TOPIC:
829 SilcChannelEntry channel;
835 channel = va_arg(vp, SilcChannelEntry);
836 topic = va_arg(vp, char *);
838 /* XXX should use irssi routines */
841 silc_say(client, conn,
842 "Topic on channel %s: %s", channel->channel_name,
851 /* Internal routine to verify public key. If the `completion' is provided
852 it will be called to indicate whether public was verified or not. */
856 SilcClientConnection conn;
861 SilcSKEPKType pk_type;
862 SilcVerifyPublicKey completion;
866 static void verify_public_key_completion(const char *line, void *context)
868 PublicKeyVerify verify = (PublicKeyVerify)context;
870 if (line[0] == 'Y' || line[0] == 'y') {
871 /* Call the completion */
872 if (verify->completion)
873 verify->completion(TRUE, verify->context);
875 /* Save the key for future checking */
876 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
877 verify->pk_len, SILC_PKCS_FILE_PEM);
879 /* Call the completion */
880 if (verify->completion)
881 verify->completion(FALSE, verify->context);
883 silc_say(verify->client,
884 verify->conn, "Will not accept the %s key", verify->entity);
887 silc_free(verify->filename);
888 silc_free(verify->entity);
893 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
894 SilcSocketType conn_type, unsigned char *pk,
895 uint32 pk_len, SilcSKEPKType pk_type,
896 SilcVerifyPublicKey completion, void *context)
899 char file[256], filename[256], *fingerprint;
902 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
903 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
904 "server" : "client");
905 PublicKeyVerify verify;
907 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
908 silc_say(client, conn, "We don't support %s public key type %d",
911 completion(FALSE, context);
915 pw = getpwuid(getuid());
918 completion(FALSE, context);
922 memset(filename, 0, sizeof(filename));
923 memset(file, 0, sizeof(file));
925 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
926 conn_type == SILC_SOCKET_TYPE_ROUTER) {
927 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
928 conn->sock->hostname, conn->sock->port);
929 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
930 pw->pw_dir, entity, file);
932 /* Replace all whitespaces with `_'. */
933 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
934 for (i = 0; i < strlen(fingerprint); i++)
935 if (fingerprint[i] == ' ')
936 fingerprint[i] = '_';
938 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
939 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
940 pw->pw_dir, entity, file);
941 silc_free(fingerprint);
944 /* Take fingerprint of the public key */
945 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
947 verify = silc_calloc(1, sizeof(*verify));
948 verify->client = client;
950 verify->filename = strdup(filename);
951 verify->entity = strdup(entity);
953 verify->pk_len = pk_len;
954 verify->pk_type = pk_type;
955 verify->completion = completion;
956 verify->context = context;
958 /* Check whether this key already exists */
959 if (stat(filename, &st) < 0) {
960 /* Key does not exist, ask user to verify the key and save it */
962 silc_say(client, conn, "Received %s public key", entity);
963 silc_say(client, conn, "Fingerprint for the %s key is", entity);
964 silc_say(client, conn, "%s", fingerprint);
966 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
967 "Would you like to accept the key (y/n)? ", 0,
969 silc_free(fingerprint);
972 /* The key already exists, verify it. */
973 SilcPublicKey public_key;
974 unsigned char *encpk;
977 /* Load the key file */
978 if (!silc_pkcs_load_public_key(filename, &public_key,
980 if (!silc_pkcs_load_public_key(filename, &public_key,
981 SILC_PKCS_FILE_BIN)) {
982 silc_say(client, conn, "Received %s public key", entity);
983 silc_say(client, conn, "Fingerprint for the %s key is", entity);
984 silc_say(client, conn, "%s", fingerprint);
985 silc_say(client, conn, "Could not load your local copy of the %s key",
987 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
988 "Would you like to accept the key "
991 silc_free(fingerprint);
995 /* Encode the key data */
996 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
998 silc_say(client, conn, "Received %s public key", entity);
999 silc_say(client, conn, "Fingerprint for the %s key is", entity);
1000 silc_say(client, conn, "%s", fingerprint);
1001 silc_say(client, conn, "Your local copy of the %s key is malformed",
1003 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1004 "Would you like to accept the key "
1005 "anyway (y/n)? ", 0,
1007 silc_free(fingerprint);
1011 /* Compare the keys */
1012 if (memcmp(encpk, pk, encpk_len)) {
1013 silc_say(client, conn, "Received %s public key", entity);
1014 silc_say(client, conn, "Fingerprint for the %s key is", entity);
1015 silc_say(client, conn, "%s", fingerprint);
1016 silc_say(client, conn, "%s key does not match with your local copy",
1018 silc_say(client, conn,
1019 "It is possible that the key has expired or changed");
1020 silc_say(client, conn, "It is also possible that some one is performing "
1021 "man-in-the-middle attack");
1023 /* Ask user to verify the key and save it */
1024 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1025 "Would you like to accept the key "
1026 "anyway (y/n)? ", 0,
1028 silc_free(fingerprint);
1032 /* Local copy matched */
1034 completion(TRUE, context);
1035 silc_free(fingerprint);
1039 /* Verifies received public key. The `conn_type' indicates which entity
1040 (server, client etc.) has sent the public key. If user decides to trust
1041 the key may be saved as trusted public key for later use. The
1042 `completion' must be called after the public key has been verified. */
1045 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1046 SilcSocketType conn_type, unsigned char *pk,
1047 uint32 pk_len, SilcSKEPKType pk_type,
1048 SilcVerifyPublicKey completion, void *context)
1050 silc_verify_public_key_internal(client, conn, conn_type, pk,
1052 completion, context);
1055 /* Asks passphrase from user on the input line. */
1058 SilcAskPassphrase completion;
1062 static void ask_passphrase_completion(const char *passphrase, void *context)
1064 AskPassphrase p = (AskPassphrase)context;
1065 p->completion((unsigned char *)passphrase,
1066 passphrase ? strlen(passphrase) : 0, p->context);
1070 static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1071 SilcAskPassphrase completion, void *context)
1073 AskPassphrase p = silc_calloc(1, sizeof(*p));
1074 p->completion = completion;
1075 p->context = context;
1077 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1078 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1081 /* Find authentication method and authentication data by hostname and
1082 port. The hostname may be IP address as well. The found authentication
1083 method and authentication data is returned to `auth_meth', `auth_data'
1084 and `auth_data_len'. The function returns TRUE if authentication method
1085 is found and FALSE if not. `conn' may be NULL. */
1088 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1089 char *hostname, uint16 port,
1090 SilcProtocolAuthMeth *auth_meth,
1091 unsigned char **auth_data,
1092 uint32 *auth_data_len)
1095 /* XXX must resolve from configuration whether this connection has
1096 any specific authentication data */
1098 *auth_meth = SILC_AUTH_NONE;
1105 /* Notifies application that failure packet was received. This is called
1106 if there is some protocol active in the client. The `protocol' is the
1107 protocol context. The `failure' is opaque pointer to the failure
1108 indication. Note, that the `failure' is protocol dependant and application
1109 must explicitly cast it to correct type. Usually `failure' is 32 bit
1110 failure type (see protocol specs for all protocol failure types). */
1113 silc_failure(SilcClient client, SilcClientConnection conn,
1114 SilcProtocol protocol, void *failure)
1116 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1117 SilcSKEStatus status = (SilcSKEStatus)failure;
1119 if (status == SILC_SKE_STATUS_BAD_VERSION)
1120 silc_say_error("You are running incompatible client version (it may be "
1121 "too old or too new)");
1122 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1123 silc_say_error("Server does not support your public key type");
1124 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1125 silc_say_error("Server does not support one of your proposed KE group");
1126 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1127 silc_say_error("Server does not support one of your proposed cipher");
1128 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1129 silc_say_error("Server does not support one of your proposed PKCS");
1130 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1131 silc_say_error("Server does not support one of your proposed "
1133 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1134 silc_say_error("Server does not support one of your proposed HMAC");
1135 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1136 silc_say_error("Incorrect signature");
1139 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1140 uint32 err = (uint32)failure;
1142 if (err == SILC_AUTH_FAILED)
1143 silc_say(client, conn, "Authentication 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). */
1155 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1156 SilcClientEntry client_entry, char *hostname,
1158 SilcKeyAgreementCallback *completion,
1163 /* We will just display the info on the screen and return FALSE and user
1164 will have to start the key agreement with a command. */
1167 memset(host, 0, sizeof(host));
1168 snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port);
1171 silc_say(client, conn, "%s wants to perform key agreement %s",
1172 client_entry->nickname, hostname ? host : "");
1180 /* SILC client operations */
1181 SilcClientOperations ops = {
1183 silc_channel_message,
1184 silc_private_message,
1190 silc_get_auth_method,
1191 silc_verify_public_key,
1192 silc_ask_passphrase,
1197 static int my_silc_scheduler(void)
1199 silc_schedule_one(0);
1203 static CHATNET_REC *create_chatnet(void)
1205 return g_malloc0(sizeof(CHATNET_REC));
1208 static SERVER_SETUP_REC *create_server_setup(void)
1210 return g_malloc0(sizeof(SERVER_SETUP_REC));
1213 static CHANNEL_SETUP_REC *create_channel_setup(void)
1215 return g_malloc0(sizeof(CHANNEL_SETUP_REC));
1218 static SERVER_CONNECT_REC *create_server_connect(void)
1220 return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
1223 /* Checks user information and saves them to the config file it they
1224 do not exist there already. */
1226 static void silc_init_userinfo(void)
1228 const char *set, *nick, *user_name;
1231 /* check if nick/username/realname wasn't read from setup.. */
1232 set = settings_get_str("real_name");
1233 if (set == NULL || *set == '\0') {
1234 str = g_getenv("SILCNAME");
1236 str = g_getenv("IRCNAME");
1237 settings_set_str("real_name",
1238 str != NULL ? str : g_get_real_name());
1242 user_name = settings_get_str("user_name");
1243 if (user_name == NULL || *user_name == '\0') {
1244 str = g_getenv("SILCUSER");
1246 str = g_getenv("IRCUSER");
1247 settings_set_str("user_name",
1248 str != NULL ? str : g_get_user_name());
1250 user_name = settings_get_str("user_name");
1254 nick = settings_get_str("nick");
1255 if (nick == NULL || *nick == '\0') {
1256 str = g_getenv("SILCNICK");
1258 str = g_getenv("IRCNICK");
1259 settings_set_str("nick", str != NULL ? str : user_name);
1261 nick = settings_get_str("nick");
1264 /* alternate nick */
1265 set = settings_get_str("alternate_nick");
1266 if (set == NULL || *set == '\0') {
1267 if (strlen(nick) < 9)
1268 str = g_strconcat(nick, "_", NULL);
1270 str = g_strdup(nick);
1271 str[strlen(str)-1] = '_';
1273 settings_set_str("alternate_nick", str);
1278 set = settings_get_str("hostname");
1279 if (set == NULL || *set == '\0') {
1280 str = g_getenv("SILCHOST");
1282 str = g_getenv("IRCHOST");
1284 settings_set_str("hostname", str);
1290 static void silc_log_info(char *message)
1292 fprintf(stderr, "%s\n", message);
1295 static void silc_log_warning(char *message)
1297 fprintf(stderr, "%s\n", message);
1300 static void silc_log_error(char *message)
1302 fprintf(stderr, "%s\n", message);
1305 /* Init SILC. Called from src/fe-text/silc.c */
1307 void silc_core_init(void)
1309 static struct poptOption options[] = {
1310 { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0,
1311 "Create new public key pair", NULL },
1312 { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0,
1313 "Set the PKCS of the public key pair", "PKCS" },
1314 { "bits", 0, POPT_ARG_INT, &opt_bits, 0,
1315 "Set the length of the public key pair", "VALUE" },
1316 { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0,
1317 "Show the contents of the public key", "FILE" },
1318 { "debug", 'd', POPT_ARG_NONE, &opt_debug, 0,
1319 "Enable debugging", NULL },
1320 { NULL, '\0', 0, NULL }
1323 args_register(options);
1326 /* Finalize init. Called from src/fe-text/silc.c */
1328 void silc_core_init_finish(void)
1330 CHAT_PROTOCOL_REC *rec;
1332 if (opt_create_keypair == TRUE) {
1333 /* Create new key pair and exit */
1334 silc_cipher_register_default();
1335 silc_pkcs_register_default();
1336 silc_hash_register_default();
1337 silc_hmac_register_default();
1338 silc_client_create_key_pair(opt_pkcs, opt_bits,
1339 NULL, NULL, NULL, NULL, NULL);
1345 silc_cipher_register_default();
1346 silc_pkcs_register_default();
1347 silc_hash_register_default();
1348 silc_hmac_register_default();
1349 silc_client_show_key(opt_keyfile);
1353 silc_debug = opt_debug;
1354 silc_log_set_callbacks(silc_log_info, silc_log_warning,
1355 silc_log_error, NULL);
1357 /* Do some irssi initializing */
1358 settings_add_bool("server", "skip_motd", FALSE);
1359 settings_add_str("server", "alternate_nick", NULL);
1360 silc_init_userinfo();
1362 /* Allocate SILC client */
1363 silc_client = silc_client_alloc(&ops, NULL);
1365 /* Load local config file */
1366 silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
1368 /* Get user information */
1369 silc_client->username = g_strdup(settings_get_str("user_name"));
1370 silc_client->hostname = silc_net_localhost();
1371 silc_client->realname = g_strdup(settings_get_str("real_name"));
1373 /* Register all configured ciphers, PKCS and hash functions. */
1375 silc_config->client = silc_client;
1376 if (!silc_client_config_register_ciphers(silc_config))
1377 silc_cipher_register_default();
1378 if (!silc_client_config_register_pkcs(silc_config))
1379 silc_pkcs_register_default();
1380 if (!silc_client_config_register_hashfuncs(silc_config))
1381 silc_hash_register_default();
1382 if (!silc_client_config_register_hmacs(silc_config))
1383 silc_hmac_register_default();
1385 /* Register default ciphers, pkcs, hash funtions and hmacs. */
1386 silc_cipher_register_default();
1387 silc_pkcs_register_default();
1388 silc_hash_register_default();
1389 silc_hmac_register_default();
1392 /* Check ~/.silc directory and public and private keys */
1393 if (silc_client_check_silc_dir() == FALSE) {
1398 /* Load public and private key */
1399 if (silc_client_load_keys(silc_client) == FALSE) {
1404 /* Initialize the SILC client */
1405 if (!silc_client_init(silc_client)) {
1410 /* Register SILC to the irssi */
1411 rec = g_new0(CHAT_PROTOCOL_REC, 1);
1413 rec->fullname = "Secure Internet Live Conferencing";
1414 rec->chatnet = "silcnet";
1415 rec->create_chatnet = create_chatnet;
1416 rec->create_server_setup = create_server_setup;
1417 rec->create_channel_setup = create_channel_setup;
1418 rec->create_server_connect = create_server_connect;
1419 rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
1420 silc_server_connect;
1421 rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
1422 silc_channel_create;
1423 rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
1426 chat_protocol_register(rec);
1430 silc_channels_init();
1431 silc_queries_init();
1433 idletag = g_timeout_add(50, (GSourceFunc) my_silc_scheduler, NULL);
1436 /* Deinit SILC. Called from src/fe-text/silc.c */
1438 void silc_core_deinit(void)
1440 if (idletag != -1) {
1441 signal_emit("chat protocol deinit", 1,
1442 chat_protocol_find("SILC"));
1444 silc_server_deinit();
1445 silc_channels_deinit();
1446 silc_queries_deinit();
1448 chat_protocol_unregister("SILC");
1450 g_source_remove(idletag);
1453 g_free(silc_client->username);
1454 g_free(silc_client->realname);
1455 silc_client_free(silc_client);