5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 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 * Command reply functions are "the otherside" of the command functions.
22 * Reply to a command sent by server is handled by these functions.
27 * Revision 1.4 2000/07/05 06:12:34 priikone
28 * Tweaked NAMES command reply for better. Should now show users
29 * joined to a channel.
31 * Revision 1.3 2000/07/04 08:27:14 priikone
32 * Changes to LEAVE command -- more consistent now and does error
33 * handling better. Added INVITE, PING and part of NAMES commands.
34 * SilcPacketContext is included into command structure.
36 * Revision 1.2 2000/07/03 05:49:49 priikone
37 * Implemented LEAVE command. Minor bug fixes.
39 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
40 * Imported from internal CVS/Added Log headers.
45 #include "clientincludes.h"
47 /* Client command reply list. */
48 SilcClientCommandReply silc_command_reply_list[] =
50 SILC_CLIENT_CMD_REPLY(whois, WHOIS),
51 SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
52 SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
53 SILC_CLIENT_CMD_REPLY(nick, NICK),
54 SILC_CLIENT_CMD_REPLY(list, LIST),
55 SILC_CLIENT_CMD_REPLY(topic, TOPIC),
56 SILC_CLIENT_CMD_REPLY(invite, INVITE),
57 SILC_CLIENT_CMD_REPLY(quit, QUIT),
58 SILC_CLIENT_CMD_REPLY(kill, KILL),
59 SILC_CLIENT_CMD_REPLY(info, INFO),
60 SILC_CLIENT_CMD_REPLY(away, AWAY),
61 SILC_CLIENT_CMD_REPLY(connect, CONNECT),
62 SILC_CLIENT_CMD_REPLY(ping, PING),
63 SILC_CLIENT_CMD_REPLY(oper, OPER),
64 SILC_CLIENT_CMD_REPLY(join, JOIN),
65 SILC_CLIENT_CMD_REPLY(motd, MOTD),
66 SILC_CLIENT_CMD_REPLY(umode, UMODE),
67 SILC_CLIENT_CMD_REPLY(cmode, CMODE),
68 SILC_CLIENT_CMD_REPLY(kick, KICK),
69 SILC_CLIENT_CMD_REPLY(restart, RESTART),
70 SILC_CLIENT_CMD_REPLY(close, CLOSE),
71 SILC_CLIENT_CMD_REPLY(die, DIE),
72 SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
73 SILC_CLIENT_CMD_REPLY(leave, LEAVE),
74 SILC_CLIENT_CMD_REPLY(names, NAMES),
79 /* Status message structure. Messages are defined below. */
81 SilcCommandStatus status;
83 } SilcCommandStatusMessage;
85 /* Status messages returned by the server */
86 #define STAT(x) SILC_STATUS_ERR_##x
87 const SilcCommandStatusMessage silc_command_status_messages[] = {
89 { STAT(NO_SUCH_NICK), "No such nickname" },
90 { STAT(NO_SUCH_CHANNEL), "No such channel" },
91 { STAT(NO_SUCH_SERVER), "No such server" },
92 { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" },
93 { STAT(NO_RECIPIENT), "No recipient given" },
94 { STAT(UNKNOWN_COMMAND), "Unknown command" },
95 { STAT(WILDCARDS), "Unknown command" },
96 { STAT(NO_CLIENT_ID), "No Client ID given" },
97 { STAT(NO_CHANNEL_ID), "No Channel ID given" },
98 { STAT(NO_SERVER_ID), "No Server ID given" },
99 { STAT(BAD_CLIENT_ID), "Bad Client ID" },
100 { STAT(BAD_CHANNEL_ID), "Bad Channel ID" },
101 { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
102 { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
103 { STAT(NICKNAME_IN_USE), "Nickname already exists" },
104 { STAT(NOT_ON_CHANNEL), "You are not on that channel" },
105 { STAT(USER_ON_CHANNEL), "User already on channel" },
106 { STAT(NOT_REGISTERED), "You have not registered" },
107 { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
108 { STAT(TOO_MANY_PARAMS), "Too many parameters" },
109 { STAT(PERM_DENIED), "Your host is not among the privileged" },
110 { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
111 { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" },
112 { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" },
113 { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" },
114 { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
115 { STAT(UNKNOWN_MODE), "Unknown mode" },
116 { STAT(NOT_YOU), "Cannot change mode for other users" },
117 { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
118 { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" },
119 { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" },
120 { STAT(BAD_NICKNAME), "Bad nickname" },
121 { STAT(BAD_CHANNEL), "Bad channel name" },
122 { STAT(AUTH_FAILED), "Authentication failed" },
127 /* Process received command reply. */
129 void silc_client_command_reply_process(SilcClient client,
130 SilcSocketConnection sock,
131 SilcPacketContext *packet)
133 SilcBuffer buffer = packet->buffer;
134 SilcClientCommandReplyContext ctx;
135 SilcCommandPayload payload;
137 /* Get command reply payload from packet */
138 payload = silc_command_parse_payload(buffer);
140 /* Silently ignore bad reply packet */
141 SILC_LOG_DEBUG(("Bad command reply packet"));
145 /* Allocate command reply context. This must be free'd by the
146 command reply routine receiving it. */
147 ctx = silc_calloc(1, sizeof(*ctx));
148 ctx->client = client;
150 ctx->payload = payload;
151 ctx->packet = packet;
153 /* Check for pending commands and mark to be exeucted */
154 SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
156 /* Execute command reply */
157 SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
160 /* Returns status message string */
163 silc_client_command_status_message(SilcCommandStatus status)
167 for (i = 0; silc_command_status_messages[i].message; i++) {
168 if (silc_command_status_messages[i].status == status)
172 if (silc_command_status_messages[i].message == NULL)
175 return silc_command_status_messages[i].message;
178 /* Free command reply context and its internals. */
180 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
183 silc_command_free_payload(cmd->payload);
188 /* Received reply for WHOIS command. This maybe called several times
189 for one WHOIS command as server may reply with list of results. */
191 SILC_CLIENT_CMD_REPLY_FUNC(whois)
193 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
194 SilcClient client = cmd->client;
195 SilcCommandStatus status;
198 SILC_LOG_DEBUG(("Start"));
200 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
201 SILC_GET16_MSB(status, tmp);
202 if (status != SILC_STATUS_OK) {
203 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
205 silc_say(cmd->client, "%s: %s", tmp,
206 silc_client_command_status_message(status));
209 silc_say(cmd->client, "%s", silc_client_command_status_message(status));
214 /* Display one whois reply */
215 if (status == SILC_STATUS_OK) {
218 unsigned char *id_data;
219 char *nickname = NULL, *username = NULL;
220 char *realname = NULL;
223 memset(buf, 0, sizeof(buf));
225 argc = silc_command_get_arg_num(cmd->payload);
226 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
228 nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
230 strncat(buf, nickname, len);
231 strncat(buf, " is ", 4);
234 username = silc_command_get_arg_type(cmd->payload, 4, &len);
236 strncat(buf, username, len);
239 realname = silc_command_get_arg_type(cmd->payload, 5, &len);
241 strncat(buf, " (", 2);
242 strncat(buf, realname, len);
243 strncat(buf, ")", 1);
247 /* Save received Client ID to ID cache */
248 /* XXX Maybe should not be saved as /MSG will get confused */
249 id = silc_id_str2id(id_data, SILC_ID_CLIENT);
250 client->current_win->client_id_cache_count[(int)nickname[0] - 32] =
251 silc_idcache_add(&client->current_win->
252 client_id_cache[(int)nickname[0] - 32],
253 client->current_win->
254 client_id_cache_count[(int)nickname[0] - 32],
255 strdup(nickname), SILC_ID_CLIENT, id, NULL);
258 silc_say(cmd->client, "%s", buf);
261 if (status == SILC_STATUS_LIST_START) {
265 if (status == SILC_STATUS_LIST_END) {
269 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
272 silc_client_command_reply_free(cmd);
275 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
279 /* Received reply for IDENTIFY command. This maybe called several times
280 for one IDENTIFY command as server may reply with list of results.
281 This is totally silent and does not print anything on screen. */
283 SILC_CLIENT_CMD_REPLY_FUNC(identify)
285 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
286 SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
287 SilcClientEntry client_entry;
288 SilcCommandStatus status;
291 SILC_LOG_DEBUG(("Start"));
293 #define CIDC(x) win->client_id_cache[(x) - 32]
294 #define CIDCC(x) win->client_id_cache_count[(x) - 32]
296 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
297 SILC_GET16_MSB(status, tmp);
298 if (status != SILC_STATUS_OK) {
299 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
301 silc_say(cmd->client, "%s: %s", tmp,
302 silc_client_command_status_message(status));
305 silc_say(cmd->client, "%s", silc_client_command_status_message(status));
310 /* Display one whois reply */
311 if (status == SILC_STATUS_OK) {
312 unsigned char *id_data;
315 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
316 nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
318 /* Allocate client entry */
319 client_entry = silc_calloc(1, sizeof(*client_entry));
320 client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
321 client_entry->nickname = strdup(nickname);
323 /* Save received Client ID to ID cache */
325 silc_idcache_add(&CIDC(nickname[0]), CIDCC(nickname[0]),
326 client_entry->nickname, SILC_ID_CLIENT,
327 client_entry->id, client_entry);
330 if (status == SILC_STATUS_LIST_START) {
334 if (status == SILC_STATUS_LIST_END) {
338 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
341 silc_client_command_reply_free(cmd);
346 /* Received reply for command NICK. If everything went without errors
347 we just received our new Client ID. */
349 SILC_CLIENT_CMD_REPLY_FUNC(nick)
351 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
352 SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
353 SilcCommandStatus status;
354 unsigned char *tmp, *id_string;
357 SILC_LOG_DEBUG(("Start"));
359 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
360 SILC_GET16_MSB(status, tmp);
361 if (status != SILC_STATUS_OK) {
362 silc_say(cmd->client, "Cannot set nickname: %s",
363 silc_client_command_status_message(status));
367 argc = silc_command_get_arg_num(cmd->payload);
368 if (argc < 2 || argc > 2) {
369 silc_say(cmd->client, "Cannot set nickname: bad reply to command");
373 /* Take received Client ID */
374 id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
375 silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
377 /* Update nickname on screen */
378 cmd->client->screen->bottom_line->nickname = win->nickname;
379 silc_screen_print_bottom_line(cmd->client->screen, 0);
382 silc_client_command_reply_free(cmd);
385 SILC_CLIENT_CMD_REPLY_FUNC(list)
389 SILC_CLIENT_CMD_REPLY_FUNC(topic)
393 /* Received reply to invite command. */
395 SILC_CLIENT_CMD_REPLY_FUNC(invite)
397 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
398 SilcCommandStatus status;
401 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
402 SILC_GET16_MSB(status, tmp);
403 if (status != SILC_STATUS_OK) {
404 silc_say(cmd->client, "%s", silc_client_command_status_message(status));
405 silc_client_command_reply_free(cmd);
409 silc_client_command_reply_free(cmd);
412 SILC_CLIENT_CMD_REPLY_FUNC(quit)
416 SILC_CLIENT_CMD_REPLY_FUNC(kill)
420 SILC_CLIENT_CMD_REPLY_FUNC(info)
424 SILC_CLIENT_CMD_REPLY_FUNC(away)
428 SILC_CLIENT_CMD_REPLY_FUNC(connect)
432 /* Received reply to PING command. The reply time is shown to user. */
434 SILC_CLIENT_CMD_REPLY_FUNC(ping)
436 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
437 SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
438 SilcCommandStatus status;
442 time_t diff, curtime;
444 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
445 SILC_GET16_MSB(status, tmp);
446 if (status != SILC_STATUS_OK) {
447 silc_say(cmd->client, "%s", silc_client_command_status_message(status));
451 curtime = time(NULL);
452 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
454 for (i = 0; i < win->ping_count; i++) {
455 if (!SILC_ID_SERVER_COMPARE(win->ping[i].dest_id, id)) {
456 diff = curtime - win->ping[i].start_time;
457 silc_say(cmd->client, "Ping reply from %s: %d second%s",
458 win->ping[i].dest_name, diff, diff == 1 ? "" : "s");
460 win->ping[i].start_time = 0;
461 silc_free(win->ping[i].dest_id);
462 win->ping[i].dest_id = NULL;
463 silc_free(win->ping[i].dest_name);
464 win->ping[i].dest_name = NULL;
470 silc_client_command_reply_free(cmd);
473 SILC_CLIENT_CMD_REPLY_FUNC(oper)
477 /* Received reply for JOIN command. */
479 SILC_CLIENT_CMD_REPLY_FUNC(join)
481 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
482 SilcClient client = cmd->client;
483 SilcCommandStatus status;
484 unsigned int argc, mode;
485 unsigned char *id_string;
486 char *topic, *tmp, *channel_name;
488 SILC_LOG_DEBUG(("Start"));
490 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
491 SILC_GET16_MSB(status, tmp);
492 if (status != SILC_STATUS_OK) {
493 silc_say(cmd->client, "%s", silc_client_command_status_message(status));
497 argc = silc_command_get_arg_num(cmd->payload);
498 if (argc < 3 || argc > 4) {
499 silc_say(cmd->client, "Cannot join channel: Bad reply packet");
503 /* Get channel name */
504 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
505 channel_name = strdup(tmp);
508 id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
510 /* Get channel mode */
511 tmp = silc_command_get_arg_type(cmd->payload, 4, NULL);
512 SILC_GET32_MSB(mode, tmp);
515 topic = silc_command_get_arg_type(cmd->payload, 5, NULL);
517 /* Save received Channel ID */
518 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
521 /* Print channel name on screen */
522 client->screen->bottom_line->channel = channel_name;
523 silc_screen_print_bottom_line(client->screen, 0);
526 silc_say(client, "Topic for %s: %s", channel_name, topic);
529 silc_client_command_reply_free(cmd);
532 SILC_CLIENT_CMD_REPLY_FUNC(motd)
536 SILC_CLIENT_CMD_REPLY_FUNC(umode)
540 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
544 SILC_CLIENT_CMD_REPLY_FUNC(kick)
548 SILC_CLIENT_CMD_REPLY_FUNC(restart)
552 SILC_CLIENT_CMD_REPLY_FUNC(close)
556 SILC_CLIENT_CMD_REPLY_FUNC(die)
560 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
564 /* Reply to LEAVE command. */
566 SILC_CLIENT_CMD_REPLY_FUNC(leave)
568 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
569 SilcClient client = cmd->client;
570 SilcCommandStatus status;
573 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
574 SILC_GET16_MSB(status, tmp);
575 if (status != SILC_STATUS_OK)
576 silc_say(cmd->client, "%s", silc_client_command_status_message(status));
578 silc_client_command_reply_free(cmd);
581 /* Reply to NAMES command. Received list of client names on the channel
584 SILC_CLIENT_CMD_REPLY_FUNC(names)
586 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
587 SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
588 SilcCommandStatus status;
589 SilcIDCache *id_cache = NULL;
590 SilcChannelEntry channel;
591 SilcChannelID *channel_id = NULL;
596 SILC_LOG_DEBUG(("Start"));
598 #define CIDC(x) win->channel_id_cache[(x)]
599 #define CIDCC(x) win->channel_id_cache_count[(x)]
601 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
602 SILC_GET16_MSB(status, tmp);
603 if (status != SILC_STATUS_OK) {
604 silc_say(cmd->client, "%s", silc_client_command_status_message(status));
609 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
612 channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
614 /* Get the name list of the channel */
615 name_list = silc_command_get_arg_type(cmd->payload, 3, &len);
619 /* Get the channel name */
620 for (i = 0; i < 96; i++) {
623 if (silc_idcache_find_by_id(CIDC(i), CIDCC(i), (void *)channel_id,
624 SILC_ID_CHANNEL, &id_cache))
630 channel = (SilcChannelEntry)id_cache->context;
632 /* If there are pending commands set for this command reply we will
633 execute them and let them handle the received name list. */
635 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
637 /* there is no pending callback it means that this command reply
638 has been received without calling the command, ie. server has sent
639 the reply without getting the command from us first. This happens
640 with SILC servers that sends NAMES reply after joining to a channel. */
642 /* Remove commas from list */
643 for (i = 0; i < len; i++)
644 if (name_list[i] == ',')
647 silc_say(cmd->client, "Users on %s: %s", channel->channel_name, name_list);
652 silc_free(channel_id);
653 silc_client_command_reply_free(cmd);
658 /* Private message received. This processes the private message and
659 finally displays it on the screen. */
661 SILC_CLIENT_CMD_REPLY_FUNC(msg)
663 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
664 SilcClient client = cmd->client;
665 SilcBuffer buffer = (SilcBuffer)cmd->context;
666 unsigned short nick_len;
667 unsigned char *nickname, *message;
668 SilcIDCache *id_cache;
669 unsigned char *id_string;
673 silc_buffer_unformat(buffer,
674 SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
676 silc_buffer_pull(buffer, 2 + nick_len);
679 /* Get ID of the sender */
680 id_string = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char *));
681 silc_buffer_push(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
682 memcpy(id_string, buffer->data, SILC_ID_CLIENT_LEN);
683 silc_buffer_pull(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
684 id = silc_id_str2id(id_string, SILC_ID_CLIENT);
685 silc_free(id_string);
687 /* Nickname should be verified if we don't have it in the cache */
688 if (silc_idcache_find_by_data(client->current_win->
689 client_id_cache[nickname[0] - 32],
690 client->current_win->
691 client_id_cache_count[nickname[0] - 32],
692 nickname, &id_cache) == FALSE) {
694 SilcClientCommandContext ctx;
697 /* Private message from unknown source, try to resolve it. */
704 message = silc_calloc(buffer->len + 1, sizeof(char));
705 memcpy(message, buffer->data, buffer->len);
706 silc_print(client, "*%s* %s", nickname, message);
707 memset(message, 0, buffer->len);