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(join, JOIN),
58 SILC_SERVER_CMD_REPLY(users, USERS),
63 /* Process received command reply. */
65 void silc_server_command_reply_process(SilcServer server,
66 SilcSocketConnection sock,
69 SilcServerCommandReply *cmd;
70 SilcServerCommandReplyContext ctx;
71 SilcCommandPayload payload;
75 SILC_LOG_DEBUG(("Start"));
77 /* Get command reply payload from packet */
78 payload = silc_command_payload_parse(buffer);
80 /* Silently ignore bad reply packet */
81 SILC_LOG_DEBUG(("Bad command reply packet"));
85 /* Allocate command reply context. This must be free'd by the
86 command reply routine receiving it. */
87 ctx = silc_calloc(1, sizeof(*ctx));
89 ctx->sock = silc_socket_dup(sock);
90 ctx->payload = payload;
91 ctx->args = silc_command_get_args(ctx->payload);
92 ident = silc_command_get_ident(ctx->payload);
94 /* Check for pending commands and mark to be exeucted */
95 silc_server_command_pending_check(server, ctx,
96 silc_command_get(ctx->payload), ident);
98 /* Execute command reply */
99 command = silc_command_get(ctx->payload);
100 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
101 if (cmd->cmd == command)
104 if (cmd == NULL || !cmd->cb) {
105 silc_server_command_reply_free(ctx);
112 /* Free command reply context and its internals. */
114 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
117 silc_command_free_payload(cmd->payload);
119 silc_socket_free(cmd->sock); /* Decrease the reference counter */
124 /* Caches the received WHOIS information. If we are normal server currently
125 we cache global information only for short period of time. */
126 /* XXX cache expirying not implemented yet! */
129 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
131 SilcServer server = cmd->server;
133 unsigned char *tmp, *id_data;
134 char *nickname, *username, *realname;
135 SilcClientID *client_id;
136 SilcClientEntry client;
137 SilcIDCacheEntry cache = NULL;
140 unsigned int mode = 0;
142 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
143 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
144 username = silc_argument_get_arg_type(cmd->args, 4, &len);
145 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
146 if (!id_data || !nickname || !username || !realname) {
147 SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
148 nickname ? nickname : "",
149 username ? username : "",
150 realname ? realname : ""));
154 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
156 SILC_GET32_MSB(mode, tmp);
158 client_id = silc_id_payload_parse_id(id_data, id_len);
162 /* Check if we have this client cached already. */
164 client = silc_idlist_find_client_by_id(server->local_list, client_id,
167 client = silc_idlist_find_client_by_id(server->global_list,
173 /* If router did not find such Client ID in its lists then this must
174 be bogus client or some router in the net is buggy. */
175 if (server->server_type == SILC_ROUTER)
178 /* Take hostname out of nick string if it includes it. */
179 if (strchr(nickname, '@')) {
180 int len = strcspn(nickname, "@");
181 nick = silc_calloc(len + 1, sizeof(char));
182 memcpy(nick, nickname, len);
184 nick = strdup(nickname);
187 /* We don't have that client anywhere, add it. The client is added
188 to global list since server didn't have it in the lists so it must be
190 client = silc_idlist_add_client(server->global_list, nick,
192 strdup(realname), client_id,
193 cmd->sock->user_data, NULL);
197 client->data.registered = TRUE;
200 /* We have the client already, update the data */
202 SILC_LOG_DEBUG(("Updating client data"));
204 /* Take hostname out of nick string if it includes it. */
205 if (strchr(nickname, '@')) {
206 int len = strcspn(nickname, "@");
207 nick = silc_calloc(len + 1, sizeof(char));
208 memcpy(nick, nickname, len);
210 nick = strdup(nickname);
213 if (client->nickname)
214 silc_free(client->nickname);
215 if (client->username)
216 silc_free(client->username);
217 if (client->userinfo)
218 silc_free(client->userinfo);
220 client->nickname = nick;
221 client->username = strdup(username);
222 client->userinfo = strdup(realname);
227 cache->data_len = strlen(nick);
228 silc_idcache_sort_by_data(global ? server->global_list->clients :
229 server->local_list->clients);
232 silc_free(client_id);
238 /* Reiceved reply for WHOIS command. We sent the whois request to our
239 primary router, if we are normal server, and thus has now received reply
240 to the command. We will figure out what client originally sent us the
241 command and will send the reply to it. If we are router we will figure
242 out who server sent us the command and send reply to that one. */
244 SILC_SERVER_CMD_REPLY_FUNC(whois)
246 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
247 SilcCommandStatus status;
249 COMMAND_CHECK_STATUS_LIST;
251 if (!silc_server_command_reply_whois_save(cmd))
254 /* Execute any pending commands */
255 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
258 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
259 silc_server_command_reply_free(cmd);
262 /* Caches the received WHOWAS information for a short period of time. */
265 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
267 SilcServer server = cmd->server;
269 unsigned char *id_data;
270 char *nickname, *username, *realname;
271 SilcClientID *client_id;
272 SilcClientEntry client;
273 SilcIDCacheEntry cache = NULL;
277 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
278 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
279 username = silc_argument_get_arg_type(cmd->args, 4, &len);
280 if (!id_data || !nickname || !username)
283 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
285 client_id = silc_id_payload_parse_id(id_data, id_len);
289 /* Check if we have this client cached already. */
291 client = silc_idlist_find_client_by_id(server->local_list, client_id,
294 client = silc_idlist_find_client_by_id(server->global_list,
300 /* If router did not find such Client ID in its lists then this must
301 be bogus client or some router in the net is buggy. */
302 if (server->server_type == SILC_ROUTER)
305 /* Take hostname out of nick string if it includes it. */
306 if (strchr(nickname, '@')) {
307 int len = strcspn(nickname, "@");
308 nick = silc_calloc(len + 1, sizeof(char));
309 memcpy(nick, nickname, len);
311 nick = strdup(nickname);
314 /* We don't have that client anywhere, add it. The client is added
315 to global list since server didn't have it in the lists so it must be
317 client = silc_idlist_add_client(server->global_list, nick,
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,
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);
527 /* Get the info string */
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 for forwarded JOIN command. Router has created or joined
564 the client to the channel. We save some channel information locally
567 SILC_SERVER_CMD_REPLY_FUNC(join)
569 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
570 SilcServer server = cmd->server;
571 SilcCommandStatus status;
573 SilcClientID *client_id = NULL;
574 SilcChannelEntry entry;
575 SilcHmac hmac = NULL;
576 unsigned int id_len, len, list_count;
577 unsigned char *id_string;
578 char *channel_name, *tmp;
579 unsigned int mode, created;
580 SilcBuffer keyp, client_id_list, client_mode_list;
582 COMMAND_CHECK_STATUS;
584 /* Get channel name */
585 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
590 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
595 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
598 client_id = silc_id_payload_parse_id(tmp, len);
603 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
606 SILC_GET32_MSB(mode, tmp);
608 /* Get created boolean value */
609 tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
612 SILC_GET32_MSB(created, tmp);
613 if (created != 0 && created != 1)
616 /* Get channel key */
617 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
620 keyp = silc_buffer_alloc(len);
621 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
622 silc_buffer_put(keyp, tmp, len);
624 id = silc_id_payload_parse_id(id_string, id_len);
629 tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
631 if (!silc_hmac_alloc(tmp, NULL, &hmac))
635 /* Get the list count */
636 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
639 SILC_GET32_MSB(list_count, tmp);
641 /* Get Client ID list */
642 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
646 client_id_list = silc_buffer_alloc(len);
647 silc_buffer_pull_tail(client_id_list, len);
648 silc_buffer_put(client_id_list, tmp, len);
650 /* Get client mode list */
651 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
655 client_mode_list = silc_buffer_alloc(len);
656 silc_buffer_pull_tail(client_mode_list, len);
657 silc_buffer_put(client_mode_list, tmp, len);
659 /* See whether we already have the channel. */
660 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
662 /* Add new channel */
664 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
665 (created == 0 ? "existing" : "created"), channel_name,
666 silc_id_render(id, SILC_ID_CHANNEL)));
668 /* Add the channel to our local list. */
669 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
670 SILC_CHANNEL_MODE_NONE, id,
671 server->router, NULL, hmac);
680 /* If channel was not created we know there is global users on the
682 entry->global_users = (created == 0 ? TRUE : FALSE);
684 /* If channel was just created the mask must be zero */
685 if (!entry->global_users && mode) {
686 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
687 "new channel, forcing it to zero", cmd->sock->hostname));
691 /* Save channel mode */
694 /* Save channel key */
695 silc_server_save_channel_key(server, keyp, entry);
696 silc_buffer_free(keyp);
698 /* Save the users to the channel */
699 silc_server_save_users_on_channel(server, cmd->sock, entry,
700 client_id, client_id_list,
701 client_mode_list, list_count);
703 silc_buffer_free(client_id_list);
704 silc_buffer_free(client_mode_list);
706 /* Execute any pending commands */
707 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
710 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
712 silc_free(client_id);
713 silc_server_command_reply_free(cmd);
716 SILC_SERVER_CMD_REPLY_FUNC(users)
718 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
719 SilcServer server = cmd->server;
720 SilcCommandStatus status;
721 SilcChannelEntry channel;
722 SilcChannelID *channel_id = NULL;
723 SilcBuffer client_id_list;
724 SilcBuffer client_mode_list;
726 unsigned int tmp_len;
727 unsigned int list_count;
729 COMMAND_CHECK_STATUS;
732 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
735 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
739 /* Get channel entry */
740 channel = silc_idlist_find_channel_by_id(server->local_list,
743 channel = silc_idlist_find_channel_by_id(server->global_list,
749 /* Get the list count */
750 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
753 SILC_GET32_MSB(list_count, tmp);
755 /* Get Client ID list */
756 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
760 client_id_list = silc_buffer_alloc(tmp_len);
761 silc_buffer_pull_tail(client_id_list, tmp_len);
762 silc_buffer_put(client_id_list, tmp, tmp_len);
764 /* Get client mode list */
765 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
769 client_mode_list = silc_buffer_alloc(tmp_len);
770 silc_buffer_pull_tail(client_mode_list, tmp_len);
771 silc_buffer_put(client_mode_list, tmp, tmp_len);
773 /* Save the users to the channel */
774 silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
775 client_id_list, client_mode_list,
778 silc_buffer_free(client_id_list);
779 silc_buffer_free(client_mode_list);
781 /* Execute any pending commands */
782 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
785 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
787 silc_free(channel_id);
788 silc_server_command_reply_free(cmd);