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->data,
445 SilcChannelPayload entry;
446 memset(buf, 0, sizeof(buf));
447 silc_dlist_start(list);
448 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
449 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
451 char *name = silc_channel_get_name(entry, &name_len);
454 strncat(buf, m, strlen(m));
455 strncat(buf, name, name_len);
456 strncat(buf, " ", 1);
460 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
461 SILCTXT_WHOIS_CHANNELS, buf);
462 silc_channel_payload_list_free(list);
467 memset(buf, 0, sizeof(buf));
469 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
470 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
471 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
473 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
474 "SILC Operator " : "[Unknown mode] ");
476 if (mode & SILC_UMODE_GONE)
479 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
480 SILCTXT_WHOIS_MODES, buf);
483 if (idle && nickname) {
484 memset(buf, 0, sizeof(buf));
485 snprintf(buf, sizeof(buf) - 1, "%lu %s",
486 idle > 60 ? (idle / 60) : idle,
487 idle > 60 ? "minutes" : "seconds");
489 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
490 SILCTXT_WHOIS_IDLE, buf);
494 fingerprint = silc_fingerprint(fingerprint, 20);
495 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
496 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
497 silc_free(fingerprint);
502 case SILC_COMMAND_WHOWAS:
504 char *nickname, *username, *realname;
506 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
507 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
509 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
512 silc_say_error("%s: %s", tmp,
513 silc_client_command_status_message(status));
520 (void)va_arg(vp, SilcClientEntry);
521 nickname = va_arg(vp, char *);
522 username = va_arg(vp, char *);
523 realname = va_arg(vp, char *);
525 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
526 SILCTXT_WHOWAS_USERINFO, nickname, username,
527 realname ? realname : "");
531 case SILC_COMMAND_INVITE:
533 SilcChannelEntry channel;
535 SilcArgumentPayload args;
541 channel = va_arg(vp, SilcChannelEntry);
542 invite_list = va_arg(vp, char *);
544 args = silc_command_get_args(cmd_payload);
546 argc = silc_argument_get_arg_num(args);
549 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
550 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
553 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
554 SILCTXT_CHANNEL_NO_INVITE_LIST,
555 channel->channel_name);
559 case SILC_COMMAND_JOIN:
561 char *channel, *mode, *topic;
563 SilcChannelEntry channel_entry;
564 SilcBuffer client_id_list;
570 channel = va_arg(vp, char *);
571 channel_entry = va_arg(vp, SilcChannelEntry);
572 modei = va_arg(vp, uint32);
573 (void)va_arg(vp, uint32);
574 (void)va_arg(vp, unsigned char *);
575 (void)va_arg(vp, unsigned char *);
576 (void)va_arg(vp, unsigned char *);
577 topic = va_arg(vp, char *);
578 (void)va_arg(vp, unsigned char *);
579 list_count = va_arg(vp, uint32);
580 client_id_list = va_arg(vp, SilcBuffer);
582 chanrec = silc_channel_find(server, channel);
584 chanrec = silc_channel_create(server, channel, TRUE);
587 g_free_not_null(chanrec->topic);
588 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
589 signal_emit("channel topic changed", 1, chanrec);
592 mode = silc_client_chmode(modei,
593 channel_entry->channel_key ?
594 channel_entry->channel_key->cipher->name : "",
595 channel_entry->hmac ?
596 silc_hmac_get_name(channel_entry->hmac) : "");
597 g_free_not_null(chanrec->mode);
598 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
599 signal_emit("channel mode changed", 1, chanrec);
601 /* Resolve the client information */
602 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
603 silc_client_join_get_users,
608 case SILC_COMMAND_NICK:
610 SilcClientEntry client = va_arg(vp, SilcClientEntry);
616 old = g_strdup(server->nick);
617 server_change_nick(SERVER(server), client->nickname);
618 nicklist_rename_unique(SERVER(server),
619 server->conn->local_entry, server->nick,
620 client, client->nickname);
622 signal_emit("message own_nick", 4, server, server->nick, old, "");
627 case SILC_COMMAND_LIST:
636 (void)va_arg(vp, SilcChannelEntry);
637 name = va_arg(vp, char *);
638 topic = va_arg(vp, char *);
639 usercount = va_arg(vp, int);
641 if (status == SILC_STATUS_LIST_START ||
642 status == SILC_STATUS_OK)
643 printformat_module("fe-common/silc", server, NULL,
644 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
647 snprintf(users, sizeof(users) - 1, "N/A");
649 snprintf(users, sizeof(users) - 1, "%d", usercount);
650 printformat_module("fe-common/silc", server, NULL,
651 MSGLEVEL_CRAP, SILCTXT_LIST,
652 name, users, topic ? topic : "");
656 case SILC_COMMAND_UMODE:
663 mode = va_arg(vp, uint32);
665 if (mode & SILC_UMODE_SERVER_OPERATOR)
666 printformat_module("fe-common/silc", server, NULL,
667 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
669 if (mode & SILC_UMODE_ROUTER_OPERATOR)
670 printformat_module("fe-common/silc", server, NULL,
671 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
675 case SILC_COMMAND_OPER:
679 printformat_module("fe-common/silc", server, NULL,
680 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
683 case SILC_COMMAND_SILCOPER:
687 printformat_module("fe-common/silc", server, NULL,
688 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
691 case SILC_COMMAND_USERS:
693 SilcChannelEntry channel;
699 channel = va_arg(vp, SilcChannelEntry);
701 printformat_module("fe-common/silc", server, channel->channel_name,
702 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
703 channel->channel_name);
705 silc_list_start(channel->clients);
706 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
707 SilcClientEntry e = chu->client;
713 memset(stat, 0, sizeof(stat));
714 mode = silc_client_chumode_char(chu->mode);
715 if (e->mode & SILC_UMODE_GONE)
722 printformat_module("fe-common/silc", server, channel->channel_name,
723 MSGLEVEL_CRAP, SILCTXT_USERS,
725 e->username ? e->username : "",
726 e->hostname ? e->hostname : "",
727 e->realname ? e->realname : "");
734 case SILC_COMMAND_BAN:
736 SilcChannelEntry channel;
742 channel = va_arg(vp, SilcChannelEntry);
743 ban_list = va_arg(vp, char *);
746 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
747 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
750 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
751 SILCTXT_CHANNEL_NO_BAN_LIST,
752 channel->channel_name);
756 case SILC_COMMAND_GETKEY:
760 SilcPublicKey public_key;
763 GetkeyContext getkey;
768 id_type = va_arg(vp, uint32);
769 entry = va_arg(vp, void *);
770 public_key = va_arg(vp, SilcPublicKey);
773 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
775 getkey = silc_calloc(1, sizeof(*getkey));
776 getkey->entry = entry;
777 getkey->id_type = id_type;
778 getkey->client = client;
780 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
782 silc_verify_public_key_internal(client, conn,
783 (id_type == SILC_ID_CLIENT ?
784 SILC_SOCKET_TYPE_CLIENT :
785 SILC_SOCKET_TYPE_SERVER),
786 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
787 silc_getkey_cb, getkey);
790 printformat_module("fe-common/silc", server, NULL,
791 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
796 case SILC_COMMAND_INFO:
798 SilcServerEntry server_entry;
805 server_entry = va_arg(vp, SilcServerEntry);
806 server_name = va_arg(vp, char *);
807 server_info = va_arg(vp, char *);
809 if (server_name && server_info )
811 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
812 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
817 case SILC_COMMAND_TOPIC:
819 SilcChannelEntry channel;
825 channel = va_arg(vp, SilcChannelEntry);
826 topic = va_arg(vp, char *);
829 chanrec = silc_channel_find_entry(server, channel);
831 g_free_not_null(chanrec->topic);
832 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
833 signal_emit("channel topic changed", 1, chanrec);
835 printformat_module("fe-common/silc", server, channel->channel_name,
836 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
837 channel->channel_name, topic);
839 printformat_module("fe-common/silc", server, channel->channel_name,
840 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
841 channel->channel_name);
851 /* Internal routine to verify public key. If the `completion' is provided
852 it will be called to indicate whether public was verified or not. */
856 SilcClientConnection conn;
861 SilcSKEPKType pk_type;
862 SilcVerifyPublicKey completion;
866 static void verify_public_key_completion(const char *line, void *context)
868 PublicKeyVerify verify = (PublicKeyVerify)context;
870 if (line[0] == 'Y' || line[0] == 'y') {
871 /* Call the completion */
872 if (verify->completion)
873 verify->completion(TRUE, verify->context);
875 /* Save the key for future checking */
876 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
877 verify->pk_len, SILC_PKCS_FILE_PEM);
879 /* Call the completion */
880 if (verify->completion)
881 verify->completion(FALSE, verify->context);
883 printformat_module("fe-common/silc", NULL, NULL,
884 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
887 silc_free(verify->filename);
888 silc_free(verify->entity);
889 silc_free(verify->pk);
894 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
895 SilcSocketType conn_type, unsigned char *pk,
896 uint32 pk_len, SilcSKEPKType pk_type,
897 SilcVerifyPublicKey completion, void *context)
900 char file[256], filename[256], *fingerprint, *babbleprint, *format;
903 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
904 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
905 "server" : "client");
906 PublicKeyVerify verify;
908 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
909 printformat_module("fe-common/silc", NULL, NULL,
910 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
913 completion(FALSE, context);
917 pw = getpwuid(getuid());
920 completion(FALSE, context);
924 memset(filename, 0, sizeof(filename));
925 memset(file, 0, sizeof(file));
927 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
928 conn_type == SILC_SOCKET_TYPE_ROUTER) {
929 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
930 conn->sock->ip, conn->sock->port);
931 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
932 pw->pw_dir, entity, file);
934 /* Replace all whitespaces with `_'. */
935 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
936 for (i = 0; i < strlen(fingerprint); i++)
937 if (fingerprint[i] == ' ')
938 fingerprint[i] = '_';
940 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
941 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
942 pw->pw_dir, entity, file);
943 silc_free(fingerprint);
946 /* Take fingerprint of the public key */
947 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
948 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
950 verify = silc_calloc(1, sizeof(*verify));
951 verify->client = client;
953 verify->filename = strdup(filename);
954 verify->entity = strdup(entity);
955 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
956 memcpy(verify->pk, pk, pk_len);
957 verify->pk_len = pk_len;
958 verify->pk_type = pk_type;
959 verify->completion = completion;
960 verify->context = context;
962 /* Check whether this key already exists */
963 if (stat(filename, &st) < 0) {
964 /* Key does not exist, ask user to verify the key and save it */
966 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
967 SILCTXT_PUBKEY_RECEIVED, entity);
968 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
969 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
970 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
971 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
972 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
973 SILCTXT_PUBKEY_ACCEPT);
974 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
977 silc_free(fingerprint);
980 /* The key already exists, verify it. */
981 SilcPublicKey public_key;
982 unsigned char *encpk;
985 /* Load the key file */
986 if (!silc_pkcs_load_public_key(filename, &public_key,
988 if (!silc_pkcs_load_public_key(filename, &public_key,
989 SILC_PKCS_FILE_BIN)) {
990 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
991 SILCTXT_PUBKEY_RECEIVED, entity);
992 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
993 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
994 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
995 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
996 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
997 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
998 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
999 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1000 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1003 silc_free(fingerprint);
1007 /* Encode the key data */
1008 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1010 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1011 SILCTXT_PUBKEY_RECEIVED, entity);
1012 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1013 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1014 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1015 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1016 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1017 SILCTXT_PUBKEY_MALFORMED, entity);
1018 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1019 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1020 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1023 silc_free(fingerprint);
1027 /* Compare the keys */
1028 if (memcmp(encpk, pk, encpk_len)) {
1029 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1030 SILCTXT_PUBKEY_RECEIVED, entity);
1031 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1032 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1033 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1034 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1035 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1036 SILCTXT_PUBKEY_NO_MATCH, entity);
1037 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1038 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1039 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1040 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1042 /* Ask user to verify the key and save it */
1043 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1044 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1045 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1048 silc_free(fingerprint);
1052 /* Local copy matched */
1054 completion(TRUE, context);
1055 silc_free(fingerprint);
1059 /* Verifies received public key. The `conn_type' indicates which entity
1060 (server, client etc.) has sent the public key. If user decides to trust
1061 the key may be saved as trusted public key for later use. The
1062 `completion' must be called after the public key has been verified. */
1065 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1066 SilcSocketType conn_type, unsigned char *pk,
1067 uint32 pk_len, SilcSKEPKType pk_type,
1068 SilcVerifyPublicKey completion, void *context)
1070 silc_verify_public_key_internal(client, conn, conn_type, pk,
1072 completion, context);
1075 /* Asks passphrase from user on the input line. */
1078 SilcAskPassphrase completion;
1082 void ask_passphrase_completion(const char *passphrase, void *context)
1084 AskPassphrase p = (AskPassphrase)context;
1085 p->completion((unsigned char *)passphrase,
1086 passphrase ? strlen(passphrase) : 0, p->context);
1090 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1091 SilcAskPassphrase completion, void *context)
1093 AskPassphrase p = silc_calloc(1, sizeof(*p));
1094 p->completion = completion;
1095 p->context = context;
1097 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1098 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1102 SilcGetAuthMeth completion;
1104 } *InternalGetAuthMethod;
1106 /* Callback called when we've received the authentication method information
1107 from the server after we've requested it. This will get the authentication
1108 data from the user if needed. */
1110 static void silc_get_auth_method_callback(SilcClient client,
1111 SilcClientConnection conn,
1112 SilcAuthMethod auth_meth,
1115 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1117 SILC_LOG_DEBUG(("Start"));
1119 switch (auth_meth) {
1120 case SILC_AUTH_NONE:
1121 /* No authentication required. */
1122 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1124 case SILC_AUTH_PASSWORD:
1125 /* Do not ask the passphrase from user, the library will ask it if
1126 we do not provide it here. */
1127 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1129 case SILC_AUTH_PUBLIC_KEY:
1130 /* Do not get the authentication data now, the library will generate
1131 it using our default key, if we do not provide it here. */
1132 /* XXX In the future when we support multiple local keys and multiple
1133 local certificates we will need to ask from user which one to use. */
1134 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1138 silc_free(internal);
1141 /* Find authentication method and authentication data by hostname and
1142 port. The hostname may be IP address as well. The found authentication
1143 method and authentication data is returned to `auth_meth', `auth_data'
1144 and `auth_data_len'. The function returns TRUE if authentication method
1145 is found and FALSE if not. `conn' may be NULL. */
1147 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1148 char *hostname, uint16 port,
1149 SilcGetAuthMeth completion, void *context)
1151 InternalGetAuthMethod internal;
1153 SILC_LOG_DEBUG(("Start"));
1155 /* XXX must resolve from configuration whether this connection has
1156 any specific authentication data */
1158 /* If we do not have this connection configured by the user in a
1159 configuration file then resolve the authentication method from the
1160 server for this session. */
1161 internal = silc_calloc(1, sizeof(*internal));
1162 internal->completion = completion;
1163 internal->context = context;
1165 silc_client_request_authentication_method(client, conn,
1166 silc_get_auth_method_callback,
1170 /* Notifies application that failure packet was received. This is called
1171 if there is some protocol active in the client. The `protocol' is the
1172 protocol context. The `failure' is opaque pointer to the failure
1173 indication. Note, that the `failure' is protocol dependant and application
1174 must explicitly cast it to correct type. Usually `failure' is 32 bit
1175 failure type (see protocol specs for all protocol failure types). */
1177 void silc_failure(SilcClient client, SilcClientConnection conn,
1178 SilcProtocol protocol, void *failure)
1180 SILC_LOG_DEBUG(("Start"));
1182 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1183 SilcSKEStatus status = (SilcSKEStatus)failure;
1185 if (status == SILC_SKE_STATUS_BAD_VERSION)
1186 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1187 SILCTXT_KE_BAD_VERSION);
1188 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1189 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1190 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1191 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1192 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1193 SILCTXT_KE_UNKNOWN_GROUP);
1194 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1195 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1196 SILCTXT_KE_UNKNOWN_CIPHER);
1197 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1198 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1199 SILCTXT_KE_UNKNOWN_PKCS);
1200 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1201 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1202 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1203 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1204 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1205 SILCTXT_KE_UNKNOWN_HMAC);
1206 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1207 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1208 SILCTXT_KE_INCORRECT_SIGNATURE);
1209 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1210 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1211 SILCTXT_KE_INVALID_COOKIE);
1214 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1215 uint32 err = (uint32)failure;
1217 if (err == SILC_AUTH_FAILED)
1218 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1219 SILCTXT_AUTH_FAILED);
1223 /* Asks whether the user would like to perform the key agreement protocol.
1224 This is called after we have received an key agreement packet or an
1225 reply to our key agreement packet. This returns TRUE if the user wants
1226 the library to perform the key agreement protocol and FALSE if it is not
1227 desired (application may start it later by calling the function
1228 silc_client_perform_key_agreement). */
1230 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1231 SilcClientEntry client_entry, const char *hostname,
1232 uint16 port, SilcKeyAgreementCallback *completion,
1237 SILC_LOG_DEBUG(("Start"));
1239 /* We will just display the info on the screen and return FALSE and user
1240 will have to start the key agreement with a command. */
1243 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1246 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1247 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1249 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1250 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1251 client_entry->nickname, hostname, portstr);
1259 void silc_ftp(SilcClient client, SilcClientConnection conn,
1260 SilcClientEntry client_entry, uint32 session_id,
1261 const char *hostname, uint16 port)
1263 SILC_SERVER_REC *server;
1265 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1267 SILC_LOG_DEBUG(("Start"));
1269 server = conn->context;
1271 ftp->client_entry = client_entry;
1272 ftp->session_id = session_id;
1275 silc_dlist_add(server->ftp_sessions, ftp);
1276 server->current_session = ftp;
1279 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1282 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1283 SILCTXT_FILE_REQUEST, client_entry->nickname);
1285 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1286 SILCTXT_FILE_REQUEST_HOST,
1287 client_entry->nickname, hostname, portstr);
1290 /* SILC client operations */
1291 SilcClientOperations ops = {
1293 silc_channel_message,
1294 silc_private_message,
1300 silc_get_auth_method,
1301 silc_verify_public_key,
1302 silc_ask_passphrase,