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));
417 silc_say_error("%s", silc_client_command_status_message(status));
424 client_entry = va_arg(vp, SilcClientEntry);
425 nickname = va_arg(vp, char *);
426 username = va_arg(vp, char *);
427 realname = va_arg(vp, char *);
428 channels = va_arg(vp, SilcBuffer);
429 mode = va_arg(vp, uint32);
430 idle = va_arg(vp, uint32);
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);
494 case SILC_COMMAND_WHOWAS:
496 char *nickname, *username, *realname;
498 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
499 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
501 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
504 silc_say_error("%s: %s", tmp,
505 silc_client_command_status_message(status));
507 silc_say_error("%s", silc_client_command_status_message(status));
514 (void)va_arg(vp, SilcClientEntry);
515 nickname = va_arg(vp, char *);
516 username = va_arg(vp, char *);
517 realname = va_arg(vp, char *);
519 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
520 SILCTXT_WHOWAS_USERINFO, nickname, username,
521 realname ? realname : "");
525 case SILC_COMMAND_INVITE:
527 SilcChannelEntry channel;
529 SilcArgumentPayload args;
535 channel = va_arg(vp, SilcChannelEntry);
536 invite_list = va_arg(vp, char *);
538 args = silc_command_get_args(cmd_payload);
540 argc = silc_argument_get_arg_num(args);
543 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
544 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
547 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
548 SILCTXT_CHANNEL_NO_INVITE_LIST,
549 channel->channel_name);
553 case SILC_COMMAND_JOIN:
555 char *channel, *mode, *topic;
557 SilcChannelEntry channel_entry;
558 SilcBuffer client_id_list;
564 channel = va_arg(vp, char *);
565 channel_entry = va_arg(vp, SilcChannelEntry);
566 modei = va_arg(vp, uint32);
567 (void)va_arg(vp, uint32);
568 (void)va_arg(vp, unsigned char *);
569 (void)va_arg(vp, unsigned char *);
570 (void)va_arg(vp, unsigned char *);
571 topic = va_arg(vp, char *);
572 (void)va_arg(vp, unsigned char *);
573 list_count = va_arg(vp, uint32);
574 client_id_list = va_arg(vp, SilcBuffer);
576 chanrec = silc_channel_find(server, channel);
578 chanrec = silc_channel_create(server, channel, TRUE);
581 g_free_not_null(chanrec->topic);
582 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
583 signal_emit("channel topic changed", 1, chanrec);
586 mode = silc_client_chmode(modei,
587 channel_entry->channel_key ?
588 channel_entry->channel_key->cipher->name : "",
589 channel_entry->hmac ?
590 silc_hmac_get_name(channel_entry->hmac) : "");
591 g_free_not_null(chanrec->mode);
592 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
593 signal_emit("channel mode changed", 1, chanrec);
595 /* Resolve the client information */
596 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
597 silc_client_join_get_users,
602 case SILC_COMMAND_NICK:
604 SilcClientEntry client = va_arg(vp, SilcClientEntry);
610 old = g_strdup(server->nick);
611 server_change_nick(SERVER(server), client->nickname);
612 nicklist_rename_unique(SERVER(server),
613 server->conn->local_entry, server->nick,
614 client, client->nickname);
616 signal_emit("message own_nick", 4, server, server->nick, old, "");
621 case SILC_COMMAND_LIST:
630 (void)va_arg(vp, SilcChannelEntry);
631 name = va_arg(vp, char *);
632 topic = va_arg(vp, char *);
633 usercount = va_arg(vp, int);
635 if (status == SILC_STATUS_LIST_START ||
636 status == SILC_STATUS_OK)
637 printformat_module("fe-common/silc", server, NULL,
638 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
640 snprintf(users, sizeof(users) - 1, "%d", usercount);
641 printformat_module("fe-common/silc", server, NULL,
642 MSGLEVEL_CRAP, SILCTXT_LIST,
643 name, users, topic ? topic : "");
647 case SILC_COMMAND_UMODE:
654 mode = va_arg(vp, uint32);
656 if (mode & SILC_UMODE_SERVER_OPERATOR)
657 printformat_module("fe-common/silc", server, NULL,
658 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
660 if (mode & SILC_UMODE_ROUTER_OPERATOR)
661 printformat_module("fe-common/silc", server, NULL,
662 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
666 case SILC_COMMAND_OPER:
670 printformat_module("fe-common/silc", server, NULL,
671 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
674 case SILC_COMMAND_SILCOPER:
678 printformat_module("fe-common/silc", server, NULL,
679 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
682 case SILC_COMMAND_USERS:
684 SilcChannelEntry channel;
690 channel = va_arg(vp, SilcChannelEntry);
692 printformat_module("fe-common/silc", server, channel->channel_name,
693 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
694 channel->channel_name);
696 silc_list_start(channel->clients);
697 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
698 SilcClientEntry e = chu->client;
704 memset(stat, 0, sizeof(stat));
705 mode = silc_client_chumode_char(chu->mode);
706 if (e->mode & SILC_UMODE_GONE)
713 printformat_module("fe-common/silc", server, channel->channel_name,
714 MSGLEVEL_CRAP, SILCTXT_USERS,
716 e->username ? e->username : "",
717 e->hostname ? e->hostname : "",
718 e->realname ? e->realname : "");
725 case SILC_COMMAND_BAN:
727 SilcChannelEntry channel;
733 channel = va_arg(vp, SilcChannelEntry);
734 ban_list = va_arg(vp, char *);
737 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
738 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
741 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
742 SILCTXT_CHANNEL_NO_BAN_LIST,
743 channel->channel_name);
747 case SILC_COMMAND_GETKEY:
751 SilcPublicKey public_key;
754 GetkeyContext getkey;
759 id_type = va_arg(vp, uint32);
760 entry = va_arg(vp, void *);
761 public_key = va_arg(vp, SilcPublicKey);
764 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
766 getkey = silc_calloc(1, sizeof(*getkey));
767 getkey->entry = entry;
768 getkey->id_type = id_type;
769 getkey->client = client;
771 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
773 silc_verify_public_key_internal(client, conn,
774 (id_type == SILC_ID_CLIENT ?
775 SILC_SOCKET_TYPE_CLIENT :
776 SILC_SOCKET_TYPE_SERVER),
777 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
778 silc_getkey_cb, getkey);
781 printformat_module("fe-common/silc", server, NULL,
782 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
787 case SILC_COMMAND_TOPIC:
789 SilcChannelEntry channel;
795 channel = va_arg(vp, SilcChannelEntry);
796 topic = va_arg(vp, char *);
799 chanrec = silc_channel_find_entry(server, channel);
801 g_free_not_null(chanrec->topic);
802 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
803 signal_emit("channel topic changed", 1, chanrec);
805 printformat_module("fe-common/silc", server, channel->channel_name,
806 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
807 channel->channel_name, topic);
809 printformat_module("fe-common/silc", server, channel->channel_name,
810 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
811 channel->channel_name);
821 /* Internal routine to verify public key. If the `completion' is provided
822 it will be called to indicate whether public was verified or not. */
826 SilcClientConnection conn;
831 SilcSKEPKType pk_type;
832 SilcVerifyPublicKey completion;
836 static void verify_public_key_completion(const char *line, void *context)
838 PublicKeyVerify verify = (PublicKeyVerify)context;
840 if (line[0] == 'Y' || line[0] == 'y') {
841 /* Call the completion */
842 if (verify->completion)
843 verify->completion(TRUE, verify->context);
845 /* Save the key for future checking */
846 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
847 verify->pk_len, SILC_PKCS_FILE_PEM);
849 /* Call the completion */
850 if (verify->completion)
851 verify->completion(FALSE, verify->context);
853 printformat_module("fe-common/silc", NULL, NULL,
854 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
857 silc_free(verify->filename);
858 silc_free(verify->entity);
859 silc_free(verify->pk);
864 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
865 SilcSocketType conn_type, unsigned char *pk,
866 uint32 pk_len, SilcSKEPKType pk_type,
867 SilcVerifyPublicKey completion, void *context)
870 char file[256], filename[256], *fingerprint, *format;
873 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
874 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
875 "server" : "client");
876 PublicKeyVerify verify;
878 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
879 printformat_module("fe-common/silc", NULL, NULL,
880 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
883 completion(FALSE, context);
887 pw = getpwuid(getuid());
890 completion(FALSE, context);
894 memset(filename, 0, sizeof(filename));
895 memset(file, 0, sizeof(file));
897 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
898 conn_type == SILC_SOCKET_TYPE_ROUTER) {
899 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
900 conn->sock->hostname, conn->sock->port);
901 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
902 pw->pw_dir, entity, file);
904 /* Replace all whitespaces with `_'. */
905 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
906 for (i = 0; i < strlen(fingerprint); i++)
907 if (fingerprint[i] == ' ')
908 fingerprint[i] = '_';
910 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
911 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
912 pw->pw_dir, entity, file);
913 silc_free(fingerprint);
916 /* Take fingerprint of the public key */
917 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
919 verify = silc_calloc(1, sizeof(*verify));
920 verify->client = client;
922 verify->filename = strdup(filename);
923 verify->entity = strdup(entity);
924 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
925 memcpy(verify->pk, pk, pk_len);
926 verify->pk_len = pk_len;
927 verify->pk_type = pk_type;
928 verify->completion = completion;
929 verify->context = context;
931 /* Check whether this key already exists */
932 if (stat(filename, &st) < 0) {
933 /* Key does not exist, ask user to verify the key and save it */
935 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
936 SILCTXT_PUBKEY_RECEIVED, entity);
937 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
938 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
939 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
940 SILCTXT_PUBKEY_ACCEPT);
941 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
944 silc_free(fingerprint);
947 /* The key already exists, verify it. */
948 SilcPublicKey public_key;
949 unsigned char *encpk;
952 /* Load the key file */
953 if (!silc_pkcs_load_public_key(filename, &public_key,
955 if (!silc_pkcs_load_public_key(filename, &public_key,
956 SILC_PKCS_FILE_BIN)) {
957 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
958 SILCTXT_PUBKEY_RECEIVED, entity);
959 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
960 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
961 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
962 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
963 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
964 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
965 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
968 silc_free(fingerprint);
972 /* Encode the key data */
973 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
975 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
976 SILCTXT_PUBKEY_RECEIVED, entity);
977 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
978 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
979 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
980 SILCTXT_PUBKEY_MALFORMED, entity);
981 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
982 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
983 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
986 silc_free(fingerprint);
990 /* Compare the keys */
991 if (memcmp(encpk, pk, encpk_len)) {
992 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
993 SILCTXT_PUBKEY_RECEIVED, entity);
994 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
995 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
996 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
997 SILCTXT_PUBKEY_NO_MATCH, entity);
998 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
999 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1000 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1001 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1003 /* Ask user to verify the key and save it */
1004 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1005 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1006 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1009 silc_free(fingerprint);
1013 /* Local copy matched */
1015 completion(TRUE, context);
1016 silc_free(fingerprint);
1020 /* Verifies received public key. The `conn_type' indicates which entity
1021 (server, client etc.) has sent the public key. If user decides to trust
1022 the key may be saved as trusted public key for later use. The
1023 `completion' must be called after the public key has been verified. */
1026 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1027 SilcSocketType conn_type, unsigned char *pk,
1028 uint32 pk_len, SilcSKEPKType pk_type,
1029 SilcVerifyPublicKey completion, void *context)
1031 silc_verify_public_key_internal(client, conn, conn_type, pk,
1033 completion, context);
1036 /* Asks passphrase from user on the input line. */
1039 SilcAskPassphrase completion;
1043 void ask_passphrase_completion(const char *passphrase, void *context)
1045 AskPassphrase p = (AskPassphrase)context;
1046 p->completion((unsigned char *)passphrase,
1047 passphrase ? strlen(passphrase) : 0, p->context);
1051 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1052 SilcAskPassphrase completion, void *context)
1054 AskPassphrase p = silc_calloc(1, sizeof(*p));
1055 p->completion = completion;
1056 p->context = context;
1058 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1059 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1063 SilcGetAuthMeth completion;
1065 } *InternalGetAuthMethod;
1067 /* Callback called when we've received the authentication method information
1068 from the server after we've requested it. This will get the authentication
1069 data from the user if needed. */
1071 static void silc_get_auth_method_callback(SilcClient client,
1072 SilcClientConnection conn,
1073 SilcAuthMethod auth_meth,
1076 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1078 SILC_LOG_DEBUG(("Start"));
1080 switch (auth_meth) {
1081 case SILC_AUTH_NONE:
1082 /* No authentication required. */
1083 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1085 case SILC_AUTH_PASSWORD:
1086 /* Do not ask the passphrase from user, the library will ask it if
1087 we do not provide it here. */
1088 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1090 case SILC_AUTH_PUBLIC_KEY:
1091 /* Do not get the authentication data now, the library will generate
1092 it using our default key, if we do not provide it here. */
1093 /* XXX In the future when we support multiple local keys and multiple
1094 local certificates we will need to ask from user which one to use. */
1095 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1099 silc_free(internal);
1102 /* Find authentication method and authentication data by hostname and
1103 port. The hostname may be IP address as well. The found authentication
1104 method and authentication data is returned to `auth_meth', `auth_data'
1105 and `auth_data_len'. The function returns TRUE if authentication method
1106 is found and FALSE if not. `conn' may be NULL. */
1108 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1109 char *hostname, uint16 port,
1110 SilcGetAuthMeth completion, void *context)
1112 InternalGetAuthMethod internal;
1114 SILC_LOG_DEBUG(("Start"));
1116 /* XXX must resolve from configuration whether this connection has
1117 any specific authentication data */
1119 /* If we do not have this connection configured by the user in a
1120 configuration file then resolve the authentication method from the
1121 server for this session. */
1122 internal = silc_calloc(1, sizeof(*internal));
1123 internal->completion = completion;
1124 internal->context = context;
1126 silc_client_request_authentication_method(client, conn,
1127 silc_get_auth_method_callback,
1131 /* Notifies application that failure packet was received. This is called
1132 if there is some protocol active in the client. The `protocol' is the
1133 protocol context. The `failure' is opaque pointer to the failure
1134 indication. Note, that the `failure' is protocol dependant and application
1135 must explicitly cast it to correct type. Usually `failure' is 32 bit
1136 failure type (see protocol specs for all protocol failure types). */
1138 void silc_failure(SilcClient client, SilcClientConnection conn,
1139 SilcProtocol protocol, void *failure)
1141 SILC_LOG_DEBUG(("Start"));
1143 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1144 SilcSKEStatus status = (SilcSKEStatus)failure;
1146 if (status == SILC_SKE_STATUS_BAD_VERSION)
1147 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1148 SILCTXT_KE_BAD_VERSION);
1149 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1150 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1151 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1152 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1153 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1154 SILCTXT_KE_UNKNOWN_GROUP);
1155 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1156 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1157 SILCTXT_KE_UNKNOWN_CIPHER);
1158 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1159 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1160 SILCTXT_KE_UNKNOWN_PKCS);
1161 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1162 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1163 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1164 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1165 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1166 SILCTXT_KE_UNKNOWN_HMAC);
1167 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1168 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1169 SILCTXT_KE_INCORRECT_SIGNATURE);
1170 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1171 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1172 SILCTXT_KE_INVALID_COOKIE);
1175 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1176 uint32 err = (uint32)failure;
1178 if (err == SILC_AUTH_FAILED)
1179 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1180 SILCTXT_AUTH_FAILED);
1184 /* Asks whether the user would like to perform the key agreement protocol.
1185 This is called after we have received an key agreement packet or an
1186 reply to our key agreement packet. This returns TRUE if the user wants
1187 the library to perform the key agreement protocol and FALSE if it is not
1188 desired (application may start it later by calling the function
1189 silc_client_perform_key_agreement). */
1191 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1192 SilcClientEntry client_entry, const char *hostname,
1193 uint16 port, SilcKeyAgreementCallback *completion,
1198 SILC_LOG_DEBUG(("Start"));
1200 /* We will just display the info on the screen and return FALSE and user
1201 will have to start the key agreement with a command. */
1204 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1207 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1208 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1210 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1211 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1212 client_entry->nickname, hostname, portstr);
1220 void silc_ftp(SilcClient client, SilcClientConnection conn,
1221 SilcClientEntry client_entry, uint32 session_id,
1222 const char *hostname, uint16 port)
1225 SILC_LOG_DEBUG(("Start"));
1228 silc_client_file_receive(client, conn, NULL, NULL, client_entry,
1233 /* SILC client operations */
1234 SilcClientOperations ops = {
1236 silc_channel_message,
1237 silc_private_message,
1243 silc_get_auth_method,
1244 silc_verify_public_key,
1245 silc_ask_passphrase,