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);
166 if (flags & SILC_MESSAGE_FLAG_DATA) {
167 /* MIME object received, try to display it as well as we can */
171 memset(type, 0, sizeof(type));
172 if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
173 NULL, 0, &data, NULL))
176 /* Then figure out what we can display */
177 if (strstr(type, "text/") && !strstr(type, "text/t140") &&
178 !strstr(type, "text/vnd")) {
179 /* It is something textual, display it */
180 message = (const unsigned char *)data;
189 signal_emit("message private", 4, server, message,
190 sender->nickname ? sender->nickname : "[<unknown>]",
191 sender->username ? userhost : NULL);
194 /* Notify message to the client. The notify arguments are sent in the
195 same order as servers sends them. The arguments are same as received
196 from the server except for ID's. If ID is received application receives
197 the corresponding entry to the ID. For example, if Client ID is received
198 application receives SilcClientEntry. Also, if the notify type is
199 for channel the channel entry is sent to application (even if server
200 does not send it). */
202 void silc_notify(SilcClient client, SilcClientConnection conn,
203 SilcNotifyType type, ...)
206 SILC_SERVER_REC *server;
207 SILC_CHANNEL_REC *chanrec;
208 SILC_NICK_REC *nickrec;
209 SilcClientEntry client_entry, client_entry2;
210 SilcChannelEntry channel, channel2;
211 SilcServerEntry server_entry;
217 GSList *list1, *list_tmp;
219 SILC_LOG_DEBUG(("Start"));
223 server = conn == NULL ? NULL : conn->context;
226 case SILC_NOTIFY_TYPE_NONE:
227 /* Some generic notice from server */
228 printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
231 case SILC_NOTIFY_TYPE_INVITE:
233 * Invited or modified invite list.
236 SILC_LOG_DEBUG(("Notify: INVITE"));
238 channel = va_arg(va, SilcChannelEntry);
239 name = va_arg(va, char *);
240 client_entry = va_arg(va, SilcClientEntry);
242 memset(userhost, 0, sizeof(userhost));
243 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
244 client_entry->username, client_entry->hostname);
245 signal_emit("message invite", 4, server, channel ? channel->channel_name :
246 name, client_entry->nickname, userhost);
249 case SILC_NOTIFY_TYPE_JOIN:
254 SILC_LOG_DEBUG(("Notify: JOIN"));
256 client_entry = va_arg(va, SilcClientEntry);
257 channel = va_arg(va, SilcChannelEntry);
259 if (client_entry == server->conn->local_entry) {
260 /* You joined to channel */
261 chanrec = silc_channel_find(server, channel->channel_name);
262 if (chanrec != NULL && !chanrec->joined)
263 chanrec->entry = channel;
265 chanrec = silc_channel_find_entry(server, channel);
266 if (chanrec != NULL) {
267 SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
269 nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
273 memset(userhost, 0, sizeof(userhost));
274 if (client_entry->username)
275 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
276 client_entry->username, client_entry->hostname);
277 signal_emit("message join", 4, server, channel->channel_name,
278 client_entry->nickname,
279 client_entry->username == NULL ? "" : userhost);
282 case SILC_NOTIFY_TYPE_LEAVE:
287 SILC_LOG_DEBUG(("Notify: LEAVE"));
289 client_entry = va_arg(va, SilcClientEntry);
290 channel = va_arg(va, SilcChannelEntry);
292 memset(userhost, 0, sizeof(userhost));
293 if (client_entry->username)
294 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
295 client_entry->username, client_entry->hostname);
296 signal_emit("message part", 5, server, channel->channel_name,
297 client_entry->nickname, client_entry->username ?
298 userhost : "", client_entry->nickname);
300 chanrec = silc_channel_find_entry(server, channel);
301 if (chanrec != NULL) {
302 nickrec = silc_nicklist_find(chanrec, client_entry);
304 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
308 case SILC_NOTIFY_TYPE_SIGNOFF:
313 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
315 client_entry = va_arg(va, SilcClientEntry);
316 tmp = va_arg(va, char *);
318 silc_server_free_ftp(server, client_entry);
320 memset(userhost, 0, sizeof(userhost));
321 if (client_entry->username)
322 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
323 client_entry->username, client_entry->hostname);
324 signal_emit("message quit", 4, server, client_entry->nickname,
325 client_entry->username ? userhost : "",
328 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
329 for (list_tmp = list1; list_tmp != NULL; list_tmp =
330 list_tmp->next->next) {
331 CHANNEL_REC *channel = list_tmp->data;
332 NICK_REC *nickrec = list_tmp->next->data;
334 nicklist_remove(channel, nickrec);
338 case SILC_NOTIFY_TYPE_TOPIC_SET:
343 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
345 idtype = va_arg(va, int);
346 entry = va_arg(va, void *);
347 tmp = va_arg(va, char *);
348 channel = va_arg(va, SilcChannelEntry);
350 chanrec = silc_channel_find_entry(server, channel);
351 if (chanrec != NULL) {
352 g_free_not_null(chanrec->topic);
353 chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
354 signal_emit("channel topic changed", 1, chanrec);
357 if (idtype == SILC_ID_CLIENT) {
358 client_entry = (SilcClientEntry)entry;
359 memset(userhost, 0, sizeof(userhost));
360 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
361 client_entry->username, client_entry->hostname);
362 signal_emit("message topic", 5, server, channel->channel_name,
363 tmp, client_entry->nickname, userhost);
364 } else if (idtype == SILC_ID_SERVER) {
365 server_entry = (SilcServerEntry)entry;
366 signal_emit("message topic", 5, server, channel->channel_name,
367 tmp, server_entry->server_name,
368 server_entry->server_name);
369 } else if (idtype == SILC_ID_CHANNEL) {
370 channel = (SilcChannelEntry)entry;
371 signal_emit("message topic", 5, server, channel->channel_name,
372 tmp, channel->channel_name, channel->channel_name);
376 case SILC_NOTIFY_TYPE_NICK_CHANGE:
381 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
383 client_entry = va_arg(va, SilcClientEntry);
384 client_entry2 = va_arg(va, SilcClientEntry);
386 if (!strcmp(client_entry->nickname, client_entry2->nickname))
389 memset(userhost, 0, sizeof(userhost));
390 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
391 client_entry2->username, client_entry2->hostname);
392 nicklist_rename_unique(SERVER(server),
393 client_entry, client_entry->nickname,
394 client_entry2, client_entry2->nickname);
395 signal_emit("message nick", 4, server, client_entry2->nickname,
396 client_entry->nickname, userhost);
399 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
401 * Changed channel mode.
404 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
406 idtype = va_arg(va, int);
407 entry = va_arg(va, void *);
408 mode = va_arg(va, SilcUInt32);
409 (void)va_arg(va, char *);
410 (void)va_arg(va, char *);
411 channel = va_arg(va, SilcChannelEntry);
413 tmp = silc_client_chmode(mode,
414 channel->channel_key ?
415 channel->channel_key->cipher->name : "",
417 silc_hmac_get_name(channel->hmac) : "");
419 chanrec = silc_channel_find_entry(server, channel);
420 if (chanrec != NULL) {
421 g_free_not_null(chanrec->mode);
422 chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
423 signal_emit("channel mode changed", 1, chanrec);
426 if (idtype == SILC_ID_CLIENT) {
427 client_entry = (SilcClientEntry)entry;
428 printformat_module("fe-common/silc", server, channel->channel_name,
429 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
430 channel->channel_name, tmp ? tmp : "removed all",
431 client_entry->nickname);
432 } else if (idtype == SILC_ID_SERVER) {
433 server_entry = (SilcServerEntry)entry;
434 printformat_module("fe-common/silc", server, channel->channel_name,
435 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
436 channel->channel_name, tmp ? tmp : "removed all",
437 server_entry->server_name);
438 } else if (idtype == SILC_ID_CHANNEL) {
439 channel2 = (SilcChannelEntry)entry;
440 printformat_module("fe-common/silc", server, channel->channel_name,
441 MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
442 channel->channel_name, tmp ? tmp : "removed all",
443 channel2->channel_name);
449 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
451 * Changed user's mode on channel.
454 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
456 idtype = va_arg(va, int);
457 entry = va_arg(va, void *);
458 mode = va_arg(va, SilcUInt32);
459 client_entry2 = va_arg(va, SilcClientEntry);
460 channel = va_arg(va, SilcChannelEntry);
462 tmp = silc_client_chumode(mode);
463 chanrec = silc_channel_find_entry(server, channel);
464 if (chanrec != NULL) {
467 if (client_entry2 == server->conn->local_entry)
468 chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
470 nick = silc_nicklist_find(chanrec, client_entry2);
472 nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
473 nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
474 signal_emit("nick mode changed", 2, chanrec, nick);
478 if (idtype == SILC_ID_CLIENT) {
479 client_entry = (SilcClientEntry)entry;
480 printformat_module("fe-common/silc", server, channel->channel_name,
481 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
482 channel->channel_name, client_entry2->nickname,
483 tmp ? tmp : "removed all",
484 client_entry->nickname);
485 } else if (idtype == SILC_ID_SERVER) {
486 server_entry = (SilcServerEntry)entry;
487 printformat_module("fe-common/silc", server, channel->channel_name,
488 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
489 channel->channel_name, client_entry2->nickname,
490 tmp ? tmp : "removed all",
491 server_entry->server_name);
492 } else if (idtype == SILC_ID_CHANNEL) {
493 channel2 = (SilcChannelEntry)entry;
494 printformat_module("fe-common/silc", server, channel->channel_name,
495 MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
496 channel->channel_name, client_entry2->nickname,
497 tmp ? tmp : "removed all",
498 channel2->channel_name);
501 if (mode & SILC_CHANNEL_UMODE_CHANFO)
502 printformat_module("fe-common/silc",
503 server, channel->channel_name, MSGLEVEL_CRAP,
504 SILCTXT_CHANNEL_FOUNDER,
505 channel->channel_name, client_entry2->nickname);
510 case SILC_NOTIFY_TYPE_MOTD:
515 SILC_LOG_DEBUG(("Notify: MOTD"));
517 tmp = va_arg(va, char *);
519 if (!settings_get_bool("skip_motd"))
520 printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
523 case SILC_NOTIFY_TYPE_KICKED:
525 * Someone was kicked from channel.
528 SILC_LOG_DEBUG(("Notify: KICKED"));
530 client_entry = va_arg(va, SilcClientEntry);
531 tmp = va_arg(va, char *);
532 client_entry2 = va_arg(va, SilcClientEntry);
533 channel = va_arg(va, SilcChannelEntry);
535 chanrec = silc_channel_find_entry(server, channel);
537 if (client_entry == conn->local_entry) {
538 printformat_module("fe-common/silc", server, channel->channel_name,
539 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
540 channel->channel_name,
541 client_entry ? client_entry2->nickname : "",
544 chanrec->kicked = TRUE;
545 channel_destroy((CHANNEL_REC *)chanrec);
548 printformat_module("fe-common/silc", server, channel->channel_name,
549 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
550 client_entry->nickname, channel->channel_name,
551 client_entry2 ? client_entry2->nickname : "",
555 SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
557 nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
562 case SILC_NOTIFY_TYPE_KILLED:
564 * Someone was killed from the network.
567 SILC_LOG_DEBUG(("Notify: KILLED"));
569 client_entry = va_arg(va, SilcClientEntry);
570 tmp = va_arg(va, char *);
571 idtype = va_arg(va, int);
572 entry = va_arg(va, SilcClientEntry);
574 if (client_entry == conn->local_entry) {
575 if (idtype == SILC_ID_CLIENT) {
576 client_entry2 = (SilcClientEntry)entry;
577 printformat_module("fe-common/silc", server, NULL,
578 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
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_YOU,
585 server_entry->server_name, tmp ? tmp : "");
586 } else if (idtype == SILC_ID_CHANNEL) {
587 channel = (SilcChannelEntry)entry;
588 printformat_module("fe-common/silc", server, NULL,
589 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
590 channel->channel_name, tmp ? tmp : "");
593 list1 = nicklist_get_same_unique(SERVER(server), client_entry);
594 for (list_tmp = list1; list_tmp != NULL; list_tmp =
595 list_tmp->next->next) {
596 CHANNEL_REC *channel = list_tmp->data;
597 NICK_REC *nickrec = list_tmp->next->data;
598 nicklist_remove(channel, nickrec);
601 if (idtype == SILC_ID_CLIENT) {
602 client_entry2 = (SilcClientEntry)entry;
603 printformat_module("fe-common/silc", server, NULL,
604 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
605 client_entry->nickname,
606 client_entry2 ? client_entry2->nickname : "",
608 } else if (idtype == SILC_ID_SERVER) {
609 server_entry = (SilcServerEntry)entry;
610 printformat_module("fe-common/silc", server, NULL,
611 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
612 client_entry->nickname,
613 server_entry->server_name, tmp ? tmp : "");
614 } else if (idtype == SILC_ID_CHANNEL) {
615 channel = (SilcChannelEntry)entry;
616 printformat_module("fe-common/silc", server, NULL,
617 MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
618 client_entry->nickname,
619 channel->channel_name, tmp ? tmp : "");
624 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
627 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
630 * Server has quit the network.
633 SilcClientEntry *clients;
634 SilcUInt32 clients_count;
636 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
638 (void)va_arg(va, void *);
639 clients = va_arg(va, SilcClientEntry *);
640 clients_count = va_arg(va, SilcUInt32);
642 for (i = 0; i < clients_count; i++) {
643 memset(userhost, 0, sizeof(userhost));
644 if (clients[i]->username)
645 snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
646 clients[i]->username, clients[i]->hostname);
647 signal_emit("message quit", 4, server, clients[i]->nickname,
648 clients[i]->username ? userhost : "",
651 silc_server_free_ftp(server, clients[i]);
653 list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
654 for (list_tmp = list1; list_tmp != NULL; list_tmp =
655 list_tmp->next->next) {
656 CHANNEL_REC *channel = list_tmp->data;
657 NICK_REC *nickrec = list_tmp->next->data;
658 nicklist_remove(channel, nickrec);
664 case SILC_NOTIFY_TYPE_ERROR:
666 SilcStatus error = va_arg(va, int);
668 silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
669 "%s", silc_client_status_message(error));
675 printformat_module("fe-common/silc", server, NULL,
676 MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
683 /* Called to indicate that connection was either successfully established
684 or connecting failed. This is also the first time application receives
685 the SilcClientConnection object which it should save somewhere. */
687 void silc_connect(SilcClient client, SilcClientConnection conn,
688 SilcClientConnectionStatus status)
690 SILC_SERVER_REC *server = conn->context;
693 silc_client_close_connection(client, conn);
698 case SILC_CLIENT_CONN_SUCCESS:
699 /* We have successfully connected to server */
700 server->connected = TRUE;
701 signal_emit("event connected", 1, server);
704 case SILC_CLIENT_CONN_SUCCESS_RESUME:
705 /* We have successfully resumed old detached session */
706 server->connected = TRUE;
707 signal_emit("event connected", 1, server);
709 /* If we resumed old session check whether we need to update
711 if (strcmp(server->nick, conn->local_entry->nickname)) {
713 old = g_strdup(server->nick);
714 server_change_nick(SERVER(server), conn->local_entry->nickname);
715 nicklist_rename_unique(SERVER(server),
716 conn->local_entry, server->nick,
717 conn->local_entry, conn->local_entry->nickname);
718 signal_emit("message own_nick", 4, server, server->nick, old, "");
724 server->connection_lost = TRUE;
726 server->conn->context = NULL;
727 server_disconnect(SERVER(server));
732 /* Called to indicate that connection was disconnected to the server. */
734 void silc_disconnect(SilcClient client, SilcClientConnection conn)
736 SILC_SERVER_REC *server = conn->context;
738 SILC_LOG_DEBUG(("Start"));
740 if (!server || server->connection_lost)
743 if (server->conn && server->conn->local_entry) {
744 nicklist_rename_unique(SERVER(server),
745 server->conn->local_entry, server->nick,
746 server->conn->local_entry,
747 silc_client->username);
748 silc_change_nick(server, silc_client->username);
751 server->conn->context = NULL;
753 server->connection_lost = TRUE;
754 server_disconnect(SERVER(server));
757 /* Command handler. This function is called always in the command function.
758 If error occurs it will be called as well. `conn' is the associated
759 client connection. `cmd_context' is the command context that was
760 originally sent to the command. `success' is FALSE if error occured
761 during command. `command' is the command being processed. It must be
762 noted that this is not reply from server. This is merely called just
763 after application has called the command. Just to tell application
764 that the command really was processed. */
766 void silc_command(SilcClient client, SilcClientConnection conn,
767 SilcClientCommandContext cmd_context, int success,
770 SILC_SERVER_REC *server = conn->context;
772 SILC_LOG_DEBUG(("Start"));
778 case SILC_COMMAND_INVITE:
779 printformat_module("fe-common/silc", server, NULL,
780 MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
781 cmd_context->argv[2],
782 (cmd_context->argv[1][0] == '*' ?
783 (char *)conn->current_channel->channel_name :
784 (char *)cmd_context->argv[1]));
791 /* Client info resolving callback when JOIN command reply is received.
792 This will cache all users on the channel. */
794 static void silc_client_join_get_users(SilcClient client,
795 SilcClientConnection conn,
796 SilcClientEntry *clients,
797 SilcUInt32 clients_count,
800 SilcChannelEntry channel = (SilcChannelEntry)context;
801 SilcHashTableList htl;
803 SILC_SERVER_REC *server = conn->context;
804 SILC_CHANNEL_REC *chanrec;
805 SilcClientEntry founder = NULL;
808 SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
809 silc_hash_table_count(channel->user_list)));
814 chanrec = silc_channel_find(server, channel->channel_name);
818 silc_hash_table_list(channel->user_list, &htl);
819 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
820 if (!chu->client->nickname)
822 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
823 founder = chu->client;
824 silc_nicklist_insert(chanrec, chu, FALSE);
826 silc_hash_table_list_reset(&htl);
828 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
829 nicklist_set_own(CHANNEL(chanrec), ownnick);
830 signal_emit("channel joined", 1, chanrec);
831 chanrec->entry = channel;
834 printformat_module("fe-common/silc", server, channel->channel_name,
835 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
836 channel->channel_name, chanrec->topic);
838 fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
841 if (founder == conn->local_entry)
842 printformat_module("fe-common/silc",
843 server, channel->channel_name, MSGLEVEL_CRAP,
844 SILCTXT_CHANNEL_FOUNDER_YOU,
845 channel->channel_name);
847 printformat_module("fe-common/silc",
848 server, channel->channel_name, MSGLEVEL_CRAP,
849 SILCTXT_CHANNEL_FOUNDER,
850 channel->channel_name, founder->nickname);
856 SilcClientConnection conn;
862 void silc_getkey_cb(bool success, void *context)
864 GetkeyContext getkey = (GetkeyContext)context;
865 char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
866 char *name = (getkey->id_type == SILC_ID_CLIENT ?
867 ((SilcClientEntry)getkey->entry)->nickname :
868 ((SilcServerEntry)getkey->entry)->server_name);
871 printformat_module("fe-common/silc", NULL, NULL,
872 MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
874 printformat_module("fe-common/silc", NULL, NULL,
875 MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
878 silc_free(getkey->fingerprint);
882 /* Command reply handler. This function is called always in the command reply
883 function. If error occurs it will be called as well. Normal scenario
884 is that it will be called after the received command data has been parsed
885 and processed. The function is used to pass the received command data to
888 `conn' is the associated client connection. `cmd_payload' is the command
889 payload data received from server and it can be ignored. It is provided
890 if the application would like to re-parse the received command data,
891 however, it must be noted that the data is parsed already by the library
892 thus the payload can be ignored. `success' is FALSE if error occured.
893 In this case arguments are not sent to the application. `command' is the
894 command reply being processed. The function has variable argument list
895 and each command defines the number and type of arguments it passes to the
896 application (on error they are not sent). */
899 silc_command_reply(SilcClient client, SilcClientConnection conn,
900 SilcCommandPayload cmd_payload, int success,
901 SilcCommand command, SilcStatus status, ...)
904 SILC_SERVER_REC *server = conn->context;
905 SILC_CHANNEL_REC *chanrec;
908 va_start(vp, status);
910 SILC_LOG_DEBUG(("Start"));
913 case SILC_COMMAND_WHOIS:
915 char buf[1024], *nickname, *username, *realname, *nick;
916 unsigned char *fingerprint;
917 SilcUInt32 idle, mode;
918 SilcBuffer channels, user_modes;
919 SilcClientEntry client_entry;
921 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
922 /* Print the unknown nick for user */
924 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
927 silc_say_error("%s: %s", tmp,
928 silc_client_status_message(status));
930 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
931 /* Try to find the entry for the unknown client ID, since we
932 might have, and print the nickname of it for user. */
935 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
938 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
941 client_entry = silc_client_get_client_by_id(client, conn,
943 if (client_entry && client_entry->nickname)
944 silc_say_error("%s: %s", client_entry->nickname,
945 silc_client_status_message(status));
946 silc_free(client_id);
955 client_entry = va_arg(vp, SilcClientEntry);
956 nickname = va_arg(vp, char *);
957 username = va_arg(vp, char *);
958 realname = va_arg(vp, char *);
959 channels = va_arg(vp, SilcBuffer);
960 mode = va_arg(vp, SilcUInt32);
961 idle = va_arg(vp, SilcUInt32);
962 fingerprint = va_arg(vp, unsigned char *);
963 user_modes = va_arg(vp, SilcBuffer);
965 silc_parse_userfqdn(nickname, &nick, NULL);
966 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
967 SILCTXT_WHOIS_USERINFO, nickname,
968 client_entry->username, client_entry->hostname,
969 nick, client_entry->nickname);
970 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
971 SILCTXT_WHOIS_REALNAME, realname);
974 if (channels && user_modes) {
976 SilcDList list = silc_channel_payload_parse_list(channels->data,
978 if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
980 SilcChannelPayload entry;
983 memset(buf, 0, sizeof(buf));
984 silc_dlist_start(list);
985 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
987 char *m = silc_client_chumode_char(umodes[i++]);
988 char *name = silc_channel_get_name(entry, &name_len);
991 strncat(buf, m, strlen(m));
992 strncat(buf, name, name_len);
993 strncat(buf, " ", 1);
997 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
998 SILCTXT_WHOIS_CHANNELS, buf);
999 silc_channel_payload_list_free(list);
1005 memset(buf, 0, sizeof(buf));
1007 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
1008 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
1009 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
1011 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
1012 "SILC Operator" : "[Unknown mode]");
1014 if (mode & SILC_UMODE_GONE)
1015 strcat(buf, " [away]");
1016 if (mode & SILC_UMODE_INDISPOSED)
1017 strcat(buf, " [indisposed]");
1018 if (mode & SILC_UMODE_BUSY)
1019 strcat(buf, " [busy]");
1020 if (mode & SILC_UMODE_PAGE)
1021 strcat(buf, " [page to reach]");
1022 if (mode & SILC_UMODE_HYPER)
1023 strcat(buf, " [hyper active]");
1024 if (mode & SILC_UMODE_ROBOT)
1025 strcat(buf, " [robot]");
1026 if (mode & SILC_UMODE_ANONYMOUS)
1027 strcat(buf, " [anonymous]");
1028 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
1029 strcat(buf, " [blocks private messages]");
1030 if (mode & SILC_UMODE_DETACHED)
1031 strcat(buf, " [detached]");
1033 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1034 SILCTXT_WHOIS_MODES, buf);
1037 if (idle && nickname) {
1038 memset(buf, 0, sizeof(buf));
1039 snprintf(buf, sizeof(buf) - 1, "%lu %s",
1040 idle > 60 ? (idle / 60) : idle,
1041 idle > 60 ? "minutes" : "seconds");
1043 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1044 SILCTXT_WHOIS_IDLE, buf);
1048 fingerprint = silc_fingerprint(fingerprint, 20);
1049 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1050 SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1051 silc_free(fingerprint);
1056 case SILC_COMMAND_IDENTIFY:
1058 SilcClientEntry client_entry;
1060 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1061 /* Print the unknown nick for user */
1062 unsigned char *tmp =
1063 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1066 silc_say_error("%s: %s", tmp,
1067 silc_client_status_message(status));
1069 } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1070 /* Try to find the entry for the unknown client ID, since we
1071 might have, and print the nickname of it for user. */
1073 unsigned char *tmp =
1074 silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1077 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1080 client_entry = silc_client_get_client_by_id(client, conn,
1082 if (client_entry && client_entry->nickname)
1083 silc_say_error("%s: %s", client_entry->nickname,
1084 silc_client_status_message(status));
1085 silc_free(client_id);
1094 case SILC_COMMAND_WHOWAS:
1096 char *nickname, *username, *realname;
1098 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1099 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1101 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1104 silc_say_error("%s: %s", tmp,
1105 silc_client_status_message(status));
1112 (void)va_arg(vp, SilcClientEntry);
1113 nickname = va_arg(vp, char *);
1114 username = va_arg(vp, char *);
1115 realname = va_arg(vp, char *);
1117 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1118 SILCTXT_WHOWAS_USERINFO, nickname, username,
1119 realname ? realname : "");
1123 case SILC_COMMAND_INVITE:
1125 SilcChannelEntry channel;
1127 SilcArgumentPayload args;
1133 channel = va_arg(vp, SilcChannelEntry);
1134 invite_list = va_arg(vp, char *);
1136 args = silc_command_get_args(cmd_payload);
1138 argc = silc_argument_get_arg_num(args);
1141 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1142 SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1145 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1146 SILCTXT_CHANNEL_NO_INVITE_LIST,
1147 channel->channel_name);
1151 case SILC_COMMAND_JOIN:
1153 char *channel, *mode, *topic;
1155 SilcChannelEntry channel_entry;
1156 SilcBuffer client_id_list;
1157 SilcUInt32 list_count;
1162 channel = va_arg(vp, char *);
1163 channel_entry = va_arg(vp, SilcChannelEntry);
1164 modei = va_arg(vp, SilcUInt32);
1165 (void)va_arg(vp, SilcUInt32);
1166 (void)va_arg(vp, unsigned char *);
1167 (void)va_arg(vp, unsigned char *);
1168 (void)va_arg(vp, unsigned char *);
1169 topic = va_arg(vp, char *);
1170 (void)va_arg(vp, unsigned char *);
1171 list_count = va_arg(vp, SilcUInt32);
1172 client_id_list = va_arg(vp, SilcBuffer);
1174 chanrec = silc_channel_find(server, channel);
1176 chanrec = silc_channel_create(server, channel, TRUE);
1179 g_free_not_null(chanrec->topic);
1180 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1181 signal_emit("channel topic changed", 1, chanrec);
1184 mode = silc_client_chmode(modei,
1185 channel_entry->channel_key ?
1186 channel_entry->channel_key->cipher->name : "",
1187 channel_entry->hmac ?
1188 silc_hmac_get_name(channel_entry->hmac) : "");
1189 g_free_not_null(chanrec->mode);
1190 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1191 signal_emit("channel mode changed", 1, chanrec);
1193 /* Resolve the client information */
1194 silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1195 silc_client_join_get_users,
1201 case SILC_COMMAND_NICK:
1203 SilcClientEntry client = va_arg(vp, SilcClientEntry);
1209 old = g_strdup(server->nick);
1210 server_change_nick(SERVER(server), client->nickname);
1211 nicklist_rename_unique(SERVER(server),
1212 server->conn->local_entry, server->nick,
1213 client, client->nickname);
1214 signal_emit("message own_nick", 4, server, server->nick, old, "");
1219 case SILC_COMMAND_LIST:
1228 (void)va_arg(vp, SilcChannelEntry);
1229 name = va_arg(vp, char *);
1230 topic = va_arg(vp, char *);
1231 usercount = va_arg(vp, int);
1233 if (status == SILC_STATUS_LIST_START ||
1234 status == SILC_STATUS_OK)
1235 printformat_module("fe-common/silc", server, NULL,
1236 MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1239 snprintf(users, sizeof(users) - 1, "N/A");
1241 snprintf(users, sizeof(users) - 1, "%d", usercount);
1242 printformat_module("fe-common/silc", server, NULL,
1243 MSGLEVEL_CRAP, SILCTXT_LIST,
1244 name, users, topic ? topic : "");
1248 case SILC_COMMAND_UMODE:
1255 mode = va_arg(vp, SilcUInt32);
1257 if (mode & SILC_UMODE_SERVER_OPERATOR &&
1258 !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1259 printformat_module("fe-common/silc", server, NULL,
1260 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1262 if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1263 !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1264 printformat_module("fe-common/silc", server, NULL,
1265 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1267 server->umode = mode;
1268 signal_emit("user mode changed", 2, server, NULL);
1272 case SILC_COMMAND_OPER:
1276 server->umode |= SILC_UMODE_SERVER_OPERATOR;
1277 signal_emit("user mode changed", 2, server, NULL);
1279 printformat_module("fe-common/silc", server, NULL,
1280 MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1283 case SILC_COMMAND_SILCOPER:
1287 server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1288 signal_emit("user mode changed", 2, server, NULL);
1290 printformat_module("fe-common/silc", server, NULL,
1291 MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1294 case SILC_COMMAND_USERS:
1296 SilcHashTableList htl;
1297 SilcChannelEntry channel;
1298 SilcChannelUser chu;
1303 channel = va_arg(vp, SilcChannelEntry);
1305 printformat_module("fe-common/silc", server, channel->channel_name,
1306 MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1307 channel->channel_name);
1309 silc_hash_table_list(channel->user_list, &htl);
1310 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1311 SilcClientEntry e = chu->client;
1312 char stat[5], *mode;
1317 memset(stat, 0, sizeof(stat));
1318 mode = silc_client_chumode_char(chu->mode);
1319 if (e->mode & SILC_UMODE_GONE)
1321 else if (e->mode & SILC_UMODE_INDISPOSED)
1323 else if (e->mode & SILC_UMODE_BUSY)
1325 else if (e->mode & SILC_UMODE_PAGE)
1327 else if (e->mode & SILC_UMODE_HYPER)
1329 else if (e->mode & SILC_UMODE_ROBOT)
1331 else if (e->mode & SILC_UMODE_ANONYMOUS)
1338 printformat_module("fe-common/silc", server, channel->channel_name,
1339 MSGLEVEL_CRAP, SILCTXT_USERS,
1341 e->username ? e->username : "",
1342 e->hostname ? e->hostname : "",
1343 e->realname ? e->realname : "");
1347 silc_hash_table_list_reset(&htl);
1351 case SILC_COMMAND_BAN:
1353 SilcChannelEntry channel;
1359 channel = va_arg(vp, SilcChannelEntry);
1360 ban_list = va_arg(vp, char *);
1363 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1364 SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1367 printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1368 SILCTXT_CHANNEL_NO_BAN_LIST,
1369 channel->channel_name);
1373 case SILC_COMMAND_GETKEY:
1377 SilcPublicKey public_key;
1380 GetkeyContext getkey;
1386 id_type = va_arg(vp, SilcUInt32);
1387 entry = va_arg(vp, void *);
1388 public_key = va_arg(vp, SilcPublicKey);
1391 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1393 getkey = silc_calloc(1, sizeof(*getkey));
1394 getkey->entry = entry;
1395 getkey->id_type = id_type;
1396 getkey->client = client;
1397 getkey->conn = conn;
1398 getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1400 name = (id_type == SILC_ID_CLIENT ?
1401 ((SilcClientEntry)entry)->nickname :
1402 ((SilcServerEntry)entry)->server_name);
1404 silc_verify_public_key_internal(client, conn, name,
1405 (id_type == SILC_ID_CLIENT ?
1406 SILC_SOCKET_TYPE_CLIENT :
1407 SILC_SOCKET_TYPE_SERVER),
1408 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1409 silc_getkey_cb, getkey);
1412 printformat_module("fe-common/silc", server, NULL,
1413 MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1418 case SILC_COMMAND_INFO:
1420 SilcServerEntry server_entry;
1427 server_entry = va_arg(vp, SilcServerEntry);
1428 server_name = va_arg(vp, char *);
1429 server_info = va_arg(vp, char *);
1431 if (server_name && server_info )
1433 printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1434 printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1439 case SILC_COMMAND_TOPIC:
1441 SilcChannelEntry channel;
1447 channel = va_arg(vp, SilcChannelEntry);
1448 topic = va_arg(vp, char *);
1451 chanrec = silc_channel_find_entry(server, channel);
1453 g_free_not_null(chanrec->topic);
1454 chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1455 signal_emit("channel topic changed", 1, chanrec);
1457 printformat_module("fe-common/silc", server, channel->channel_name,
1458 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1459 channel->channel_name, topic);
1461 printformat_module("fe-common/silc", server, channel->channel_name,
1462 MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1463 channel->channel_name);
1475 SilcClientConnection conn;
1481 SilcSKEPKType pk_type;
1482 SilcVerifyPublicKey completion;
1486 static void verify_public_key_completion(const char *line, void *context)
1488 PublicKeyVerify verify = (PublicKeyVerify)context;
1490 if (line[0] == 'Y' || line[0] == 'y') {
1491 /* Call the completion */
1492 if (verify->completion)
1493 verify->completion(TRUE, verify->context);
1495 /* Save the key for future checking */
1496 silc_pkcs_save_public_key_data(verify->filename, verify->pk,
1497 verify->pk_len, SILC_PKCS_FILE_PEM);
1499 /* Call the completion */
1500 if (verify->completion)
1501 verify->completion(FALSE, verify->context);
1503 printformat_module("fe-common/silc", NULL, NULL,
1504 MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
1505 verify->entity_name ? verify->entity_name :
1509 silc_free(verify->filename);
1510 silc_free(verify->entity);
1511 silc_free(verify->entity_name);
1512 silc_free(verify->pk);
1516 /* Internal routine to verify public key. If the `completion' is provided
1517 it will be called to indicate whether public was verified or not. For
1518 server/router public key this will check for filename that includes the
1519 remote host's IP address and remote host's hostname. */
1522 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1523 const char *name, SilcSocketType conn_type,
1524 unsigned char *pk, SilcUInt32 pk_len,
1525 SilcSKEPKType pk_type,
1526 SilcVerifyPublicKey completion, void *context)
1529 char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1530 char *fingerprint, *babbleprint, *format;
1533 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1534 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1535 "server" : "client");
1536 PublicKeyVerify verify;
1538 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1539 printformat_module("fe-common/silc", NULL, NULL,
1540 MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
1543 completion(FALSE, context);
1547 pw = getpwuid(getuid());
1550 completion(FALSE, context);
1554 memset(filename, 0, sizeof(filename));
1555 memset(filename2, 0, sizeof(filename2));
1556 memset(file, 0, sizeof(file));
1558 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1559 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1561 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1562 conn->sock->ip, conn->sock->port);
1563 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1564 get_irssi_dir(), entity, file);
1566 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1567 conn->sock->hostname, conn->sock->port);
1568 snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
1569 get_irssi_dir(), entity, file);
1574 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1575 name, conn->sock->port);
1576 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1577 get_irssi_dir(), entity, file);
1582 /* Replace all whitespaces with `_'. */
1583 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1584 for (i = 0; i < strlen(fingerprint); i++)
1585 if (fingerprint[i] == ' ')
1586 fingerprint[i] = '_';
1588 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1589 snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
1590 get_irssi_dir(), entity, file);
1591 silc_free(fingerprint);
1596 /* Take fingerprint of the public key */
1597 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1598 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1600 verify = silc_calloc(1, sizeof(*verify));
1601 verify->client = client;
1602 verify->conn = conn;
1603 verify->filename = strdup(ipf);
1604 verify->entity = strdup(entity);
1605 verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1606 (name ? strdup(name) : strdup(conn->sock->hostname))
1608 verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1609 memcpy(verify->pk, pk, pk_len);
1610 verify->pk_len = pk_len;
1611 verify->pk_type = pk_type;
1612 verify->completion = completion;
1613 verify->context = context;
1615 /* Check whether this key already exists */
1616 if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1617 /* Key does not exist, ask user to verify the key and save it */
1619 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1620 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1621 verify->entity_name : entity);
1622 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1623 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1624 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1625 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1626 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1627 SILCTXT_PUBKEY_ACCEPT);
1628 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1631 silc_free(fingerprint);
1634 /* The key already exists, verify it. */
1635 SilcPublicKey public_key;
1636 unsigned char *encpk;
1637 SilcUInt32 encpk_len;
1639 /* Load the key file, try for both IP filename and hostname filename */
1640 if (!silc_pkcs_load_public_key(ipf, &public_key,
1641 SILC_PKCS_FILE_PEM) &&
1642 !silc_pkcs_load_public_key(ipf, &public_key,
1643 SILC_PKCS_FILE_BIN) &&
1644 (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
1645 SILC_PKCS_FILE_PEM) &&
1646 !silc_pkcs_load_public_key(hostf, &public_key,
1647 SILC_PKCS_FILE_BIN)))) {
1648 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1649 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1650 verify->entity_name : entity);
1651 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1652 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1653 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1654 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1655 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1656 SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1657 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1658 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1659 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1662 silc_free(fingerprint);
1666 /* Encode the key data */
1667 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1669 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1670 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1671 verify->entity_name : entity);
1672 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1673 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1674 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1675 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1676 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1677 SILCTXT_PUBKEY_MALFORMED, entity);
1678 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1679 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1680 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1683 silc_free(fingerprint);
1687 /* Compare the keys */
1688 if (memcmp(encpk, pk, encpk_len)) {
1689 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1690 SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
1691 verify->entity_name : entity);
1692 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1693 SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1694 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1695 SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1696 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1697 SILCTXT_PUBKEY_NO_MATCH, entity);
1698 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1699 SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1700 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1701 SILCTXT_PUBKEY_MITM_ATTACK, entity);
1703 /* Ask user to verify the key and save it */
1704 format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1705 SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1706 keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1709 silc_free(fingerprint);
1713 /* Local copy matched */
1715 completion(TRUE, context);
1716 silc_free(fingerprint);
1720 /* Verifies received public key. The `conn_type' indicates which entity
1721 (server, client etc.) has sent the public key. If user decides to trust
1722 the key may be saved as trusted public key for later use. The
1723 `completion' must be called after the public key has been verified. */
1726 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1727 SilcSocketType conn_type, unsigned char *pk,
1728 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1729 SilcVerifyPublicKey completion, void *context)
1731 silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1733 completion, context);
1736 /* Asks passphrase from user on the input line. */
1739 SilcAskPassphrase completion;
1743 void ask_passphrase_completion(const char *passphrase, void *context)
1745 AskPassphrase p = (AskPassphrase)context;
1746 p->completion((unsigned char *)passphrase,
1747 passphrase ? strlen(passphrase) : 0, p->context);
1751 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1752 SilcAskPassphrase completion, void *context)
1754 AskPassphrase p = silc_calloc(1, sizeof(*p));
1755 p->completion = completion;
1756 p->context = context;
1758 keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1759 "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1763 SilcGetAuthMeth completion;
1765 } *InternalGetAuthMethod;
1767 /* Callback called when we've received the authentication method information
1768 from the server after we've requested it. This will get the authentication
1769 data from the user if needed. */
1771 static void silc_get_auth_method_callback(SilcClient client,
1772 SilcClientConnection conn,
1773 SilcAuthMethod auth_meth,
1776 InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1778 SILC_LOG_DEBUG(("Start"));
1780 switch (auth_meth) {
1781 case SILC_AUTH_NONE:
1782 /* No authentication required. */
1783 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1785 case SILC_AUTH_PASSWORD:
1786 /* Do not ask the passphrase from user, the library will ask it if
1787 we do not provide it here. */
1788 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1790 case SILC_AUTH_PUBLIC_KEY:
1791 /* Do not get the authentication data now, the library will generate
1792 it using our default key, if we do not provide it here. */
1793 /* XXX In the future when we support multiple local keys and multiple
1794 local certificates we will need to ask from user which one to use. */
1795 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1799 silc_free(internal);
1802 /* Find authentication method and authentication data by hostname and
1803 port. The hostname may be IP address as well. The found authentication
1804 method and authentication data is returned to `auth_meth', `auth_data'
1805 and `auth_data_len'. The function returns TRUE if authentication method
1806 is found and FALSE if not. `conn' may be NULL. */
1808 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1809 char *hostname, SilcUInt16 port,
1810 SilcGetAuthMeth completion, void *context)
1812 InternalGetAuthMethod internal;
1814 SILC_LOG_DEBUG(("Start"));
1816 /* XXX must resolve from configuration whether this connection has
1817 any specific authentication data */
1819 /* If we do not have this connection configured by the user in a
1820 configuration file then resolve the authentication method from the
1821 server for this session. */
1822 internal = silc_calloc(1, sizeof(*internal));
1823 internal->completion = completion;
1824 internal->context = context;
1826 silc_client_request_authentication_method(client, conn,
1827 silc_get_auth_method_callback,
1831 /* Notifies application that failure packet was received. This is called
1832 if there is some protocol active in the client. The `protocol' is the
1833 protocol context. The `failure' is opaque pointer to the failure
1834 indication. Note, that the `failure' is protocol dependant and application
1835 must explicitly cast it to correct type. Usually `failure' is 32 bit
1836 failure type (see protocol specs for all protocol failure types). */
1838 void silc_failure(SilcClient client, SilcClientConnection conn,
1839 SilcProtocol protocol, void *failure)
1841 SILC_LOG_DEBUG(("Start"));
1843 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1844 SilcSKEStatus status = (SilcSKEStatus)failure;
1846 if (status == SILC_SKE_STATUS_BAD_VERSION)
1847 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1848 SILCTXT_KE_BAD_VERSION);
1849 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1850 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1851 SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1852 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1853 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1854 SILCTXT_KE_UNKNOWN_GROUP);
1855 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1856 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1857 SILCTXT_KE_UNKNOWN_CIPHER);
1858 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1859 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1860 SILCTXT_KE_UNKNOWN_PKCS);
1861 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1862 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1863 SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1864 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1865 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1866 SILCTXT_KE_UNKNOWN_HMAC);
1867 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1868 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1869 SILCTXT_KE_INCORRECT_SIGNATURE);
1870 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1871 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1872 SILCTXT_KE_INVALID_COOKIE);
1875 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1876 SilcUInt32 err = (SilcUInt32)failure;
1878 if (err == SILC_AUTH_FAILED)
1879 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1880 SILCTXT_AUTH_FAILED);
1884 /* Asks whether the user would like to perform the key agreement protocol.
1885 This is called after we have received an key agreement packet or an
1886 reply to our key agreement packet. This returns TRUE if the user wants
1887 the library to perform the key agreement protocol and FALSE if it is not
1888 desired (application may start it later by calling the function
1889 silc_client_perform_key_agreement). */
1891 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1892 SilcClientEntry client_entry, const char *hostname,
1893 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1898 SILC_LOG_DEBUG(("Start"));
1900 /* We will just display the info on the screen and return FALSE and user
1901 will have to start the key agreement with a command. */
1904 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1907 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1908 SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1910 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1911 SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
1912 client_entry->nickname, hostname, portstr);
1920 /* Notifies application that file transfer protocol session is being
1921 requested by the remote client indicated by the `client_entry' from
1922 the `hostname' and `port'. The `session_id' is the file transfer
1923 session and it can be used to either accept or reject the file
1924 transfer request, by calling the silc_client_file_receive or
1925 silc_client_file_close, respectively. */
1927 void silc_ftp(SilcClient client, SilcClientConnection conn,
1928 SilcClientEntry client_entry, SilcUInt32 session_id,
1929 const char *hostname, SilcUInt16 port)
1931 SILC_SERVER_REC *server;
1933 FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1935 SILC_LOG_DEBUG(("Start"));
1937 server = conn->context;
1939 ftp->client_entry = client_entry;
1940 ftp->session_id = session_id;
1943 silc_dlist_add(server->ftp_sessions, ftp);
1944 server->current_session = ftp;
1947 snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1950 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1951 SILCTXT_FILE_REQUEST, client_entry->nickname);
1953 printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1954 SILCTXT_FILE_REQUEST_HOST,
1955 client_entry->nickname, hostname, portstr);
1958 /* Delivers SILC session detachment data indicated by `detach_data' to the
1959 application. If application has issued SILC_COMMAND_DETACH command
1960 the client session in the SILC network is not quit. The client remains
1961 in the network but is detached. The detachment data may be used later
1962 to resume the session in the SILC Network. The appliation is
1963 responsible of saving the `detach_data', to for example in a file.
1965 The detachment data can be given as argument to the functions
1966 silc_client_connect_to_server, or silc_client_add_connection when
1967 creating connection to remote server, inside SilcClientConnectionParams
1968 structure. If it is provided the client library will attempt to resume
1969 the session in the network. After the connection is created
1970 successfully, the application is responsible of setting the user
1971 interface for user into the same state it was before detaching (showing
1972 same channels, channel modes, etc). It can do this by fetching the
1973 information (like joined channels) from the client library. */
1976 silc_detach(SilcClient client, SilcClientConnection conn,
1977 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1981 /* Save the detachment data to file. */
1983 memset(file, 0, sizeof(file));
1984 snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
1985 silc_file_writefile(file, detach_data, detach_data_len);
1989 /* SILC client operations */
1990 SilcClientOperations ops = {
1992 silc_channel_message,
1993 silc_private_message,
1999 silc_get_auth_method,
2000 silc_verify_public_key,
2001 silc_ask_passphrase,