5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 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 "serverincludes.h"
23 #include "server_internal.h"
24 #include "command_reply.h"
26 #define COMMAND_CHECK_STATUS \
28 SILC_LOG_DEBUG(("Start")); \
29 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
30 if (status != SILC_STATUS_OK) { \
31 silc_server_command_reply_free(cmd); \
36 #define COMMAND_CHECK_STATUS_LIST \
38 SILC_LOG_DEBUG(("Start")); \
39 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
40 if (status != SILC_STATUS_OK && \
41 status != SILC_STATUS_LIST_START && \
42 status != SILC_STATUS_LIST_ITEM && \
43 status != SILC_STATUS_LIST_END) { \
44 silc_server_command_reply_free(cmd); \
49 /* Server command reply list. Not all commands have reply function as
50 they are never sent by server. More maybe added later if need appears. */
51 SilcServerCommandReply silc_command_reply_list[] =
53 SILC_SERVER_CMD_REPLY(whois, WHOIS),
54 SILC_SERVER_CMD_REPLY(whowas, WHOWAS),
55 SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
56 SILC_SERVER_CMD_REPLY(info, INFO),
57 SILC_SERVER_CMD_REPLY(motd, MOTD),
58 SILC_SERVER_CMD_REPLY(join, JOIN),
59 SILC_SERVER_CMD_REPLY(users, USERS),
64 /* Process received command reply. */
66 void silc_server_command_reply_process(SilcServer server,
67 SilcSocketConnection sock,
70 SilcServerCommandReply *cmd;
71 SilcServerCommandReplyContext ctx;
72 SilcCommandPayload payload;
76 SILC_LOG_DEBUG(("Start"));
78 /* Get command reply payload from packet */
79 payload = silc_command_payload_parse(buffer);
81 /* Silently ignore bad reply packet */
82 SILC_LOG_DEBUG(("Bad command reply packet"));
86 /* Allocate command reply context. This must be free'd by the
87 command reply routine receiving it. */
88 ctx = silc_calloc(1, sizeof(*ctx));
90 ctx->sock = silc_socket_dup(sock);
91 ctx->payload = payload;
92 ctx->args = silc_command_get_args(ctx->payload);
93 ident = silc_command_get_ident(ctx->payload);
95 /* Check for pending commands and mark to be exeucted */
96 silc_server_command_pending_check(server, ctx,
97 silc_command_get(ctx->payload), ident);
99 /* Execute command reply */
100 command = silc_command_get(ctx->payload);
101 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
102 if (cmd->cmd == command)
105 if (cmd == NULL || !cmd->cb) {
106 silc_server_command_reply_free(ctx);
113 /* Free command reply context and its internals. */
115 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
118 silc_command_free_payload(cmd->payload);
120 silc_socket_free(cmd->sock); /* Decrease the reference counter */
125 /* Caches the received WHOIS information. */
128 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
130 SilcServer server = cmd->server;
132 unsigned char *tmp, *id_data;
133 char *nickname, *username, *realname;
134 SilcClientID *client_id;
135 SilcClientEntry client;
136 SilcIDCacheEntry cache = NULL;
139 unsigned int mode = 0;
141 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
142 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
143 username = silc_argument_get_arg_type(cmd->args, 4, &len);
144 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
145 if (!id_data || !nickname || !username || !realname) {
146 SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
147 nickname ? nickname : "",
148 username ? username : "",
149 realname ? realname : ""));
153 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
155 SILC_GET32_MSB(mode, tmp);
157 client_id = silc_id_payload_parse_id(id_data, id_len);
161 /* Check if we have this client cached already. */
163 client = silc_idlist_find_client_by_id(server->local_list, client_id,
166 client = silc_idlist_find_client_by_id(server->global_list,
172 /* If router did not find such Client ID in its lists then this must
173 be bogus client or some router in the net is buggy. */
174 if (server->server_type == SILC_ROUTER)
177 /* Take hostname out of nick string if it includes it. */
178 if (strchr(nickname, '@')) {
179 int len = strcspn(nickname, "@");
180 nick = silc_calloc(len + 1, sizeof(char));
181 memcpy(nick, nickname, len);
183 nick = strdup(nickname);
186 /* We don't have that client anywhere, add it. The client is added
187 to global list since server didn't have it in the lists so it must be
189 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
191 strdup(realname), client_id,
192 cmd->sock->user_data, NULL);
196 client->data.registered = TRUE;
199 /* We have the client already, update the data */
201 SILC_LOG_DEBUG(("Updating client data"));
203 /* Take hostname out of nick string if it includes it. */
204 if (strchr(nickname, '@')) {
205 int len = strcspn(nickname, "@");
206 nick = silc_calloc(len + 1, sizeof(char));
207 memcpy(nick, nickname, len);
209 nick = strdup(nickname);
212 if (client->nickname)
213 silc_free(client->nickname);
214 if (client->username)
215 silc_free(client->username);
216 if (client->userinfo)
217 silc_free(client->userinfo);
219 client->nickname = nick;
220 client->username = strdup(username);
221 client->userinfo = strdup(realname);
226 cache->data_len = strlen(nick);
227 silc_idcache_sort_by_data(global ? server->global_list->clients :
228 server->local_list->clients);
231 silc_free(client_id);
237 /* Reiceved reply for WHOIS command. We sent the whois request to our
238 primary router, if we are normal server, and thus has now received reply
239 to the command. We will figure out what client originally sent us the
240 command and will send the reply to it. If we are router we will figure
241 out who server sent us the command and send reply to that one. */
243 SILC_SERVER_CMD_REPLY_FUNC(whois)
245 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
246 SilcCommandStatus status;
248 COMMAND_CHECK_STATUS_LIST;
250 if (!silc_server_command_reply_whois_save(cmd))
253 /* Execute any pending commands */
254 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
257 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
258 silc_server_command_reply_free(cmd);
261 /* Caches the received WHOWAS information for a short period of time. */
264 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
266 SilcServer server = cmd->server;
268 unsigned char *id_data;
269 char *nickname, *username, *realname;
270 SilcClientID *client_id;
271 SilcClientEntry client;
272 SilcIDCacheEntry cache = NULL;
276 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
277 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
278 username = silc_argument_get_arg_type(cmd->args, 4, &len);
279 if (!id_data || !nickname || !username)
282 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
284 client_id = silc_id_payload_parse_id(id_data, id_len);
288 /* Check if we have this client cached already. */
290 client = silc_idlist_find_client_by_id(server->local_list, client_id,
293 client = silc_idlist_find_client_by_id(server->global_list,
299 /* If router did not find such Client ID in its lists then this must
300 be bogus client or some router in the net is buggy. */
301 if (server->server_type == SILC_ROUTER)
304 /* Take hostname out of nick string if it includes it. */
305 if (strchr(nickname, '@')) {
306 int len = strcspn(nickname, "@");
307 nick = silc_calloc(len + 1, sizeof(char));
308 memcpy(nick, nickname, len);
310 nick = strdup(nickname);
313 /* We don't have that client anywhere, add it. The client is added
314 to global list since server didn't have it in the lists so it must be
316 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
317 strdup(username), strdup(realname),
318 silc_id_dup(client_id, SILC_ID_CLIENT),
319 cmd->sock->user_data, NULL);
323 client->data.registered = FALSE;
324 client = silc_idlist_find_client_by_id(server->global_list,
326 cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
328 /* We have the client already, update the data */
330 /* Take hostname out of nick string if it includes it. */
331 if (strchr(nickname, '@')) {
332 int len = strcspn(nickname, "@");
333 nick = silc_calloc(len + 1, sizeof(char));
334 memcpy(nick, nickname, len);
336 nick = strdup(nickname);
339 if (client->nickname)
340 silc_free(client->nickname);
341 if (client->username)
342 silc_free(client->username);
344 client->nickname = nick;
345 client->username = strdup(username);
349 cache->data_len = strlen(nick);
350 silc_idcache_sort_by_data(global ? server->global_list->clients :
351 server->local_list->clients);
355 silc_free(client_id);
360 /* Received reply for WHOWAS command. Cache the client information only for
361 a short period of time. */
363 SILC_SERVER_CMD_REPLY_FUNC(whowas)
365 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
366 SilcCommandStatus status;
368 COMMAND_CHECK_STATUS_LIST;
370 if (!silc_server_command_reply_whowas_save(cmd))
373 /* Execute any pending commands */
374 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
377 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
378 silc_server_command_reply_free(cmd);
381 /* Caches the received IDENTIFY information. */
384 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
386 SilcServer server = cmd->server;
388 unsigned char *id_data;
389 char *nickname, *username;
390 SilcClientID *client_id;
391 SilcClientEntry client;
392 SilcIDCacheEntry cache = NULL;
396 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
397 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
398 username = silc_argument_get_arg_type(cmd->args, 4, &len);
402 client_id = silc_id_payload_parse_id(id_data, id_len);
406 /* Check if we have this client cached already. */
408 client = silc_idlist_find_client_by_id(server->local_list, client_id,
411 client = silc_idlist_find_client_by_id(server->global_list,
417 /* If router did not find such Client ID in its lists then this must
418 be bogus client or some router in the net is buggy. */
419 if (server->server_type == SILC_ROUTER)
422 /* Take hostname out of nick string if it includes it. */
424 if (strchr(nickname, '@')) {
425 int len = strcspn(nickname, "@");
426 nick = silc_calloc(len + 1, sizeof(char));
427 memcpy(nick, nickname, len);
429 nick = strdup(nickname);
433 /* We don't have that client anywhere, add it. The client is added
434 to global list since server didn't have it in the lists so it must be
436 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
437 username ? strdup(username) : NULL, NULL,
438 client_id, cmd->sock->user_data, NULL);
439 client->data.registered = TRUE;
441 /* We have the client already, update the data */
443 SILC_LOG_DEBUG(("Updating client data"));
445 /* Take hostname out of nick string if it includes it. */
447 if (strchr(nickname, '@')) {
448 int len = strcspn(nickname, "@");
449 nick = silc_calloc(len + 1, sizeof(char));
450 memcpy(nick, nickname, len);
452 nick = strdup(nickname);
456 if (nickname && client->nickname)
457 silc_free(client->nickname);
460 client->nickname = nick;
462 if (username && client->username) {
463 silc_free(client->username);
464 client->username = strdup(username);
467 if (nickname && cache) {
469 cache->data_len = strlen(nick);
470 silc_idcache_sort_by_data(global ? server->global_list->clients :
471 server->local_list->clients);
474 silc_free(client_id);
480 /* Received reply for forwarded IDENTIFY command. We have received the
481 requested identify information now and we will cache it. After this we
482 will call the pending command so that the requestee gets the information
485 SILC_SERVER_CMD_REPLY_FUNC(identify)
487 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
488 SilcCommandStatus status;
490 COMMAND_CHECK_STATUS_LIST;
492 if (!silc_server_command_reply_identify_save(cmd))
495 /* Execute any pending commands */
496 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
499 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
500 silc_server_command_reply_free(cmd);
503 /* Received reply fro INFO command. Cache the server and its information */
505 SILC_SERVER_CMD_REPLY_FUNC(info)
507 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
508 SilcServer server = cmd->server;
509 SilcCommandStatus status;
510 SilcServerEntry entry;
511 SilcServerID *server_id;
512 unsigned int tmp_len;
513 unsigned char *tmp, *name;
515 COMMAND_CHECK_STATUS;
518 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
521 server_id = silc_id_payload_parse_id(tmp, tmp_len);
526 name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
530 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
532 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
535 /* Add the server to global list */
536 server_id = silc_id_dup(server_id, SILC_ID_SERVER);
537 entry = silc_idlist_add_server(server->global_list, name, 0,
538 server_id, NULL, NULL);
540 silc_free(server_id);
546 /* Get the info string */
547 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
551 entry->server_info = tmp ? strdup(tmp) : NULL;
553 /* Execute any pending commands */
554 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
557 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
558 silc_server_command_reply_free(cmd);
561 /* Received reply fro MOTD command. */
563 SILC_SERVER_CMD_REPLY_FUNC(motd)
565 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
566 SilcServer server = cmd->server;
567 SilcCommandStatus status;
568 SilcServerEntry entry;
569 SilcServerID *server_id;
570 unsigned int tmp_len;
573 COMMAND_CHECK_STATUS;
576 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
579 server_id = silc_id_payload_parse_id(tmp, tmp_len);
583 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
585 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
592 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
598 /* Execute any pending commands */
599 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
604 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
605 silc_server_command_reply_free(cmd);
608 /* Received reply for forwarded JOIN command. Router has created or joined
609 the client to the channel. We save some channel information locally
612 SILC_SERVER_CMD_REPLY_FUNC(join)
614 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
615 SilcServer server = cmd->server;
616 SilcCommandStatus status;
618 SilcClientID *client_id = NULL;
619 SilcChannelEntry entry;
620 SilcHmac hmac = NULL;
621 unsigned int id_len, len, list_count;
622 unsigned char *id_string;
623 char *channel_name, *tmp;
624 unsigned int mode, created;
625 SilcBuffer keyp, client_id_list, client_mode_list;
627 COMMAND_CHECK_STATUS;
629 /* Get channel name */
630 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
635 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
640 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
643 client_id = silc_id_payload_parse_id(tmp, len);
648 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
651 SILC_GET32_MSB(mode, tmp);
653 /* Get created boolean value */
654 tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
657 SILC_GET32_MSB(created, tmp);
658 if (created != 0 && created != 1)
661 /* Get channel key */
662 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
665 keyp = silc_buffer_alloc(len);
666 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
667 silc_buffer_put(keyp, tmp, len);
669 id = silc_id_payload_parse_id(id_string, id_len);
674 tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
676 if (!silc_hmac_alloc(tmp, NULL, &hmac))
680 /* Get the list count */
681 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
684 SILC_GET32_MSB(list_count, tmp);
686 /* Get Client ID list */
687 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
691 client_id_list = silc_buffer_alloc(len);
692 silc_buffer_pull_tail(client_id_list, len);
693 silc_buffer_put(client_id_list, tmp, len);
695 /* Get client mode list */
696 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
700 client_mode_list = silc_buffer_alloc(len);
701 silc_buffer_pull_tail(client_mode_list, len);
702 silc_buffer_put(client_mode_list, tmp, len);
704 /* See whether we already have the channel. */
705 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
707 /* Add new channel */
709 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
710 (created == 0 ? "existing" : "created"), channel_name,
711 silc_id_render(id, SILC_ID_CHANNEL)));
713 /* Add the channel to our local list. */
714 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
715 SILC_CHANNEL_MODE_NONE, id,
716 server->router, NULL, hmac);
725 if (entry->hmac_name && hmac) {
726 silc_free(entry->hmac_name);
727 entry->hmac_name = strdup(hmac->hmac->name);
730 /* Get the ban list */
731 tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
734 silc_free(entry->ban_list);
735 entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
736 memcpy(entry->ban_list, tmp, len);
739 /* Get the invite list */
740 tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
742 if (entry->invite_list)
743 silc_free(entry->invite_list);
744 entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
745 memcpy(entry->invite_list, tmp, len);
749 tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
752 silc_free(entry->topic);
753 entry->topic = strdup(tmp);
756 /* If channel was not created we know there is global users on the
758 entry->global_users = (created == 0 ? TRUE : FALSE);
760 /* If channel was just created the mask must be zero */
761 if (!entry->global_users && mode) {
762 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
763 "new channel, forcing it to zero", cmd->sock->hostname));
767 /* Save channel mode */
770 /* Save channel key */
771 silc_server_save_channel_key(server, keyp, entry);
772 silc_buffer_free(keyp);
774 /* Save the users to the channel */
775 silc_server_save_users_on_channel(server, cmd->sock, entry,
776 client_id, client_id_list,
777 client_mode_list, list_count);
779 silc_buffer_free(client_id_list);
780 silc_buffer_free(client_mode_list);
782 /* Execute any pending commands */
783 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
786 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
788 silc_free(client_id);
789 silc_server_command_reply_free(cmd);
792 SILC_SERVER_CMD_REPLY_FUNC(users)
794 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
795 SilcServer server = cmd->server;
796 SilcCommandStatus status;
797 SilcChannelEntry channel;
798 SilcChannelID *channel_id = NULL;
799 SilcBuffer client_id_list;
800 SilcBuffer client_mode_list;
802 unsigned int tmp_len;
803 unsigned int list_count;
805 COMMAND_CHECK_STATUS;
808 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
811 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
815 /* Get channel entry */
816 channel = silc_idlist_find_channel_by_id(server->local_list,
819 channel = silc_idlist_find_channel_by_id(server->global_list,
825 /* Get the list count */
826 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
829 SILC_GET32_MSB(list_count, tmp);
831 /* Get Client ID list */
832 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
836 client_id_list = silc_buffer_alloc(tmp_len);
837 silc_buffer_pull_tail(client_id_list, tmp_len);
838 silc_buffer_put(client_id_list, tmp, tmp_len);
840 /* Get client mode list */
841 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
845 client_mode_list = silc_buffer_alloc(tmp_len);
846 silc_buffer_pull_tail(client_mode_list, tmp_len);
847 silc_buffer_put(client_mode_list, tmp, tmp_len);
849 /* Save the users to the channel */
850 silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
851 client_id_list, client_mode_list,
854 silc_buffer_free(client_id_list);
855 silc_buffer_free(client_mode_list);
857 /* Execute any pending commands */
858 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
861 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
863 silc_free(channel_id);
864 silc_server_command_reply_free(cmd);