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 objecet 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;
406 SilcClientEntry client_entry;
408 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
409 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
411 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
414 silc_say_error("%s: %s", tmp,
415 silc_client_command_status_message(status));
422 client_entry = va_arg(vp, SilcClientEntry);
423 nickname = va_arg(vp, char *);
424 username = va_arg(vp, char *);
425 realname = va_arg(vp, char *);
426 channels = va_arg(vp, SilcBuffer);
427 mode = va_arg(vp, uint32);
428 idle = va_arg(vp, uint32);
430 silc_parse_userfqdn(nickname, &nick, NULL);
431 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
432 SILCTXT_WHOIS_USERINFO, nickname,
433 client_entry->username, client_entry->hostname,
434 nick, client_entry->nickname);
435 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
436 SILCTXT_WHOIS_REALNAME, realname);
440 SilcDList list = silc_channel_payload_parse_list(channels);
442 SilcChannelPayload entry;
443 memset(buf, 0, sizeof(buf));
444 silc_dlist_start(list);
445 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
446 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
448 char *name = silc_channel_get_name(entry, &name_len);
451 strncat(buf, m, strlen(m));
452 strncat(buf, name, name_len);
453 strncat(buf, " ", 1);
457 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
458 SILCTXT_WHOIS_CHANNELS, buf);
459 silc_channel_payload_list_free(list);
464 memset(buf, 0, sizeof(buf));
466 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
467 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
468 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
470 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
471 "SILC Operator " : "[Unknown mode] ");
473 if (mode & SILC_UMODE_GONE)
476 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
477 SILCTXT_WHOIS_MODES, buf);
480 if (idle && nickname) {
481 memset(buf, 0, sizeof(buf));
482 snprintf(buf, sizeof(buf) - 1, "%lu %s",
483 idle > 60 ? (idle / 60) : idle,
484 idle > 60 ? "minutes" : "seconds");
486 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
487 SILCTXT_WHOIS_IDLE, buf);
492 case SILC_COMMAND_WHOWAS:
494 char *nickname, *username, *realname;
496 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
497 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
499 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
502 silc_say_error("%s: %s", tmp,
503 silc_client_command_status_message(status));
510 (void)va_arg(vp, SilcClientEntry);
511 nickname = va_arg(vp, char *);
512 username = va_arg(vp, char *);
513 realname = va_arg(vp, char *);
515 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
516 SILCTXT_WHOWAS_USERINFO, nickname, username,
517 realname ? realname : "");
521 case SILC_COMMAND_INVITE:
523 SilcChannelEntry channel;
525 SilcArgumentPayload args;
531 channel = va_arg(vp, SilcChannelEntry);
532 invite_list = va_arg(vp, char *);
534 args = silc_command_get_args(cmd_payload);
536 argc = silc_argument_get_arg_num(args);
539 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
540 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
543 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
544 SILCTXT_CHANNEL_NO_INVITE_LIST,
545 channel->channel_name);
549 case SILC_COMMAND_JOIN:
551 char *channel, *mode, *topic;
553 SilcChannelEntry channel_entry;
554 SilcBuffer client_id_list;
560 channel = va_arg(vp, char *);
561 channel_entry = va_arg(vp, SilcChannelEntry);
562 modei = va_arg(vp, uint32);
563 (void)va_arg(vp, uint32);
564 (void)va_arg(vp, unsigned char *);
565 (void)va_arg(vp, unsigned char *);
566 (void)va_arg(vp, unsigned char *);
567 topic = va_arg(vp, char *);
568 (void)va_arg(vp, unsigned char *);
569 list_count = va_arg(vp, uint32);
570 client_id_list = va_arg(vp, SilcBuffer);
572 chanrec = silc_channel_find(server, channel);
574 chanrec = silc_channel_create(server, channel, TRUE);
577 g_free_not_null(chanrec->topic);
578 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
579 signal_emit("channel topic changed", 1, chanrec);
582 mode = silc_client_chmode(modei,
583 channel_entry->channel_key ?
584 channel_entry->channel_key->cipher->name : "",
585 channel_entry->hmac ?
586 silc_hmac_get_name(channel_entry->hmac) : "");
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:
626 (void)va_arg(vp, SilcChannelEntry);
627 name = va_arg(vp, char *);
628 topic = va_arg(vp, char *);
629 usercount = va_arg(vp, int);
631 if (status == SILC_STATUS_LIST_START ||
632 status == SILC_STATUS_OK)
633 printformat_module("fe-common/silc", server, NULL,
634 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
636 snprintf(users, sizeof(users) - 1, "%d", usercount);
637 printformat_module("fe-common/silc", server, NULL,
638 MSGLEVEL_CRAP, SILCTXT_LIST,
639 name, users, topic ? topic : "");
643 case SILC_COMMAND_UMODE:
650 mode = va_arg(vp, uint32);
652 if (mode & SILC_UMODE_SERVER_OPERATOR)
653 printformat_module("fe-common/silc", server, NULL,
654 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
656 if (mode & SILC_UMODE_ROUTER_OPERATOR)
657 printformat_module("fe-common/silc", server, NULL,
658 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
662 case SILC_COMMAND_OPER:
666 printformat_module("fe-common/silc", server, NULL,
667 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
670 case SILC_COMMAND_SILCOPER:
674 printformat_module("fe-common/silc", server, NULL,
675 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
678 case SILC_COMMAND_USERS:
680 SilcChannelEntry channel;
686 channel = va_arg(vp, SilcChannelEntry);
688 printformat_module("fe-common/silc", server, channel->channel_name,
689 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
690 channel->channel_name);
692 silc_list_start(channel->clients);
693 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
694 SilcClientEntry e = chu->client;
700 memset(stat, 0, sizeof(stat));
701 mode = silc_client_chumode_char(chu->mode);
702 if (e->mode & SILC_UMODE_GONE)
709 printformat_module("fe-common/silc", server, channel->channel_name,
710 MSGLEVEL_CRAP, SILCTXT_USERS,
712 e->username ? e->username : "",
713 e->hostname ? e->hostname : "",
714 e->realname ? e->realname : "");
721 case SILC_COMMAND_BAN:
723 SilcChannelEntry channel;
729 channel = va_arg(vp, SilcChannelEntry);
730 ban_list = va_arg(vp, char *);
733 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
734 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
737 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
738 SILCTXT_CHANNEL_NO_BAN_LIST,
739 channel->channel_name);
743 case SILC_COMMAND_GETKEY:
747 SilcPublicKey public_key;
750 GetkeyContext getkey;
755 id_type = va_arg(vp, uint32);
756 entry = va_arg(vp, void *);
757 public_key = va_arg(vp, SilcPublicKey);
760 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
762 getkey = silc_calloc(1, sizeof(*getkey));
763 getkey->entry = entry;
764 getkey->id_type = id_type;
765 getkey->client = client;
767 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
769 silc_verify_public_key_internal(client, conn,
770 (id_type == SILC_ID_CLIENT ?
771 SILC_SOCKET_TYPE_CLIENT :
772 SILC_SOCKET_TYPE_SERVER),
773 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
774 silc_getkey_cb, getkey);
777 printformat_module("fe-common/silc", server, NULL,
778 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
783 case SILC_COMMAND_TOPIC:
785 SilcChannelEntry channel;
791 channel = va_arg(vp, SilcChannelEntry);
792 topic = va_arg(vp, char *);
795 chanrec = silc_channel_find_entry(server, channel);
797 g_free_not_null(chanrec->topic);
798 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
799 signal_emit("channel topic changed", 1, chanrec);
801 printformat_module("fe-common/silc", server, channel->channel_name,
802 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
803 channel->channel_name, topic);
805 printformat_module("fe-common/silc", server, channel->channel_name,
806 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
807 channel->channel_name);
817 /* Internal routine to verify public key. If the `completion' is provided
818 it will be called to indicate whether public was verified or not. */
822 SilcClientConnection conn;
827 SilcSKEPKType pk_type;
828 SilcVerifyPublicKey completion;
832 static void verify_public_key_completion(const char *line, void *context)
834 PublicKeyVerify verify = (PublicKeyVerify)context;
836 if (line[0] == 'Y' || line[0] == 'y') {
837 /* Call the completion */
838 if (verify->completion)
839 verify->completion(TRUE, verify->context);
841 /* Save the key for future checking */
842 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
843 verify->pk_len, SILC_PKCS_FILE_PEM);
845 /* Call the completion */
846 if (verify->completion)
847 verify->completion(FALSE, verify->context);
849 printformat_module("fe-common/silc", NULL, NULL,
850 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
853 silc_free(verify->filename);
854 silc_free(verify->entity);
855 silc_free(verify->pk);
860 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
861 SilcSocketType conn_type, unsigned char *pk,
862 uint32 pk_len, SilcSKEPKType pk_type,
863 SilcVerifyPublicKey completion, void *context)
866 char file[256], filename[256], *fingerprint, *format;
869 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
870 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
871 "server" : "client");
872 PublicKeyVerify verify;
874 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
875 printformat_module("fe-common/silc", NULL, NULL,
876 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
879 completion(FALSE, context);
883 pw = getpwuid(getuid());
886 completion(FALSE, context);
890 memset(filename, 0, sizeof(filename));
891 memset(file, 0, sizeof(file));
893 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
894 conn_type == SILC_SOCKET_TYPE_ROUTER) {
895 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
896 conn->sock->hostname, conn->sock->port);
897 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
898 pw->pw_dir, entity, file);
900 /* Replace all whitespaces with `_'. */
901 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
902 for (i = 0; i < strlen(fingerprint); i++)
903 if (fingerprint[i] == ' ')
904 fingerprint[i] = '_';
906 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
907 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
908 pw->pw_dir, entity, file);
909 silc_free(fingerprint);
912 /* Take fingerprint of the public key */
913 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
915 verify = silc_calloc(1, sizeof(*verify));
916 verify->client = client;
918 verify->filename = strdup(filename);
919 verify->entity = strdup(entity);
920 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
921 memcpy(verify->pk, pk, pk_len);
922 verify->pk_len = pk_len;
923 verify->pk_type = pk_type;
924 verify->completion = completion;
925 verify->context = context;
927 /* Check whether this key already exists */
928 if (stat(filename, &st) < 0) {
929 /* Key does not exist, ask user to verify the key and save it */
931 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
932 SILCTXT_PUBKEY_RECEIVED, entity);
933 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
934 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
935 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
936 SILCTXT_PUBKEY_ACCEPT);
937 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
940 silc_free(fingerprint);
943 /* The key already exists, verify it. */
944 SilcPublicKey public_key;
945 unsigned char *encpk;
948 /* Load the key file */
949 if (!silc_pkcs_load_public_key(filename, &public_key,
951 if (!silc_pkcs_load_public_key(filename, &public_key,
952 SILC_PKCS_FILE_BIN)) {
953 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
954 SILCTXT_PUBKEY_RECEIVED, entity);
955 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
956 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
957 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
958 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
959 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
960 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
961 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
964 silc_free(fingerprint);
968 /* Encode the key data */
969 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
971 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
972 SILCTXT_PUBKEY_RECEIVED, entity);
973 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
974 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
975 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
976 SILCTXT_PUBKEY_MALFORMED, entity);
977 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
978 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
979 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
982 silc_free(fingerprint);
986 /* Compare the keys */
987 if (memcmp(encpk, pk, encpk_len)) {
988 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
989 SILCTXT_PUBKEY_RECEIVED, entity);
990 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
991 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
992 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
993 SILCTXT_PUBKEY_NO_MATCH, entity);
994 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
995 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
996 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
997 SILCTXT_PUBKEY_MITM_ATTACK, entity);
999 /* Ask user to verify the key and save it */
1000 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1001 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1002 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
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 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 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);
1059 SilcGetAuthMeth completion;
1061 } *InternalGetAuthMethod;
1063 /* Callback called when we've received the authentication method information
1064 from the server after we've requested it. This will get the authentication
1065 data from the user if needed. */
1067 static void silc_get_auth_method_callback(SilcClient client,
1068 SilcClientConnection conn,
1069 SilcAuthMethod auth_meth,
1072 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1074 SILC_LOG_DEBUG(("Start"));
1076 switch (auth_meth) {
1077 case SILC_AUTH_NONE:
1078 /* No authentication required. */
1079 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1081 case SILC_AUTH_PASSWORD:
1082 /* Do not ask the passphrase from user, the library will ask it if
1083 we do not provide it here. */
1084 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1086 case SILC_AUTH_PUBLIC_KEY:
1087 /* Do not get the authentication data now, the library will generate
1088 it using our default key, if we do not provide it here. */
1089 /* XXX In the future when we support multiple local keys and multiple
1090 local certificates we will need to ask from user which one to use. */
1091 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1095 silc_free(internal);
1098 /* Find authentication method and authentication data by hostname and
1099 port. The hostname may be IP address as well. The found authentication
1100 method and authentication data is returned to `auth_meth', `auth_data'
1101 and `auth_data_len'. The function returns TRUE if authentication method
1102 is found and FALSE if not. `conn' may be NULL. */
1104 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1105 char *hostname, uint16 port,
1106 SilcGetAuthMeth completion, void *context)
1108 InternalGetAuthMethod internal;
1110 SILC_LOG_DEBUG(("Start"));
1112 /* XXX must resolve from configuration whether this connection has
1113 any specific authentication data */
1115 /* If we do not have this connection configured by the user in a
1116 configuration file then resolve the authentication method from the
1117 server for this session. */
1118 internal = silc_calloc(1, sizeof(*internal));
1119 internal->completion = completion;
1120 internal->context = context;
1122 silc_client_request_authentication_method(client, conn,
1123 silc_get_auth_method_callback,
1127 /* Notifies application that failure packet was received. This is called
1128 if there is some protocol active in the client. The `protocol' is the
1129 protocol context. The `failure' is opaque pointer to the failure
1130 indication. Note, that the `failure' is protocol dependant and application
1131 must explicitly cast it to correct type. Usually `failure' is 32 bit
1132 failure type (see protocol specs for all protocol failure types). */
1134 void silc_failure(SilcClient client, SilcClientConnection conn,
1135 SilcProtocol protocol, void *failure)
1137 SILC_LOG_DEBUG(("Start"));
1139 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1140 SilcSKEStatus status = (SilcSKEStatus)failure;
1142 if (status == SILC_SKE_STATUS_BAD_VERSION)
1143 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1144 SILCTXT_KE_BAD_VERSION);
1145 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1146 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1147 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1148 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1149 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1150 SILCTXT_KE_UNKNOWN_GROUP);
1151 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1152 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1153 SILCTXT_KE_UNKNOWN_CIPHER);
1154 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1155 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1156 SILCTXT_KE_UNKNOWN_PKCS);
1157 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1158 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1159 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1160 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1161 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1162 SILCTXT_KE_UNKNOWN_HMAC);
1163 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1164 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1165 SILCTXT_KE_INCORRECT_SIGNATURE);
1166 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1167 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1168 SILCTXT_KE_INVALID_COOKIE);
1171 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1172 uint32 err = (uint32)failure;
1174 if (err == SILC_AUTH_FAILED)
1175 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1176 SILCTXT_AUTH_FAILED);
1180 /* Asks whether the user would like to perform the key agreement protocol.
1181 This is called after we have received an key agreement packet or an
1182 reply to our key agreement packet. This returns TRUE if the user wants
1183 the library to perform the key agreement protocol and FALSE if it is not
1184 desired (application may start it later by calling the function
1185 silc_client_perform_key_agreement). */
1187 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1188 SilcClientEntry client_entry, const char *hostname,
1189 uint16 port, SilcKeyAgreementCallback *completion,
1194 SILC_LOG_DEBUG(("Start"));
1196 /* We will just display the info on the screen and return FALSE and user
1197 will have to start the key agreement with a command. */
1200 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1203 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1204 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1206 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1207 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1208 client_entry->nickname, hostname, portstr);
1216 void silc_ftp(SilcClient client, SilcClientConnection conn,
1217 SilcClientEntry client_entry, uint32 session_id,
1218 const char *hostname, uint16 port)
1220 SILC_SERVER_REC *server;
1222 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1224 SILC_LOG_DEBUG(("Start"));
1226 server = conn->context;
1228 ftp->client_entry = client_entry;
1229 ftp->session_id = session_id;
1232 silc_dlist_add(server->ftp_sessions, ftp);
1233 server->current_session = ftp;
1236 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1239 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1240 SILCTXT_FILE_REQUEST, client_entry->nickname);
1242 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1243 SILCTXT_FILE_REQUEST_HOST,
1244 client_entry->nickname, hostname, portstr);
1247 /* SILC client operations */
1248 SilcClientOperations ops = {
1250 silc_channel_message,
1251 silc_private_message,
1257 silc_get_auth_method,
1258 silc_verify_public_key,
1259 silc_ask_passphrase,