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_DATA) {
110 /* MIME object received, try to display it as well as we can */
114 memset(type, 0, sizeof(type));
115 if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
116 NULL, 0, &data, NULL))
119 /* Then figure out what we can display */
120 if (strstr(type, "text/") && !strstr(type, "text/t140") &&
121 !strstr(type, "text/vnd")) {
122 /* It is something textual, display it */
123 message = (const unsigned char *)data;
132 if (flags & SILC_MESSAGE_FLAG_ACTION)
133 printformat_module("fe-common/silc", server, channel->channel_name,
134 MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
135 nick == NULL ? "[<unknown>]" : nick->nick, message);
136 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
137 printformat_module("fe-common/silc", server, channel->channel_name,
138 MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
139 nick == NULL ? "[<unknown>]" : nick->nick, message);
141 signal_emit("message public", 6, server, message,
142 nick == NULL ? "[<unknown>]" : nick->nick,
143 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
144 chanrec->name, nick);
147 /* Private message to the client. The `sender' is the nickname of the
148 sender received in the packet. */
150 void silc_private_message(SilcClient client, SilcClientConnection conn,
151 SilcClientEntry sender, SilcMessageFlags flags,
152 const unsigned char *message,
153 SilcUInt32 message_len)
155 SILC_SERVER_REC *server;
158 SILC_LOG_DEBUG(("Start"));
160 server = conn == NULL ? NULL : conn->context;
161 memset(userhost, 0, sizeof(userhost));
162 if (sender->username)
163 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
164 sender->username, sender->hostname);
165 signal_emit("message private", 4, server, message,
166 sender->nickname ? sender->nickname : "[<unknown>]",
167 sender->username ? userhost : NULL);
170 /* Notify message to the client. The notify arguments are sent in the
171 same order as servers sends them. The arguments are same as received
172 from the server except for ID's. If ID is received application receives
173 the corresponding entry to the ID. For example, if Client ID is received
174 application receives SilcClientEntry. Also, if the notify type is
175 for channel the channel entry is sent to application (even if server
176 does not send it). */
178 void silc_notify(SilcClient client, SilcClientConnection conn,
179 SilcNotifyType type, ...)
182 SILC_SERVER_REC *server;
183 SILC_CHANNEL_REC *chanrec;
184 SILC_NICK_REC *nickrec;
185 SilcClientEntry client_entry, client_entry2;
186 SilcChannelEntry channel, channel2;
187 SilcServerEntry server_entry;
193 GSList *list1, *list_tmp;
195 SILC_LOG_DEBUG(("Start"));
199 server = conn == NULL ? NULL : conn->context;
202 case SILC_NOTIFY_TYPE_NONE:
203 /* Some generic notice from server */
204 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
207 case SILC_NOTIFY_TYPE_INVITE:
209 * Invited or modified invite list.
212 SILC_LOG_DEBUG(("Notify: INVITE"));
214 channel = va_arg(va, SilcChannelEntry);
215 name = va_arg(va, char *);
216 client_entry = va_arg(va, SilcClientEntry);
218 memset(userhost, 0, sizeof(userhost));
219 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
220 client_entry->username, client_entry->hostname);
221 signal_emit("message invite", 4, server, channel ? channel->channel_name :
222 name, client_entry->nickname, userhost);
225 case SILC_NOTIFY_TYPE_JOIN:
230 SILC_LOG_DEBUG(("Notify: JOIN"));
232 client_entry = va_arg(va, SilcClientEntry);
233 channel = va_arg(va, SilcChannelEntry);
235 if (client_entry == server->conn->local_entry) {
236 /* You joined to channel */
237 chanrec = silc_channel_find(server, channel->channel_name);
238 if (chanrec != NULL && !chanrec->joined)
239 chanrec->entry = channel;
241 chanrec = silc_channel_find_entry(server, channel);
242 if (chanrec != NULL) {
243 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
245 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
249 memset(userhost, 0, sizeof(userhost));
250 if (client_entry->username)
251 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
252 client_entry->username, client_entry->hostname);
253 signal_emit("message join", 4, server, channel->channel_name,
254 client_entry->nickname,
255 client_entry->username == NULL ? "" : userhost);
258 case SILC_NOTIFY_TYPE_LEAVE:
263 SILC_LOG_DEBUG(("Notify: LEAVE"));
265 client_entry = va_arg(va, SilcClientEntry);
266 channel = va_arg(va, SilcChannelEntry);
268 memset(userhost, 0, sizeof(userhost));
269 if (client_entry->username)
270 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
271 client_entry->username, client_entry->hostname);
272 signal_emit("message part", 5, server, channel->channel_name,
273 client_entry->nickname, client_entry->username ?
274 userhost : "", client_entry->nickname);
276 chanrec = silc_channel_find_entry(server, channel);
277 if (chanrec != NULL) {
278 nickrec = silc_nicklist_find(chanrec, client_entry);
280 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
284 case SILC_NOTIFY_TYPE_SIGNOFF:
289 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
291 client_entry = va_arg(va, SilcClientEntry);
292 tmp = va_arg(va, char *);
294 silc_server_free_ftp(server, client_entry);
296 memset(userhost, 0, sizeof(userhost));
297 if (client_entry->username)
298 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
299 client_entry->username, client_entry->hostname);
300 signal_emit("message quit", 4, server, client_entry->nickname,
301 client_entry->username ? userhost : "",
304 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
305 for (list_tmp = list1; list_tmp != NULL; list_tmp =
306 list_tmp->next->next) {
307 CHANNEL_REC *channel = list_tmp->data;
308 NICK_REC *nickrec = list_tmp->next->data;
310 nicklist_remove(channel, nickrec);
314 case SILC_NOTIFY_TYPE_TOPIC_SET:
319 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
321 idtype = va_arg(va, int);
322 entry = va_arg(va, void *);
323 tmp = va_arg(va, char *);
324 channel = va_arg(va, SilcChannelEntry);
326 chanrec = silc_channel_find_entry(server, channel);
327 if (chanrec != NULL) {
328 g_free_not_null(chanrec->topic);
329 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
330 signal_emit("channel topic changed", 1, chanrec);
333 if (idtype == SILC_ID_CLIENT) {
334 client_entry = (SilcClientEntry)entry;
335 memset(userhost, 0, sizeof(userhost));
336 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
337 client_entry->username, client_entry->hostname);
338 signal_emit("message topic", 5, server, channel->channel_name,
339 tmp, client_entry->nickname, userhost);
340 } else if (idtype == SILC_ID_SERVER) {
341 server_entry = (SilcServerEntry)entry;
342 signal_emit("message topic", 5, server, channel->channel_name,
343 tmp, server_entry->server_name,
344 server_entry->server_name);
345 } else if (idtype == SILC_ID_CHANNEL) {
346 channel = (SilcChannelEntry)entry;
347 signal_emit("message topic", 5, server, channel->channel_name,
348 tmp, channel->channel_name, channel->channel_name);
352 case SILC_NOTIFY_TYPE_NICK_CHANGE:
357 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
359 client_entry = va_arg(va, SilcClientEntry);
360 client_entry2 = va_arg(va, SilcClientEntry);
362 if (!strcmp(client_entry->nickname, client_entry2->nickname))
365 memset(userhost, 0, sizeof(userhost));
366 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
367 client_entry2->username, client_entry2->hostname);
368 nicklist_rename_unique(SERVER(server),
369 client_entry, client_entry->nickname,
370 client_entry2, client_entry2->nickname);
371 signal_emit("message nick", 4, server, client_entry2->nickname,
372 client_entry->nickname, userhost);
375 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
377 * Changed channel mode.
380 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
382 idtype = va_arg(va, int);
383 entry = va_arg(va, void *);
384 mode = va_arg(va, SilcUInt32);
385 (void)va_arg(va, char *);
386 (void)va_arg(va, char *);
387 channel = va_arg(va, SilcChannelEntry);
389 tmp = silc_client_chmode(mode,
390 channel->channel_key ?
391 channel->channel_key->cipher->name : "",
393 silc_hmac_get_name(channel->hmac) : "");
395 chanrec = silc_channel_find_entry(server, channel);
396 if (chanrec != NULL) {
397 g_free_not_null(chanrec->mode);
398 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
399 signal_emit("channel mode changed", 1, chanrec);
402 if (idtype == SILC_ID_CLIENT) {
403 client_entry = (SilcClientEntry)entry;
404 printformat_module("fe-common/silc", server, channel->channel_name,
405 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
406 channel->channel_name, tmp ? tmp : "removed all",
407 client_entry->nickname);
408 } else if (idtype == SILC_ID_SERVER) {
409 server_entry = (SilcServerEntry)entry;
410 printformat_module("fe-common/silc", server, channel->channel_name,
411 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
412 channel->channel_name, tmp ? tmp : "removed all",
413 server_entry->server_name);
414 } else if (idtype == SILC_ID_CHANNEL) {
415 channel2 = (SilcChannelEntry)entry;
416 printformat_module("fe-common/silc", server, channel->channel_name,
417 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
418 channel->channel_name, tmp ? tmp : "removed all",
419 channel2->channel_name);
425 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
427 * Changed user's mode on channel.
430 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
432 idtype = va_arg(va, int);
433 entry = va_arg(va, void *);
434 mode = va_arg(va, SilcUInt32);
435 client_entry2 = va_arg(va, SilcClientEntry);
436 channel = va_arg(va, SilcChannelEntry);
438 tmp = silc_client_chumode(mode);
439 chanrec = silc_channel_find_entry(server, channel);
440 if (chanrec != NULL) {
443 if (client_entry2 == server->conn->local_entry)
444 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
446 nick = silc_nicklist_find(chanrec, client_entry2);
448 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
449 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
450 signal_emit("nick mode changed", 2, chanrec, nick);
454 if (idtype == SILC_ID_CLIENT) {
455 client_entry = (SilcClientEntry)entry;
456 printformat_module("fe-common/silc", server, channel->channel_name,
457 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
458 channel->channel_name, client_entry2->nickname,
459 tmp ? tmp : "removed all",
460 client_entry->nickname);
461 } else if (idtype == SILC_ID_SERVER) {
462 server_entry = (SilcServerEntry)entry;
463 printformat_module("fe-common/silc", server, channel->channel_name,
464 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
465 channel->channel_name, client_entry2->nickname,
466 tmp ? tmp : "removed all",
467 server_entry->server_name);
468 } else if (idtype == SILC_ID_CHANNEL) {
469 channel2 = (SilcChannelEntry)entry;
470 printformat_module("fe-common/silc", server, channel->channel_name,
471 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
472 channel->channel_name, client_entry2->nickname,
473 tmp ? tmp : "removed all",
474 channel2->channel_name);
477 if (mode & SILC_CHANNEL_UMODE_CHANFO)
478 printformat_module("fe-common/silc",
479 server, channel->channel_name, MSGLEVEL_CRAP,
480 SILCTXT_CHANNEL_FOUNDER,
481 channel->channel_name, client_entry2->nickname);
486 case SILC_NOTIFY_TYPE_MOTD:
491 SILC_LOG_DEBUG(("Notify: MOTD"));
493 tmp = va_arg(va, char *);
495 if (!settings_get_bool("skip_motd"))
496 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
499 case SILC_NOTIFY_TYPE_KICKED:
501 * Someone was kicked from channel.
504 SILC_LOG_DEBUG(("Notify: KICKED"));
506 client_entry = va_arg(va, SilcClientEntry);
507 tmp = va_arg(va, char *);
508 client_entry2 = va_arg(va, SilcClientEntry);
509 channel = va_arg(va, SilcChannelEntry);
511 chanrec = silc_channel_find_entry(server, channel);
513 if (client_entry == conn->local_entry) {
514 printformat_module("fe-common/silc", server, channel->channel_name,
515 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
516 channel->channel_name,
517 client_entry ? client_entry2->nickname : "",
520 chanrec->kicked = TRUE;
521 channel_destroy((CHANNEL_REC *)chanrec);
524 printformat_module("fe-common/silc", server, channel->channel_name,
525 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
526 client_entry->nickname, channel->channel_name,
527 client_entry2 ? client_entry2->nickname : "",
531 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
533 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
538 case SILC_NOTIFY_TYPE_KILLED:
540 * Someone was killed from the network.
543 SILC_LOG_DEBUG(("Notify: KILLED"));
545 client_entry = va_arg(va, SilcClientEntry);
546 tmp = va_arg(va, char *);
547 idtype = va_arg(va, int);
548 entry = va_arg(va, SilcClientEntry);
550 if (client_entry == conn->local_entry) {
551 if (idtype == SILC_ID_CLIENT) {
552 client_entry2 = (SilcClientEntry)entry;
553 printformat_module("fe-common/silc", server, NULL,
554 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
555 client_entry2 ? client_entry2->nickname : "",
557 } else if (idtype == SILC_ID_SERVER) {
558 server_entry = (SilcServerEntry)entry;
559 printformat_module("fe-common/silc", server, NULL,
560 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
561 server_entry->server_name, tmp ? tmp : "");
562 } else if (idtype == SILC_ID_CHANNEL) {
563 channel = (SilcChannelEntry)entry;
564 printformat_module("fe-common/silc", server, NULL,
565 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
566 channel->channel_name, tmp ? tmp : "");
569 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
570 for (list_tmp = list1; list_tmp != NULL; list_tmp =
571 list_tmp->next->next) {
572 CHANNEL_REC *channel = list_tmp->data;
573 NICK_REC *nickrec = list_tmp->next->data;
574 nicklist_remove(channel, nickrec);
577 if (idtype == SILC_ID_CLIENT) {
578 client_entry2 = (SilcClientEntry)entry;
579 printformat_module("fe-common/silc", server, NULL,
580 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
581 client_entry->nickname,
582 client_entry2 ? client_entry2->nickname : "",
584 } else if (idtype == SILC_ID_SERVER) {
585 server_entry = (SilcServerEntry)entry;
586 printformat_module("fe-common/silc", server, NULL,
587 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
588 client_entry->nickname,
589 server_entry->server_name, tmp ? tmp : "");
590 } else if (idtype == SILC_ID_CHANNEL) {
591 channel = (SilcChannelEntry)entry;
592 printformat_module("fe-common/silc", server, NULL,
593 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
594 client_entry->nickname,
595 channel->channel_name, tmp ? tmp : "");
600 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
603 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
606 * Server has quit the network.
609 SilcClientEntry *clients;
610 SilcUInt32 clients_count;
612 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
614 (void)va_arg(va, void *);
615 clients = va_arg(va, SilcClientEntry *);
616 clients_count = va_arg(va, SilcUInt32);
618 for (i = 0; i < clients_count; i++) {
619 memset(userhost, 0, sizeof(userhost));
620 if (clients[i]->username)
621 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
622 clients[i]->username, clients[i]->hostname);
623 signal_emit("message quit", 4, server, clients[i]->nickname,
624 clients[i]->username ? userhost : "",
627 silc_server_free_ftp(server, clients[i]);
629 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
630 for (list_tmp = list1; list_tmp != NULL; list_tmp =
631 list_tmp->next->next) {
632 CHANNEL_REC *channel = list_tmp->data;
633 NICK_REC *nickrec = list_tmp->next->data;
634 nicklist_remove(channel, nickrec);
640 case SILC_NOTIFY_TYPE_ERROR:
642 SilcStatus error = va_arg(va, int);
644 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
645 "%s", silc_client_status_message(error));
651 printformat_module("fe-common/silc", server, NULL,
652 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
659 /* Called to indicate that connection was either successfully established
660 or connecting failed. This is also the first time application receives
661 the SilcClientConnection object which it should save somewhere. */
663 void silc_connect(SilcClient client, SilcClientConnection conn,
664 SilcClientConnectionStatus status)
666 SILC_SERVER_REC *server = conn->context;
669 silc_client_close_connection(client, conn);
674 case SILC_CLIENT_CONN_SUCCESS:
675 /* We have successfully connected to server */
676 server->connected = TRUE;
677 signal_emit("event connected", 1, server);
680 case SILC_CLIENT_CONN_SUCCESS_RESUME:
681 /* We have successfully resumed old detached session */
682 server->connected = TRUE;
683 signal_emit("event connected", 1, server);
685 /* If we resumed old session check whether we need to update
687 if (strcmp(server->nick, conn->local_entry->nickname)) {
689 old = g_strdup(server->nick);
690 server_change_nick(SERVER(server), conn->local_entry->nickname);
691 nicklist_rename_unique(SERVER(server),
692 conn->local_entry, server->nick,
693 conn->local_entry, conn->local_entry->nickname);
694 signal_emit("message own_nick", 4, server, server->nick, old, "");
700 server->connection_lost = TRUE;
702 server->conn->context = NULL;
703 server_disconnect(SERVER(server));
708 /* Called to indicate that connection was disconnected to the server. */
710 void silc_disconnect(SilcClient client, SilcClientConnection conn)
712 SILC_SERVER_REC *server = conn->context;
714 SILC_LOG_DEBUG(("Start"));
716 if (!server || server->connection_lost)
719 if (server->conn && server->conn->local_entry) {
720 nicklist_rename_unique(SERVER(server),
721 server->conn->local_entry, server->nick,
722 server->conn->local_entry,
723 silc_client->username);
724 silc_change_nick(server, silc_client->username);
727 server->conn->context = NULL;
729 server->connection_lost = TRUE;
730 server_disconnect(SERVER(server));
733 /* Command handler. This function is called always in the command function.
734 If error occurs it will be called as well. `conn' is the associated
735 client connection. `cmd_context' is the command context that was
736 originally sent to the command. `success' is FALSE if error occured
737 during command. `command' is the command being processed. It must be
738 noted that this is not reply from server. This is merely called just
739 after application has called the command. Just to tell application
740 that the command really was processed. */
742 void silc_command(SilcClient client, SilcClientConnection conn,
743 SilcClientCommandContext cmd_context, int success,
746 SILC_SERVER_REC *server = conn->context;
748 SILC_LOG_DEBUG(("Start"));
754 case SILC_COMMAND_INVITE:
755 printformat_module("fe-common/silc", server, NULL,
756 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
757 cmd_context->argv[2],
758 (cmd_context->argv[1][0] == '*' ?
759 (char *)conn->current_channel->channel_name :
760 (char *)cmd_context->argv[1]));
767 /* Client info resolving callback when JOIN command reply is received.
768 This will cache all users on the channel. */
770 static void silc_client_join_get_users(SilcClient client,
771 SilcClientConnection conn,
772 SilcClientEntry *clients,
773 SilcUInt32 clients_count,
776 SilcChannelEntry channel = (SilcChannelEntry)context;
777 SilcHashTableList htl;
779 SILC_SERVER_REC *server = conn->context;
780 SILC_CHANNEL_REC *chanrec;
781 SilcClientEntry founder = NULL;
784 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
785 silc_hash_table_count(channel->user_list)));
790 chanrec = silc_channel_find(server, channel->channel_name);
794 silc_hash_table_list(channel->user_list, &htl);
795 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
796 if (!chu->client->nickname)
798 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
799 founder = chu->client;
800 silc_nicklist_insert(chanrec, chu, FALSE);
802 silc_hash_table_list_reset(&htl);
804 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
805 nicklist_set_own(CHANNEL(chanrec), ownnick);
806 signal_emit("channel joined", 1, chanrec);
807 chanrec->entry = channel;
810 printformat_module("fe-common/silc", server, channel->channel_name,
811 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
812 channel->channel_name, chanrec->topic);
814 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
817 if (founder == conn->local_entry)
818 printformat_module("fe-common/silc",
819 server, channel->channel_name, MSGLEVEL_CRAP,
820 SILCTXT_CHANNEL_FOUNDER_YOU,
821 channel->channel_name);
823 printformat_module("fe-common/silc",
824 server, channel->channel_name, MSGLEVEL_CRAP,
825 SILCTXT_CHANNEL_FOUNDER,
826 channel->channel_name, founder->nickname);
832 SilcClientConnection conn;
838 void silc_getkey_cb(bool success, void *context)
840 GetkeyContext getkey = (GetkeyContext)context;
841 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
842 char *name = (getkey->id_type == SILC_ID_CLIENT ?
843 ((SilcClientEntry)getkey->entry)->nickname :
844 ((SilcServerEntry)getkey->entry)->server_name);
847 printformat_module("fe-common/silc", NULL, NULL,
848 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
850 printformat_module("fe-common/silc", NULL, NULL,
851 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
854 silc_free(getkey->fingerprint);
858 /* Command reply handler. This function is called always in the command reply
859 function. If error occurs it will be called as well. Normal scenario
860 is that it will be called after the received command data has been parsed
861 and processed. The function is used to pass the received command data to
864 `conn' is the associated client connection. `cmd_payload' is the command
865 payload data received from server and it can be ignored. It is provided
866 if the application would like to re-parse the received command data,
867 however, it must be noted that the data is parsed already by the library
868 thus the payload can be ignored. `success' is FALSE if error occured.
869 In this case arguments are not sent to the application. `command' is the
870 command reply being processed. The function has variable argument list
871 and each command defines the number and type of arguments it passes to the
872 application (on error they are not sent). */
875 silc_command_reply(SilcClient client, SilcClientConnection conn,
876 SilcCommandPayload cmd_payload, int success,
877 SilcCommand command, SilcStatus status, ...)
880 SILC_SERVER_REC *server = conn->context;
881 SILC_CHANNEL_REC *chanrec;
884 va_start(vp, status);
886 SILC_LOG_DEBUG(("Start"));
889 case SILC_COMMAND_WHOIS:
891 char buf[1024], *nickname, *username, *realname, *nick;
892 unsigned char *fingerprint;
893 SilcUInt32 idle, mode;
894 SilcBuffer channels, user_modes;
895 SilcClientEntry client_entry;
897 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
898 /* Print the unknown nick for user */
900 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
903 silc_say_error("%s: %s", tmp,
904 silc_client_status_message(status));
906 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
907 /* Try to find the entry for the unknown client ID, since we
908 might have, and print the nickname of it for user. */
911 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
914 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
917 client_entry = silc_client_get_client_by_id(client, conn,
919 if (client_entry && client_entry->nickname)
920 silc_say_error("%s: %s", client_entry->nickname,
921 silc_client_status_message(status));
922 silc_free(client_id);
931 client_entry = va_arg(vp, SilcClientEntry);
932 nickname = va_arg(vp, char *);
933 username = va_arg(vp, char *);
934 realname = va_arg(vp, char *);
935 channels = va_arg(vp, SilcBuffer);
936 mode = va_arg(vp, SilcUInt32);
937 idle = va_arg(vp, SilcUInt32);
938 fingerprint = va_arg(vp, unsigned char *);
939 user_modes = va_arg(vp, SilcBuffer);
941 silc_parse_userfqdn(nickname, &nick, NULL);
942 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
943 SILCTXT_WHOIS_USERINFO, nickname,
944 client_entry->username, client_entry->hostname,
945 nick, client_entry->nickname);
946 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
947 SILCTXT_WHOIS_REALNAME, realname);
950 if (channels && user_modes) {
952 SilcDList list = silc_channel_payload_parse_list(channels->data,
954 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
956 SilcChannelPayload entry;
959 memset(buf, 0, sizeof(buf));
960 silc_dlist_start(list);
961 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
963 char *m = silc_client_chumode_char(umodes[i++]);
964 char *name = silc_channel_get_name(entry, &name_len);
967 strncat(buf, m, strlen(m));
968 strncat(buf, name, name_len);
969 strncat(buf, " ", 1);
973 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
974 SILCTXT_WHOIS_CHANNELS, buf);
975 silc_channel_payload_list_free(list);
981 memset(buf, 0, sizeof(buf));
983 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
984 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
985 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
987 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
988 "SILC Operator" : "[Unknown mode]");
990 if (mode & SILC_UMODE_GONE)
991 strcat(buf, " [away]");
992 if (mode & SILC_UMODE_INDISPOSED)
993 strcat(buf, " [indisposed]");
994 if (mode & SILC_UMODE_BUSY)
995 strcat(buf, " [busy]");
996 if (mode & SILC_UMODE_PAGE)
997 strcat(buf, " [page to reach]");
998 if (mode & SILC_UMODE_HYPER)
999 strcat(buf, " [hyper active]");
1000 if (mode & SILC_UMODE_ROBOT)
1001 strcat(buf, " [robot]");
1002 if (mode & SILC_UMODE_ANONYMOUS)
1003 strcat(buf, " [anonymous]");
1004 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
1005 strcat(buf, " [blocks private messages]");
1006 if (mode & SILC_UMODE_DETACHED)
1007 strcat(buf, " [detached]");
1009 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1010 SILCTXT_WHOIS_MODES, buf);
1013 if (idle && nickname) {
1014 memset(buf, 0, sizeof(buf));
1015 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1016 idle > 60 ? (idle / 60) : idle,
1017 idle > 60 ? "minutes" : "seconds");
1019 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1020 SILCTXT_WHOIS_IDLE, buf);
1024 fingerprint = silc_fingerprint(fingerprint, 20);
1025 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1026 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1027 silc_free(fingerprint);
1032 case SILC_COMMAND_IDENTIFY:
1034 SilcClientEntry client_entry;
1036 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1037 /* Print the unknown nick for user */
1038 unsigned char *tmp =
1039 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1042 silc_say_error("%s: %s", tmp,
1043 silc_client_status_message(status));
1045 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1046 /* Try to find the entry for the unknown client ID, since we
1047 might have, and print the nickname of it for user. */
1049 unsigned char *tmp =
1050 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1053 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1056 client_entry = silc_client_get_client_by_id(client, conn,
1058 if (client_entry && client_entry->nickname)
1059 silc_say_error("%s: %s", client_entry->nickname,
1060 silc_client_status_message(status));
1061 silc_free(client_id);
1070 case SILC_COMMAND_WHOWAS:
1072 char *nickname, *username, *realname;
1074 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1075 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1077 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1080 silc_say_error("%s: %s", tmp,
1081 silc_client_status_message(status));
1088 (void)va_arg(vp, SilcClientEntry);
1089 nickname = va_arg(vp, char *);
1090 username = va_arg(vp, char *);
1091 realname = va_arg(vp, char *);
1093 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1094 SILCTXT_WHOWAS_USERINFO, nickname, username,
1095 realname ? realname : "");
1099 case SILC_COMMAND_INVITE:
1101 SilcChannelEntry channel;
1103 SilcArgumentPayload args;
1109 channel = va_arg(vp, SilcChannelEntry);
1110 invite_list = va_arg(vp, char *);
1112 args = silc_command_get_args(cmd_payload);
1114 argc = silc_argument_get_arg_num(args);
1117 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1118 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1121 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1122 SILCTXT_CHANNEL_NO_INVITE_LIST,
1123 channel->channel_name);
1127 case SILC_COMMAND_JOIN:
1129 char *channel, *mode, *topic;
1131 SilcChannelEntry channel_entry;
1132 SilcBuffer client_id_list;
1133 SilcUInt32 list_count;
1138 channel = va_arg(vp, char *);
1139 channel_entry = va_arg(vp, SilcChannelEntry);
1140 modei = va_arg(vp, SilcUInt32);
1141 (void)va_arg(vp, SilcUInt32);
1142 (void)va_arg(vp, unsigned char *);
1143 (void)va_arg(vp, unsigned char *);
1144 (void)va_arg(vp, unsigned char *);
1145 topic = va_arg(vp, char *);
1146 (void)va_arg(vp, unsigned char *);
1147 list_count = va_arg(vp, SilcUInt32);
1148 client_id_list = va_arg(vp, SilcBuffer);
1150 chanrec = silc_channel_find(server, channel);
1152 chanrec = silc_channel_create(server, channel, TRUE);
1155 g_free_not_null(chanrec->topic);
1156 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1157 signal_emit("channel topic changed", 1, chanrec);
1160 mode = silc_client_chmode(modei,
1161 channel_entry->channel_key ?
1162 channel_entry->channel_key->cipher->name : "",
1163 channel_entry->hmac ?
1164 silc_hmac_get_name(channel_entry->hmac) : "");
1165 g_free_not_null(chanrec->mode);
1166 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1167 signal_emit("channel mode changed", 1, chanrec);
1169 /* Resolve the client information */
1170 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1171 silc_client_join_get_users,
1177 case SILC_COMMAND_NICK:
1179 SilcClientEntry client = va_arg(vp, SilcClientEntry);
1185 old = g_strdup(server->nick);
1186 server_change_nick(SERVER(server), client->nickname);
1187 nicklist_rename_unique(SERVER(server),
1188 server->conn->local_entry, server->nick,
1189 client, client->nickname);
1190 signal_emit("message own_nick", 4, server, server->nick, old, "");
1195 case SILC_COMMAND_LIST:
1204 (void)va_arg(vp, SilcChannelEntry);
1205 name = va_arg(vp, char *);
1206 topic = va_arg(vp, char *);
1207 usercount = va_arg(vp, int);
1209 if (status == SILC_STATUS_LIST_START ||
1210 status == SILC_STATUS_OK)
1211 printformat_module("fe-common/silc", server, NULL,
1212 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1215 snprintf(users, sizeof(users) - 1, "N/A");
1217 snprintf(users, sizeof(users) - 1, "%d", usercount);
1218 printformat_module("fe-common/silc", server, NULL,
1219 MSGLEVEL_CRAP, SILCTXT_LIST,
1220 name, users, topic ? topic : "");
1224 case SILC_COMMAND_UMODE:
1231 mode = va_arg(vp, SilcUInt32);
1233 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1234 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1235 printformat_module("fe-common/silc", server, NULL,
1236 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1238 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1239 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1240 printformat_module("fe-common/silc", server, NULL,
1241 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1243 server->umode = mode;
1244 signal_emit("user mode changed", 2, server, NULL);
1248 case SILC_COMMAND_OPER:
1252 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1253 signal_emit("user mode changed", 2, server, NULL);
1255 printformat_module("fe-common/silc", server, NULL,
1256 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1259 case SILC_COMMAND_SILCOPER:
1263 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1264 signal_emit("user mode changed", 2, server, NULL);
1266 printformat_module("fe-common/silc", server, NULL,
1267 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1270 case SILC_COMMAND_USERS:
1272 SilcHashTableList htl;
1273 SilcChannelEntry channel;
1274 SilcChannelUser chu;
1279 channel = va_arg(vp, SilcChannelEntry);
1281 printformat_module("fe-common/silc", server, channel->channel_name,
1282 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1283 channel->channel_name);
1285 silc_hash_table_list(channel->user_list, &htl);
1286 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1287 SilcClientEntry e = chu->client;
1288 char stat[5], *mode;
1293 memset(stat, 0, sizeof(stat));
1294 mode = silc_client_chumode_char(chu->mode);
1295 if (e->mode & SILC_UMODE_GONE)
1297 else if (e->mode & SILC_UMODE_INDISPOSED)
1299 else if (e->mode & SILC_UMODE_BUSY)
1301 else if (e->mode & SILC_UMODE_PAGE)
1303 else if (e->mode & SILC_UMODE_HYPER)
1305 else if (e->mode & SILC_UMODE_ROBOT)
1307 else if (e->mode & SILC_UMODE_ANONYMOUS)
1314 printformat_module("fe-common/silc", server, channel->channel_name,
1315 MSGLEVEL_CRAP, SILCTXT_USERS,
1317 e->username ? e->username : "",
1318 e->hostname ? e->hostname : "",
1319 e->realname ? e->realname : "");
1323 silc_hash_table_list_reset(&htl);
1327 case SILC_COMMAND_BAN:
1329 SilcChannelEntry channel;
1335 channel = va_arg(vp, SilcChannelEntry);
1336 ban_list = va_arg(vp, char *);
1339 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1340 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1343 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1344 SILCTXT_CHANNEL_NO_BAN_LIST,
1345 channel->channel_name);
1349 case SILC_COMMAND_GETKEY:
1353 SilcPublicKey public_key;
1356 GetkeyContext getkey;
1362 id_type = va_arg(vp, SilcUInt32);
1363 entry = va_arg(vp, void *);
1364 public_key = va_arg(vp, SilcPublicKey);
1367 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1369 getkey = silc_calloc(1, sizeof(*getkey));
1370 getkey->entry = entry;
1371 getkey->id_type = id_type;
1372 getkey->client = client;
1373 getkey->conn = conn;
1374 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1376 name = (id_type == SILC_ID_CLIENT ?
1377 ((SilcClientEntry)entry)->nickname :
1378 ((SilcServerEntry)entry)->server_name);
1380 silc_verify_public_key_internal(client, conn, name,
1381 (id_type == SILC_ID_CLIENT ?
1382 SILC_SOCKET_TYPE_CLIENT :
1383 SILC_SOCKET_TYPE_SERVER),
1384 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1385 silc_getkey_cb, getkey);
1388 printformat_module("fe-common/silc", server, NULL,
1389 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1394 case SILC_COMMAND_INFO:
1396 SilcServerEntry server_entry;
1403 server_entry = va_arg(vp, SilcServerEntry);
1404 server_name = va_arg(vp, char *);
1405 server_info = va_arg(vp, char *);
1407 if (server_name && server_info )
1409 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1410 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1415 case SILC_COMMAND_TOPIC:
1417 SilcChannelEntry channel;
1423 channel = va_arg(vp, SilcChannelEntry);
1424 topic = va_arg(vp, char *);
1427 chanrec = silc_channel_find_entry(server, channel);
1429 g_free_not_null(chanrec->topic);
1430 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1431 signal_emit("channel topic changed", 1, chanrec);
1433 printformat_module("fe-common/silc", server, channel->channel_name,
1434 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1435 channel->channel_name, topic);
1437 printformat_module("fe-common/silc", server, channel->channel_name,
1438 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1439 channel->channel_name);
1451 SilcClientConnection conn;
1457 SilcSKEPKType pk_type;
1458 SilcVerifyPublicKey completion;
1462 static void verify_public_key_completion(const char *line, void *context)
1464 PublicKeyVerify verify = (PublicKeyVerify)context;
1466 if (line[0] == 'Y' || line[0] == 'y') {
1467 /* Call the completion */
1468 if (verify->completion)
1469 verify->completion(TRUE, verify->context);
1471 /* Save the key for future checking */
1472 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
1473 verify->pk_len, SILC_PKCS_FILE_PEM);
1475 /* Call the completion */
1476 if (verify->completion)
1477 verify->completion(FALSE, verify->context);
1479 printformat_module("fe-common/silc", NULL, NULL,
1480 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
1481 verify->entity_name ? verify->entity_name :
1485 silc_free(verify->filename);
1486 silc_free(verify->entity);
1487 silc_free(verify->entity_name);
1488 silc_free(verify->pk);
1492 /* Internal routine to verify public key. If the `completion' is provided
1493 it will be called to indicate whether public was verified or not. For
1494 server/router public key this will check for filename that includes the
1495 remote host's IP address and remote host's hostname. */
1498 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1499 const char *name, SilcSocketType conn_type,
1500 unsigned char *pk, SilcUInt32 pk_len,
1501 SilcSKEPKType pk_type,
1502 SilcVerifyPublicKey completion, void *context)
1505 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1506 char *fingerprint, *babbleprint, *format;
1509 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1510 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1511 "server" : "client");
1512 PublicKeyVerify verify;
1514 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1515 printformat_module("fe-common/silc", NULL, NULL,
1516 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
1519 completion(FALSE, context);
1523 pw = getpwuid(getuid());
1526 completion(FALSE, context);
1530 memset(filename, 0, sizeof(filename));
1531 memset(filename2, 0, sizeof(filename2));
1532 memset(file, 0, sizeof(file));
1534 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1535 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1537 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1538 conn->sock->ip, conn->sock->port);
1539 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1540 get_irssi_dir(), entity, file);
1542 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1543 conn->sock->hostname, conn->sock->port);
1544 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
1545 get_irssi_dir(), entity, file);
1550 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1551 name, conn->sock->port);
1552 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1553 get_irssi_dir(), entity, file);
1558 /* Replace all whitespaces with `_'. */
1559 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1560 for (i = 0; i < strlen(fingerprint); i++)
1561 if (fingerprint[i] == ' ')
1562 fingerprint[i] = '_';
1564 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1565 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1566 get_irssi_dir(), entity, file);
1567 silc_free(fingerprint);
1572 /* Take fingerprint of the public key */
1573 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1574 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1576 verify = silc_calloc(1, sizeof(*verify));
1577 verify->client = client;
1578 verify->conn = conn;
1579 verify->filename = strdup(ipf);
1580 verify->entity = strdup(entity);
1581 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1582 (name ? strdup(name) : strdup(conn->sock->hostname))
1584 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1585 memcpy(verify->pk, pk, pk_len);
1586 verify->pk_len = pk_len;
1587 verify->pk_type = pk_type;
1588 verify->completion = completion;
1589 verify->context = context;
1591 /* Check whether this key already exists */
1592 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1593 /* Key does not exist, ask user to verify the key and save it */
1595 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1596 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1597 verify->entity_name : entity);
1598 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1599 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1600 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1601 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1602 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1603 SILCTXT_PUBKEY_ACCEPT);
1604 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1607 silc_free(fingerprint);
1610 /* The key already exists, verify it. */
1611 SilcPublicKey public_key;
1612 unsigned char *encpk;
1613 SilcUInt32 encpk_len;
1615 /* Load the key file, try for both IP filename and hostname filename */
1616 if (!silc_pkcs_load_public_key(ipf, &public_key,
1617 SILC_PKCS_FILE_PEM) &&
1618 !silc_pkcs_load_public_key(ipf, &public_key,
1619 SILC_PKCS_FILE_BIN) &&
1620 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
1621 SILC_PKCS_FILE_PEM) &&
1622 !silc_pkcs_load_public_key(hostf, &public_key,
1623 SILC_PKCS_FILE_BIN)))) {
1624 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1625 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1626 verify->entity_name : entity);
1627 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1628 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1629 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1630 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1631 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1632 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1633 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1634 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1635 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1638 silc_free(fingerprint);
1642 /* Encode the key data */
1643 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1645 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1646 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1647 verify->entity_name : entity);
1648 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1649 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1650 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1651 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1652 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1653 SILCTXT_PUBKEY_MALFORMED, entity);
1654 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1655 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1656 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1659 silc_free(fingerprint);
1663 /* Compare the keys */
1664 if (memcmp(encpk, pk, encpk_len)) {
1665 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1666 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1667 verify->entity_name : entity);
1668 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1669 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1670 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1671 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1672 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1673 SILCTXT_PUBKEY_NO_MATCH, entity);
1674 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1675 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1676 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1677 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1679 /* Ask user to verify the key and save it */
1680 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1681 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1682 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1685 silc_free(fingerprint);
1689 /* Local copy matched */
1691 completion(TRUE, context);
1692 silc_free(fingerprint);
1696 /* Verifies received public key. The `conn_type' indicates which entity
1697 (server, client etc.) has sent the public key. If user decides to trust
1698 the key may be saved as trusted public key for later use. The
1699 `completion' must be called after the public key has been verified. */
1702 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1703 SilcSocketType conn_type, unsigned char *pk,
1704 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1705 SilcVerifyPublicKey completion, void *context)
1707 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1709 completion, context);
1712 /* Asks passphrase from user on the input line. */
1715 SilcAskPassphrase completion;
1719 void ask_passphrase_completion(const char *passphrase, void *context)
1721 AskPassphrase p = (AskPassphrase)context;
1722 p->completion((unsigned char *)passphrase,
1723 passphrase ? strlen(passphrase) : 0, p->context);
1727 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1728 SilcAskPassphrase completion, void *context)
1730 AskPassphrase p = silc_calloc(1, sizeof(*p));
1731 p->completion = completion;
1732 p->context = context;
1734 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1735 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1739 SilcGetAuthMeth completion;
1741 } *InternalGetAuthMethod;
1743 /* Callback called when we've received the authentication method information
1744 from the server after we've requested it. This will get the authentication
1745 data from the user if needed. */
1747 static void silc_get_auth_method_callback(SilcClient client,
1748 SilcClientConnection conn,
1749 SilcAuthMethod auth_meth,
1752 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1754 SILC_LOG_DEBUG(("Start"));
1756 switch (auth_meth) {
1757 case SILC_AUTH_NONE:
1758 /* No authentication required. */
1759 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1761 case SILC_AUTH_PASSWORD:
1762 /* Do not ask the passphrase from user, the library will ask it if
1763 we do not provide it here. */
1764 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1766 case SILC_AUTH_PUBLIC_KEY:
1767 /* Do not get the authentication data now, the library will generate
1768 it using our default key, if we do not provide it here. */
1769 /* XXX In the future when we support multiple local keys and multiple
1770 local certificates we will need to ask from user which one to use. */
1771 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1775 silc_free(internal);
1778 /* Find authentication method and authentication data by hostname and
1779 port. The hostname may be IP address as well. The found authentication
1780 method and authentication data is returned to `auth_meth', `auth_data'
1781 and `auth_data_len'. The function returns TRUE if authentication method
1782 is found and FALSE if not. `conn' may be NULL. */
1784 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1785 char *hostname, SilcUInt16 port,
1786 SilcGetAuthMeth completion, void *context)
1788 InternalGetAuthMethod internal;
1790 SILC_LOG_DEBUG(("Start"));
1792 /* XXX must resolve from configuration whether this connection has
1793 any specific authentication data */
1795 /* If we do not have this connection configured by the user in a
1796 configuration file then resolve the authentication method from the
1797 server for this session. */
1798 internal = silc_calloc(1, sizeof(*internal));
1799 internal->completion = completion;
1800 internal->context = context;
1802 silc_client_request_authentication_method(client, conn,
1803 silc_get_auth_method_callback,
1807 /* Notifies application that failure packet was received. This is called
1808 if there is some protocol active in the client. The `protocol' is the
1809 protocol context. The `failure' is opaque pointer to the failure
1810 indication. Note, that the `failure' is protocol dependant and application
1811 must explicitly cast it to correct type. Usually `failure' is 32 bit
1812 failure type (see protocol specs for all protocol failure types). */
1814 void silc_failure(SilcClient client, SilcClientConnection conn,
1815 SilcProtocol protocol, void *failure)
1817 SILC_LOG_DEBUG(("Start"));
1819 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1820 SilcSKEStatus status = (SilcSKEStatus)failure;
1822 if (status == SILC_SKE_STATUS_BAD_VERSION)
1823 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1824 SILCTXT_KE_BAD_VERSION);
1825 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1826 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1827 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1828 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1829 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1830 SILCTXT_KE_UNKNOWN_GROUP);
1831 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1832 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1833 SILCTXT_KE_UNKNOWN_CIPHER);
1834 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1835 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1836 SILCTXT_KE_UNKNOWN_PKCS);
1837 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1838 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1839 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1840 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1841 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1842 SILCTXT_KE_UNKNOWN_HMAC);
1843 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1844 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1845 SILCTXT_KE_INCORRECT_SIGNATURE);
1846 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1847 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1848 SILCTXT_KE_INVALID_COOKIE);
1851 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1852 SilcUInt32 err = (SilcUInt32)failure;
1854 if (err == SILC_AUTH_FAILED)
1855 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1856 SILCTXT_AUTH_FAILED);
1860 /* Asks whether the user would like to perform the key agreement protocol.
1861 This is called after we have received an key agreement packet or an
1862 reply to our key agreement packet. This returns TRUE if the user wants
1863 the library to perform the key agreement protocol and FALSE if it is not
1864 desired (application may start it later by calling the function
1865 silc_client_perform_key_agreement). */
1867 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1868 SilcClientEntry client_entry, const char *hostname,
1869 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1874 SILC_LOG_DEBUG(("Start"));
1876 /* We will just display the info on the screen and return FALSE and user
1877 will have to start the key agreement with a command. */
1880 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1883 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1884 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1886 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1887 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1888 client_entry->nickname, hostname, portstr);
1896 /* Notifies application that file transfer protocol session is being
1897 requested by the remote client indicated by the `client_entry' from
1898 the `hostname' and `port'. The `session_id' is the file transfer
1899 session and it can be used to either accept or reject the file
1900 transfer request, by calling the silc_client_file_receive or
1901 silc_client_file_close, respectively. */
1903 void silc_ftp(SilcClient client, SilcClientConnection conn,
1904 SilcClientEntry client_entry, SilcUInt32 session_id,
1905 const char *hostname, SilcUInt16 port)
1907 SILC_SERVER_REC *server;
1909 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1911 SILC_LOG_DEBUG(("Start"));
1913 server = conn->context;
1915 ftp->client_entry = client_entry;
1916 ftp->session_id = session_id;
1919 silc_dlist_add(server->ftp_sessions, ftp);
1920 server->current_session = ftp;
1923 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1926 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1927 SILCTXT_FILE_REQUEST, client_entry->nickname);
1929 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1930 SILCTXT_FILE_REQUEST_HOST,
1931 client_entry->nickname, hostname, portstr);
1934 /* Delivers SILC session detachment data indicated by `detach_data' to the
1935 application. If application has issued SILC_COMMAND_DETACH command
1936 the client session in the SILC network is not quit. The client remains
1937 in the network but is detached. The detachment data may be used later
1938 to resume the session in the SILC Network. The appliation is
1939 responsible of saving the `detach_data', to for example in a file.
1941 The detachment data can be given as argument to the functions
1942 silc_client_connect_to_server, or silc_client_add_connection when
1943 creating connection to remote server, inside SilcClientConnectionParams
1944 structure. If it is provided the client library will attempt to resume
1945 the session in the network. After the connection is created
1946 successfully, the application is responsible of setting the user
1947 interface for user into the same state it was before detaching (showing
1948 same channels, channel modes, etc). It can do this by fetching the
1949 information (like joined channels) from the client library. */
1952 silc_detach(SilcClient client, SilcClientConnection conn,
1953 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1957 /* Save the detachment data to file. */
1959 memset(file, 0, sizeof(file));
1960 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
1961 silc_file_writefile(file, detach_data, detach_data_len);
1965 /* SILC client operations */
1966 SilcClientOperations ops = {
1968 silc_channel_message,
1969 silc_private_message,
1975 silc_get_auth_method,
1976 silc_verify_public_key,
1977 silc_ask_passphrase,