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"
42 /* Command line option variables */
43 static bool opt_create_keypair = FALSE;
44 static bool opt_debug = FALSE;
45 static char *opt_pkcs = NULL;
46 static char *opt_keyfile = NULL;
47 static int opt_bits = 0;
51 SilcClient silc_client = NULL;
52 SilcClientConfig silc_config = NULL;
53 extern SilcClientOperations ops;
54 extern int silc_debug;
56 /* SIM (SILC Module) table */
57 SilcSimContext **sims = NULL;
58 uint32 sims_count = 0;
61 static void silc_say(SilcClient client, SilcClientConnection conn,
64 silc_channel_message(SilcClient client, SilcClientConnection conn,
65 SilcClientEntry sender, SilcChannelEntry channel,
66 SilcMessageFlags flags, char *msg);
68 silc_private_message(SilcClient client, SilcClientConnection conn,
69 SilcClientEntry sender, SilcMessageFlags flags,
71 static void silc_notify(SilcClient client, SilcClientConnection conn,
72 SilcNotifyType type, ...);
74 silc_connect(SilcClient client, SilcClientConnection conn, int success);
76 silc_disconnect(SilcClient client, SilcClientConnection conn);
78 silc_command(SilcClient client, SilcClientConnection conn,
79 SilcClientCommandContext cmd_context, int success,
82 silc_command_reply(SilcClient client, SilcClientConnection conn,
83 SilcCommandPayload cmd_payload, int success,
84 SilcCommand command, SilcCommandStatus status, ...);
86 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
87 SilcSocketType conn_type, unsigned char *pk,
88 uint32 pk_len, SilcSKEPKType pk_type,
89 SilcVerifyPublicKey completion, void *context);
91 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
92 SilcSocketType conn_type, unsigned char *pk,
93 uint32 pk_len, SilcSKEPKType pk_type,
94 SilcVerifyPublicKey completion, void *context);
95 static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
96 SilcAskPassphrase completion, void *context);
98 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
99 char *hostname, uint16 port,
100 SilcProtocolAuthMeth *auth_meth,
101 unsigned char **auth_data,
102 uint32 *auth_data_len);
104 silc_failure(SilcClient client, SilcClientConnection conn,
105 SilcProtocol protocol, void *failure);
107 silc_key_agreement(SilcClient client, SilcClientConnection conn,
108 SilcClientEntry client_entry, char *hostname,
110 SilcKeyAgreementCallback *completion,
113 static void silc_say(SilcClient client, SilcClientConnection conn,
116 SILC_SERVER_REC *server;
120 server = conn == NULL ? NULL : conn->context;
123 str = g_strdup_vprintf(msg, va);
124 printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
129 static void silc_say_error(char *msg, ...)
135 str = g_strdup_vprintf(msg, va);
136 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
142 /* Message for a channel. The `sender' is the nickname of the sender
143 received in the packet. The `channel_name' is the name of the channel. */
146 silc_channel_message(SilcClient client, SilcClientConnection conn,
147 SilcClientEntry sender, SilcChannelEntry channel,
148 SilcMessageFlags flags, char *msg)
150 SILC_SERVER_REC *server;
152 SILC_CHANNEL_REC *chanrec;
154 server = conn == NULL ? NULL : conn->context;
155 chanrec = silc_channel_find_entry(server, channel);
157 nick = silc_nicklist_find(chanrec, sender);
159 if (flags & SILC_MESSAGE_FLAG_ACTION)
161 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
164 signal_emit("message public", 6, server, msg,
165 nick == NULL ? "[<unknown>]" : nick->nick,
166 nick == NULL ? NULL : nick->host,
167 chanrec->name, nick);
170 /* Private message to the client. The `sender' is the nickname of the
171 sender received in the packet. */
174 silc_private_message(SilcClient client, SilcClientConnection conn,
175 SilcClientEntry sender, SilcMessageFlags flags,
178 SILC_SERVER_REC *server;
180 server = conn == NULL ? NULL : conn->context;
181 signal_emit("message private", 4, server, msg,
182 sender->nickname ? sender->nickname : "[<unknown>]",
183 sender->username ? sender->username : NULL);
186 /* Notify message to the client. The notify arguments are sent in the
187 same order as servers sends them. The arguments are same as received
188 from the server except for ID's. If ID is received application receives
189 the corresponding entry to the ID. For example, if Client ID is received
190 application receives SilcClientEntry. Also, if the notify type is
191 for channel the channel entry is sent to application (even if server
192 does not send it). */
199 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
200 static NOTIFY_REC notifies[] = {
201 { SILC_NOTIFY_TYPE_NONE, NULL },
202 { SILC_NOTIFY_TYPE_INVITE, "invite" },
203 { SILC_NOTIFY_TYPE_JOIN, "join" },
204 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
205 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
206 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
207 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
208 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
209 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
210 { SILC_NOTIFY_TYPE_MOTD, "motd" },
211 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
212 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
213 { SILC_NOTIFY_TYPE_KICKED, "kick" },
214 { SILC_NOTIFY_TYPE_KILLED, "kill" },
215 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
216 { SILC_NOTIFY_TYPE_BAN, "ban" },
219 static void silc_notify(SilcClient client, SilcClientConnection conn,
220 SilcNotifyType type, ...)
222 SILC_SERVER_REC *server;
225 server = conn == NULL ? NULL : conn->context;
228 if (type == SILC_NOTIFY_TYPE_NONE) {
229 /* Some generic notice from server */
230 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
231 } else if (type < MAX_NOTIFY) {
232 /* Send signal about the notify event */
234 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
235 signal_emit(signal, 2, server, va);
238 printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
244 /* Called to indicate that connection was either successfully established
245 or connecting failed. This is also the first time application receives
246 the SilcClientConnection objecet which it should save somewhere. */
249 silc_connect(SilcClient client, SilcClientConnection conn, int success)
251 SILC_SERVER_REC *server = conn->context;
254 server->connected = TRUE;
255 signal_emit("event connected", 1, server);
257 server->connection_lost = TRUE;
258 server->conn->context = NULL;
259 server_disconnect(SERVER(server));
263 /* Called to indicate that connection was disconnected to the server. */
266 silc_disconnect(SilcClient client, SilcClientConnection conn)
268 SILC_SERVER_REC *server = conn->context;
270 server->conn->context = NULL;
272 server->connection_lost = TRUE;
273 server_disconnect(SERVER(server));
276 /* Command handler. This function is called always in the command function.
277 If error occurs it will be called as well. `conn' is the associated
278 client connection. `cmd_context' is the command context that was
279 originally sent to the command. `success' is FALSE if error occured
280 during command. `command' is the command being processed. It must be
281 noted that this is not reply from server. This is merely called just
282 after application has called the command. Just to tell application
283 that the command really was processed. */
286 silc_command(SilcClient client, SilcClientConnection conn,
287 SilcClientCommandContext cmd_context, int success,
292 /* Client info resolving callback when JOIN command reply is received.
293 This will cache all users on the channel. */
295 void silc_client_join_get_users(SilcClient client,
296 SilcClientConnection conn,
297 SilcClientEntry *clients,
298 uint32 clients_count,
301 SilcChannelEntry channel = (SilcChannelEntry)context;
303 SILC_SERVER_REC *server = conn->context;
304 SILC_CHANNEL_REC *chanrec;
310 chanrec = silc_channel_find_entry(server, channel);
314 silc_list_start(channel->clients);
315 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END)
316 silc_nicklist_insert(chanrec, chu, FALSE);
318 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
319 nicklist_set_own(CHANNEL(chanrec), ownnick);
320 signal_emit("channel joined", 1, chanrec);
321 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
324 /* Command reply handler. This function is called always in the command reply
325 function. If error occurs it will be called as well. Normal scenario
326 is that it will be called after the received command data has been parsed
327 and processed. The function is used to pass the received command data to
330 `conn' is the associated client connection. `cmd_payload' is the command
331 payload data received from server and it can be ignored. It is provided
332 if the application would like to re-parse the received command data,
333 however, it must be noted that the data is parsed already by the library
334 thus the payload can be ignored. `success' is FALSE if error occured.
335 In this case arguments are not sent to the application. `command' is the
336 command reply being processed. The function has variable argument list
337 and each command defines the number and type of arguments it passes to the
338 application (on error they are not sent). */
341 silc_command_reply(SilcClient client, SilcClientConnection conn,
342 SilcCommandPayload cmd_payload, int success,
343 SilcCommand command, SilcCommandStatus status, ...)
346 SILC_SERVER_REC *server = conn->context;
347 SILC_CHANNEL_REC *chanrec;
350 va_start(vp, status);
353 case SILC_COMMAND_WHOIS:
355 char buf[1024], *nickname, *username, *realname;
360 /* XXX should use irssi routines */
362 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
363 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
365 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
368 client->ops->say(client, conn, "%s: %s", tmp,
369 silc_client_command_status_message(status));
371 client->ops->say(client, conn, "%s",
372 silc_client_command_status_message(status));
379 (void)va_arg(vp, SilcClientEntry);
380 nickname = va_arg(vp, char *);
381 username = va_arg(vp, char *);
382 realname = va_arg(vp, char *);
383 channels = va_arg(vp, SilcBuffer);
384 mode = va_arg(vp, uint32);
385 idle = va_arg(vp, uint32);
387 memset(buf, 0, sizeof(buf));
390 len = strlen(nickname);
391 strncat(buf, nickname, len);
392 strncat(buf, " is ", 4);
396 strncat(buf, username, strlen(username));
400 strncat(buf, " (", 2);
401 strncat(buf, realname, strlen(realname));
402 strncat(buf, ")", 1);
405 client->ops->say(client, conn, "%s", buf);
408 SilcDList list = silc_channel_payload_parse_list(channels);
410 SilcChannelPayload entry;
412 memset(buf, 0, sizeof(buf));
413 strcat(buf, "on channels: ");
415 silc_dlist_start(list);
416 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
417 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
419 char *name = silc_channel_get_name(entry, &name_len);
422 strncat(buf, m, strlen(m));
423 strncat(buf, name, name_len);
424 strncat(buf, " ", 1);
428 client->ops->say(client, conn, "%s", buf);
429 silc_channel_payload_list_free(list);
434 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
435 (mode & SILC_UMODE_ROUTER_OPERATOR))
436 client->ops->say(client, conn, "%s is %s", nickname,
437 (mode & SILC_UMODE_SERVER_OPERATOR) ?
439 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
440 "SILC Operator" : "[Unknown mode]");
442 if (mode & SILC_UMODE_GONE)
443 client->ops->say(client, conn, "%s is gone", nickname);
446 if (idle && nickname)
447 client->ops->say(client, conn, "%s has been idle %d %s",
449 idle > 60 ? (idle / 60) : idle,
450 idle > 60 ? "minutes" : "seconds");
454 case SILC_COMMAND_WHOWAS:
456 char buf[1024], *nickname, *username, *realname;
459 /* XXX should use irssi routines */
461 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
462 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
464 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
467 client->ops->say(client, conn, "%s: %s", tmp,
468 silc_client_command_status_message(status));
470 client->ops->say(client, conn, "%s",
471 silc_client_command_status_message(status));
478 (void)va_arg(vp, SilcClientEntry);
479 nickname = va_arg(vp, char *);
480 username = va_arg(vp, char *);
481 realname = va_arg(vp, char *);
483 memset(buf, 0, sizeof(buf));
486 len = strlen(nickname);
487 strncat(buf, nickname, len);
488 strncat(buf, " was ", 5);
492 strncat(buf, username, strlen(nickname));
496 strncat(buf, " (", 2);
497 strncat(buf, realname, strlen(realname));
498 strncat(buf, ")", 1);
501 client->ops->say(client, conn, "%s", buf);
505 case SILC_COMMAND_INVITE:
507 SilcChannelEntry channel;
513 /* XXX should use irssi routines */
515 channel = va_arg(vp, SilcChannelEntry);
516 invite_list = va_arg(vp, char *);
519 silc_say(client, conn, "%s invite list: %s", channel->channel_name,
522 silc_say(client, conn, "%s invite list not set",
523 channel->channel_name);
527 case SILC_COMMAND_JOIN:
529 char *channel, *mode, *topic;
531 SilcChannelEntry channel_entry;
532 SilcBuffer client_id_list;
535 channel = va_arg(vp, char *);
536 channel_entry = va_arg(vp, SilcChannelEntry);
537 modei = va_arg(vp, uint32);
538 (void)va_arg(vp, uint32);
539 (void)va_arg(vp, unsigned char *);
540 (void)va_arg(vp, unsigned char *);
541 (void)va_arg(vp, unsigned char *);
542 topic = va_arg(vp, char *);
543 (void)va_arg(vp, unsigned char *);
544 list_count = va_arg(vp, uint32);
545 client_id_list = va_arg(vp, SilcBuffer);
550 chanrec = silc_channel_find(server, channel);
551 if (chanrec != NULL && !success)
552 channel_destroy(CHANNEL(chanrec));
553 else if (chanrec == NULL && success)
554 chanrec = silc_channel_create(server, channel, TRUE);
557 g_free_not_null(chanrec->topic);
558 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
559 signal_emit("channel topic changed", 1, chanrec);
560 silc_say(client, conn, "Topic for %s: %s", channel, topic);
563 mode = silc_client_chmode(modei, channel_entry);
564 g_free_not_null(chanrec->mode);
565 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
566 signal_emit("channel mode changed", 1, chanrec);
568 /* Resolve the client information */
569 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
570 silc_client_join_get_users,
575 case SILC_COMMAND_NICK:
577 SilcClientEntry client = va_arg(vp, SilcClientEntry);
583 old = g_strdup(server->nick);
584 server_change_nick(SERVER(server), client->nickname);
585 nicklist_rename_unique(SERVER(server),
586 server->conn->local_entry, server->nick,
587 client, client->nickname);
589 signal_emit("message own_nick", 4, server, server->nick, old, "");
594 case SILC_COMMAND_LIST:
598 unsigned char buf[256], tmp[16];
604 /* XXX should use irssi routines */
606 (void)va_arg(vp, SilcChannelEntry);
607 name = va_arg(vp, char *);
608 topic = va_arg(vp, char *);
609 usercount = va_arg(vp, int);
611 if (status == SILC_STATUS_LIST_START ||
612 status == SILC_STATUS_OK)
613 silc_say(client, conn,
614 " Channel Users Topic");
616 memset(buf, 0, sizeof(buf));
617 strncat(buf, " ", 2);
619 strncat(buf, name, len > 40 ? 40 : len);
621 for (i = 0; i < 40 - len; i++)
625 memset(tmp, 0, sizeof(tmp));
627 snprintf(tmp, sizeof(tmp), "%d", usercount);
632 for (i = 0; i < 10 - len; i++)
638 strncat(buf, topic, len);
641 silc_say(client, conn, "%s", buf);
645 case SILC_COMMAND_UMODE:
652 mode = va_arg(vp, uint32);
658 case SILC_COMMAND_OPER:
660 if (status == SILC_STATUS_OK) {
661 conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
662 if (app->screen->bottom_line->umode)
663 silc_free(app->screen->bottom_line->umode);
664 app->screen->bottom_line->umode = strdup("Server Operator");;
665 silc_screen_print_bottom_line(app->screen, 0);
670 case SILC_COMMAND_SILCOPER:
672 if (status == SILC_STATUS_OK) {
673 conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
674 if (app->screen->bottom_line->umode)
675 silc_free(app->screen->bottom_line->umode);
676 app->screen->bottom_line->umode = strdup("SILC Operator");;
677 silc_screen_print_bottom_line(app->screen, 0);
682 case SILC_COMMAND_USERS:
684 SilcChannelEntry channel;
692 channel = va_arg(vp, SilcChannelEntry);
694 /* There are two ways to do this, either parse the list (that
695 the command_reply sends (just take it with va_arg()) or just
696 traverse the channel's client list. I'll do the latter. See
697 JOIN command reply for example for the list. */
699 silc_say(client, conn, "Users on %s", channel->channel_name);
701 line = silc_calloc(1024, sizeof(*line));
703 silc_list_start(channel->clients);
704 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
705 SilcClientEntry e = chu->client;
709 memset(line, 0, line_len);
711 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
713 line_len += strlen(e->nickname) + strlen(e->server) + 100;
714 line = silc_calloc(line_len, sizeof(*line));
717 memset(tmp, 0, sizeof(tmp));
718 m = silc_client_chumode_char(chu->mode);
720 strncat(line, " ", 1);
721 strncat(line, e->nickname, strlen(e->nickname));
722 strncat(line, e->server ? "@" : "", 1);
726 len1 = strlen(e->server);
727 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
731 memset(&line[29], 0, len1 - 29);
733 for (i = 0; i < 30 - len1 - 1; i++)
737 if (e->mode & SILC_UMODE_GONE)
741 strcat(tmp, m ? m : "");
742 strncat(line, tmp, strlen(tmp));
745 for (i = 0; i < 5 - strlen(tmp); i++)
748 strcat(line, e->username ? e->username : "");
750 silc_say(client, conn, "%s", line);
760 case SILC_COMMAND_BAN:
762 SilcChannelEntry channel;
768 /* XXX should use irssi routines */
770 channel = va_arg(vp, SilcChannelEntry);
771 ban_list = va_arg(vp, char *);
774 silc_say(client, conn, "%s ban list: %s", channel->channel_name,
777 silc_say(client, conn, "%s ban list not set", channel->channel_name);
781 case SILC_COMMAND_GETKEY:
785 SilcPublicKey public_key;
789 id_type = va_arg(vp, uint32);
790 entry = va_arg(vp, void *);
791 public_key = va_arg(vp, SilcPublicKey);
793 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
795 if (id_type == SILC_ID_CLIENT) {
796 silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
797 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
804 case SILC_COMMAND_TOPIC:
806 SilcChannelEntry channel;
812 channel = va_arg(vp, SilcChannelEntry);
813 topic = va_arg(vp, char *);
815 /* XXX should use irssi routines */
818 silc_say(client, conn,
819 "Topic on channel %s: %s", channel->channel_name,
828 /* Internal routine to verify public key. If the `completion' is provided
829 it will be called to indicate whether public was verified or not. */
833 SilcClientConnection conn;
838 SilcSKEPKType pk_type;
839 SilcVerifyPublicKey completion;
843 static void verify_public_key_completion(const char *line, void *context)
845 PublicKeyVerify verify = (PublicKeyVerify)context;
847 if (line[0] == 'Y' || line[0] == 'y') {
848 /* Call the completion */
849 if (verify->completion)
850 verify->completion(TRUE, verify->context);
852 /* Save the key for future checking */
853 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
854 verify->pk_len, SILC_PKCS_FILE_PEM);
856 /* Call the completion */
857 if (verify->completion)
858 verify->completion(FALSE, verify->context);
860 silc_say(verify->client,
861 verify->conn, "Will not accept the %s key", verify->entity);
864 silc_free(verify->filename);
865 silc_free(verify->entity);
870 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
871 SilcSocketType conn_type, unsigned char *pk,
872 uint32 pk_len, SilcSKEPKType pk_type,
873 SilcVerifyPublicKey completion, void *context)
876 char file[256], filename[256], *fingerprint;
879 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
880 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
881 "server" : "client");
882 PublicKeyVerify verify;
884 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
885 silc_say(client, conn, "We don't support %s public key type %d",
888 completion(FALSE, context);
892 pw = getpwuid(getuid());
895 completion(FALSE, context);
899 memset(filename, 0, sizeof(filename));
900 memset(file, 0, sizeof(file));
902 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
903 conn_type == SILC_SOCKET_TYPE_ROUTER) {
904 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
905 conn->sock->hostname, conn->sock->port);
906 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
907 pw->pw_dir, entity, file);
909 /* Replace all whitespaces with `_'. */
910 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
911 for (i = 0; i < strlen(fingerprint); i++)
912 if (fingerprint[i] == ' ')
913 fingerprint[i] = '_';
915 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
916 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
917 pw->pw_dir, entity, file);
918 silc_free(fingerprint);
921 /* Take fingerprint of the public key */
922 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
924 verify = silc_calloc(1, sizeof(*verify));
925 verify->client = client;
927 verify->filename = strdup(filename);
928 verify->entity = strdup(entity);
930 verify->pk_len = pk_len;
931 verify->pk_type = pk_type;
932 verify->completion = completion;
933 verify->context = context;
935 /* Check whether this key already exists */
936 if (stat(filename, &st) < 0) {
937 /* Key does not exist, ask user to verify the key and save it */
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);
943 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
944 "Would you like to accept the key (y/n)? ", 0,
946 silc_free(fingerprint);
949 /* The key already exists, verify it. */
950 SilcPublicKey public_key;
951 unsigned char *encpk;
954 /* Load the key file */
955 if (!silc_pkcs_load_public_key(filename, &public_key,
957 if (!silc_pkcs_load_public_key(filename, &public_key,
958 SILC_PKCS_FILE_BIN)) {
959 silc_say(client, conn, "Received %s public key", entity);
960 silc_say(client, conn, "Fingerprint for the %s key is", entity);
961 silc_say(client, conn, "%s", fingerprint);
962 silc_say(client, conn, "Could not load your local copy of the %s key",
964 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
965 "Would you like to accept the key "
968 silc_free(fingerprint);
972 /* Encode the key data */
973 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
975 silc_say(client, conn, "Received %s public key", entity);
976 silc_say(client, conn, "Fingerprint for the %s key is", entity);
977 silc_say(client, conn, "%s", fingerprint);
978 silc_say(client, conn, "Your local copy of the %s key is malformed",
980 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
981 "Would you like to accept the key "
984 silc_free(fingerprint);
988 /* Compare the keys */
989 if (memcmp(encpk, pk, encpk_len)) {
990 silc_say(client, conn, "Received %s public key", entity);
991 silc_say(client, conn, "Fingerprint for the %s key is", entity);
992 silc_say(client, conn, "%s", fingerprint);
993 silc_say(client, conn, "%s key does not match with your local copy",
995 silc_say(client, conn,
996 "It is possible that the key has expired or changed");
997 silc_say(client, conn, "It is also possible that some one is performing "
998 "man-in-the-middle attack");
1000 /* Ask user to verify the key and save it */
1001 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1002 "Would you like to accept the key "
1003 "anyway (y/n)? ", 0,
1005 silc_free(fingerprint);
1009 /* Local copy matched */
1011 completion(TRUE, context);
1012 silc_free(fingerprint);
1016 /* Verifies received public key. The `conn_type' indicates which entity
1017 (server, client etc.) has sent the public key. If user decides to trust
1018 the key may be saved as trusted public key for later use. The
1019 `completion' must be called after the public key has been verified. */
1022 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1023 SilcSocketType conn_type, unsigned char *pk,
1024 uint32 pk_len, SilcSKEPKType pk_type,
1025 SilcVerifyPublicKey completion, void *context)
1027 silc_verify_public_key_internal(client, conn, conn_type, pk,
1029 completion, context);
1032 /* Asks passphrase from user on the input line. */
1035 SilcAskPassphrase completion;
1039 static void ask_passphrase_completion(const char *passphrase, void *context)
1041 AskPassphrase p = (AskPassphrase)context;
1042 p->completion((unsigned char *)passphrase,
1043 passphrase ? strlen(passphrase) : 0, p->context);
1047 static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1048 SilcAskPassphrase completion, void *context)
1050 AskPassphrase p = silc_calloc(1, sizeof(*p));
1051 p->completion = completion;
1052 p->context = context;
1054 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1055 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1058 /* Find authentication method and authentication data by hostname and
1059 port. The hostname may be IP address as well. The found authentication
1060 method and authentication data is returned to `auth_meth', `auth_data'
1061 and `auth_data_len'. The function returns TRUE if authentication method
1062 is found and FALSE if not. `conn' may be NULL. */
1065 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1066 char *hostname, uint16 port,
1067 SilcProtocolAuthMeth *auth_meth,
1068 unsigned char **auth_data,
1069 uint32 *auth_data_len)
1072 /* XXX must resolve from configuration whether this connection has
1073 any specific authentication data */
1075 *auth_meth = SILC_AUTH_NONE;
1082 /* Notifies application that failure packet was received. This is called
1083 if there is some protocol active in the client. The `protocol' is the
1084 protocol context. The `failure' is opaque pointer to the failure
1085 indication. Note, that the `failure' is protocol dependant and application
1086 must explicitly cast it to correct type. Usually `failure' is 32 bit
1087 failure type (see protocol specs for all protocol failure types). */
1090 silc_failure(SilcClient client, SilcClientConnection conn,
1091 SilcProtocol protocol, void *failure)
1093 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1094 SilcSKEStatus status = (SilcSKEStatus)failure;
1096 if (status == SILC_SKE_STATUS_BAD_VERSION)
1097 silc_say_error("You are running incompatible client version (it may be "
1098 "too old or too new)");
1099 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1100 silc_say_error("Server does not support your public key type");
1101 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1102 silc_say_error("Server does not support one of your proposed KE group");
1103 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1104 silc_say_error("Server does not support one of your proposed cipher");
1105 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1106 silc_say_error("Server does not support one of your proposed PKCS");
1107 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1108 silc_say_error("Server does not support one of your proposed "
1110 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1111 silc_say_error("Server does not support one of your proposed HMAC");
1112 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1113 silc_say_error("Incorrect signature");
1116 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1117 uint32 err = (uint32)failure;
1119 if (err == SILC_AUTH_FAILED)
1120 silc_say(client, conn, "Authentication failed");
1124 /* Asks whether the user would like to perform the key agreement protocol.
1125 This is called after we have received an key agreement packet or an
1126 reply to our key agreement packet. This returns TRUE if the user wants
1127 the library to perform the key agreement protocol and FALSE if it is not
1128 desired (application may start it later by calling the function
1129 silc_client_perform_key_agreement). */
1132 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1133 SilcClientEntry client_entry, char *hostname,
1135 SilcKeyAgreementCallback *completion,
1140 /* We will just display the info on the screen and return FALSE and user
1141 will have to start the key agreement with a command. */
1144 memset(host, 0, sizeof(host));
1145 snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port);
1148 silc_say(client, conn, "%s wants to perform key agreement %s",
1149 client_entry->nickname, hostname ? host : "");
1157 /* SILC client operations */
1158 SilcClientOperations ops = {
1160 silc_channel_message,
1161 silc_private_message,
1167 silc_get_auth_method,
1168 silc_verify_public_key,
1169 silc_ask_passphrase,
1174 static int my_silc_scheduler(void)
1176 silc_schedule_one(0);
1180 static CHATNET_REC *create_chatnet(void)
1182 return g_malloc0(sizeof(CHATNET_REC));
1185 static SERVER_SETUP_REC *create_server_setup(void)
1187 return g_malloc0(sizeof(SERVER_SETUP_REC));
1190 static CHANNEL_SETUP_REC *create_channel_setup(void)
1192 return g_malloc0(sizeof(CHANNEL_SETUP_REC));
1195 static SERVER_CONNECT_REC *create_server_connect(void)
1197 return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
1200 /* Checks user information and saves them to the config file it they
1201 do not exist there already. */
1203 static void silc_init_userinfo(void)
1205 const char *set, *nick, *user_name;
1208 /* check if nick/username/realname wasn't read from setup.. */
1209 set = settings_get_str("real_name");
1210 if (set == NULL || *set == '\0') {
1211 str = g_getenv("SILCNAME");
1213 str = g_getenv("IRCNAME");
1214 settings_set_str("real_name",
1215 str != NULL ? str : g_get_real_name());
1219 user_name = settings_get_str("user_name");
1220 if (user_name == NULL || *user_name == '\0') {
1221 str = g_getenv("SILCUSER");
1223 str = g_getenv("IRCUSER");
1224 settings_set_str("user_name",
1225 str != NULL ? str : g_get_user_name());
1227 user_name = settings_get_str("user_name");
1231 nick = settings_get_str("nick");
1232 if (nick == NULL || *nick == '\0') {
1233 str = g_getenv("SILCNICK");
1235 str = g_getenv("IRCNICK");
1236 settings_set_str("nick", str != NULL ? str : user_name);
1238 nick = settings_get_str("nick");
1241 /* alternate nick */
1242 set = settings_get_str("alternate_nick");
1243 if (set == NULL || *set == '\0') {
1244 if (strlen(nick) < 9)
1245 str = g_strconcat(nick, "_", NULL);
1247 str = g_strdup(nick);
1248 str[strlen(str)-1] = '_';
1250 settings_set_str("alternate_nick", str);
1255 set = settings_get_str("hostname");
1256 if (set == NULL || *set == '\0') {
1257 str = g_getenv("SILCHOST");
1259 str = g_getenv("IRCHOST");
1261 settings_set_str("hostname", str);
1267 static void silc_log_info(char *message)
1269 fprintf(stderr, "%s\n", message);
1272 static void silc_log_warning(char *message)
1274 fprintf(stderr, "%s\n", message);
1277 static void silc_log_error(char *message)
1279 fprintf(stderr, "%s\n", message);
1282 /* Init SILC. Called from src/fe-text/silc.c */
1284 void silc_core_init(void)
1286 static struct poptOption options[] = {
1287 { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0,
1288 "Create new public key pair", NULL },
1289 { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0,
1290 "Set the PKCS of the public key pair", "PKCS" },
1291 { "bits", 0, POPT_ARG_INT, &opt_bits, 0,
1292 "Set the length of the public key pair", "VALUE" },
1293 { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0,
1294 "Show the contents of the public key", "FILE" },
1295 { "debug", 'd', POPT_ARG_NONE, &opt_debug, 0,
1296 "Enable debugging", NULL },
1297 { NULL, '\0', 0, NULL }
1300 args_register(options);
1303 /* Finalize init. Called from src/fe-text/silc.c */
1305 void silc_core_init_finish(void)
1307 CHAT_PROTOCOL_REC *rec;
1309 if (opt_create_keypair == TRUE) {
1310 /* Create new key pair and exit */
1311 silc_cipher_register_default();
1312 silc_pkcs_register_default();
1313 silc_hash_register_default();
1314 silc_hmac_register_default();
1315 silc_client_create_key_pair(opt_pkcs, opt_bits,
1316 NULL, NULL, NULL, NULL, NULL);
1322 silc_cipher_register_default();
1323 silc_pkcs_register_default();
1324 silc_hash_register_default();
1325 silc_hmac_register_default();
1326 silc_client_show_key(opt_keyfile);
1330 silc_debug = opt_debug;
1331 silc_log_set_callbacks(silc_log_info, silc_log_warning,
1332 silc_log_error, NULL);
1334 silc_init_userinfo();
1336 /* Allocate SILC client */
1337 silc_client = silc_client_alloc(&ops, NULL);
1339 /* Load local config file */
1340 silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
1342 /* Get user information */
1343 silc_client->username = g_strdup(settings_get_str("user_name"));
1344 silc_client->hostname = silc_net_localhost();
1345 silc_client->realname = g_strdup(settings_get_str("real_name"));
1347 /* Register all configured ciphers, PKCS and hash functions. */
1349 silc_config->client = silc_client;
1350 if (!silc_client_config_register_ciphers(silc_config))
1351 silc_cipher_register_default();
1352 if (!silc_client_config_register_pkcs(silc_config))
1353 silc_pkcs_register_default();
1354 if (!silc_client_config_register_hashfuncs(silc_config))
1355 silc_hash_register_default();
1356 if (!silc_client_config_register_hmacs(silc_config))
1357 silc_hmac_register_default();
1359 /* Register default ciphers, pkcs, hash funtions and hmacs. */
1360 silc_cipher_register_default();
1361 silc_pkcs_register_default();
1362 silc_hash_register_default();
1363 silc_hmac_register_default();
1366 /* Check ~/.silc directory and public and private keys */
1367 if (silc_client_check_silc_dir() == FALSE) {
1372 /* Load public and private key */
1373 if (silc_client_load_keys(silc_client) == FALSE) {
1378 /* Initialize the SILC client */
1379 if (!silc_client_init(silc_client)) {
1384 /* Register SILC to the irssi */
1385 rec = g_new0(CHAT_PROTOCOL_REC, 1);
1387 rec->fullname = "Secure Internet Live Conferencing";
1388 rec->chatnet = "silcnet";
1389 rec->create_chatnet = create_chatnet;
1390 rec->create_server_setup = create_server_setup;
1391 rec->create_channel_setup = create_channel_setup;
1392 rec->create_server_connect = create_server_connect;
1393 rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
1394 silc_server_connect;
1395 rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
1396 silc_channel_create;
1397 rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
1400 chat_protocol_register(rec);
1404 silc_channels_init();
1405 silc_queries_init();
1407 idletag = g_timeout_add(50, (GSourceFunc) my_silc_scheduler, NULL);
1410 /* Deinit SILC. Called from src/fe-text/silc.c */
1412 void silc_core_deinit(void)
1414 if (idletag != -1) {
1415 signal_emit("chat protocol deinit", 1,
1416 chat_protocol_find("SILC"));
1418 silc_server_deinit();
1419 silc_channels_deinit();
1420 silc_queries_deinit();
1422 chat_protocol_unregister("SILC");
1424 g_source_remove(idletag);
1427 g_free(silc_client->username);
1428 g_free(silc_client->realname);
1429 silc_client_free(silc_client);