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"
44 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
45 const char *name, SilcSocketType conn_type,
46 unsigned char *pk, SilcUInt32 pk_len,
47 SilcSKEPKType pk_type,
48 SilcVerifyPublicKey completion, void *context);
50 void silc_say(SilcClient client, SilcClientConnection conn,
51 SilcClientMessageType type, char *msg, ...)
53 SILC_SERVER_REC *server;
57 server = conn == NULL ? NULL : conn->context;
60 str = g_strdup_vprintf(msg, va);
61 printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
66 void silc_say_error(char *msg, ...)
72 str = g_strdup_vprintf(msg, va);
73 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
79 /* Message for a channel. The `sender' is the nickname of the sender
80 received in the packet. The `channel_name' is the name of the channel. */
82 void silc_channel_message(SilcClient client, SilcClientConnection conn,
83 SilcClientEntry sender, SilcChannelEntry channel,
84 SilcMessageFlags flags, const unsigned char *message,
85 SilcUInt32 message_len)
87 SILC_SERVER_REC *server;
89 SILC_CHANNEL_REC *chanrec;
91 SILC_LOG_DEBUG(("Start"));
96 server = conn == NULL ? NULL : conn->context;
97 chanrec = silc_channel_find_entry(server, channel);
101 nick = silc_nicklist_find(chanrec, sender);
103 /* We didn't find client but it clearly exists, add it. */
104 SilcChannelUser chu = silc_client_on_channel(channel, sender);
106 nick = silc_nicklist_insert(chanrec, chu, FALSE);
109 if (flags & SILC_MESSAGE_FLAG_ACTION)
110 printformat_module("fe-common/silc", server, channel->channel_name,
111 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
112 nick == NULL ? "[<unknown>]" : nick->nick, message);
113 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
114 printformat_module("fe-common/silc", server, channel->channel_name,
115 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
116 nick == NULL ? "[<unknown>]" : nick->nick, message);
117 else if (flags & SILC_MESSAGE_FLAG_DATA) {
118 /* MIME object received, try to display it as well as we can */
122 memset(type, 0, sizeof(type));
123 if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
124 NULL, 0, &data, NULL))
127 /* Then figure out what we can display */
128 if (strstr(type, "text/") && !strstr(type, "text/t140") &&
129 !strstr(type, "text/vnd")) {
131 /* It is something textual */
132 signal_emit("message public", 6, server, data,
133 nick == NULL ? "[<unknown>]" : nick->nick,
134 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
135 chanrec->name, nick);
138 signal_emit("message public", 6, server, message,
139 nick == NULL ? "[<unknown>]" : nick->nick,
140 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
141 chanrec->name, nick);
144 /* Private message to the client. The `sender' is the nickname of the
145 sender received in the packet. */
147 void silc_private_message(SilcClient client, SilcClientConnection conn,
148 SilcClientEntry sender, SilcMessageFlags flags,
149 const unsigned char *message,
150 SilcUInt32 message_len)
152 SILC_SERVER_REC *server;
155 SILC_LOG_DEBUG(("Start"));
157 server = conn == NULL ? NULL : conn->context;
158 memset(userhost, 0, sizeof(userhost));
159 if (sender->username)
160 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
161 sender->username, sender->hostname);
162 signal_emit("message private", 4, server, message,
163 sender->nickname ? sender->nickname : "[<unknown>]",
164 sender->username ? userhost : NULL);
167 /* Notify message to the client. The notify arguments are sent in the
168 same order as servers sends them. The arguments are same as received
169 from the server except for ID's. If ID is received application receives
170 the corresponding entry to the ID. For example, if Client ID is received
171 application receives SilcClientEntry. Also, if the notify type is
172 for channel the channel entry is sent to application (even if server
173 does not send it). */
175 void silc_notify(SilcClient client, SilcClientConnection conn,
176 SilcNotifyType type, ...)
179 SILC_SERVER_REC *server;
180 SILC_CHANNEL_REC *chanrec;
181 SILC_NICK_REC *nickrec;
182 SilcClientEntry client_entry, client_entry2;
183 SilcChannelEntry channel, channel2;
184 SilcServerEntry server_entry;
190 GSList *list1, *list_tmp;
192 SILC_LOG_DEBUG(("Start"));
196 server = conn == NULL ? NULL : conn->context;
199 case SILC_NOTIFY_TYPE_NONE:
200 /* Some generic notice from server */
201 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
204 case SILC_NOTIFY_TYPE_INVITE:
206 * Invited or modified invite list.
209 SILC_LOG_DEBUG(("Notify: INVITE"));
211 channel = va_arg(va, SilcChannelEntry);
212 name = va_arg(va, char *);
213 client_entry = va_arg(va, SilcClientEntry);
215 memset(userhost, 0, sizeof(userhost));
216 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
217 client_entry->username, client_entry->hostname);
218 signal_emit("message invite", 4, server, channel ? channel->channel_name :
219 name, client_entry->nickname, userhost);
222 case SILC_NOTIFY_TYPE_JOIN:
227 SILC_LOG_DEBUG(("Notify: JOIN"));
229 client_entry = va_arg(va, SilcClientEntry);
230 channel = va_arg(va, SilcChannelEntry);
232 if (client_entry == server->conn->local_entry) {
233 /* You joined to channel */
234 chanrec = silc_channel_find(server, channel->channel_name);
235 if (chanrec != NULL && !chanrec->joined)
236 chanrec->entry = channel;
238 chanrec = silc_channel_find_entry(server, channel);
239 if (chanrec != NULL) {
240 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
242 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
246 memset(userhost, 0, sizeof(userhost));
247 if (client_entry->username)
248 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
249 client_entry->username, client_entry->hostname);
250 signal_emit("message join", 4, server, channel->channel_name,
251 client_entry->nickname,
252 client_entry->username == NULL ? "" : userhost);
255 case SILC_NOTIFY_TYPE_LEAVE:
260 SILC_LOG_DEBUG(("Notify: LEAVE"));
262 client_entry = va_arg(va, SilcClientEntry);
263 channel = va_arg(va, SilcChannelEntry);
265 memset(userhost, 0, sizeof(userhost));
266 if (client_entry->username)
267 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
268 client_entry->username, client_entry->hostname);
269 signal_emit("message part", 5, server, channel->channel_name,
270 client_entry->nickname, client_entry->username ?
271 userhost : "", client_entry->nickname);
273 chanrec = silc_channel_find_entry(server, channel);
274 if (chanrec != NULL) {
275 nickrec = silc_nicklist_find(chanrec, client_entry);
277 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
281 case SILC_NOTIFY_TYPE_SIGNOFF:
286 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
288 client_entry = va_arg(va, SilcClientEntry);
289 tmp = va_arg(va, char *);
291 silc_server_free_ftp(server, client_entry);
293 memset(userhost, 0, sizeof(userhost));
294 if (client_entry->username)
295 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
296 client_entry->username, client_entry->hostname);
297 signal_emit("message quit", 4, server, client_entry->nickname,
298 client_entry->username ? userhost : "",
301 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
302 for (list_tmp = list1; list_tmp != NULL; list_tmp =
303 list_tmp->next->next) {
304 CHANNEL_REC *channel = list_tmp->data;
305 NICK_REC *nickrec = list_tmp->next->data;
307 nicklist_remove(channel, nickrec);
311 case SILC_NOTIFY_TYPE_TOPIC_SET:
316 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
318 idtype = va_arg(va, int);
319 entry = va_arg(va, void *);
320 tmp = va_arg(va, char *);
321 channel = va_arg(va, SilcChannelEntry);
323 chanrec = silc_channel_find_entry(server, channel);
324 if (chanrec != NULL) {
325 g_free_not_null(chanrec->topic);
326 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
327 signal_emit("channel topic changed", 1, chanrec);
330 if (idtype == SILC_ID_CLIENT) {
331 client_entry = (SilcClientEntry)entry;
332 memset(userhost, 0, sizeof(userhost));
333 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
334 client_entry->username, client_entry->hostname);
335 signal_emit("message topic", 5, server, channel->channel_name,
336 tmp, client_entry->nickname, userhost);
337 } else if (idtype == SILC_ID_SERVER) {
338 server_entry = (SilcServerEntry)entry;
339 signal_emit("message topic", 5, server, channel->channel_name,
340 tmp, server_entry->server_name,
341 server_entry->server_name);
342 } else if (idtype == SILC_ID_CHANNEL) {
343 channel = (SilcChannelEntry)entry;
344 signal_emit("message topic", 5, server, channel->channel_name,
345 tmp, channel->channel_name, channel->channel_name);
349 case SILC_NOTIFY_TYPE_NICK_CHANGE:
354 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
356 client_entry = va_arg(va, SilcClientEntry);
357 client_entry2 = va_arg(va, SilcClientEntry);
359 if (!strcmp(client_entry->nickname, client_entry2->nickname))
362 memset(userhost, 0, sizeof(userhost));
363 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
364 client_entry2->username, client_entry2->hostname);
365 nicklist_rename_unique(SERVER(server),
366 client_entry, client_entry->nickname,
367 client_entry2, client_entry2->nickname);
368 signal_emit("message nick", 4, server, client_entry2->nickname,
369 client_entry->nickname, userhost);
372 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
374 * Changed channel mode.
377 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
379 idtype = va_arg(va, int);
380 entry = va_arg(va, void *);
381 mode = va_arg(va, SilcUInt32);
382 (void)va_arg(va, char *);
383 (void)va_arg(va, char *);
384 channel = va_arg(va, SilcChannelEntry);
386 tmp = silc_client_chmode(mode,
387 channel->channel_key ?
388 channel->channel_key->cipher->name : "",
390 silc_hmac_get_name(channel->hmac) : "");
392 chanrec = silc_channel_find_entry(server, channel);
393 if (chanrec != NULL) {
394 g_free_not_null(chanrec->mode);
395 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
396 signal_emit("channel mode changed", 1, chanrec);
399 if (idtype == SILC_ID_CLIENT) {
400 client_entry = (SilcClientEntry)entry;
401 printformat_module("fe-common/silc", server, channel->channel_name,
402 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
403 channel->channel_name, tmp ? tmp : "removed all",
404 client_entry->nickname);
405 } else if (idtype == SILC_ID_SERVER) {
406 server_entry = (SilcServerEntry)entry;
407 printformat_module("fe-common/silc", server, channel->channel_name,
408 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
409 channel->channel_name, tmp ? tmp : "removed all",
410 server_entry->server_name);
411 } else if (idtype == SILC_ID_CHANNEL) {
412 channel2 = (SilcChannelEntry)entry;
413 printformat_module("fe-common/silc", server, channel->channel_name,
414 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
415 channel->channel_name, tmp ? tmp : "removed all",
416 channel2->channel_name);
422 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
424 * Changed user's mode on channel.
427 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
429 idtype = va_arg(va, int);
430 entry = va_arg(va, void *);
431 mode = va_arg(va, SilcUInt32);
432 client_entry2 = va_arg(va, SilcClientEntry);
433 channel = va_arg(va, SilcChannelEntry);
435 tmp = silc_client_chumode(mode);
436 chanrec = silc_channel_find_entry(server, channel);
437 if (chanrec != NULL) {
440 if (client_entry2 == server->conn->local_entry)
441 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
443 nick = silc_nicklist_find(chanrec, client_entry2);
445 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
446 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
447 signal_emit("nick mode changed", 2, chanrec, nick);
451 if (idtype == SILC_ID_CLIENT) {
452 client_entry = (SilcClientEntry)entry;
453 printformat_module("fe-common/silc", server, channel->channel_name,
454 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
455 channel->channel_name, client_entry2->nickname,
456 tmp ? tmp : "removed all",
457 client_entry->nickname);
458 } else if (idtype == SILC_ID_SERVER) {
459 server_entry = (SilcServerEntry)entry;
460 printformat_module("fe-common/silc", server, channel->channel_name,
461 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
462 channel->channel_name, client_entry2->nickname,
463 tmp ? tmp : "removed all",
464 server_entry->server_name);
465 } else if (idtype == SILC_ID_CHANNEL) {
466 channel2 = (SilcChannelEntry)entry;
467 printformat_module("fe-common/silc", server, channel->channel_name,
468 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
469 channel->channel_name, client_entry2->nickname,
470 tmp ? tmp : "removed all",
471 channel2->channel_name);
474 if (mode & SILC_CHANNEL_UMODE_CHANFO)
475 printformat_module("fe-common/silc",
476 server, channel->channel_name, MSGLEVEL_CRAP,
477 SILCTXT_CHANNEL_FOUNDER,
478 channel->channel_name, client_entry2->nickname);
483 case SILC_NOTIFY_TYPE_MOTD:
488 SILC_LOG_DEBUG(("Notify: MOTD"));
490 tmp = va_arg(va, char *);
492 if (!settings_get_bool("skip_motd"))
493 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
496 case SILC_NOTIFY_TYPE_KICKED:
498 * Someone was kicked from channel.
501 SILC_LOG_DEBUG(("Notify: KICKED"));
503 client_entry = va_arg(va, SilcClientEntry);
504 tmp = va_arg(va, char *);
505 client_entry2 = va_arg(va, SilcClientEntry);
506 channel = va_arg(va, SilcChannelEntry);
508 chanrec = silc_channel_find_entry(server, channel);
510 if (client_entry == conn->local_entry) {
511 printformat_module("fe-common/silc", server, channel->channel_name,
512 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
513 channel->channel_name,
514 client_entry ? client_entry2->nickname : "",
517 chanrec->kicked = TRUE;
518 channel_destroy((CHANNEL_REC *)chanrec);
521 printformat_module("fe-common/silc", server, channel->channel_name,
522 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
523 client_entry->nickname, channel->channel_name,
524 client_entry2 ? client_entry2->nickname : "",
528 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
530 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
535 case SILC_NOTIFY_TYPE_KILLED:
537 * Someone was killed from the network.
540 SILC_LOG_DEBUG(("Notify: KILLED"));
542 client_entry = va_arg(va, SilcClientEntry);
543 tmp = va_arg(va, char *);
544 idtype = va_arg(va, int);
545 entry = va_arg(va, SilcClientEntry);
547 if (client_entry == conn->local_entry) {
548 if (idtype == SILC_ID_CLIENT) {
549 client_entry2 = (SilcClientEntry)entry;
550 printformat_module("fe-common/silc", server, NULL,
551 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
552 client_entry2 ? client_entry2->nickname : "",
554 } else if (idtype == SILC_ID_SERVER) {
555 server_entry = (SilcServerEntry)entry;
556 printformat_module("fe-common/silc", server, NULL,
557 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
558 server_entry->server_name, tmp ? tmp : "");
559 } else if (idtype == SILC_ID_CHANNEL) {
560 channel = (SilcChannelEntry)entry;
561 printformat_module("fe-common/silc", server, NULL,
562 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
563 channel->channel_name, tmp ? tmp : "");
566 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
567 for (list_tmp = list1; list_tmp != NULL; list_tmp =
568 list_tmp->next->next) {
569 CHANNEL_REC *channel = list_tmp->data;
570 NICK_REC *nickrec = list_tmp->next->data;
571 nicklist_remove(channel, nickrec);
574 if (idtype == SILC_ID_CLIENT) {
575 client_entry2 = (SilcClientEntry)entry;
576 printformat_module("fe-common/silc", server, NULL,
577 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
578 client_entry->nickname,
579 client_entry2 ? client_entry2->nickname : "",
581 } else if (idtype == SILC_ID_SERVER) {
582 server_entry = (SilcServerEntry)entry;
583 printformat_module("fe-common/silc", server, NULL,
584 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
585 client_entry->nickname,
586 server_entry->server_name, tmp ? tmp : "");
587 } else if (idtype == SILC_ID_CHANNEL) {
588 channel = (SilcChannelEntry)entry;
589 printformat_module("fe-common/silc", server, NULL,
590 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
591 client_entry->nickname,
592 channel->channel_name, tmp ? tmp : "");
597 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
600 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
603 * Server has quit the network.
606 SilcClientEntry *clients;
607 SilcUInt32 clients_count;
609 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
611 (void)va_arg(va, void *);
612 clients = va_arg(va, SilcClientEntry *);
613 clients_count = va_arg(va, SilcUInt32);
615 for (i = 0; i < clients_count; i++) {
616 memset(userhost, 0, sizeof(userhost));
617 if (clients[i]->username)
618 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
619 clients[i]->username, clients[i]->hostname);
620 signal_emit("message quit", 4, server, clients[i]->nickname,
621 clients[i]->username ? userhost : "",
624 silc_server_free_ftp(server, clients[i]);
626 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
627 for (list_tmp = list1; list_tmp != NULL; list_tmp =
628 list_tmp->next->next) {
629 CHANNEL_REC *channel = list_tmp->data;
630 NICK_REC *nickrec = list_tmp->next->data;
631 nicklist_remove(channel, nickrec);
637 case SILC_NOTIFY_TYPE_ERROR:
639 SilcStatus error = va_arg(va, int);
641 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
642 "%s", silc_client_status_message(error));
648 printformat_module("fe-common/silc", server, NULL,
649 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
656 /* Called to indicate that connection was either successfully established
657 or connecting failed. This is also the first time application receives
658 the SilcClientConnection object which it should save somewhere. */
660 void silc_connect(SilcClient client, SilcClientConnection conn,
661 SilcClientConnectionStatus status)
663 SILC_SERVER_REC *server = conn->context;
666 silc_client_close_connection(client, conn);
671 case SILC_CLIENT_CONN_SUCCESS:
672 /* We have successfully connected to server */
673 server->connected = TRUE;
674 signal_emit("event connected", 1, server);
677 case SILC_CLIENT_CONN_SUCCESS_RESUME:
678 /* We have successfully resumed old detached session */
679 server->connected = TRUE;
680 signal_emit("event connected", 1, server);
682 /* If we resumed old session check whether we need to update
684 if (strcmp(server->nick, conn->local_entry->nickname)) {
686 old = g_strdup(server->nick);
687 server_change_nick(SERVER(server), conn->local_entry->nickname);
688 nicklist_rename_unique(SERVER(server),
689 conn->local_entry, server->nick,
690 conn->local_entry, conn->local_entry->nickname);
691 signal_emit("message own_nick", 4, server, server->nick, old, "");
697 server->connection_lost = TRUE;
699 server->conn->context = NULL;
700 server_disconnect(SERVER(server));
705 /* Called to indicate that connection was disconnected to the server. */
707 void silc_disconnect(SilcClient client, SilcClientConnection conn)
709 SILC_SERVER_REC *server = conn->context;
711 SILC_LOG_DEBUG(("Start"));
713 if (!server || server->connection_lost)
716 if (server->conn && server->conn->local_entry) {
717 nicklist_rename_unique(SERVER(server),
718 server->conn->local_entry, server->nick,
719 server->conn->local_entry,
720 silc_client->username);
721 silc_change_nick(server, silc_client->username);
724 server->conn->context = NULL;
726 server->connection_lost = TRUE;
727 server_disconnect(SERVER(server));
730 /* Command handler. This function is called always in the command function.
731 If error occurs it will be called as well. `conn' is the associated
732 client connection. `cmd_context' is the command context that was
733 originally sent to the command. `success' is FALSE if error occured
734 during command. `command' is the command being processed. It must be
735 noted that this is not reply from server. This is merely called just
736 after application has called the command. Just to tell application
737 that the command really was processed. */
739 void silc_command(SilcClient client, SilcClientConnection conn,
740 SilcClientCommandContext cmd_context, int success,
743 SILC_SERVER_REC *server = conn->context;
745 SILC_LOG_DEBUG(("Start"));
751 case SILC_COMMAND_INVITE:
752 printformat_module("fe-common/silc", server, NULL,
753 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
754 cmd_context->argv[2],
755 (cmd_context->argv[1][0] == '*' ?
756 (char *)conn->current_channel->channel_name :
757 (char *)cmd_context->argv[1]));
764 /* Client info resolving callback when JOIN command reply is received.
765 This will cache all users on the channel. */
767 static void silc_client_join_get_users(SilcClient client,
768 SilcClientConnection conn,
769 SilcClientEntry *clients,
770 SilcUInt32 clients_count,
773 SilcChannelEntry channel = (SilcChannelEntry)context;
774 SilcHashTableList htl;
776 SILC_SERVER_REC *server = conn->context;
777 SILC_CHANNEL_REC *chanrec;
778 SilcClientEntry founder = NULL;
781 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
782 silc_hash_table_count(channel->user_list)));
787 chanrec = silc_channel_find(server, channel->channel_name);
791 silc_hash_table_list(channel->user_list, &htl);
792 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
793 if (!chu->client->nickname)
795 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
796 founder = chu->client;
797 silc_nicklist_insert(chanrec, chu, FALSE);
799 silc_hash_table_list_reset(&htl);
801 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
802 nicklist_set_own(CHANNEL(chanrec), ownnick);
803 signal_emit("channel joined", 1, chanrec);
804 chanrec->entry = channel;
807 printformat_module("fe-common/silc", server, channel->channel_name,
808 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
809 channel->channel_name, chanrec->topic);
811 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
814 if (founder == conn->local_entry)
815 printformat_module("fe-common/silc",
816 server, channel->channel_name, MSGLEVEL_CRAP,
817 SILCTXT_CHANNEL_FOUNDER_YOU,
818 channel->channel_name);
820 printformat_module("fe-common/silc",
821 server, channel->channel_name, MSGLEVEL_CRAP,
822 SILCTXT_CHANNEL_FOUNDER,
823 channel->channel_name, founder->nickname);
829 SilcClientConnection conn;
835 void silc_getkey_cb(bool success, void *context)
837 GetkeyContext getkey = (GetkeyContext)context;
838 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
839 char *name = (getkey->id_type == SILC_ID_CLIENT ?
840 ((SilcClientEntry)getkey->entry)->nickname :
841 ((SilcServerEntry)getkey->entry)->server_name);
844 printformat_module("fe-common/silc", NULL, NULL,
845 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
847 printformat_module("fe-common/silc", NULL, NULL,
848 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
851 silc_free(getkey->fingerprint);
855 /* Command reply handler. This function is called always in the command reply
856 function. If error occurs it will be called as well. Normal scenario
857 is that it will be called after the received command data has been parsed
858 and processed. The function is used to pass the received command data to
861 `conn' is the associated client connection. `cmd_payload' is the command
862 payload data received from server and it can be ignored. It is provided
863 if the application would like to re-parse the received command data,
864 however, it must be noted that the data is parsed already by the library
865 thus the payload can be ignored. `success' is FALSE if error occured.
866 In this case arguments are not sent to the application. `command' is the
867 command reply being processed. The function has variable argument list
868 and each command defines the number and type of arguments it passes to the
869 application (on error they are not sent). */
872 silc_command_reply(SilcClient client, SilcClientConnection conn,
873 SilcCommandPayload cmd_payload, int success,
874 SilcCommand command, SilcStatus status, ...)
877 SILC_SERVER_REC *server = conn->context;
878 SILC_CHANNEL_REC *chanrec;
881 va_start(vp, status);
883 SILC_LOG_DEBUG(("Start"));
886 case SILC_COMMAND_WHOIS:
888 char buf[1024], *nickname, *username, *realname, *nick;
889 unsigned char *fingerprint;
890 SilcUInt32 idle, mode;
891 SilcBuffer channels, user_modes;
892 SilcClientEntry client_entry;
894 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
895 /* Print the unknown nick for user */
897 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
900 silc_say_error("%s: %s", tmp,
901 silc_client_status_message(status));
903 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
904 /* Try to find the entry for the unknown client ID, since we
905 might have, and print the nickname of it for user. */
908 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
911 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
914 client_entry = silc_client_get_client_by_id(client, conn,
916 if (client_entry && client_entry->nickname)
917 silc_say_error("%s: %s", client_entry->nickname,
918 silc_client_status_message(status));
919 silc_free(client_id);
928 client_entry = va_arg(vp, SilcClientEntry);
929 nickname = va_arg(vp, char *);
930 username = va_arg(vp, char *);
931 realname = va_arg(vp, char *);
932 channels = va_arg(vp, SilcBuffer);
933 mode = va_arg(vp, SilcUInt32);
934 idle = va_arg(vp, SilcUInt32);
935 fingerprint = va_arg(vp, unsigned char *);
936 user_modes = va_arg(vp, SilcBuffer);
938 silc_parse_userfqdn(nickname, &nick, NULL);
939 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
940 SILCTXT_WHOIS_USERINFO, nickname,
941 client_entry->username, client_entry->hostname,
942 nick, client_entry->nickname);
943 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
944 SILCTXT_WHOIS_REALNAME, realname);
947 if (channels && user_modes) {
949 SilcDList list = silc_channel_payload_parse_list(channels->data,
951 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
953 SilcChannelPayload entry;
956 memset(buf, 0, sizeof(buf));
957 silc_dlist_start(list);
958 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
960 char *m = silc_client_chumode_char(umodes[i++]);
961 char *name = silc_channel_get_name(entry, &name_len);
964 strncat(buf, m, strlen(m));
965 strncat(buf, name, name_len);
966 strncat(buf, " ", 1);
970 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
971 SILCTXT_WHOIS_CHANNELS, buf);
972 silc_channel_payload_list_free(list);
978 memset(buf, 0, sizeof(buf));
980 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
981 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
982 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
984 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
985 "SILC Operator" : "[Unknown mode]");
987 if (mode & SILC_UMODE_GONE)
988 strcat(buf, " [away]");
989 if (mode & SILC_UMODE_INDISPOSED)
990 strcat(buf, " [indisposed]");
991 if (mode & SILC_UMODE_BUSY)
992 strcat(buf, " [busy]");
993 if (mode & SILC_UMODE_PAGE)
994 strcat(buf, " [page to reach]");
995 if (mode & SILC_UMODE_HYPER)
996 strcat(buf, " [hyper active]");
997 if (mode & SILC_UMODE_ROBOT)
998 strcat(buf, " [robot]");
999 if (mode & SILC_UMODE_ANONYMOUS)
1000 strcat(buf, " [anonymous]");
1001 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
1002 strcat(buf, " [blocks private messages]");
1003 if (mode & SILC_UMODE_DETACHED)
1004 strcat(buf, " [detached]");
1006 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1007 SILCTXT_WHOIS_MODES, buf);
1010 if (idle && nickname) {
1011 memset(buf, 0, sizeof(buf));
1012 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1013 idle > 60 ? (idle / 60) : idle,
1014 idle > 60 ? "minutes" : "seconds");
1016 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1017 SILCTXT_WHOIS_IDLE, buf);
1021 fingerprint = silc_fingerprint(fingerprint, 20);
1022 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1023 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1024 silc_free(fingerprint);
1029 case SILC_COMMAND_IDENTIFY:
1031 SilcClientEntry client_entry;
1033 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1034 /* Print the unknown nick for user */
1035 unsigned char *tmp =
1036 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1039 silc_say_error("%s: %s", tmp,
1040 silc_client_status_message(status));
1042 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1043 /* Try to find the entry for the unknown client ID, since we
1044 might have, and print the nickname of it for user. */
1046 unsigned char *tmp =
1047 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1050 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1053 client_entry = silc_client_get_client_by_id(client, conn,
1055 if (client_entry && client_entry->nickname)
1056 silc_say_error("%s: %s", client_entry->nickname,
1057 silc_client_status_message(status));
1058 silc_free(client_id);
1067 case SILC_COMMAND_WHOWAS:
1069 char *nickname, *username, *realname;
1071 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1072 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1074 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1077 silc_say_error("%s: %s", tmp,
1078 silc_client_status_message(status));
1085 (void)va_arg(vp, SilcClientEntry);
1086 nickname = va_arg(vp, char *);
1087 username = va_arg(vp, char *);
1088 realname = va_arg(vp, char *);
1090 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1091 SILCTXT_WHOWAS_USERINFO, nickname, username,
1092 realname ? realname : "");
1096 case SILC_COMMAND_INVITE:
1098 SilcChannelEntry channel;
1100 SilcArgumentPayload args;
1106 channel = va_arg(vp, SilcChannelEntry);
1107 invite_list = va_arg(vp, char *);
1109 args = silc_command_get_args(cmd_payload);
1111 argc = silc_argument_get_arg_num(args);
1114 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1115 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1118 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1119 SILCTXT_CHANNEL_NO_INVITE_LIST,
1120 channel->channel_name);
1124 case SILC_COMMAND_JOIN:
1126 char *channel, *mode, *topic;
1128 SilcChannelEntry channel_entry;
1129 SilcBuffer client_id_list;
1130 SilcUInt32 list_count;
1135 channel = va_arg(vp, char *);
1136 channel_entry = va_arg(vp, SilcChannelEntry);
1137 modei = va_arg(vp, SilcUInt32);
1138 (void)va_arg(vp, SilcUInt32);
1139 (void)va_arg(vp, unsigned char *);
1140 (void)va_arg(vp, unsigned char *);
1141 (void)va_arg(vp, unsigned char *);
1142 topic = va_arg(vp, char *);
1143 (void)va_arg(vp, unsigned char *);
1144 list_count = va_arg(vp, SilcUInt32);
1145 client_id_list = va_arg(vp, SilcBuffer);
1147 chanrec = silc_channel_find(server, channel);
1149 chanrec = silc_channel_create(server, channel, TRUE);
1152 g_free_not_null(chanrec->topic);
1153 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1154 signal_emit("channel topic changed", 1, chanrec);
1157 mode = silc_client_chmode(modei,
1158 channel_entry->channel_key ?
1159 channel_entry->channel_key->cipher->name : "",
1160 channel_entry->hmac ?
1161 silc_hmac_get_name(channel_entry->hmac) : "");
1162 g_free_not_null(chanrec->mode);
1163 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1164 signal_emit("channel mode changed", 1, chanrec);
1166 /* Resolve the client information */
1167 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1168 silc_client_join_get_users,
1174 case SILC_COMMAND_NICK:
1176 SilcClientEntry client = va_arg(vp, SilcClientEntry);
1182 old = g_strdup(server->nick);
1183 server_change_nick(SERVER(server), client->nickname);
1184 nicklist_rename_unique(SERVER(server),
1185 server->conn->local_entry, server->nick,
1186 client, client->nickname);
1187 signal_emit("message own_nick", 4, server, server->nick, old, "");
1192 case SILC_COMMAND_LIST:
1201 (void)va_arg(vp, SilcChannelEntry);
1202 name = va_arg(vp, char *);
1203 topic = va_arg(vp, char *);
1204 usercount = va_arg(vp, int);
1206 if (status == SILC_STATUS_LIST_START ||
1207 status == SILC_STATUS_OK)
1208 printformat_module("fe-common/silc", server, NULL,
1209 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1212 snprintf(users, sizeof(users) - 1, "N/A");
1214 snprintf(users, sizeof(users) - 1, "%d", usercount);
1215 printformat_module("fe-common/silc", server, NULL,
1216 MSGLEVEL_CRAP, SILCTXT_LIST,
1217 name, users, topic ? topic : "");
1221 case SILC_COMMAND_UMODE:
1228 mode = va_arg(vp, SilcUInt32);
1230 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1231 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1232 printformat_module("fe-common/silc", server, NULL,
1233 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1235 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1236 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1237 printformat_module("fe-common/silc", server, NULL,
1238 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1240 server->umode = mode;
1241 signal_emit("user mode changed", 2, server, NULL);
1245 case SILC_COMMAND_OPER:
1249 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1250 signal_emit("user mode changed", 2, server, NULL);
1252 printformat_module("fe-common/silc", server, NULL,
1253 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1256 case SILC_COMMAND_SILCOPER:
1260 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1261 signal_emit("user mode changed", 2, server, NULL);
1263 printformat_module("fe-common/silc", server, NULL,
1264 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1267 case SILC_COMMAND_USERS:
1269 SilcHashTableList htl;
1270 SilcChannelEntry channel;
1271 SilcChannelUser chu;
1276 channel = va_arg(vp, SilcChannelEntry);
1278 printformat_module("fe-common/silc", server, channel->channel_name,
1279 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1280 channel->channel_name);
1282 silc_hash_table_list(channel->user_list, &htl);
1283 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1284 SilcClientEntry e = chu->client;
1285 char stat[5], *mode;
1290 memset(stat, 0, sizeof(stat));
1291 mode = silc_client_chumode_char(chu->mode);
1292 if (e->mode & SILC_UMODE_GONE)
1294 else if (e->mode & SILC_UMODE_INDISPOSED)
1296 else if (e->mode & SILC_UMODE_BUSY)
1298 else if (e->mode & SILC_UMODE_PAGE)
1300 else if (e->mode & SILC_UMODE_HYPER)
1302 else if (e->mode & SILC_UMODE_ROBOT)
1304 else if (e->mode & SILC_UMODE_ANONYMOUS)
1311 printformat_module("fe-common/silc", server, channel->channel_name,
1312 MSGLEVEL_CRAP, SILCTXT_USERS,
1314 e->username ? e->username : "",
1315 e->hostname ? e->hostname : "",
1316 e->realname ? e->realname : "");
1320 silc_hash_table_list_reset(&htl);
1324 case SILC_COMMAND_BAN:
1326 SilcChannelEntry channel;
1332 channel = va_arg(vp, SilcChannelEntry);
1333 ban_list = va_arg(vp, char *);
1336 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1337 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1340 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1341 SILCTXT_CHANNEL_NO_BAN_LIST,
1342 channel->channel_name);
1346 case SILC_COMMAND_GETKEY:
1350 SilcPublicKey public_key;
1353 GetkeyContext getkey;
1359 id_type = va_arg(vp, SilcUInt32);
1360 entry = va_arg(vp, void *);
1361 public_key = va_arg(vp, SilcPublicKey);
1364 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1366 getkey = silc_calloc(1, sizeof(*getkey));
1367 getkey->entry = entry;
1368 getkey->id_type = id_type;
1369 getkey->client = client;
1370 getkey->conn = conn;
1371 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1373 name = (id_type == SILC_ID_CLIENT ?
1374 ((SilcClientEntry)entry)->nickname :
1375 ((SilcServerEntry)entry)->server_name);
1377 silc_verify_public_key_internal(client, conn, name,
1378 (id_type == SILC_ID_CLIENT ?
1379 SILC_SOCKET_TYPE_CLIENT :
1380 SILC_SOCKET_TYPE_SERVER),
1381 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1382 silc_getkey_cb, getkey);
1385 printformat_module("fe-common/silc", server, NULL,
1386 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1391 case SILC_COMMAND_INFO:
1393 SilcServerEntry server_entry;
1400 server_entry = va_arg(vp, SilcServerEntry);
1401 server_name = va_arg(vp, char *);
1402 server_info = va_arg(vp, char *);
1404 if (server_name && server_info )
1406 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1407 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1412 case SILC_COMMAND_TOPIC:
1414 SilcChannelEntry channel;
1420 channel = va_arg(vp, SilcChannelEntry);
1421 topic = va_arg(vp, char *);
1424 chanrec = silc_channel_find_entry(server, channel);
1426 g_free_not_null(chanrec->topic);
1427 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1428 signal_emit("channel topic changed", 1, chanrec);
1430 printformat_module("fe-common/silc", server, channel->channel_name,
1431 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1432 channel->channel_name, topic);
1434 printformat_module("fe-common/silc", server, channel->channel_name,
1435 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1436 channel->channel_name);
1448 SilcClientConnection conn;
1454 SilcSKEPKType pk_type;
1455 SilcVerifyPublicKey completion;
1459 static void verify_public_key_completion(const char *line, void *context)
1461 PublicKeyVerify verify = (PublicKeyVerify)context;
1463 if (line[0] == 'Y' || line[0] == 'y') {
1464 /* Call the completion */
1465 if (verify->completion)
1466 verify->completion(TRUE, verify->context);
1468 /* Save the key for future checking */
1469 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
1470 verify->pk_len, SILC_PKCS_FILE_PEM);
1472 /* Call the completion */
1473 if (verify->completion)
1474 verify->completion(FALSE, verify->context);
1476 printformat_module("fe-common/silc", NULL, NULL,
1477 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
1478 verify->entity_name ? verify->entity_name :
1482 silc_free(verify->filename);
1483 silc_free(verify->entity);
1484 silc_free(verify->entity_name);
1485 silc_free(verify->pk);
1489 /* Internal routine to verify public key. If the `completion' is provided
1490 it will be called to indicate whether public was verified or not. For
1491 server/router public key this will check for filename that includes the
1492 remote host's IP address and remote host's hostname. */
1495 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1496 const char *name, SilcSocketType conn_type,
1497 unsigned char *pk, SilcUInt32 pk_len,
1498 SilcSKEPKType pk_type,
1499 SilcVerifyPublicKey completion, void *context)
1502 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1503 char *fingerprint, *babbleprint, *format;
1506 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1507 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1508 "server" : "client");
1509 PublicKeyVerify verify;
1511 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1512 printformat_module("fe-common/silc", NULL, NULL,
1513 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
1516 completion(FALSE, context);
1520 pw = getpwuid(getuid());
1523 completion(FALSE, context);
1527 memset(filename, 0, sizeof(filename));
1528 memset(filename2, 0, sizeof(filename2));
1529 memset(file, 0, sizeof(file));
1531 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1532 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1534 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1535 conn->sock->ip, conn->sock->port);
1536 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1537 get_irssi_dir(), entity, file);
1539 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1540 conn->sock->hostname, conn->sock->port);
1541 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
1542 get_irssi_dir(), entity, file);
1547 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1548 name, conn->sock->port);
1549 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1550 get_irssi_dir(), entity, file);
1555 /* Replace all whitespaces with `_'. */
1556 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1557 for (i = 0; i < strlen(fingerprint); i++)
1558 if (fingerprint[i] == ' ')
1559 fingerprint[i] = '_';
1561 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1562 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1563 get_irssi_dir(), entity, file);
1564 silc_free(fingerprint);
1569 /* Take fingerprint of the public key */
1570 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1571 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1573 verify = silc_calloc(1, sizeof(*verify));
1574 verify->client = client;
1575 verify->conn = conn;
1576 verify->filename = strdup(ipf);
1577 verify->entity = strdup(entity);
1578 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1579 (name ? strdup(name) : strdup(conn->sock->hostname))
1581 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1582 memcpy(verify->pk, pk, pk_len);
1583 verify->pk_len = pk_len;
1584 verify->pk_type = pk_type;
1585 verify->completion = completion;
1586 verify->context = context;
1588 /* Check whether this key already exists */
1589 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1590 /* Key does not exist, ask user to verify the key and save it */
1592 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1593 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1594 verify->entity_name : entity);
1595 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1596 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1597 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1598 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1599 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1600 SILCTXT_PUBKEY_ACCEPT);
1601 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1604 silc_free(fingerprint);
1607 /* The key already exists, verify it. */
1608 SilcPublicKey public_key;
1609 unsigned char *encpk;
1610 SilcUInt32 encpk_len;
1612 /* Load the key file, try for both IP filename and hostname filename */
1613 if (!silc_pkcs_load_public_key(ipf, &public_key,
1614 SILC_PKCS_FILE_PEM) &&
1615 !silc_pkcs_load_public_key(ipf, &public_key,
1616 SILC_PKCS_FILE_BIN) &&
1617 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
1618 SILC_PKCS_FILE_PEM) &&
1619 !silc_pkcs_load_public_key(hostf, &public_key,
1620 SILC_PKCS_FILE_BIN)))) {
1621 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1622 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1623 verify->entity_name : entity);
1624 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1625 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1626 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1627 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1628 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1629 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1630 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1631 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1632 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1635 silc_free(fingerprint);
1639 /* Encode the key data */
1640 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1642 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1643 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1644 verify->entity_name : entity);
1645 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1646 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1647 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1648 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1649 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1650 SILCTXT_PUBKEY_MALFORMED, entity);
1651 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1652 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1653 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1656 silc_free(fingerprint);
1660 /* Compare the keys */
1661 if (memcmp(encpk, pk, encpk_len)) {
1662 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1663 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1664 verify->entity_name : entity);
1665 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1666 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1667 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1668 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1669 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1670 SILCTXT_PUBKEY_NO_MATCH, entity);
1671 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1672 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1673 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1674 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1676 /* Ask user to verify the key and save it */
1677 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1678 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1679 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1682 silc_free(fingerprint);
1686 /* Local copy matched */
1688 completion(TRUE, context);
1689 silc_free(fingerprint);
1693 /* Verifies received public key. The `conn_type' indicates which entity
1694 (server, client etc.) has sent the public key. If user decides to trust
1695 the key may be saved as trusted public key for later use. The
1696 `completion' must be called after the public key has been verified. */
1699 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1700 SilcSocketType conn_type, unsigned char *pk,
1701 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1702 SilcVerifyPublicKey completion, void *context)
1704 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1706 completion, context);
1709 /* Asks passphrase from user on the input line. */
1712 SilcAskPassphrase completion;
1716 void ask_passphrase_completion(const char *passphrase, void *context)
1718 AskPassphrase p = (AskPassphrase)context;
1719 p->completion((unsigned char *)passphrase,
1720 passphrase ? strlen(passphrase) : 0, p->context);
1724 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1725 SilcAskPassphrase completion, void *context)
1727 AskPassphrase p = silc_calloc(1, sizeof(*p));
1728 p->completion = completion;
1729 p->context = context;
1731 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1732 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1736 SilcGetAuthMeth completion;
1738 } *InternalGetAuthMethod;
1740 /* Callback called when we've received the authentication method information
1741 from the server after we've requested it. This will get the authentication
1742 data from the user if needed. */
1744 static void silc_get_auth_method_callback(SilcClient client,
1745 SilcClientConnection conn,
1746 SilcAuthMethod auth_meth,
1749 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1751 SILC_LOG_DEBUG(("Start"));
1753 switch (auth_meth) {
1754 case SILC_AUTH_NONE:
1755 /* No authentication required. */
1756 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1758 case SILC_AUTH_PASSWORD:
1759 /* Do not ask the passphrase from user, the library will ask it if
1760 we do not provide it here. */
1761 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1763 case SILC_AUTH_PUBLIC_KEY:
1764 /* Do not get the authentication data now, the library will generate
1765 it using our default key, if we do not provide it here. */
1766 /* XXX In the future when we support multiple local keys and multiple
1767 local certificates we will need to ask from user which one to use. */
1768 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1772 silc_free(internal);
1775 /* Find authentication method and authentication data by hostname and
1776 port. The hostname may be IP address as well. The found authentication
1777 method and authentication data is returned to `auth_meth', `auth_data'
1778 and `auth_data_len'. The function returns TRUE if authentication method
1779 is found and FALSE if not. `conn' may be NULL. */
1781 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1782 char *hostname, SilcUInt16 port,
1783 SilcGetAuthMeth completion, void *context)
1785 InternalGetAuthMethod internal;
1787 SILC_LOG_DEBUG(("Start"));
1789 /* XXX must resolve from configuration whether this connection has
1790 any specific authentication data */
1792 /* If we do not have this connection configured by the user in a
1793 configuration file then resolve the authentication method from the
1794 server for this session. */
1795 internal = silc_calloc(1, sizeof(*internal));
1796 internal->completion = completion;
1797 internal->context = context;
1799 silc_client_request_authentication_method(client, conn,
1800 silc_get_auth_method_callback,
1804 /* Notifies application that failure packet was received. This is called
1805 if there is some protocol active in the client. The `protocol' is the
1806 protocol context. The `failure' is opaque pointer to the failure
1807 indication. Note, that the `failure' is protocol dependant and application
1808 must explicitly cast it to correct type. Usually `failure' is 32 bit
1809 failure type (see protocol specs for all protocol failure types). */
1811 void silc_failure(SilcClient client, SilcClientConnection conn,
1812 SilcProtocol protocol, void *failure)
1814 SILC_LOG_DEBUG(("Start"));
1816 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1817 SilcSKEStatus status = (SilcSKEStatus)failure;
1819 if (status == SILC_SKE_STATUS_BAD_VERSION)
1820 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1821 SILCTXT_KE_BAD_VERSION);
1822 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1823 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1824 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1825 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1826 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1827 SILCTXT_KE_UNKNOWN_GROUP);
1828 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1829 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1830 SILCTXT_KE_UNKNOWN_CIPHER);
1831 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1832 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1833 SILCTXT_KE_UNKNOWN_PKCS);
1834 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1835 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1836 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1837 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1838 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1839 SILCTXT_KE_UNKNOWN_HMAC);
1840 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1841 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1842 SILCTXT_KE_INCORRECT_SIGNATURE);
1843 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1844 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1845 SILCTXT_KE_INVALID_COOKIE);
1848 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1849 SilcUInt32 err = (SilcUInt32)failure;
1851 if (err == SILC_AUTH_FAILED)
1852 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1853 SILCTXT_AUTH_FAILED);
1857 /* Asks whether the user would like to perform the key agreement protocol.
1858 This is called after we have received an key agreement packet or an
1859 reply to our key agreement packet. This returns TRUE if the user wants
1860 the library to perform the key agreement protocol and FALSE if it is not
1861 desired (application may start it later by calling the function
1862 silc_client_perform_key_agreement). */
1864 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1865 SilcClientEntry client_entry, const char *hostname,
1866 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1871 SILC_LOG_DEBUG(("Start"));
1873 /* We will just display the info on the screen and return FALSE and user
1874 will have to start the key agreement with a command. */
1877 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1880 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1881 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1883 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1884 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1885 client_entry->nickname, hostname, portstr);
1893 /* Notifies application that file transfer protocol session is being
1894 requested by the remote client indicated by the `client_entry' from
1895 the `hostname' and `port'. The `session_id' is the file transfer
1896 session and it can be used to either accept or reject the file
1897 transfer request, by calling the silc_client_file_receive or
1898 silc_client_file_close, respectively. */
1900 void silc_ftp(SilcClient client, SilcClientConnection conn,
1901 SilcClientEntry client_entry, SilcUInt32 session_id,
1902 const char *hostname, SilcUInt16 port)
1904 SILC_SERVER_REC *server;
1906 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1908 SILC_LOG_DEBUG(("Start"));
1910 server = conn->context;
1912 ftp->client_entry = client_entry;
1913 ftp->session_id = session_id;
1916 silc_dlist_add(server->ftp_sessions, ftp);
1917 server->current_session = ftp;
1920 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1923 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1924 SILCTXT_FILE_REQUEST, client_entry->nickname);
1926 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1927 SILCTXT_FILE_REQUEST_HOST,
1928 client_entry->nickname, hostname, portstr);
1931 /* Delivers SILC session detachment data indicated by `detach_data' to the
1932 application. If application has issued SILC_COMMAND_DETACH command
1933 the client session in the SILC network is not quit. The client remains
1934 in the network but is detached. The detachment data may be used later
1935 to resume the session in the SILC Network. The appliation is
1936 responsible of saving the `detach_data', to for example in a file.
1938 The detachment data can be given as argument to the functions
1939 silc_client_connect_to_server, or silc_client_add_connection when
1940 creating connection to remote server, inside SilcClientConnectionParams
1941 structure. If it is provided the client library will attempt to resume
1942 the session in the network. After the connection is created
1943 successfully, the application is responsible of setting the user
1944 interface for user into the same state it was before detaching (showing
1945 same channels, channel modes, etc). It can do this by fetching the
1946 information (like joined channels) from the client library. */
1949 silc_detach(SilcClient client, SilcClientConnection conn,
1950 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1954 /* Save the detachment data to file. */
1956 memset(file, 0, sizeof(file));
1957 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
1958 silc_file_writefile(file, detach_data, detach_data_len);
1962 /* SILC client operations */
1963 SilcClientOperations ops = {
1965 silc_channel_message,
1966 silc_private_message,
1972 silc_get_auth_method,
1973 silc_verify_public_key,
1974 silc_ask_passphrase,