5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "chat-protocols.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43 SilcSocketType conn_type, unsigned char *pk,
44 uint32 pk_len, SilcSKEPKType pk_type,
45 SilcVerifyPublicKey completion, void *context);
47 void silc_say(SilcClient client, SilcClientConnection conn,
48 SilcClientMessageType type, char *msg, ...)
50 SILC_SERVER_REC *server;
54 server = conn == NULL ? NULL : conn->context;
57 str = g_strdup_vprintf(msg, va);
58 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
63 void silc_say_error(char *msg, ...)
69 str = g_strdup_vprintf(msg, va);
70 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
76 /* Message for a channel. The `sender' is the nickname of the sender
77 received in the packet. The `channel_name' is the name of the channel. */
79 void silc_channel_message(SilcClient client, SilcClientConnection conn,
80 SilcClientEntry sender, SilcChannelEntry channel,
81 SilcMessageFlags flags, char *msg)
83 SILC_SERVER_REC *server;
85 SILC_CHANNEL_REC *chanrec;
87 SILC_LOG_DEBUG(("Start"));
89 server = conn == NULL ? NULL : conn->context;
90 chanrec = silc_channel_find_entry(server, channel);
94 nick = silc_nicklist_find(chanrec, sender);
96 /* We didn't find client but it clearly exists, add it. */
99 silc_list_start(channel->clients);
100 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
101 if (chu->client == sender) {
102 nick = silc_nicklist_insert(chanrec, chu, FALSE);
108 if (flags & SILC_MESSAGE_FLAG_ACTION)
109 printformat_module("fe-common/silc", server, channel->channel_name,
110 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
111 nick == NULL ? "[<unknown>]" : nick->nick, msg);
112 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
113 printformat_module("fe-common/silc", server, channel->channel_name,
114 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
115 nick == NULL ? "[<unknown>]" : nick->nick, msg);
117 signal_emit("message public", 6, server, msg,
118 nick == NULL ? "[<unknown>]" : nick->nick,
119 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
120 chanrec->name, nick);
123 /* Private message to the client. The `sender' is the nickname of the
124 sender received in the packet. */
126 void silc_private_message(SilcClient client, SilcClientConnection conn,
127 SilcClientEntry sender, SilcMessageFlags flags,
130 SILC_SERVER_REC *server;
133 SILC_LOG_DEBUG(("Start"));
135 server = conn == NULL ? NULL : conn->context;
136 memset(userhost, 0, sizeof(userhost));
137 if (sender->username)
138 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
139 sender->username, sender->hostname);
140 signal_emit("message private", 4, server, msg,
141 sender->nickname ? sender->nickname : "[<unknown>]",
142 sender->username ? userhost : NULL);
145 /* Notify message to the client. The notify arguments are sent in the
146 same order as servers sends them. The arguments are same as received
147 from the server except for ID's. If ID is received application receives
148 the corresponding entry to the ID. For example, if Client ID is received
149 application receives SilcClientEntry. Also, if the notify type is
150 for channel the channel entry is sent to application (even if server
151 does not send it). */
158 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
159 static NOTIFY_REC notifies[] = {
160 { SILC_NOTIFY_TYPE_NONE, NULL },
161 { SILC_NOTIFY_TYPE_INVITE, "invite" },
162 { SILC_NOTIFY_TYPE_JOIN, "join" },
163 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
164 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
165 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
166 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
167 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
168 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
169 { SILC_NOTIFY_TYPE_MOTD, "motd" },
170 { SILC_NOTIFY_TYPE_CHANNEL_CHANGE, "channel_change" },
171 { SILC_NOTIFY_TYPE_SERVER_SIGNOFF, "server_signoff" },
172 { SILC_NOTIFY_TYPE_KICKED, "kick" },
173 { SILC_NOTIFY_TYPE_KILLED, "kill" },
174 { SILC_NOTIFY_TYPE_UMODE_CHANGE, "umode" },
175 { SILC_NOTIFY_TYPE_BAN, "ban" },
178 void silc_notify(SilcClient client, SilcClientConnection conn,
179 SilcNotifyType type, ...)
181 SILC_SERVER_REC *server;
184 SILC_LOG_DEBUG(("Start"));
186 server = conn == NULL ? NULL : conn->context;
189 if (type == SILC_NOTIFY_TYPE_NONE) {
190 /* Some generic notice from server */
191 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
192 } else if (type < MAX_NOTIFY) {
193 /* Send signal about the notify event */
195 g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
196 signal_emit(signal, 2, server, va);
199 printformat_module("fe-common/silc", server, NULL,
200 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
206 /* Called to indicate that connection was either successfully established
207 or connecting failed. This is also the first time application receives
208 the SilcClientConnection object which it should save somewhere. */
210 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
212 SILC_SERVER_REC *server = conn->context;
214 if (!server && !success) {
215 silc_client_close_connection(client, NULL, conn);
220 server->connected = TRUE;
221 signal_emit("event connected", 1, server);
223 server->connection_lost = TRUE;
224 server->conn->context = NULL;
225 server_disconnect(SERVER(server));
229 /* Called to indicate that connection was disconnected to the server. */
231 void silc_disconnect(SilcClient client, SilcClientConnection conn)
233 SILC_SERVER_REC *server = conn->context;
235 SILC_LOG_DEBUG(("Start"));
238 nicklist_rename_unique(SERVER(server),
239 server->conn->local_entry, server->nick,
240 server->conn->local_entry,
241 silc_client->username);
242 silc_change_nick(server, silc_client->username);
245 server->conn->context = NULL;
247 server->connection_lost = TRUE;
248 server_disconnect(SERVER(server));
251 /* Command handler. This function is called always in the command function.
252 If error occurs it will be called as well. `conn' is the associated
253 client connection. `cmd_context' is the command context that was
254 originally sent to the command. `success' is FALSE if error occured
255 during command. `command' is the command being processed. It must be
256 noted that this is not reply from server. This is merely called just
257 after application has called the command. Just to tell application
258 that the command really was processed. */
260 void silc_command(SilcClient client, SilcClientConnection conn,
261 SilcClientCommandContext cmd_context, int success,
264 SILC_SERVER_REC *server = conn->context;
266 SILC_LOG_DEBUG(("Start"));
272 case SILC_COMMAND_INVITE:
273 printformat_module("fe-common/silc", server, NULL,
274 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
275 cmd_context->argv[2],
276 (cmd_context->argv[1][0] == '*' ?
277 (char *)conn->current_channel->channel_name :
278 (char *)cmd_context->argv[1]));
285 /* Client info resolving callback when JOIN command reply is received.
286 This will cache all users on the channel. */
288 static void silc_client_join_get_users(SilcClient client,
289 SilcClientConnection conn,
290 SilcClientEntry *clients,
291 uint32 clients_count,
294 SilcChannelEntry channel = (SilcChannelEntry)context;
296 SILC_SERVER_REC *server = conn->context;
297 SILC_CHANNEL_REC *chanrec;
298 SilcClientEntry founder = NULL;
304 chanrec = silc_channel_find(server, channel->channel_name);
308 silc_list_start(channel->clients);
309 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
310 if (!chu->client->nickname)
312 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
313 founder = chu->client;
314 silc_nicklist_insert(chanrec, chu, FALSE);
317 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
318 nicklist_set_own(CHANNEL(chanrec), ownnick);
319 signal_emit("channel joined", 1, chanrec);
322 printformat_module("fe-common/silc", server, channel->channel_name,
323 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
324 channel->channel_name, chanrec->topic);
326 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
329 if (founder == conn->local_entry)
330 printformat_module("fe-common/silc",
331 server, channel->channel_name, MSGLEVEL_CRAP,
332 SILCTXT_CHANNEL_FOUNDER_YOU,
333 channel->channel_name);
335 printformat_module("fe-common/silc",
336 server, channel->channel_name, MSGLEVEL_CRAP,
337 SILCTXT_CHANNEL_FOUNDER,
338 channel->channel_name, founder->nickname);
344 SilcClientConnection conn;
350 void silc_getkey_cb(bool success, void *context)
352 GetkeyContext getkey = (GetkeyContext)context;
353 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
354 char *name = (getkey->id_type == SILC_ID_CLIENT ?
355 ((SilcClientEntry)getkey->entry)->nickname :
356 ((SilcServerEntry)getkey->entry)->server_name);
359 printformat_module("fe-common/silc", NULL, NULL,
360 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
362 printformat_module("fe-common/silc", NULL, NULL,
363 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
366 silc_free(getkey->fingerprint);
370 /* Command reply handler. This function is called always in the command reply
371 function. If error occurs it will be called as well. Normal scenario
372 is that it will be called after the received command data has been parsed
373 and processed. The function is used to pass the received command data to
376 `conn' is the associated client connection. `cmd_payload' is the command
377 payload data received from server and it can be ignored. It is provided
378 if the application would like to re-parse the received command data,
379 however, it must be noted that the data is parsed already by the library
380 thus the payload can be ignored. `success' is FALSE if error occured.
381 In this case arguments are not sent to the application. `command' is the
382 command reply being processed. The function has variable argument list
383 and each command defines the number and type of arguments it passes to the
384 application (on error they are not sent). */
387 silc_command_reply(SilcClient client, SilcClientConnection conn,
388 SilcCommandPayload cmd_payload, int success,
389 SilcCommand command, SilcCommandStatus status, ...)
392 SILC_SERVER_REC *server = conn->context;
393 SILC_CHANNEL_REC *chanrec;
396 va_start(vp, status);
398 SILC_LOG_DEBUG(("Start"));
401 case SILC_COMMAND_WHOIS:
403 char buf[1024], *nickname, *username, *realname, *nick;
404 unsigned char *fingerprint;
407 SilcClientEntry client_entry;
409 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
410 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
412 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
415 silc_say_error("%s: %s", tmp,
416 silc_client_command_status_message(status));
423 client_entry = va_arg(vp, SilcClientEntry);
424 nickname = va_arg(vp, char *);
425 username = va_arg(vp, char *);
426 realname = va_arg(vp, char *);
427 channels = va_arg(vp, SilcBuffer);
428 mode = va_arg(vp, uint32);
429 idle = va_arg(vp, uint32);
430 fingerprint = va_arg(vp, unsigned char *);
432 silc_parse_userfqdn(nickname, &nick, NULL);
433 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
434 SILCTXT_WHOIS_USERINFO, nickname,
435 client_entry->username, client_entry->hostname,
436 nick, client_entry->nickname);
437 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
438 SILCTXT_WHOIS_REALNAME, realname);
442 SilcDList list = silc_channel_payload_parse_list(channels);
444 SilcChannelPayload entry;
445 memset(buf, 0, sizeof(buf));
446 silc_dlist_start(list);
447 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
448 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
450 char *name = silc_channel_get_name(entry, &name_len);
453 strncat(buf, m, strlen(m));
454 strncat(buf, name, name_len);
455 strncat(buf, " ", 1);
459 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
460 SILCTXT_WHOIS_CHANNELS, buf);
461 silc_channel_payload_list_free(list);
466 memset(buf, 0, sizeof(buf));
468 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
469 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
470 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
472 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
473 "SILC Operator " : "[Unknown mode] ");
475 if (mode & SILC_UMODE_GONE)
478 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
479 SILCTXT_WHOIS_MODES, buf);
482 if (idle && nickname) {
483 memset(buf, 0, sizeof(buf));
484 snprintf(buf, sizeof(buf) - 1, "%lu %s",
485 idle > 60 ? (idle / 60) : idle,
486 idle > 60 ? "minutes" : "seconds");
488 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
489 SILCTXT_WHOIS_IDLE, buf);
493 fingerprint = silc_fingerprint(fingerprint, 20);
494 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
495 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
496 silc_free(fingerprint);
501 case SILC_COMMAND_WHOWAS:
503 char *nickname, *username, *realname;
505 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
506 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
508 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
511 silc_say_error("%s: %s", tmp,
512 silc_client_command_status_message(status));
519 (void)va_arg(vp, SilcClientEntry);
520 nickname = va_arg(vp, char *);
521 username = va_arg(vp, char *);
522 realname = va_arg(vp, char *);
524 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
525 SILCTXT_WHOWAS_USERINFO, nickname, username,
526 realname ? realname : "");
530 case SILC_COMMAND_INVITE:
532 SilcChannelEntry channel;
534 SilcArgumentPayload args;
540 channel = va_arg(vp, SilcChannelEntry);
541 invite_list = va_arg(vp, char *);
543 args = silc_command_get_args(cmd_payload);
545 argc = silc_argument_get_arg_num(args);
548 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
549 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
552 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
553 SILCTXT_CHANNEL_NO_INVITE_LIST,
554 channel->channel_name);
558 case SILC_COMMAND_JOIN:
560 char *channel, *mode, *topic;
562 SilcChannelEntry channel_entry;
563 SilcBuffer client_id_list;
569 channel = va_arg(vp, char *);
570 channel_entry = va_arg(vp, SilcChannelEntry);
571 modei = va_arg(vp, uint32);
572 (void)va_arg(vp, uint32);
573 (void)va_arg(vp, unsigned char *);
574 (void)va_arg(vp, unsigned char *);
575 (void)va_arg(vp, unsigned char *);
576 topic = va_arg(vp, char *);
577 (void)va_arg(vp, unsigned char *);
578 list_count = va_arg(vp, uint32);
579 client_id_list = va_arg(vp, SilcBuffer);
581 chanrec = silc_channel_find(server, channel);
583 chanrec = silc_channel_create(server, channel, TRUE);
586 g_free_not_null(chanrec->topic);
587 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
588 signal_emit("channel topic changed", 1, chanrec);
591 mode = silc_client_chmode(modei,
592 channel_entry->channel_key ?
593 channel_entry->channel_key->cipher->name : "",
594 channel_entry->hmac ?
595 silc_hmac_get_name(channel_entry->hmac) : "");
596 g_free_not_null(chanrec->mode);
597 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
598 signal_emit("channel mode changed", 1, chanrec);
600 /* Resolve the client information */
601 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
602 silc_client_join_get_users,
607 case SILC_COMMAND_NICK:
609 SilcClientEntry client = va_arg(vp, SilcClientEntry);
615 old = g_strdup(server->nick);
616 server_change_nick(SERVER(server), client->nickname);
617 nicklist_rename_unique(SERVER(server),
618 server->conn->local_entry, server->nick,
619 client, client->nickname);
621 signal_emit("message own_nick", 4, server, server->nick, old, "");
626 case SILC_COMMAND_LIST:
635 (void)va_arg(vp, SilcChannelEntry);
636 name = va_arg(vp, char *);
637 topic = va_arg(vp, char *);
638 usercount = va_arg(vp, int);
640 if (status == SILC_STATUS_LIST_START ||
641 status == SILC_STATUS_OK)
642 printformat_module("fe-common/silc", server, NULL,
643 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
646 snprintf(users, sizeof(users) - 1, "N/A");
648 snprintf(users, sizeof(users) - 1, "%d", usercount);
649 printformat_module("fe-common/silc", server, NULL,
650 MSGLEVEL_CRAP, SILCTXT_LIST,
651 name, users, topic ? topic : "");
655 case SILC_COMMAND_UMODE:
662 mode = va_arg(vp, uint32);
664 if (mode & SILC_UMODE_SERVER_OPERATOR)
665 printformat_module("fe-common/silc", server, NULL,
666 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
668 if (mode & SILC_UMODE_ROUTER_OPERATOR)
669 printformat_module("fe-common/silc", server, NULL,
670 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
674 case SILC_COMMAND_OPER:
678 printformat_module("fe-common/silc", server, NULL,
679 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
682 case SILC_COMMAND_SILCOPER:
686 printformat_module("fe-common/silc", server, NULL,
687 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
690 case SILC_COMMAND_USERS:
692 SilcChannelEntry channel;
698 channel = va_arg(vp, SilcChannelEntry);
700 printformat_module("fe-common/silc", server, channel->channel_name,
701 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
702 channel->channel_name);
704 silc_list_start(channel->clients);
705 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
706 SilcClientEntry e = chu->client;
712 memset(stat, 0, sizeof(stat));
713 mode = silc_client_chumode_char(chu->mode);
714 if (e->mode & SILC_UMODE_GONE)
721 printformat_module("fe-common/silc", server, channel->channel_name,
722 MSGLEVEL_CRAP, SILCTXT_USERS,
724 e->username ? e->username : "",
725 e->hostname ? e->hostname : "",
726 e->realname ? e->realname : "");
733 case SILC_COMMAND_BAN:
735 SilcChannelEntry channel;
741 channel = va_arg(vp, SilcChannelEntry);
742 ban_list = va_arg(vp, char *);
745 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
746 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
749 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
750 SILCTXT_CHANNEL_NO_BAN_LIST,
751 channel->channel_name);
755 case SILC_COMMAND_GETKEY:
759 SilcPublicKey public_key;
762 GetkeyContext getkey;
767 id_type = va_arg(vp, uint32);
768 entry = va_arg(vp, void *);
769 public_key = va_arg(vp, SilcPublicKey);
772 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
774 getkey = silc_calloc(1, sizeof(*getkey));
775 getkey->entry = entry;
776 getkey->id_type = id_type;
777 getkey->client = client;
779 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
781 silc_verify_public_key_internal(client, conn,
782 (id_type == SILC_ID_CLIENT ?
783 SILC_SOCKET_TYPE_CLIENT :
784 SILC_SOCKET_TYPE_SERVER),
785 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
786 silc_getkey_cb, getkey);
789 printformat_module("fe-common/silc", server, NULL,
790 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
795 case SILC_COMMAND_INFO:
797 SilcServerEntry server_entry;
804 server_entry = va_arg(vp, SilcServerEntry);
805 server_name = va_arg(vp, char *);
806 server_info = va_arg(vp, char *);
808 if (server_name && server_info )
810 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
811 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
816 case SILC_COMMAND_TOPIC:
818 SilcChannelEntry channel;
824 channel = va_arg(vp, SilcChannelEntry);
825 topic = va_arg(vp, char *);
828 chanrec = silc_channel_find_entry(server, channel);
830 g_free_not_null(chanrec->topic);
831 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
832 signal_emit("channel topic changed", 1, chanrec);
834 printformat_module("fe-common/silc", server, channel->channel_name,
835 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
836 channel->channel_name, topic);
838 printformat_module("fe-common/silc", server, channel->channel_name,
839 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
840 channel->channel_name);
850 /* Internal routine to verify public key. If the `completion' is provided
851 it will be called to indicate whether public was verified or not. */
855 SilcClientConnection conn;
860 SilcSKEPKType pk_type;
861 SilcVerifyPublicKey completion;
865 static void verify_public_key_completion(const char *line, void *context)
867 PublicKeyVerify verify = (PublicKeyVerify)context;
869 if (line[0] == 'Y' || line[0] == 'y') {
870 /* Call the completion */
871 if (verify->completion)
872 verify->completion(TRUE, verify->context);
874 /* Save the key for future checking */
875 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
876 verify->pk_len, SILC_PKCS_FILE_PEM);
878 /* Call the completion */
879 if (verify->completion)
880 verify->completion(FALSE, verify->context);
882 printformat_module("fe-common/silc", NULL, NULL,
883 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
886 silc_free(verify->filename);
887 silc_free(verify->entity);
888 silc_free(verify->pk);
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, *babbleprint, *format;
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 printformat_module("fe-common/silc", NULL, NULL,
909 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
912 completion(FALSE, context);
916 pw = getpwuid(getuid());
919 completion(FALSE, context);
923 memset(filename, 0, sizeof(filename));
924 memset(file, 0, sizeof(file));
926 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
927 conn_type == SILC_SOCKET_TYPE_ROUTER) {
928 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
929 conn->sock->hostname, conn->sock->port);
930 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
931 pw->pw_dir, entity, file);
933 /* Replace all whitespaces with `_'. */
934 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
935 for (i = 0; i < strlen(fingerprint); i++)
936 if (fingerprint[i] == ' ')
937 fingerprint[i] = '_';
939 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
940 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
941 pw->pw_dir, entity, file);
942 silc_free(fingerprint);
945 /* Take fingerprint of the public key */
946 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
947 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
949 verify = silc_calloc(1, sizeof(*verify));
950 verify->client = client;
952 verify->filename = strdup(filename);
953 verify->entity = strdup(entity);
954 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
955 memcpy(verify->pk, pk, pk_len);
956 verify->pk_len = pk_len;
957 verify->pk_type = pk_type;
958 verify->completion = completion;
959 verify->context = context;
961 /* Check whether this key already exists */
962 if (stat(filename, &st) < 0) {
963 /* Key does not exist, ask user to verify the key and save it */
965 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
966 SILCTXT_PUBKEY_RECEIVED, entity);
967 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
968 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
969 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
970 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
971 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
972 SILCTXT_PUBKEY_ACCEPT);
973 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
976 silc_free(fingerprint);
979 /* The key already exists, verify it. */
980 SilcPublicKey public_key;
981 unsigned char *encpk;
984 /* Load the key file */
985 if (!silc_pkcs_load_public_key(filename, &public_key,
987 if (!silc_pkcs_load_public_key(filename, &public_key,
988 SILC_PKCS_FILE_BIN)) {
989 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
990 SILCTXT_PUBKEY_RECEIVED, entity);
991 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
992 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
993 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
994 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
995 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
996 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
997 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
998 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
999 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1002 silc_free(fingerprint);
1006 /* Encode the key data */
1007 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1009 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1010 SILCTXT_PUBKEY_RECEIVED, entity);
1011 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1012 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1013 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1014 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1015 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1016 SILCTXT_PUBKEY_MALFORMED, entity);
1017 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1018 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1019 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1022 silc_free(fingerprint);
1026 /* Compare the keys */
1027 if (memcmp(encpk, pk, encpk_len)) {
1028 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1029 SILCTXT_PUBKEY_RECEIVED, entity);
1030 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1031 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1032 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1033 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1034 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1035 SILCTXT_PUBKEY_NO_MATCH, entity);
1036 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1037 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1038 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1039 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1041 /* Ask user to verify the key and save it */
1042 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1043 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1044 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1047 silc_free(fingerprint);
1051 /* Local copy matched */
1053 completion(TRUE, context);
1054 silc_free(fingerprint);
1058 /* Verifies received public key. The `conn_type' indicates which entity
1059 (server, client etc.) has sent the public key. If user decides to trust
1060 the key may be saved as trusted public key for later use. The
1061 `completion' must be called after the public key has been verified. */
1064 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1065 SilcSocketType conn_type, unsigned char *pk,
1066 uint32 pk_len, SilcSKEPKType pk_type,
1067 SilcVerifyPublicKey completion, void *context)
1069 silc_verify_public_key_internal(client, conn, conn_type, pk,
1071 completion, context);
1074 /* Asks passphrase from user on the input line. */
1077 SilcAskPassphrase completion;
1081 void ask_passphrase_completion(const char *passphrase, void *context)
1083 AskPassphrase p = (AskPassphrase)context;
1084 p->completion((unsigned char *)passphrase,
1085 passphrase ? strlen(passphrase) : 0, p->context);
1089 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1090 SilcAskPassphrase completion, void *context)
1092 AskPassphrase p = silc_calloc(1, sizeof(*p));
1093 p->completion = completion;
1094 p->context = context;
1096 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1097 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1101 SilcGetAuthMeth completion;
1103 } *InternalGetAuthMethod;
1105 /* Callback called when we've received the authentication method information
1106 from the server after we've requested it. This will get the authentication
1107 data from the user if needed. */
1109 static void silc_get_auth_method_callback(SilcClient client,
1110 SilcClientConnection conn,
1111 SilcAuthMethod auth_meth,
1114 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1116 SILC_LOG_DEBUG(("Start"));
1118 switch (auth_meth) {
1119 case SILC_AUTH_NONE:
1120 /* No authentication required. */
1121 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1123 case SILC_AUTH_PASSWORD:
1124 /* Do not ask the passphrase from user, the library will ask it if
1125 we do not provide it here. */
1126 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1128 case SILC_AUTH_PUBLIC_KEY:
1129 /* Do not get the authentication data now, the library will generate
1130 it using our default key, if we do not provide it here. */
1131 /* XXX In the future when we support multiple local keys and multiple
1132 local certificates we will need to ask from user which one to use. */
1133 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1137 silc_free(internal);
1140 /* Find authentication method and authentication data by hostname and
1141 port. The hostname may be IP address as well. The found authentication
1142 method and authentication data is returned to `auth_meth', `auth_data'
1143 and `auth_data_len'. The function returns TRUE if authentication method
1144 is found and FALSE if not. `conn' may be NULL. */
1146 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1147 char *hostname, uint16 port,
1148 SilcGetAuthMeth completion, void *context)
1150 InternalGetAuthMethod internal;
1152 SILC_LOG_DEBUG(("Start"));
1154 /* XXX must resolve from configuration whether this connection has
1155 any specific authentication data */
1157 /* If we do not have this connection configured by the user in a
1158 configuration file then resolve the authentication method from the
1159 server for this session. */
1160 internal = silc_calloc(1, sizeof(*internal));
1161 internal->completion = completion;
1162 internal->context = context;
1164 silc_client_request_authentication_method(client, conn,
1165 silc_get_auth_method_callback,
1169 /* Notifies application that failure packet was received. This is called
1170 if there is some protocol active in the client. The `protocol' is the
1171 protocol context. The `failure' is opaque pointer to the failure
1172 indication. Note, that the `failure' is protocol dependant and application
1173 must explicitly cast it to correct type. Usually `failure' is 32 bit
1174 failure type (see protocol specs for all protocol failure types). */
1176 void silc_failure(SilcClient client, SilcClientConnection conn,
1177 SilcProtocol protocol, void *failure)
1179 SILC_LOG_DEBUG(("Start"));
1181 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1182 SilcSKEStatus status = (SilcSKEStatus)failure;
1184 if (status == SILC_SKE_STATUS_BAD_VERSION)
1185 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1186 SILCTXT_KE_BAD_VERSION);
1187 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1188 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1189 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1190 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1191 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1192 SILCTXT_KE_UNKNOWN_GROUP);
1193 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1194 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1195 SILCTXT_KE_UNKNOWN_CIPHER);
1196 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1197 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1198 SILCTXT_KE_UNKNOWN_PKCS);
1199 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1200 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1201 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1202 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1203 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1204 SILCTXT_KE_UNKNOWN_HMAC);
1205 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1206 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1207 SILCTXT_KE_INCORRECT_SIGNATURE);
1208 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1209 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1210 SILCTXT_KE_INVALID_COOKIE);
1213 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1214 uint32 err = (uint32)failure;
1216 if (err == SILC_AUTH_FAILED)
1217 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1218 SILCTXT_AUTH_FAILED);
1222 /* Asks whether the user would like to perform the key agreement protocol.
1223 This is called after we have received an key agreement packet or an
1224 reply to our key agreement packet. This returns TRUE if the user wants
1225 the library to perform the key agreement protocol and FALSE if it is not
1226 desired (application may start it later by calling the function
1227 silc_client_perform_key_agreement). */
1229 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1230 SilcClientEntry client_entry, const char *hostname,
1231 uint16 port, SilcKeyAgreementCallback *completion,
1236 SILC_LOG_DEBUG(("Start"));
1238 /* We will just display the info on the screen and return FALSE and user
1239 will have to start the key agreement with a command. */
1242 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1245 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1246 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1248 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1249 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1250 client_entry->nickname, hostname, portstr);
1258 void silc_ftp(SilcClient client, SilcClientConnection conn,
1259 SilcClientEntry client_entry, uint32 session_id,
1260 const char *hostname, uint16 port)
1262 SILC_SERVER_REC *server;
1264 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1266 SILC_LOG_DEBUG(("Start"));
1268 server = conn->context;
1270 ftp->client_entry = client_entry;
1271 ftp->session_id = session_id;
1274 silc_dlist_add(server->ftp_sessions, ftp);
1275 server->current_session = ftp;
1278 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1281 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1282 SILCTXT_FILE_REQUEST, client_entry->nickname);
1284 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1285 SILCTXT_FILE_REQUEST_HOST,
1286 client_entry->nickname, hostname, portstr);
1289 /* SILC client operations */
1290 SilcClientOperations ops = {
1292 silc_channel_message,
1293 silc_private_message,
1299 silc_get_auth_method,
1300 silc_verify_public_key,
1301 silc_ask_passphrase,