5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2000 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.
21 #include "clientincludes.h"
23 /* Prints a message with three star (*) sign before the actual message
24 on the current output window. This is used to print command outputs
25 and error messages. */
27 void silc_say(SilcClient client, SilcClientConnection conn,
32 SilcClientInternal app = (SilcClientInternal)client->application;
34 memset(message, 0, sizeof(message));
35 strncat(message, "\n*** ", 5);
38 vsprintf(message + 5, msg, vp);
41 /* Print the message */
42 silc_print_to_window(app->screen->output_win[0], message);
45 /* Message for a channel. The `sender' is the nickname of the sender
46 received in the packet. The `channel_name' is the name of the channel. */
48 void silc_channel_message(SilcClient client, SilcClientConnection conn,
49 SilcClientEntry sender, SilcChannelEntry channel,
50 SilcMessageFlags flags, char *msg)
52 /* Message from client */
53 if (conn && !strcmp(conn->current_channel->channel_name,
54 channel->channel_name))
55 if (flags & SILC_MESSAGE_FLAG_ACTION)
56 silc_print(client, "* %s %s", sender ? sender->nickname : "[<unknown>]",
58 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
59 silc_print(client, "- %s %s", sender ? sender->nickname : "[<unknown>]",
62 silc_print(client, "<%s> %s", sender ? sender->nickname : "[<unknown>]",
65 if (flags & SILC_MESSAGE_FLAG_ACTION)
66 silc_print(client, "* %s:%s %s", sender ? sender->nickname :
68 channel->channel_name, msg);
69 else if (flags & SILC_MESSAGE_FLAG_NOTICE)
70 silc_print(client, "- %s:%s %s", sender ? sender->nickname :
72 channel->channel_name, msg);
74 silc_print(client, "<%s:%s> %s", sender ? sender->nickname :
76 channel->channel_name, msg);
79 /* Private message to the client. The `sender' is the nickname of the
80 sender received in the packet. */
82 void silc_private_message(SilcClient client, SilcClientConnection conn,
83 SilcClientEntry sender, SilcMessageFlags flags,
86 silc_print(client, "*%s* %s", sender->nickname, msg);
90 /* Notify message to the client. The notify arguments are sent in the
91 same order as servers sends them. The arguments are same as received
92 from the server except for ID's. If ID is received application receives
93 the corresponding entry to the ID. For example, if Client ID is received
94 application receives SilcClientEntry. Also, if the notify type is
95 for channel the channel entry is sent to application (even if server
98 void silc_notify(SilcClient client, SilcClientConnection conn,
99 SilcNotifyType type, ...)
101 SilcClientInternal app = (SilcClientInternal)client->application;
104 SilcClientEntry client_entry, client_entry2;
105 SilcChannelEntry channel_entry;
111 memset(message, 0, sizeof(message));
113 /* Get arguments (defined by protocol in silc-pp-01 -draft) */
115 case SILC_NOTIFY_TYPE_NONE:
116 tmp = va_arg(vp, char *);
119 strcpy(message, tmp);
122 case SILC_NOTIFY_TYPE_INVITE:
123 (void)va_arg(vp, SilcChannelEntry);
124 tmp = va_arg(vp, char *);
125 client_entry = va_arg(vp, SilcClientEntry);
126 snprintf(message, sizeof(message), "%s invites you to channel %s",
127 client_entry->nickname, tmp);
130 case SILC_NOTIFY_TYPE_JOIN:
131 client_entry = va_arg(vp, SilcClientEntry);
132 channel_entry = va_arg(vp, SilcChannelEntry);
133 snprintf(message, sizeof(message), "%s (%s) has joined channel %s",
134 client_entry->nickname, client_entry->username,
135 channel_entry->channel_name);
136 if (client_entry == conn->local_entry) {
139 silc_list_start(channel_entry->clients);
140 while ((chu = silc_list_get(channel_entry->clients)) != SILC_LIST_END) {
141 if (chu->client == client_entry) {
142 if (app->screen->bottom_line->mode)
143 silc_free(app->screen->bottom_line->mode);
144 app->screen->bottom_line->mode = silc_client_chumode_char(chu->mode);
145 silc_screen_print_bottom_line(app->screen, 0);
152 case SILC_NOTIFY_TYPE_LEAVE:
153 client_entry = va_arg(vp, SilcClientEntry);
154 channel_entry = va_arg(vp, SilcChannelEntry);
155 if (client_entry->server)
156 snprintf(message, sizeof(message), "%s@%s has left channel %s",
157 client_entry->nickname, client_entry->server,
158 channel_entry->channel_name);
160 snprintf(message, sizeof(message), "%s has left channel %s",
161 client_entry->nickname, channel_entry->channel_name);
164 case SILC_NOTIFY_TYPE_SIGNOFF:
165 client_entry = va_arg(vp, SilcClientEntry);
166 tmp = va_arg(vp, char *);
167 if (client_entry->server)
168 snprintf(message, sizeof(message), "Signoff: %s@%s %s%s%s",
169 client_entry->nickname, client_entry->server,
170 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
172 snprintf(message, sizeof(message), "Signoff: %s %s%s%s",
173 client_entry->nickname,
174 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
177 case SILC_NOTIFY_TYPE_TOPIC_SET:
178 client_entry = va_arg(vp, SilcClientEntry);
179 tmp = va_arg(vp, char *);
180 channel_entry = va_arg(vp, SilcChannelEntry);
181 if (client_entry->server)
182 snprintf(message, sizeof(message), "%s@%s set topic on %s: %s",
183 client_entry->nickname, client_entry->server,
184 channel_entry->channel_name, tmp);
186 snprintf(message, sizeof(message), "%s set topic on %s: %s",
187 client_entry->nickname, channel_entry->channel_name, tmp);
190 case SILC_NOTIFY_TYPE_NICK_CHANGE:
191 client_entry = va_arg(vp, SilcClientEntry);
192 client_entry2 = va_arg(vp, SilcClientEntry);
193 if (client_entry->server && client_entry2->server)
194 snprintf(message, sizeof(message), "%s@%s is known as %s@%s",
195 client_entry->nickname, client_entry->server,
196 client_entry2->nickname, client_entry2->server);
198 snprintf(message, sizeof(message), "%s is known as %s",
199 client_entry->nickname, client_entry2->nickname);
202 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
203 client_entry = va_arg(vp, SilcClientEntry);
204 tmp_int = va_arg(vp, uint32);
205 (void)va_arg(vp, char *);
206 (void)va_arg(vp, char *);
207 channel_entry = va_arg(vp, SilcChannelEntry);
209 tmp = silc_client_chmode(tmp_int, channel_entry);
213 snprintf(message, sizeof(message), "%s changed channel mode to +%s",
214 client_entry->nickname, tmp);
216 snprintf(message, sizeof(message),
217 "channel mode was changed to +%s (forced by router)",
222 snprintf(message, sizeof(message), "%s removed all channel modes",
223 client_entry->nickname);
225 snprintf(message, sizeof(message),
226 "Removed all channel modes (forced by router)");
230 if (app->screen->bottom_line->channel_mode)
231 silc_free(app->screen->bottom_line->channel_mode);
232 app->screen->bottom_line->channel_mode = tmp;
233 silc_screen_print_bottom_line(app->screen, 0);
236 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
237 client_entry = va_arg(vp, SilcClientEntry);
238 tmp_int = va_arg(vp, uint32);
239 tmp = silc_client_chumode(tmp_int);
240 client_entry2 = va_arg(vp, SilcClientEntry);
241 channel_entry = va_arg(vp, SilcChannelEntry);
243 snprintf(message, sizeof(message), "%s changed %s's mode to +%s",
244 client_entry->nickname, client_entry2->nickname, tmp);
246 snprintf(message, sizeof(message), "%s removed %s's modes",
247 client_entry->nickname, client_entry2->nickname);
248 if (client_entry2 == conn->local_entry) {
249 if (app->screen->bottom_line->mode)
250 silc_free(app->screen->bottom_line->mode);
251 app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
252 silc_screen_print_bottom_line(app->screen, 0);
257 case SILC_NOTIFY_TYPE_MOTD:
261 tmp = va_arg(vp, unsigned char *);
265 if (tmp[i++] == '\n') {
266 memset(line, 0, sizeof(line));
267 strncat(line, tmp, i - 1);
270 silc_say(client, conn, "%s", line);
280 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
284 case SILC_NOTIFY_TYPE_KICKED:
285 client_entry = va_arg(vp, SilcClientEntry);
286 tmp = va_arg(vp, char *);
287 channel_entry = va_arg(vp, SilcChannelEntry);
289 if (client_entry == conn->local_entry) {
290 snprintf(message, sizeof(message),
291 "You have been kicked off channel %s %s%s%s",
292 conn->current_channel->channel_name,
293 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
295 snprintf(message, sizeof(message),
296 "%s%s%s has been kicked off channel %s %s%s%s",
297 client_entry->nickname,
298 client_entry->server ? "@" : "",
299 client_entry->server ? client_entry->server : "",
300 conn->current_channel->channel_name,
301 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
305 case SILC_NOTIFY_TYPE_KILLED:
306 client_entry = va_arg(vp, SilcClientEntry);
307 tmp = va_arg(vp, char *);
308 channel_entry = va_arg(vp, SilcChannelEntry);
310 if (client_entry == conn->local_entry) {
311 snprintf(message, sizeof(message),
312 "You have been killed from the SILC Network %s%s%s",
313 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
315 snprintf(message, sizeof(message),
316 "%s%s%s has been killed from the SILC Network %s%s%s",
317 client_entry->nickname,
318 client_entry->server ? "@" : "",
319 client_entry->server ? client_entry->server : "",
320 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
324 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
326 SilcClientEntry *clients;
327 uint32 clients_count;
330 (void)va_arg(vp, void *);
331 clients = va_arg(vp, SilcClientEntry *);
332 clients_count = va_arg(vp, uint32);
334 for (i = 0; i < clients_count; i++) {
335 if (clients[i]->server)
336 snprintf(message, sizeof(message), "Server signoff: %s@%s %s%s%s",
337 clients[i]->nickname, clients[i]->server,
338 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
340 snprintf(message, sizeof(message), "Server signoff: %s %s%s%s",
341 clients[i]->nickname,
342 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
343 silc_print(client, "*** %s", message);
344 memset(message, 0, sizeof(message));
353 silc_print(client, "*** %s", message);
356 /* Command handler. This function is called always in the command function.
357 If error occurs it will be called as well. `conn' is the associated
358 client connection. `cmd_context' is the command context that was
359 originally sent to the command. `success' is FALSE if error occured
360 during command. `command' is the command being processed. It must be
361 noted that this is not reply from server. This is merely called just
362 after application has called the command. Just to tell application
363 that the command really was processed. */
365 void silc_command(SilcClient client, SilcClientConnection conn,
366 SilcClientCommandContext cmd_context, int success,
369 SilcClientInternal app = (SilcClientInternal)client->application;
377 case SILC_COMMAND_QUIT:
378 app->screen->bottom_line->channel = NULL;
379 silc_screen_print_bottom_line(app->screen, 0);
382 case SILC_COMMAND_LEAVE:
383 /* We won't talk anymore on this channel */
384 silc_say(client, conn, "You have left channel %s",
385 conn->current_channel->channel_name);
391 /* We've resolved all clients we don't know about, now just print the
392 users from the channel on the screen. */
394 void silc_client_show_users(SilcClient client,
395 SilcClientConnection conn,
396 SilcClientEntry *clients,
397 uint32 clients_count,
400 SilcChannelEntry channel = (SilcChannelEntry)context;
402 int k = 0, len1 = 0, len2 = 0;
403 char *name_list = NULL;
408 silc_list_start(channel->clients);
409 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
410 char *m, *n = chu->client->nickname;
417 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
419 m = silc_client_chumode_char(chu->mode);
421 memcpy(name_list + (len1 - len2), m, strlen(m));
426 memcpy(name_list + (len1 - len2), n, len2);
429 if (k == silc_list_count(channel->clients) - 1)
431 memcpy(name_list + len1, " ", 1);
436 client->ops->say(client, conn, "Users on %s: %s", channel->channel_name,
438 silc_free(name_list);
441 /* Command reply handler. This function is called always in the command reply
442 function. If error occurs it will be called as well. Normal scenario
443 is that it will be called after the received command data has been parsed
444 and processed. The function is used to pass the received command data to
447 `conn' is the associated client connection. `cmd_payload' is the command
448 payload data received from server and it can be ignored. It is provided
449 if the application would like to re-parse the received command data,
450 however, it must be noted that the data is parsed already by the library
451 thus the payload can be ignored. `success' is FALSE if error occured.
452 In this case arguments are not sent to the application. `command' is the
453 command reply being processed. The function has variable argument list
454 and each command defines the number and type of arguments it passes to the
455 application (on error they are not sent). */
457 void silc_command_reply(SilcClient client, SilcClientConnection conn,
458 SilcCommandPayload cmd_payload, int success,
459 SilcCommand command, SilcCommandStatus status, ...)
461 SilcClientInternal app = (SilcClientInternal)client->application;
465 va_start(vp, status);
469 case SILC_COMMAND_WHOIS:
471 char buf[1024], *nickname, *username, *realname;
476 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
477 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
479 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
482 client->ops->say(client, conn, "%s: %s", tmp,
483 silc_client_command_status_message(status));
485 client->ops->say(client, conn, "%s",
486 silc_client_command_status_message(status));
493 (void)va_arg(vp, SilcClientEntry);
494 nickname = va_arg(vp, char *);
495 username = va_arg(vp, char *);
496 realname = va_arg(vp, char *);
497 channels = va_arg(vp, SilcBuffer);
498 mode = va_arg(vp, uint32);
499 idle = va_arg(vp, uint32);
501 memset(buf, 0, sizeof(buf));
504 len = strlen(nickname);
505 strncat(buf, nickname, len);
506 strncat(buf, " is ", 4);
510 strncat(buf, username, strlen(username));
514 strncat(buf, " (", 2);
515 strncat(buf, realname, strlen(realname));
516 strncat(buf, ")", 1);
519 client->ops->say(client, conn, "%s", buf);
522 SilcDList list = silc_channel_payload_parse_list(channels);
524 SilcChannelPayload entry;
526 memset(buf, 0, sizeof(buf));
527 strcat(buf, "on channels: ");
529 silc_dlist_start(list);
530 while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
531 char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
533 char *name = silc_channel_get_name(entry, &name_len);
536 strncat(buf, m, strlen(m));
537 strncat(buf, name, name_len);
538 strncat(buf, " ", 1);
542 client->ops->say(client, conn, "%s", buf);
543 silc_channel_payload_list_free(list);
548 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
549 (mode & SILC_UMODE_ROUTER_OPERATOR))
550 client->ops->say(client, conn, "%s is %s", nickname,
551 (mode & SILC_UMODE_SERVER_OPERATOR) ?
553 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
554 "SILC Operator" : "[Unknown mode]");
556 if (mode & SILC_UMODE_GONE)
557 client->ops->say(client, conn, "%s is gone", nickname);
560 if (idle && nickname)
561 client->ops->say(client, conn, "%s has been idle %d %s",
563 idle > 60 ? (idle / 60) : idle,
564 idle > 60 ? "minutes" : "seconds");
568 case SILC_COMMAND_WHOWAS:
570 char buf[1024], *nickname, *username, *realname;
573 if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
574 status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
576 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
579 client->ops->say(client, conn, "%s: %s", tmp,
580 silc_client_command_status_message(status));
582 client->ops->say(client, conn, "%s",
583 silc_client_command_status_message(status));
590 (void)va_arg(vp, SilcClientEntry);
591 nickname = va_arg(vp, char *);
592 username = va_arg(vp, char *);
593 realname = va_arg(vp, char *);
595 memset(buf, 0, sizeof(buf));
598 len = strlen(nickname);
599 strncat(buf, nickname, len);
600 strncat(buf, " was ", 5);
604 strncat(buf, username, strlen(nickname));
608 strncat(buf, " (", 2);
609 strncat(buf, realname, strlen(realname));
610 strncat(buf, ")", 1);
613 client->ops->say(client, conn, "%s", buf);
617 case SILC_COMMAND_INVITE:
619 SilcChannelEntry channel;
625 channel = va_arg(vp, SilcChannelEntry);
626 invite_list = va_arg(vp, char *);
629 silc_say(client, conn, "%s invite list: %s", channel->channel_name,
632 silc_say(client, conn, "%s invite list not set",
633 channel->channel_name);
637 case SILC_COMMAND_JOIN:
641 SilcBuffer client_id_list;
643 SilcChannelEntry channel;
648 app->screen->bottom_line->channel = va_arg(vp, char *);
649 channel = va_arg(vp, SilcChannelEntry);
650 mode = va_arg(vp, uint32);
651 (void)va_arg(vp, uint32);
652 (void)va_arg(vp, unsigned char *);
653 (void)va_arg(vp, unsigned char *);
654 (void)va_arg(vp, unsigned char *);
655 topic = va_arg(vp, char *);
656 (void)va_arg(vp, unsigned char *);
657 list_count = va_arg(vp, uint32);
658 client_id_list = va_arg(vp, SilcBuffer);
661 client->ops->say(client, conn, "Topic for %s: %s",
662 app->screen->bottom_line->channel, topic);
664 app->screen->bottom_line->channel_mode =
665 silc_client_chmode(mode, channel);
666 silc_screen_print_bottom_line(app->screen, 0);
668 /* Resolve the client information */
669 silc_client_get_clients_by_list(client, conn, list_count,
671 silc_client_show_users, channel);
675 case SILC_COMMAND_NICK:
677 SilcClientEntry entry;
682 entry = va_arg(vp, SilcClientEntry);
683 silc_say(client, conn, "Your current nickname is %s", entry->nickname);
684 app->screen->bottom_line->nickname = entry->nickname;
685 silc_screen_print_bottom_line(app->screen, 0);
689 case SILC_COMMAND_LIST:
693 unsigned char buf[256], tmp[16];
699 (void)va_arg(vp, SilcChannelEntry);
700 name = va_arg(vp, char *);
701 topic = va_arg(vp, char *);
702 usercount = va_arg(vp, int);
704 if (status == SILC_STATUS_LIST_START ||
705 status == SILC_STATUS_OK)
706 silc_say(client, conn,
707 " Channel Users Topic");
709 memset(buf, 0, sizeof(buf));
710 strncat(buf, " ", 2);
712 strncat(buf, name, len > 40 ? 40 : len);
714 for (i = 0; i < 40 - len; i++)
718 memset(tmp, 0, sizeof(tmp));
720 snprintf(tmp, sizeof(tmp), "%d", usercount);
725 for (i = 0; i < 10 - len; i++)
731 strncat(buf, topic, len);
734 silc_say(client, conn, "%s", buf);
738 case SILC_COMMAND_UMODE:
745 mode = va_arg(vp, uint32);
747 if (!mode && app->screen->bottom_line->umode) {
748 silc_free(app->screen->bottom_line->umode);
749 app->screen->bottom_line->umode = NULL;
752 if (mode & SILC_UMODE_SERVER_OPERATOR) {
753 if (app->screen->bottom_line->umode)
754 silc_free(app->screen->bottom_line->umode);
755 app->screen->bottom_line->umode = strdup("Server Operator");;
758 if (mode & SILC_UMODE_ROUTER_OPERATOR) {
759 if (app->screen->bottom_line->umode)
760 silc_free(app->screen->bottom_line->umode);
761 app->screen->bottom_line->umode = strdup("SILC Operator");;
764 silc_screen_print_bottom_line(app->screen, 0);
768 case SILC_COMMAND_OPER:
769 if (status == SILC_STATUS_OK) {
770 conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
771 if (app->screen->bottom_line->umode)
772 silc_free(app->screen->bottom_line->umode);
773 app->screen->bottom_line->umode = strdup("Server Operator");;
774 silc_screen_print_bottom_line(app->screen, 0);
778 case SILC_COMMAND_SILCOPER:
779 if (status == SILC_STATUS_OK) {
780 conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
781 if (app->screen->bottom_line->umode)
782 silc_free(app->screen->bottom_line->umode);
783 app->screen->bottom_line->umode = strdup("SILC Operator");;
784 silc_screen_print_bottom_line(app->screen, 0);
788 case SILC_COMMAND_USERS:
790 SilcChannelEntry channel;
797 channel = va_arg(vp, SilcChannelEntry);
799 /* There are two ways to do this, either parse the list (that
800 the command_reply sends (just take it with va_arg()) or just
801 traverse the channel's client list. I'll do the latter. See
802 JOIN command reply for example for the list. */
804 silc_say(client, conn, "Users on %s", channel->channel_name);
806 line = silc_calloc(1024, sizeof(*line));
808 silc_list_start(channel->clients);
809 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
810 SilcClientEntry e = chu->client;
814 memset(line, 0, line_len);
816 if (chu->client == conn->local_entry) {
817 /* Update status line */
818 if (app->screen->bottom_line->mode)
819 silc_free(app->screen->bottom_line->mode);
820 app->screen->bottom_line->mode =
821 silc_client_chumode_char(chu->mode);
822 silc_screen_print_bottom_line(app->screen, 0);
825 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
827 line_len += strlen(e->nickname) + strlen(e->server) + 100;
828 line = silc_calloc(line_len, sizeof(*line));
831 memset(tmp, 0, sizeof(tmp));
832 m = silc_client_chumode_char(chu->mode);
834 strncat(line, " ", 1);
835 strncat(line, e->nickname, strlen(e->nickname));
836 strncat(line, e->server ? "@" : "", 1);
840 len1 = strlen(e->server);
841 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
845 memset(&line[29], 0, len1 - 29);
847 for (i = 0; i < 30 - len1 - 1; i++)
851 if (e->mode & SILC_UMODE_GONE)
855 strcat(tmp, m ? m : "");
856 strncat(line, tmp, strlen(tmp));
859 for (i = 0; i < 5 - strlen(tmp); i++)
862 strcat(line, e->username ? e->username : "");
864 silc_say(client, conn, "%s", line);
874 case SILC_COMMAND_BAN:
876 SilcChannelEntry channel;
882 channel = va_arg(vp, SilcChannelEntry);
883 ban_list = va_arg(vp, char *);
886 silc_say(client, conn, "%s ban list: %s", channel->channel_name,
889 silc_say(client, conn, "%s ban list not set", channel->channel_name);
893 case SILC_COMMAND_GETKEY:
897 SilcPublicKey public_key;
901 id_type = va_arg(vp, uint32);
902 entry = va_arg(vp, void *);
903 public_key = va_arg(vp, SilcPublicKey);
905 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
907 if (id_type == SILC_ID_CLIENT) {
908 silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
909 pk, pk_len, SILC_SKE_PK_TYPE_SILC);
915 case SILC_COMMAND_TOPIC:
917 SilcChannelEntry channel;
923 channel = va_arg(vp, SilcChannelEntry);
924 topic = va_arg(vp, char *);
927 silc_say(client, conn,
928 "Topic on channel %s: %s", channel->channel_name,
938 /* Called to indicate that connection was either successfully established
939 or connecting failed. This is also the first time application receives
940 the SilcClientConnection objecet which it should save somewhere. */
942 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
944 SilcClientInternal app = (SilcClientInternal)client->application;
947 app->screen->bottom_line->connection = conn->remote_host;
948 silc_screen_print_bottom_line(app->screen, 0);
953 /* Called to indicate that connection was disconnected to the server. */
955 void silc_disconnect(SilcClient client, SilcClientConnection conn)
957 SilcClientInternal app = (SilcClientInternal)client->application;
959 app->screen->bottom_line->connection = NULL;
960 silc_screen_print_bottom_line(app->screen, 0);
964 /* Asks passphrase from user on the input line. */
966 unsigned char *silc_ask_passphrase(SilcClient client,
967 SilcClientConnection conn)
969 SilcClientInternal app = (SilcClientInternal)conn->client->application;
970 char pass1[256], pass2[256];
977 wattroff(app->screen->input_win, A_INVIS);
978 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
979 wattron(app->screen->input_win, A_INVIS);
982 memset(pass1, 0, sizeof(pass1));
983 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
985 /* Print retype prompt */
986 wattroff(app->screen->input_win, A_INVIS);
987 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
988 wattron(app->screen->input_win, A_INVIS);
991 memset(pass2, 0, sizeof(pass2));
992 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
994 if (!strncmp(pass1, pass2, strlen(pass2)))
1000 ret = silc_calloc(strlen(pass1), sizeof(char));
1001 memcpy(ret, pass1, strlen(pass1));
1003 memset(pass1, 0, sizeof(pass1));
1004 memset(pass2, 0, sizeof(pass2));
1006 wattroff(app->screen->input_win, A_INVIS);
1007 silc_screen_input_reset(app->screen);
1012 /* Verifies received public key. If user decides to trust the key it is
1013 saved as public server key for later use. If user does not trust the
1014 key this returns FALSE. */
1016 int silc_verify_public_key(SilcClient client,
1017 SilcClientConnection conn,
1018 SilcSocketType conn_type,
1019 unsigned char *pk, uint32 pk_len,
1020 SilcSKEPKType pk_type)
1028 char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1029 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
1030 "server" : "client");
1032 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1033 silc_say(client, conn, "We don't support %s public key type %d",
1038 pw = getpwuid(getuid());
1042 memset(filename, 0, sizeof(filename));
1043 memset(file, 0, sizeof(file));
1045 if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1046 conn_type == SILC_SOCKET_TYPE_ROUTER) {
1047 snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
1048 conn->sock->hostname, conn->sock->port);
1049 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
1050 pw->pw_dir, entity, file);
1052 /* Replace all whitespaces with `_'. */
1053 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1054 for (i = 0; i < strlen(fingerprint); i++)
1055 if (fingerprint[i] == ' ')
1056 fingerprint[i] = '_';
1058 snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1059 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
1060 pw->pw_dir, entity, file);
1061 silc_free(fingerprint);
1064 /* Take fingerprint of the public key */
1065 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1067 /* Check whether this key already exists */
1068 if (stat(filename, &st) < 0) {
1070 silc_say(client, conn, "Received %s public key", entity);
1071 silc_say(client, conn, "Fingerprint for the %s key is", entity);
1072 silc_say(client, conn, "%s", fingerprint);
1074 /* Ask user to verify the key and save it */
1075 if (silc_client_ask_yes_no(client,
1076 "Would you like to accept the key (y/n)? "))
1078 /* Save the key for future checking */
1079 silc_pkcs_save_public_key_data(filename, pk, pk_len,
1080 SILC_PKCS_FILE_PEM);
1081 silc_free(fingerprint);
1085 /* The key already exists, verify it. */
1086 SilcPublicKey public_key;
1087 unsigned char *encpk;
1090 /* Load the key file */
1091 if (!silc_pkcs_load_public_key(filename, &public_key,
1092 SILC_PKCS_FILE_PEM))
1093 if (!silc_pkcs_load_public_key(filename, &public_key,
1094 SILC_PKCS_FILE_BIN)) {
1095 silc_say(client, conn, "Received %s public key", entity);
1096 silc_say(client, conn, "Fingerprint for the %s key is", entity);
1097 silc_say(client, conn, "%s", fingerprint);
1098 silc_say(client, conn, "Could not load your local copy of the %s key",
1100 if (silc_client_ask_yes_no(client,
1101 "Would you like to accept the key anyway (y/n)? "))
1103 /* Save the key for future checking */
1105 silc_pkcs_save_public_key_data(filename, pk, pk_len,
1106 SILC_PKCS_FILE_PEM);
1107 silc_free(fingerprint);
1111 silc_free(fingerprint);
1115 /* Encode the key data */
1116 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1118 silc_say(client, conn, "Received %s public key", entity);
1119 silc_say(client, conn, "Fingerprint for the %s key is", entity);
1120 silc_say(client, conn, "%s", fingerprint);
1121 silc_say(client, conn, "Your local copy of the %s key is malformed",
1123 if (silc_client_ask_yes_no(client,
1124 "Would you like to accept the key anyway (y/n)? "))
1126 /* Save the key for future checking */
1128 silc_pkcs_save_public_key_data(filename, pk, pk_len,
1129 SILC_PKCS_FILE_PEM);
1130 silc_free(fingerprint);
1134 silc_free(fingerprint);
1138 if (memcmp(encpk, pk, encpk_len)) {
1139 silc_say(client, conn, "Received %s public key", entity);
1140 silc_say(client, conn, "Fingerprint for the %s key is", entity);
1141 silc_say(client, conn, "%s", fingerprint);
1142 silc_say(client, conn, "%s key does not match with your local copy",
1144 silc_say(client, conn,
1145 "It is possible that the key has expired or changed");
1146 silc_say(client, conn, "It is also possible that some one is performing "
1147 "man-in-the-middle attack");
1149 /* Ask user to verify the key and save it */
1150 if (silc_client_ask_yes_no(client,
1151 "Would you like to accept the key anyway (y/n)? "))
1153 /* Save the key for future checking */
1155 silc_pkcs_save_public_key_data(filename, pk, pk_len,
1156 SILC_PKCS_FILE_PEM);
1157 silc_free(fingerprint);
1161 silc_say(client, conn, "Will not accept the %s key", entity);
1162 silc_free(fingerprint);
1166 /* Local copy matched */
1167 silc_free(fingerprint);
1171 silc_say(client, conn, "Will not accept the %s key", entity);
1172 silc_free(fingerprint);
1176 /* Find authentication method and authentication data by hostname and
1177 port. The hostname may be IP address as well. The found authentication
1178 method and authentication data is returned to `auth_meth', `auth_data'
1179 and `auth_data_len'. The function returns TRUE if authentication method
1180 is found and FALSE if not. `conn' may be NULL. */
1182 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1183 char *hostname, uint16 port,
1184 SilcProtocolAuthMeth *auth_meth,
1185 unsigned char **auth_data,
1186 uint32 *auth_data_len)
1188 SilcClientInternal app = (SilcClientInternal)client->application;
1190 if (app->config && app->config->conns) {
1191 SilcClientConfigSectionConnection *conn = NULL;
1193 /* Check if we find a match from user configured connections */
1194 conn = silc_client_config_find_connection(app->config,
1198 /* Match found. Use the configured authentication method */
1199 *auth_meth = conn->auth_meth;
1201 if (conn->auth_data) {
1202 *auth_data = strdup(conn->auth_data);
1203 *auth_data_len = strlen(conn->auth_data);
1210 *auth_meth = SILC_AUTH_NONE;
1217 /* Notifies application that failure packet was received. This is called
1218 if there is some protocol active in the client. The `protocol' is the
1219 protocol context. The `failure' is opaque pointer to the failure
1220 indication. Note, that the `failure' is protocol dependant and application
1221 must explicitly cast it to correct type. Usually `failure' is 32 bit
1222 failure type (see protocol specs for all protocol failure types). */
1224 void silc_failure(SilcClient client, SilcClientConnection conn,
1225 SilcProtocol protocol, void *failure)
1227 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1228 SilcSKEStatus status = (SilcSKEStatus)failure;
1230 if (status == SILC_SKE_STATUS_BAD_VERSION)
1231 silc_say(client, conn,
1232 "You are running incompatible client version (it may be "
1233 "too old or too new)");
1234 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1235 silc_say(client, conn, "Server does not support your public key type");
1236 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1237 silc_say(client, conn,
1238 "Server does not support one of your proposed KE group");
1239 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1240 silc_say(client, conn,
1241 "Server does not support one of your proposed cipher");
1242 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1243 silc_say(client, conn,
1244 "Server does not support one of your proposed PKCS");
1245 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1246 silc_say(client, conn,
1247 "Server does not support one of your proposed hash function");
1248 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1249 silc_say(client, conn,
1250 "Server does not support one of your proposed HMAC");
1251 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1252 silc_say(client, conn, "Incorrect signature");
1255 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1256 uint32 err = (uint32)failure;
1258 if (err == SILC_AUTH_FAILED)
1259 silc_say(client, conn, "Authentication failed");
1263 /* Asks whether the user would like to perform the key agreement protocol.
1264 This is called after we have received an key agreement packet or an
1265 reply to our key agreement packet. This returns TRUE if the user wants
1266 the library to perform the key agreement protocol and FALSE if it is not
1267 desired (application may start it later by calling the function
1268 silc_client_perform_key_agreement). */
1270 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1271 SilcClientEntry client_entry, char *hostname,
1273 SilcKeyAgreementCallback *completion,
1278 /* We will just display the info on the screen and return FALSE and user
1279 will have to start the key agreement with a command. */
1282 memset(host, 0, sizeof(host));
1283 snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port);
1286 silc_say(client, conn, "%s wants to perform key agreement %s",
1287 client_entry->nickname, hostname ? host : "");
1295 /* SILC client operations */
1296 SilcClientOperations ops = {
1298 silc_channel_message,
1299 silc_private_message,
1305 silc_get_auth_method,
1306 silc_verify_public_key,
1307 silc_ask_passphrase,