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 /* Print the unknown nick for user */
412 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));
418 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
419 /* Try to find the entry for the unknown client ID, since we
420 might have, and print the nickname of it for user. */
423 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
426 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
428 client_entry = silc_client_get_client_by_id(client, conn,
430 if (client_entry && client_entry->nickname)
431 silc_say_error("%s: %s", client_entry->nickname,
432 silc_client_command_status_message(status));
433 silc_free(client_id);
442 client_entry = va_arg(vp, SilcClientEntry);
443 nickname = va_arg(vp, char *);
444 username = va_arg(vp, char *);
445 realname = va_arg(vp, char *);
446 channels = va_arg(vp, SilcBuffer);
447 mode = va_arg(vp, uint32);
448 idle = va_arg(vp, uint32);
449 fingerprint = va_arg(vp, unsigned char *);
451 silc_parse_userfqdn(nickname, &nick, NULL);
452 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
453 SILCTXT_WHOIS_USERINFO, nickname,
454 client_entry->username, client_entry->hostname,
455 nick, client_entry->nickname);
456 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
457 SILCTXT_WHOIS_REALNAME, realname);
461 SilcDList list = silc_channel_payload_parse_list(channels->data,
464 SilcChannelPayload entry;
465 memset(buf, 0, sizeof(buf));
466 silc_dlist_start(list);
467 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
468 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
470 char *name = silc_channel_get_name(entry, &name_len);
473 strncat(buf, m, strlen(m));
474 strncat(buf, name, name_len);
475 strncat(buf, " ", 1);
479 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
480 SILCTXT_WHOIS_CHANNELS, buf);
481 silc_channel_payload_list_free(list);
486 memset(buf, 0, sizeof(buf));
488 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
489 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
490 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
492 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
493 "SILC Operator " : "[Unknown mode] ");
495 if (mode & SILC_UMODE_GONE)
498 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
499 SILCTXT_WHOIS_MODES, buf);
502 if (idle && nickname) {
503 memset(buf, 0, sizeof(buf));
504 snprintf(buf, sizeof(buf) - 1, "%lu %s",
505 idle > 60 ? (idle / 60) : idle,
506 idle > 60 ? "minutes" : "seconds");
508 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
509 SILCTXT_WHOIS_IDLE, buf);
513 fingerprint = silc_fingerprint(fingerprint, 20);
514 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
515 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
516 silc_free(fingerprint);
521 case SILC_COMMAND_IDENTIFY:
523 SilcClientEntry client_entry;
525 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
526 /* Print the unknown nick for user */
528 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
531 silc_say_error("%s: %s", tmp,
532 silc_client_command_status_message(status));
534 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
535 /* Try to find the entry for the unknown client ID, since we
536 might have, and print the nickname of it for user. */
539 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
542 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
544 client_entry = silc_client_get_client_by_id(client, conn,
546 if (client_entry && client_entry->nickname)
547 silc_say_error("%s: %s", client_entry->nickname,
548 silc_client_command_status_message(status));
549 silc_free(client_id);
558 case SILC_COMMAND_WHOWAS:
560 char *nickname, *username, *realname;
562 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
563 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
565 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
568 silc_say_error("%s: %s", tmp,
569 silc_client_command_status_message(status));
576 (void)va_arg(vp, SilcClientEntry);
577 nickname = va_arg(vp, char *);
578 username = va_arg(vp, char *);
579 realname = va_arg(vp, char *);
581 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
582 SILCTXT_WHOWAS_USERINFO, nickname, username,
583 realname ? realname : "");
587 case SILC_COMMAND_INVITE:
589 SilcChannelEntry channel;
591 SilcArgumentPayload args;
597 channel = va_arg(vp, SilcChannelEntry);
598 invite_list = va_arg(vp, char *);
600 args = silc_command_get_args(cmd_payload);
602 argc = silc_argument_get_arg_num(args);
605 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
606 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
609 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
610 SILCTXT_CHANNEL_NO_INVITE_LIST,
611 channel->channel_name);
615 case SILC_COMMAND_JOIN:
617 char *channel, *mode, *topic;
619 SilcChannelEntry channel_entry;
620 SilcBuffer client_id_list;
626 channel = va_arg(vp, char *);
627 channel_entry = va_arg(vp, SilcChannelEntry);
628 modei = va_arg(vp, uint32);
629 (void)va_arg(vp, uint32);
630 (void)va_arg(vp, unsigned char *);
631 (void)va_arg(vp, unsigned char *);
632 (void)va_arg(vp, unsigned char *);
633 topic = va_arg(vp, char *);
634 (void)va_arg(vp, unsigned char *);
635 list_count = va_arg(vp, uint32);
636 client_id_list = va_arg(vp, SilcBuffer);
638 chanrec = silc_channel_find(server, channel);
640 chanrec = silc_channel_create(server, channel, TRUE);
643 g_free_not_null(chanrec->topic);
644 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
645 signal_emit("channel topic changed", 1, chanrec);
648 mode = silc_client_chmode(modei,
649 channel_entry->channel_key ?
650 channel_entry->channel_key->cipher->name : "",
651 channel_entry->hmac ?
652 silc_hmac_get_name(channel_entry->hmac) : "");
653 g_free_not_null(chanrec->mode);
654 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
655 signal_emit("channel mode changed", 1, chanrec);
657 /* Resolve the client information */
658 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
659 silc_client_join_get_users,
664 case SILC_COMMAND_NICK:
666 SilcClientEntry client = va_arg(vp, SilcClientEntry);
672 old = g_strdup(server->nick);
673 server_change_nick(SERVER(server), client->nickname);
674 nicklist_rename_unique(SERVER(server),
675 server->conn->local_entry, server->nick,
676 client, client->nickname);
678 signal_emit("message own_nick", 4, server, server->nick, old, "");
683 case SILC_COMMAND_LIST:
692 (void)va_arg(vp, SilcChannelEntry);
693 name = va_arg(vp, char *);
694 topic = va_arg(vp, char *);
695 usercount = va_arg(vp, int);
697 if (status == SILC_STATUS_LIST_START ||
698 status == SILC_STATUS_OK)
699 printformat_module("fe-common/silc", server, NULL,
700 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
703 snprintf(users, sizeof(users) - 1, "N/A");
705 snprintf(users, sizeof(users) - 1, "%d", usercount);
706 printformat_module("fe-common/silc", server, NULL,
707 MSGLEVEL_CRAP, SILCTXT_LIST,
708 name, users, topic ? topic : "");
712 case SILC_COMMAND_UMODE:
719 mode = va_arg(vp, uint32);
721 if (mode & SILC_UMODE_SERVER_OPERATOR &&
722 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
723 printformat_module("fe-common/silc", server, NULL,
724 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
726 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
727 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
728 printformat_module("fe-common/silc", server, NULL,
729 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
731 server->umode = mode;
735 case SILC_COMMAND_OPER:
739 printformat_module("fe-common/silc", server, NULL,
740 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
743 case SILC_COMMAND_SILCOPER:
747 printformat_module("fe-common/silc", server, NULL,
748 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
751 case SILC_COMMAND_USERS:
753 SilcChannelEntry channel;
759 channel = va_arg(vp, SilcChannelEntry);
761 printformat_module("fe-common/silc", server, channel->channel_name,
762 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
763 channel->channel_name);
765 silc_list_start(channel->clients);
766 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
767 SilcClientEntry e = chu->client;
773 memset(stat, 0, sizeof(stat));
774 mode = silc_client_chumode_char(chu->mode);
775 if (e->mode & SILC_UMODE_GONE)
782 printformat_module("fe-common/silc", server, channel->channel_name,
783 MSGLEVEL_CRAP, SILCTXT_USERS,
785 e->username ? e->username : "",
786 e->hostname ? e->hostname : "",
787 e->realname ? e->realname : "");
794 case SILC_COMMAND_BAN:
796 SilcChannelEntry channel;
802 channel = va_arg(vp, SilcChannelEntry);
803 ban_list = va_arg(vp, char *);
806 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
807 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
810 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
811 SILCTXT_CHANNEL_NO_BAN_LIST,
812 channel->channel_name);
816 case SILC_COMMAND_GETKEY:
820 SilcPublicKey public_key;
823 GetkeyContext getkey;
828 id_type = va_arg(vp, uint32);
829 entry = va_arg(vp, void *);
830 public_key = va_arg(vp, SilcPublicKey);
833 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
835 getkey = silc_calloc(1, sizeof(*getkey));
836 getkey->entry = entry;
837 getkey->id_type = id_type;
838 getkey->client = client;
840 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
842 silc_verify_public_key_internal(client, conn,
843 (id_type == SILC_ID_CLIENT ?
844 SILC_SOCKET_TYPE_CLIENT :
845 SILC_SOCKET_TYPE_SERVER),
846 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
847 silc_getkey_cb, getkey);
850 printformat_module("fe-common/silc", server, NULL,
851 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
856 case SILC_COMMAND_INFO:
858 SilcServerEntry server_entry;
865 server_entry = va_arg(vp, SilcServerEntry);
866 server_name = va_arg(vp, char *);
867 server_info = va_arg(vp, char *);
869 if (server_name && server_info )
871 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
872 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
877 case SILC_COMMAND_TOPIC:
879 SilcChannelEntry channel;
885 channel = va_arg(vp, SilcChannelEntry);
886 topic = va_arg(vp, char *);
889 chanrec = silc_channel_find_entry(server, channel);
891 g_free_not_null(chanrec->topic);
892 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
893 signal_emit("channel topic changed", 1, chanrec);
895 printformat_module("fe-common/silc", server, channel->channel_name,
896 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
897 channel->channel_name, topic);
899 printformat_module("fe-common/silc", server, channel->channel_name,
900 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
901 channel->channel_name);
911 /* Internal routine to verify public key. If the `completion' is provided
912 it will be called to indicate whether public was verified or not. */
916 SilcClientConnection conn;
921 SilcSKEPKType pk_type;
922 SilcVerifyPublicKey completion;
926 static void verify_public_key_completion(const char *line, void *context)
928 PublicKeyVerify verify = (PublicKeyVerify)context;
930 if (line[0] == 'Y' || line[0] == 'y') {
931 /* Call the completion */
932 if (verify->completion)
933 verify->completion(TRUE, verify->context);
935 /* Save the key for future checking */
936 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
937 verify->pk_len, SILC_PKCS_FILE_PEM);
939 /* Call the completion */
940 if (verify->completion)
941 verify->completion(FALSE, verify->context);
943 printformat_module("fe-common/silc", NULL, NULL,
944 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
947 silc_free(verify->filename);
948 silc_free(verify->entity);
949 silc_free(verify->pk);
954 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
955 SilcSocketType conn_type, unsigned char *pk,
956 uint32 pk_len, SilcSKEPKType pk_type,
957 SilcVerifyPublicKey completion, void *context)
960 char file[256], filename[256], *fingerprint, *babbleprint, *format;
963 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
964 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
965 "server" : "client");
966 PublicKeyVerify verify;
968 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
969 printformat_module("fe-common/silc", NULL, NULL,
970 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
973 completion(FALSE, context);
977 pw = getpwuid(getuid());
980 completion(FALSE, context);
984 memset(filename, 0, sizeof(filename));
985 memset(file, 0, sizeof(file));
987 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
988 conn_type == SILC_SOCKET_TYPE_ROUTER) {
989 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
990 conn->sock->ip, conn->sock->port);
991 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
992 pw->pw_dir, entity, file);
994 /* Replace all whitespaces with `_'. */
995 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
996 for (i = 0; i < strlen(fingerprint); i++)
997 if (fingerprint[i] == ' ')
998 fingerprint[i] = '_';
1000 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1001 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
1002 pw->pw_dir, entity, file);
1003 silc_free(fingerprint);
1006 /* Take fingerprint of the public key */
1007 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1008 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1010 verify = silc_calloc(1, sizeof(*verify));
1011 verify->client = client;
1012 verify->conn = conn;
1013 verify->filename = strdup(filename);
1014 verify->entity = strdup(entity);
1015 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1016 memcpy(verify->pk, pk, pk_len);
1017 verify->pk_len = pk_len;
1018 verify->pk_type = pk_type;
1019 verify->completion = completion;
1020 verify->context = context;
1022 /* Check whether this key already exists */
1023 if (stat(filename, &st) < 0) {
1024 /* Key does not exist, ask user to verify the key and save it */
1026 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1027 SILCTXT_PUBKEY_RECEIVED, entity);
1028 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1029 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1030 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1031 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1032 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1033 SILCTXT_PUBKEY_ACCEPT);
1034 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1037 silc_free(fingerprint);
1040 /* The key already exists, verify it. */
1041 SilcPublicKey public_key;
1042 unsigned char *encpk;
1045 /* Load the key file */
1046 if (!silc_pkcs_load_public_key(filename, &public_key,
1047 SILC_PKCS_FILE_PEM))
1048 if (!silc_pkcs_load_public_key(filename, &public_key,
1049 SILC_PKCS_FILE_BIN)) {
1050 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1051 SILCTXT_PUBKEY_RECEIVED, entity);
1052 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1053 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1054 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1055 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1056 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1057 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1058 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1059 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1060 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1063 silc_free(fingerprint);
1067 /* Encode the key data */
1068 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1070 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1071 SILCTXT_PUBKEY_RECEIVED, entity);
1072 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1073 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1074 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1075 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1076 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1077 SILCTXT_PUBKEY_MALFORMED, entity);
1078 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1079 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1080 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1083 silc_free(fingerprint);
1087 /* Compare the keys */
1088 if (memcmp(encpk, pk, encpk_len)) {
1089 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1090 SILCTXT_PUBKEY_RECEIVED, entity);
1091 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1092 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1093 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1094 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1095 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1096 SILCTXT_PUBKEY_NO_MATCH, entity);
1097 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1098 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1099 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1100 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1102 /* Ask user to verify the key and save it */
1103 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1104 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1105 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1108 silc_free(fingerprint);
1112 /* Local copy matched */
1114 completion(TRUE, context);
1115 silc_free(fingerprint);
1119 /* Verifies received public key. The `conn_type' indicates which entity
1120 (server, client etc.) has sent the public key. If user decides to trust
1121 the key may be saved as trusted public key for later use. The
1122 `completion' must be called after the public key has been verified. */
1125 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1126 SilcSocketType conn_type, unsigned char *pk,
1127 uint32 pk_len, SilcSKEPKType pk_type,
1128 SilcVerifyPublicKey completion, void *context)
1130 silc_verify_public_key_internal(client, conn, conn_type, pk,
1132 completion, context);
1135 /* Asks passphrase from user on the input line. */
1138 SilcAskPassphrase completion;
1142 void ask_passphrase_completion(const char *passphrase, void *context)
1144 AskPassphrase p = (AskPassphrase)context;
1145 p->completion((unsigned char *)passphrase,
1146 passphrase ? strlen(passphrase) : 0, p->context);
1150 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1151 SilcAskPassphrase completion, void *context)
1153 AskPassphrase p = silc_calloc(1, sizeof(*p));
1154 p->completion = completion;
1155 p->context = context;
1157 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1158 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1162 SilcGetAuthMeth completion;
1164 } *InternalGetAuthMethod;
1166 /* Callback called when we've received the authentication method information
1167 from the server after we've requested it. This will get the authentication
1168 data from the user if needed. */
1170 static void silc_get_auth_method_callback(SilcClient client,
1171 SilcClientConnection conn,
1172 SilcAuthMethod auth_meth,
1175 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1177 SILC_LOG_DEBUG(("Start"));
1179 switch (auth_meth) {
1180 case SILC_AUTH_NONE:
1181 /* No authentication required. */
1182 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1184 case SILC_AUTH_PASSWORD:
1185 /* Do not ask the passphrase from user, the library will ask it if
1186 we do not provide it here. */
1187 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1189 case SILC_AUTH_PUBLIC_KEY:
1190 /* Do not get the authentication data now, the library will generate
1191 it using our default key, if we do not provide it here. */
1192 /* XXX In the future when we support multiple local keys and multiple
1193 local certificates we will need to ask from user which one to use. */
1194 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1198 silc_free(internal);
1201 /* Find authentication method and authentication data by hostname and
1202 port. The hostname may be IP address as well. The found authentication
1203 method and authentication data is returned to `auth_meth', `auth_data'
1204 and `auth_data_len'. The function returns TRUE if authentication method
1205 is found and FALSE if not. `conn' may be NULL. */
1207 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1208 char *hostname, uint16 port,
1209 SilcGetAuthMeth completion, void *context)
1211 InternalGetAuthMethod internal;
1213 SILC_LOG_DEBUG(("Start"));
1215 /* XXX must resolve from configuration whether this connection has
1216 any specific authentication data */
1218 /* If we do not have this connection configured by the user in a
1219 configuration file then resolve the authentication method from the
1220 server for this session. */
1221 internal = silc_calloc(1, sizeof(*internal));
1222 internal->completion = completion;
1223 internal->context = context;
1225 silc_client_request_authentication_method(client, conn,
1226 silc_get_auth_method_callback,
1230 /* Notifies application that failure packet was received. This is called
1231 if there is some protocol active in the client. The `protocol' is the
1232 protocol context. The `failure' is opaque pointer to the failure
1233 indication. Note, that the `failure' is protocol dependant and application
1234 must explicitly cast it to correct type. Usually `failure' is 32 bit
1235 failure type (see protocol specs for all protocol failure types). */
1237 void silc_failure(SilcClient client, SilcClientConnection conn,
1238 SilcProtocol protocol, void *failure)
1240 SILC_LOG_DEBUG(("Start"));
1242 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1243 SilcSKEStatus status = (SilcSKEStatus)failure;
1245 if (status == SILC_SKE_STATUS_BAD_VERSION)
1246 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1247 SILCTXT_KE_BAD_VERSION);
1248 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1249 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1250 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1251 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1252 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1253 SILCTXT_KE_UNKNOWN_GROUP);
1254 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1255 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1256 SILCTXT_KE_UNKNOWN_CIPHER);
1257 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1258 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1259 SILCTXT_KE_UNKNOWN_PKCS);
1260 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1261 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1262 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1263 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1264 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1265 SILCTXT_KE_UNKNOWN_HMAC);
1266 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1267 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1268 SILCTXT_KE_INCORRECT_SIGNATURE);
1269 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1270 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1271 SILCTXT_KE_INVALID_COOKIE);
1274 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1275 uint32 err = (uint32)failure;
1277 if (err == SILC_AUTH_FAILED)
1278 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1279 SILCTXT_AUTH_FAILED);
1283 /* Asks whether the user would like to perform the key agreement protocol.
1284 This is called after we have received an key agreement packet or an
1285 reply to our key agreement packet. This returns TRUE if the user wants
1286 the library to perform the key agreement protocol and FALSE if it is not
1287 desired (application may start it later by calling the function
1288 silc_client_perform_key_agreement). */
1290 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1291 SilcClientEntry client_entry, const char *hostname,
1292 uint16 port, SilcKeyAgreementCallback *completion,
1297 SILC_LOG_DEBUG(("Start"));
1299 /* We will just display the info on the screen and return FALSE and user
1300 will have to start the key agreement with a command. */
1303 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1306 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1307 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1309 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1310 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1311 client_entry->nickname, hostname, portstr);
1319 void silc_ftp(SilcClient client, SilcClientConnection conn,
1320 SilcClientEntry client_entry, uint32 session_id,
1321 const char *hostname, uint16 port)
1323 SILC_SERVER_REC *server;
1325 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1327 SILC_LOG_DEBUG(("Start"));
1329 server = conn->context;
1331 ftp->client_entry = client_entry;
1332 ftp->session_id = session_id;
1335 silc_dlist_add(server->ftp_sessions, ftp);
1336 server->current_session = ftp;
1339 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1342 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1343 SILCTXT_FILE_REQUEST, client_entry->nickname);
1345 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1346 SILCTXT_FILE_REQUEST_HOST,
1347 client_entry->nickname, hostname, portstr);
1350 /* SILC client operations */
1351 SilcClientOperations ops = {
1353 silc_channel_message,
1354 silc_private_message,
1360 silc_get_auth_method,
1361 silc_verify_public_key,
1362 silc_ask_passphrase,