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(join, JOIN),
54 SILC_SERVER_CMD_REPLY(whois, WHOIS),
55 SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
56 SILC_SERVER_CMD_REPLY(users, USERS),
61 /* Process received command reply. */
63 void silc_server_command_reply_process(SilcServer server,
64 SilcSocketConnection sock,
67 SilcServerCommandReply *cmd;
68 SilcServerCommandReplyContext ctx;
69 SilcCommandPayload payload;
73 SILC_LOG_DEBUG(("Start"));
75 /* Get command reply payload from packet */
76 payload = silc_command_payload_parse(buffer);
78 /* Silently ignore bad reply packet */
79 SILC_LOG_DEBUG(("Bad command reply packet"));
83 /* Allocate command reply context. This must be free'd by the
84 command reply routine receiving it. */
85 ctx = silc_calloc(1, sizeof(*ctx));
87 ctx->sock = silc_socket_dup(sock);
88 ctx->payload = payload;
89 ctx->args = silc_command_get_args(ctx->payload);
90 ident = silc_command_get_ident(ctx->payload);
92 /* Check for pending commands and mark to be exeucted */
93 silc_server_command_pending_check(server, ctx,
94 silc_command_get(ctx->payload), ident);
96 /* Execute command reply */
97 command = silc_command_get(ctx->payload);
98 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
99 if (cmd->cmd == command)
102 if (cmd == NULL || !cmd->cb) {
103 silc_server_command_reply_free(ctx);
110 /* Free command reply context and its internals. */
112 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
115 silc_command_free_payload(cmd->payload);
117 silc_socket_free(cmd->sock); /* Decrease the reference counter */
122 /* Caches the received WHOIS information. If we are normal server currently
123 we cache global information only for short period of time. */
124 /* XXX cache expirying not implemented yet! */
127 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
129 SilcServer server = cmd->server;
131 unsigned char *tmp, *id_data;
132 char *nickname, *username, *realname;
133 SilcClientID *client_id;
134 SilcClientEntry client;
135 SilcIDCacheEntry cache = NULL;
138 unsigned int mode = 0;
140 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
141 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
142 username = silc_argument_get_arg_type(cmd->args, 4, &len);
143 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
144 if (!id_data || !nickname || !username || !realname) {
145 SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
146 nickname ? nickname : "",
147 username ? username : "",
148 realname ? realname : ""));
152 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
154 SILC_GET32_MSB(mode, tmp);
156 client_id = silc_id_payload_parse_id(id_data, id_len);
160 /* Check if we have this client cached already. */
162 client = silc_idlist_find_client_by_id(server->local_list, client_id,
165 client = silc_idlist_find_client_by_id(server->global_list,
171 /* If router did not find such Client ID in its lists then this must
172 be bogus client or some router in the net is buggy. */
173 if (server->server_type == SILC_ROUTER)
176 /* Take hostname out of nick string if it includes it. */
177 if (strchr(nickname, '@')) {
178 int len = strcspn(nickname, "@");
179 nick = silc_calloc(len + 1, sizeof(char));
180 memcpy(nick, nickname, len);
182 nick = strdup(nickname);
185 /* We don't have that client anywhere, add it. The client is added
186 to global list since server didn't have it in the lists so it must be
188 client = silc_idlist_add_client(server->global_list, nick,
190 strdup(realname), client_id,
191 cmd->sock->user_data, NULL);
197 /* We have the client already, update the data */
199 SILC_LOG_DEBUG(("Updating client data"));
201 /* Take hostname out of nick string if it includes it. */
202 if (strchr(nickname, '@')) {
203 int len = strcspn(nickname, "@");
204 nick = silc_calloc(len + 1, sizeof(char));
205 memcpy(nick, nickname, len);
207 nick = strdup(nickname);
210 if (client->nickname)
211 silc_free(client->nickname);
212 if (client->username)
213 silc_free(client->username);
214 if (client->userinfo)
215 silc_free(client->userinfo);
217 client->nickname = nick;
218 client->username = strdup(username);
219 client->userinfo = strdup(realname);
224 silc_idcache_sort_by_data(global ? server->global_list->clients :
225 server->local_list->clients);
228 silc_free(client_id);
234 /* Reiceved reply for WHOIS command. We sent the whois request to our
235 primary router, if we are normal server, and thus has now received reply
236 to the command. We will figure out what client originally sent us the
237 command and will send the reply to it. If we are router we will figure
238 out who server sent us the command and send reply to that one. */
240 SILC_SERVER_CMD_REPLY_FUNC(whois)
242 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
243 SilcCommandStatus status;
245 COMMAND_CHECK_STATUS_LIST;
247 if (!silc_server_command_reply_whois_save(cmd))
250 /* Execute any pending commands */
251 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
254 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
255 silc_server_command_reply_free(cmd);
258 /* Caches the received IDENTIFY information. */
261 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
263 SilcServer server = cmd->server;
265 unsigned char *id_data;
266 char *nickname, *username;
267 SilcClientID *client_id;
268 SilcClientEntry client;
269 SilcIDCacheEntry cache = NULL;
273 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
274 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
275 username = silc_argument_get_arg_type(cmd->args, 4, &len);
279 client_id = silc_id_payload_parse_id(id_data, id_len);
283 /* Check if we have this client cached already. */
285 client = silc_idlist_find_client_by_id(server->local_list, client_id,
288 client = silc_idlist_find_client_by_id(server->global_list,
294 /* If router did not find such Client ID in its lists then this must
295 be bogus client or some router in the net is buggy. */
296 if (server->server_type == SILC_ROUTER)
299 /* Take hostname out of nick string if it includes it. */
301 if (strchr(nickname, '@')) {
302 int len = strcspn(nickname, "@");
303 nick = silc_calloc(len + 1, sizeof(char));
304 memcpy(nick, nickname, len);
306 nick = strdup(nickname);
310 /* We don't have that client anywhere, add it. The client is added
311 to global list since server didn't have it in the lists so it must be
313 silc_idlist_add_client(server->global_list, nick,
314 username ? strdup(username) : NULL, NULL,
315 client_id, cmd->sock->user_data, NULL);
317 /* We have the client already, update the data */
319 SILC_LOG_DEBUG(("Updating client data"));
321 /* Take hostname out of nick string if it includes it. */
323 if (strchr(nickname, '@')) {
324 int len = strcspn(nickname, "@");
325 nick = silc_calloc(len + 1, sizeof(char));
326 memcpy(nick, nickname, len);
328 nick = strdup(nickname);
332 if (nickname && client->nickname)
333 silc_free(client->nickname);
336 client->nickname = nick;
338 if (username && client->username) {
339 silc_free(client->username);
340 client->username = strdup(username);
343 if (nickname && cache) {
345 silc_idcache_sort_by_data(global ? server->global_list->clients :
346 server->local_list->clients);
349 silc_free(client_id);
355 /* Received reply for forwarded IDENTIFY command. We have received the
356 requested identify information now and we will cache it. After this we
357 will call the pending command so that the requestee gets the information
360 SILC_SERVER_CMD_REPLY_FUNC(identify)
362 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
363 SilcCommandStatus status;
365 COMMAND_CHECK_STATUS_LIST;
367 if (!silc_server_command_reply_identify_save(cmd))
370 /* Execute any pending commands */
371 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
374 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
375 silc_server_command_reply_free(cmd);
378 /* Received reply for forwarded JOIN command. Router has created or joined
379 the client to the channel. We save some channel information locally
382 SILC_SERVER_CMD_REPLY_FUNC(join)
384 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
385 SilcServer server = cmd->server;
386 SilcCommandStatus status;
388 SilcClientID *client_id = NULL;
389 SilcChannelEntry entry;
390 SilcHmac hmac = NULL;
391 unsigned int id_len, len, list_count;
392 unsigned char *id_string;
393 char *channel_name, *tmp;
394 unsigned int mode, created;
395 SilcBuffer keyp, client_id_list, client_mode_list;
397 COMMAND_CHECK_STATUS;
399 /* Get channel name */
400 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
405 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
410 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
413 client_id = silc_id_payload_parse_id(tmp, len);
418 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
421 SILC_GET32_MSB(mode, tmp);
423 /* Get created boolean value */
424 tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
427 SILC_GET32_MSB(created, tmp);
428 if (created != 0 && created != 1)
431 /* Get channel key */
432 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
435 keyp = silc_buffer_alloc(len);
436 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
437 silc_buffer_put(keyp, tmp, len);
439 id = silc_id_payload_parse_id(id_string, id_len);
444 tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
446 if (!silc_hmac_alloc(tmp, NULL, &hmac))
450 /* Get the list count */
451 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
454 SILC_GET32_MSB(list_count, tmp);
456 /* Get Client ID list */
457 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
461 client_id_list = silc_buffer_alloc(len);
462 silc_buffer_pull_tail(client_id_list, len);
463 silc_buffer_put(client_id_list, tmp, len);
465 /* Get client mode list */
466 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
470 client_mode_list = silc_buffer_alloc(len);
471 silc_buffer_pull_tail(client_mode_list, len);
472 silc_buffer_put(client_mode_list, tmp, len);
474 /* See whether we already have the channel. */
475 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
477 /* Add new channel */
479 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
480 (created == 0 ? "existing" : "created"), channel_name,
481 silc_id_render(id, SILC_ID_CHANNEL)));
483 /* Add the channel to our local list. */
484 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
485 SILC_CHANNEL_MODE_NONE, id,
486 server->router, NULL, hmac);
495 /* If channel was not created we know there is global users on the
497 entry->global_users = (created == 0 ? TRUE : FALSE);
499 /* If channel was just created the mask must be zero */
500 if (!entry->global_users && mode) {
501 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
502 "new channel, forcing it to zero", cmd->sock->hostname));
506 /* Save channel mode */
509 /* Save channel key */
510 silc_server_save_channel_key(server, keyp, entry);
511 silc_buffer_free(keyp);
513 /* Save the users to the channel */
514 silc_server_save_users_on_channel(server, cmd->sock, entry,
515 client_id, client_id_list,
516 client_mode_list, list_count);
518 silc_buffer_free(client_id_list);
519 silc_buffer_free(client_mode_list);
521 /* Execute any pending commands */
522 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
525 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
527 silc_free(client_id);
528 silc_server_command_reply_free(cmd);
531 SILC_SERVER_CMD_REPLY_FUNC(users)
533 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
534 SilcServer server = cmd->server;
535 SilcCommandStatus status;
536 SilcChannelEntry channel;
537 SilcChannelID *channel_id = NULL;
538 SilcBuffer client_id_list;
539 SilcBuffer client_mode_list;
541 unsigned int tmp_len;
542 unsigned int list_count;
544 COMMAND_CHECK_STATUS;
547 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
550 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
554 /* Get channel entry */
555 channel = silc_idlist_find_channel_by_id(server->local_list,
558 channel = silc_idlist_find_channel_by_id(server->global_list,
564 /* Get the list count */
565 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
568 SILC_GET32_MSB(list_count, tmp);
570 /* Get Client ID list */
571 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
575 client_id_list = silc_buffer_alloc(tmp_len);
576 silc_buffer_pull_tail(client_id_list, tmp_len);
577 silc_buffer_put(client_id_list, tmp, tmp_len);
579 /* Get client mode list */
580 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
584 client_mode_list = silc_buffer_alloc(tmp_len);
585 silc_buffer_pull_tail(client_mode_list, tmp_len);
586 silc_buffer_put(client_mode_list, tmp, tmp_len);
588 /* Save the users to the channel */
589 silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
590 client_id_list, client_mode_list,
593 silc_buffer_free(client_id_list);
594 silc_buffer_free(client_mode_list);
596 /* Execute any pending commands */
597 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
600 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
602 silc_free(channel_id);
603 silc_server_command_reply_free(cmd);