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. If we are normal server currently
126 we cache global information only for short period of time. */
127 /* XXX cache expirying not implemented yet! */
130 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
132 SilcServer server = cmd->server;
134 unsigned char *tmp, *id_data;
135 char *nickname, *username, *realname;
136 SilcClientID *client_id;
137 SilcClientEntry client;
138 SilcIDCacheEntry cache = NULL;
141 unsigned int mode = 0;
143 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
144 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
145 username = silc_argument_get_arg_type(cmd->args, 4, &len);
146 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
147 if (!id_data || !nickname || !username || !realname) {
148 SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
149 nickname ? nickname : "",
150 username ? username : "",
151 realname ? realname : ""));
155 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
157 SILC_GET32_MSB(mode, tmp);
159 client_id = silc_id_payload_parse_id(id_data, id_len);
163 /* Check if we have this client cached already. */
165 client = silc_idlist_find_client_by_id(server->local_list, client_id,
168 client = silc_idlist_find_client_by_id(server->global_list,
174 /* If router did not find such Client ID in its lists then this must
175 be bogus client or some router in the net is buggy. */
176 if (server->server_type == SILC_ROUTER)
179 /* Take hostname out of nick string if it includes it. */
180 if (strchr(nickname, '@')) {
181 int len = strcspn(nickname, "@");
182 nick = silc_calloc(len + 1, sizeof(char));
183 memcpy(nick, nickname, len);
185 nick = strdup(nickname);
188 /* We don't have that client anywhere, add it. The client is added
189 to global list since server didn't have it in the lists so it must be
191 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
193 strdup(realname), client_id,
194 cmd->sock->user_data, NULL);
198 client->data.registered = TRUE;
201 /* We have the client already, update the data */
203 SILC_LOG_DEBUG(("Updating client data"));
205 /* Take hostname out of nick string if it includes it. */
206 if (strchr(nickname, '@')) {
207 int len = strcspn(nickname, "@");
208 nick = silc_calloc(len + 1, sizeof(char));
209 memcpy(nick, nickname, len);
211 nick = strdup(nickname);
214 if (client->nickname)
215 silc_free(client->nickname);
216 if (client->username)
217 silc_free(client->username);
218 if (client->userinfo)
219 silc_free(client->userinfo);
221 client->nickname = nick;
222 client->username = strdup(username);
223 client->userinfo = strdup(realname);
228 cache->data_len = strlen(nick);
229 silc_idcache_sort_by_data(global ? server->global_list->clients :
230 server->local_list->clients);
233 silc_free(client_id);
239 /* Reiceved reply for WHOIS command. We sent the whois request to our
240 primary router, if we are normal server, and thus has now received reply
241 to the command. We will figure out what client originally sent us the
242 command and will send the reply to it. If we are router we will figure
243 out who server sent us the command and send reply to that one. */
245 SILC_SERVER_CMD_REPLY_FUNC(whois)
247 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
248 SilcCommandStatus status;
250 COMMAND_CHECK_STATUS_LIST;
252 if (!silc_server_command_reply_whois_save(cmd))
255 /* Execute any pending commands */
256 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
259 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
260 silc_server_command_reply_free(cmd);
263 /* Caches the received WHOWAS information for a short period of time. */
266 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
268 SilcServer server = cmd->server;
270 unsigned char *id_data;
271 char *nickname, *username, *realname;
272 SilcClientID *client_id;
273 SilcClientEntry client;
274 SilcIDCacheEntry cache = NULL;
278 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
279 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
280 username = silc_argument_get_arg_type(cmd->args, 4, &len);
281 if (!id_data || !nickname || !username)
284 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
286 client_id = silc_id_payload_parse_id(id_data, id_len);
290 /* Check if we have this client cached already. */
292 client = silc_idlist_find_client_by_id(server->local_list, client_id,
295 client = silc_idlist_find_client_by_id(server->global_list,
301 /* If router did not find such Client ID in its lists then this must
302 be bogus client or some router in the net is buggy. */
303 if (server->server_type == SILC_ROUTER)
306 /* Take hostname out of nick string if it includes it. */
307 if (strchr(nickname, '@')) {
308 int len = strcspn(nickname, "@");
309 nick = silc_calloc(len + 1, sizeof(char));
310 memcpy(nick, nickname, len);
312 nick = strdup(nickname);
315 /* We don't have that client anywhere, add it. The client is added
316 to global list since server didn't have it in the lists so it must be
318 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
319 strdup(username), strdup(realname),
320 silc_id_dup(client_id, SILC_ID_CLIENT),
321 cmd->sock->user_data, NULL);
325 client->data.registered = FALSE;
326 client = silc_idlist_find_client_by_id(server->global_list,
328 cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
330 /* We have the client already, update the data */
332 /* Take hostname out of nick string if it includes it. */
333 if (strchr(nickname, '@')) {
334 int len = strcspn(nickname, "@");
335 nick = silc_calloc(len + 1, sizeof(char));
336 memcpy(nick, nickname, len);
338 nick = strdup(nickname);
341 if (client->nickname)
342 silc_free(client->nickname);
343 if (client->username)
344 silc_free(client->username);
346 client->nickname = nick;
347 client->username = strdup(username);
351 cache->data_len = strlen(nick);
352 silc_idcache_sort_by_data(global ? server->global_list->clients :
353 server->local_list->clients);
357 silc_free(client_id);
362 /* Received reply for WHOWAS command. Cache the client information only for
363 a short period of time. */
365 SILC_SERVER_CMD_REPLY_FUNC(whowas)
367 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
368 SilcCommandStatus status;
370 COMMAND_CHECK_STATUS_LIST;
372 if (!silc_server_command_reply_whowas_save(cmd))
375 /* Execute any pending commands */
376 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
379 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
380 silc_server_command_reply_free(cmd);
383 /* Caches the received IDENTIFY information. */
386 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
388 SilcServer server = cmd->server;
390 unsigned char *id_data;
391 char *nickname, *username;
392 SilcClientID *client_id;
393 SilcClientEntry client;
394 SilcIDCacheEntry cache = NULL;
398 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
399 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
400 username = silc_argument_get_arg_type(cmd->args, 4, &len);
404 client_id = silc_id_payload_parse_id(id_data, id_len);
408 /* Check if we have this client cached already. */
410 client = silc_idlist_find_client_by_id(server->local_list, client_id,
413 client = silc_idlist_find_client_by_id(server->global_list,
419 /* If router did not find such Client ID in its lists then this must
420 be bogus client or some router in the net is buggy. */
421 if (server->server_type == SILC_ROUTER)
424 /* Take hostname out of nick string if it includes it. */
426 if (strchr(nickname, '@')) {
427 int len = strcspn(nickname, "@");
428 nick = silc_calloc(len + 1, sizeof(char));
429 memcpy(nick, nickname, len);
431 nick = strdup(nickname);
435 /* We don't have that client anywhere, add it. The client is added
436 to global list since server didn't have it in the lists so it must be
438 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
439 username ? strdup(username) : NULL, NULL,
440 client_id, cmd->sock->user_data, NULL);
441 client->data.registered = TRUE;
443 /* We have the client already, update the data */
445 SILC_LOG_DEBUG(("Updating client data"));
447 /* Take hostname out of nick string if it includes it. */
449 if (strchr(nickname, '@')) {
450 int len = strcspn(nickname, "@");
451 nick = silc_calloc(len + 1, sizeof(char));
452 memcpy(nick, nickname, len);
454 nick = strdup(nickname);
458 if (nickname && client->nickname)
459 silc_free(client->nickname);
462 client->nickname = nick;
464 if (username && client->username) {
465 silc_free(client->username);
466 client->username = strdup(username);
469 if (nickname && cache) {
471 cache->data_len = strlen(nick);
472 silc_idcache_sort_by_data(global ? server->global_list->clients :
473 server->local_list->clients);
476 silc_free(client_id);
482 /* Received reply for forwarded IDENTIFY command. We have received the
483 requested identify information now and we will cache it. After this we
484 will call the pending command so that the requestee gets the information
487 SILC_SERVER_CMD_REPLY_FUNC(identify)
489 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
490 SilcCommandStatus status;
492 COMMAND_CHECK_STATUS_LIST;
494 if (!silc_server_command_reply_identify_save(cmd))
497 /* Execute any pending commands */
498 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
501 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
502 silc_server_command_reply_free(cmd);
505 /* Received reply fro INFO command. Cache the server and its information */
507 SILC_SERVER_CMD_REPLY_FUNC(info)
509 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
510 SilcServer server = cmd->server;
511 SilcCommandStatus status;
512 SilcServerEntry entry;
513 SilcServerID *server_id;
514 unsigned int tmp_len;
515 unsigned char *tmp, *name;
517 COMMAND_CHECK_STATUS;
520 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
523 server_id = silc_id_payload_parse_id(tmp, tmp_len);
528 name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
532 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
534 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
537 /* Add the server to global list */
538 server_id = silc_id_dup(server_id, SILC_ID_SERVER);
539 entry = silc_idlist_add_server(server->global_list, name, 0,
540 server_id, NULL, NULL);
542 silc_free(server_id);
548 /* Get the info string */
549 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
553 entry->server_info = tmp ? strdup(tmp) : NULL;
555 /* Execute any pending commands */
556 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
559 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
560 silc_server_command_reply_free(cmd);
563 /* Received reply fro MOTD command. */
565 SILC_SERVER_CMD_REPLY_FUNC(motd)
567 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
568 SilcServer server = cmd->server;
569 SilcCommandStatus status;
570 SilcServerEntry entry;
571 SilcServerID *server_id;
572 unsigned int tmp_len;
575 COMMAND_CHECK_STATUS;
578 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
581 server_id = silc_id_payload_parse_id(tmp, tmp_len);
585 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
587 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
594 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
600 /* Execute any pending commands */
601 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
606 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
607 silc_server_command_reply_free(cmd);
610 /* Received reply for forwarded JOIN command. Router has created or joined
611 the client to the channel. We save some channel information locally
614 SILC_SERVER_CMD_REPLY_FUNC(join)
616 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
617 SilcServer server = cmd->server;
618 SilcCommandStatus status;
620 SilcClientID *client_id = NULL;
621 SilcChannelEntry entry;
622 SilcHmac hmac = NULL;
623 unsigned int id_len, len, list_count;
624 unsigned char *id_string;
625 char *channel_name, *tmp;
626 unsigned int mode, created;
627 SilcBuffer keyp, client_id_list, client_mode_list;
629 COMMAND_CHECK_STATUS;
631 /* Get channel name */
632 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
637 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
642 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
645 client_id = silc_id_payload_parse_id(tmp, len);
650 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
653 SILC_GET32_MSB(mode, tmp);
655 /* Get created boolean value */
656 tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
659 SILC_GET32_MSB(created, tmp);
660 if (created != 0 && created != 1)
663 /* Get channel key */
664 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
667 keyp = silc_buffer_alloc(len);
668 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
669 silc_buffer_put(keyp, tmp, len);
671 id = silc_id_payload_parse_id(id_string, id_len);
676 tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
678 if (!silc_hmac_alloc(tmp, NULL, &hmac))
682 /* Get the list count */
683 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
686 SILC_GET32_MSB(list_count, tmp);
688 /* Get Client ID list */
689 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
693 client_id_list = silc_buffer_alloc(len);
694 silc_buffer_pull_tail(client_id_list, len);
695 silc_buffer_put(client_id_list, tmp, len);
697 /* Get client mode list */
698 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
702 client_mode_list = silc_buffer_alloc(len);
703 silc_buffer_pull_tail(client_mode_list, len);
704 silc_buffer_put(client_mode_list, tmp, len);
706 /* See whether we already have the channel. */
707 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
709 /* Add new channel */
711 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
712 (created == 0 ? "existing" : "created"), channel_name,
713 silc_id_render(id, SILC_ID_CHANNEL)));
715 /* Add the channel to our local list. */
716 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
717 SILC_CHANNEL_MODE_NONE, id,
718 server->router, NULL, hmac);
727 /* Get the ban list */
728 tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
731 silc_free(entry->ban_list);
732 entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
733 memcpy(entry->ban_list, tmp, len);
736 /* Get the invite list */
737 tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
739 if (entry->invite_list)
740 silc_free(entry->invite_list);
741 entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
742 memcpy(entry->invite_list, tmp, len);
746 tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
749 silc_free(entry->topic);
750 entry->topic = strdup(tmp);
753 /* If channel was not created we know there is global users on the
755 entry->global_users = (created == 0 ? TRUE : FALSE);
757 /* If channel was just created the mask must be zero */
758 if (!entry->global_users && mode) {
759 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
760 "new channel, forcing it to zero", cmd->sock->hostname));
764 /* Save channel mode */
767 /* Save channel key */
768 silc_server_save_channel_key(server, keyp, entry);
769 silc_buffer_free(keyp);
771 /* Save the users to the channel */
772 silc_server_save_users_on_channel(server, cmd->sock, entry,
773 client_id, client_id_list,
774 client_mode_list, list_count);
776 silc_buffer_free(client_id_list);
777 silc_buffer_free(client_mode_list);
779 /* Execute any pending commands */
780 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
783 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
785 silc_free(client_id);
786 silc_server_command_reply_free(cmd);
789 SILC_SERVER_CMD_REPLY_FUNC(users)
791 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
792 SilcServer server = cmd->server;
793 SilcCommandStatus status;
794 SilcChannelEntry channel;
795 SilcChannelID *channel_id = NULL;
796 SilcBuffer client_id_list;
797 SilcBuffer client_mode_list;
799 unsigned int tmp_len;
800 unsigned int list_count;
802 COMMAND_CHECK_STATUS;
805 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
808 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
812 /* Get channel entry */
813 channel = silc_idlist_find_channel_by_id(server->local_list,
816 channel = silc_idlist_find_channel_by_id(server->global_list,
822 /* Get the list count */
823 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
826 SILC_GET32_MSB(list_count, tmp);
828 /* Get Client ID list */
829 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
833 client_id_list = silc_buffer_alloc(tmp_len);
834 silc_buffer_pull_tail(client_id_list, tmp_len);
835 silc_buffer_put(client_id_list, tmp, tmp_len);
837 /* Get client mode list */
838 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
842 client_mode_list = silc_buffer_alloc(tmp_len);
843 silc_buffer_pull_tail(client_mode_list, tmp_len);
844 silc_buffer_put(client_mode_list, tmp, tmp_len);
846 /* Save the users to the channel */
847 silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
848 client_id_list, client_mode_list,
851 silc_buffer_free(client_id_list);
852 silc_buffer_free(client_mode_list);
854 /* Execute any pending commands */
855 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
858 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
860 silc_free(channel_id);
861 silc_server_command_reply_free(cmd);