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));
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) {
110 /* Free command reply context and its internals. */
112 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
115 silc_command_free_payload(cmd->payload);
120 /* Caches the received WHOIS information. If we are normal server currently
121 we cache global information only for short period of time. */
122 /* XXX cache expirying not implemented yet! */
125 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
127 SilcServer server = cmd->server;
129 unsigned char *id_data;
130 char *nickname, *username, *realname;
131 SilcClientID *client_id;
132 SilcClientEntry client;
133 SilcIDCacheEntry cache = NULL;
137 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
138 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
139 username = silc_argument_get_arg_type(cmd->args, 4, &len);
140 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
141 if (!id_data || !nickname || !username || !realname)
144 client_id = silc_id_payload_parse_id(id_data, id_len);
148 /* Check if we have this client cached already. */
150 client = silc_idlist_find_client_by_id(server->local_list, client_id,
153 client = silc_idlist_find_client_by_id(server->global_list,
159 /* If router did not find such Client ID in its lists then this must
160 be bogus client or some router in the net is buggy. */
161 if (server->server_type == SILC_ROUTER)
164 /* Take hostname out of nick string if it includes it. */
165 if (strchr(nickname, '@')) {
166 int len = strcspn(nickname, "@");
167 nick = silc_calloc(len + 1, sizeof(char));
168 memcpy(nick, nickname, len);
170 nick = strdup(nickname);
173 /* We don't have that client anywhere, add it. The client is added
174 to global list since server didn't have it in the lists so it must be
176 silc_idlist_add_client(server->global_list, nick,
178 strdup(realname), client_id,
179 cmd->sock->user_data, NULL);
181 /* We have the client already, update the data */
183 SILC_LOG_DEBUG(("Updating client data"));
185 /* Take hostname out of nick string if it includes it. */
186 if (strchr(nickname, '@')) {
187 int len = strcspn(nickname, "@");
188 nick = silc_calloc(len + 1, sizeof(char));
189 memcpy(nick, nickname, len);
191 nick = strdup(nickname);
194 if (client->nickname)
195 silc_free(client->nickname);
196 if (client->username)
197 silc_free(client->username);
198 if (client->userinfo)
199 silc_free(client->userinfo);
201 client->nickname = nick;
202 client->username = strdup(username);
203 client->userinfo = strdup(realname);
207 silc_idcache_sort_by_data(global ? server->global_list->clients :
208 server->local_list->clients);
211 silc_free(client_id);
217 /* Reiceved reply for WHOIS command. We sent the whois request to our
218 primary router, if we are normal server, and thus has now received reply
219 to the command. We will figure out what client originally sent us the
220 command and will send the reply to it. If we are router we will figure
221 out who server sent us the command and send reply to that one. */
223 SILC_SERVER_CMD_REPLY_FUNC(whois)
225 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
226 SilcCommandStatus status;
228 COMMAND_CHECK_STATUS_LIST;
230 if (!silc_server_command_reply_whois_save(cmd))
233 /* Execute any pending commands */
234 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
237 silc_server_command_reply_free(cmd);
240 /* Caches the received IDENTIFY information. */
243 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
245 SilcServer server = cmd->server;
247 unsigned char *id_data;
248 char *nickname, *username;
249 SilcClientID *client_id;
250 SilcClientEntry client;
251 SilcIDCacheEntry cache = NULL;
255 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
256 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
257 username = silc_argument_get_arg_type(cmd->args, 4, &len);
261 client_id = silc_id_payload_parse_id(id_data, id_len);
265 /* Check if we have this client cached already. */
267 client = silc_idlist_find_client_by_id(server->local_list, client_id,
270 client = silc_idlist_find_client_by_id(server->global_list,
276 /* If router did not find such Client ID in its lists then this must
277 be bogus client or some router in the net is buggy. */
278 if (server->server_type == SILC_ROUTER)
281 /* Take hostname out of nick string if it includes it. */
283 if (strchr(nickname, '@')) {
284 int len = strcspn(nickname, "@");
285 nick = silc_calloc(len + 1, sizeof(char));
286 memcpy(nick, nickname, len);
288 nick = strdup(nickname);
292 /* We don't have that client anywhere, add it. The client is added
293 to global list since server didn't have it in the lists so it must be
295 silc_idlist_add_client(server->global_list, nick,
296 username ? strdup(username) : NULL, NULL,
297 client_id, cmd->sock->user_data, NULL);
299 /* We have the client already, update the data */
301 SILC_LOG_DEBUG(("Updating client data"));
303 /* 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);
314 if (nickname && client->nickname)
315 silc_free(client->nickname);
318 client->nickname = nick;
320 if (username && client->username) {
321 silc_free(client->username);
322 client->username = strdup(username);
325 if (nickname && cache) {
327 silc_idcache_sort_by_data(global ? server->global_list->clients :
328 server->local_list->clients);
331 silc_free(client_id);
337 /* Received reply for forwarded IDENTIFY command. We have received the
338 requested identify information now and we will cache it. After this we
339 will call the pending command so that the requestee gets the information
342 SILC_SERVER_CMD_REPLY_FUNC(identify)
344 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
345 SilcCommandStatus status;
347 COMMAND_CHECK_STATUS_LIST;
349 if (!silc_server_command_reply_identify_save(cmd))
352 /* Execute any pending commands */
353 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
356 silc_server_command_reply_free(cmd);
359 /* Received reply for forwarded JOIN command. Router has created or joined
360 the client to the channel. We save some channel information locally
363 SILC_SERVER_CMD_REPLY_FUNC(join)
365 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
366 SilcServer server = cmd->server;
367 SilcCommandStatus status;
369 SilcChannelEntry entry;
370 unsigned int id_len, len;
371 unsigned char *id_string;
372 char *channel_name, *tmp;
373 unsigned int mode, created;
376 COMMAND_CHECK_STATUS;
378 /* Get channel name */
379 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
384 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
389 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
392 SILC_GET32_MSB(mode, tmp);
394 /* Get created boolean value */
395 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
398 SILC_GET32_MSB(created, tmp);
399 if (created != 0 && created != 1)
402 /* Get channel key */
403 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
406 keyp = silc_buffer_alloc(len);
407 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
408 silc_buffer_put(keyp, tmp, len);
410 id = silc_id_payload_parse_id(id_string, id_len);
414 /* See whether we already have the channel. */
415 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
417 /* Add new channel */
419 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
420 (created == 0 ? "existing" : "created"), channel_name,
421 silc_id_render(id, SILC_ID_CHANNEL)));
423 /* Add the channel to our local list. */
424 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
425 SILC_CHANNEL_MODE_NONE, id,
426 server->router, NULL);
435 /* If channel was not created we know there is global users on the
437 entry->global_users = (created == 0 ? TRUE : FALSE);
439 /* If channel was just created the mask must be zero */
440 if (!entry->global_users && mode) {
441 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
442 "new channel, forcing it to zero", cmd->sock->hostname));
446 /* Save channel mode */
449 /* Save channel key */
450 silc_server_save_channel_key(server, keyp, entry);
451 silc_buffer_free(keyp);
453 /* Execute any pending commands */
454 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
457 silc_server_command_reply_free(cmd);
460 SILC_SERVER_CMD_REPLY_FUNC(users)
462 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
463 SilcServer server = cmd->server;
464 SilcCommandStatus status;
465 SilcChannelEntry channel;
466 SilcChannelID *channel_id = NULL;
467 SilcBuffer client_id_list;
468 SilcBuffer client_mode_list;
470 unsigned int tmp_len;
471 unsigned int list_count, i;
473 COMMAND_CHECK_STATUS;
476 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
479 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
483 /* Get the list count */
484 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
487 SILC_GET32_MSB(list_count, tmp);
489 /* Get Client ID list */
490 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
494 client_id_list = silc_buffer_alloc(tmp_len);
495 silc_buffer_pull_tail(client_id_list, tmp_len);
496 silc_buffer_put(client_id_list, tmp, tmp_len);
498 /* Get client mode list */
499 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
503 client_mode_list = silc_buffer_alloc(tmp_len);
504 silc_buffer_pull_tail(client_mode_list, tmp_len);
505 silc_buffer_put(client_mode_list, tmp, tmp_len);
507 /* Get channel entry */
508 channel = silc_idlist_find_channel_by_id(server->local_list,
511 channel = silc_idlist_find_channel_by_id(server->global_list,
517 /* Cache the received Client ID's and modes. This cache expires
518 whenever server sends notify message to channel. It means two things;
519 some user has joined or leaved the channel. XXX! */
520 for (i = 0; i < list_count; i++) {
521 unsigned short idp_len;
523 SilcClientID *client_id;
524 SilcClientEntry client;
527 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
529 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
532 silc_buffer_pull(client_id_list, idp_len);
535 SILC_GET32_MSB(mode, client_mode_list->data);
536 silc_buffer_pull(client_mode_list, 4);
538 /* Check if we have this client cached already. */
539 client = silc_idlist_find_client_by_id(server->local_list, client_id,
542 client = silc_idlist_find_client_by_id(server->global_list,
545 /* If router did not find such Client ID in its lists then this must
546 be bogus client or some router in the net is buggy. */
547 if (server->server_type == SILC_ROUTER)
550 /* We don't have that client anywhere, add it. The client is added
551 to global list since server didn't have it in the lists so it must be
553 client = silc_idlist_add_client(server->global_list, NULL, NULL,
554 NULL, client_id, cmd->sock->user_data,
557 silc_free(client_id);
561 /* We have the client already. */
562 silc_free(client_id);
565 if (!silc_server_client_on_channel(client, channel)) {
566 /* Client was not on the channel, add it. */
567 SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
568 chl->client = client;
570 chl->channel = channel;
571 silc_list_add(channel->user_list, chl);
572 silc_list_add(client->channels, chl);
576 silc_buffer_free(client_id_list);
577 silc_buffer_free(client_mode_list);
579 /* Execute any pending commands */
580 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
584 silc_free(channel_id);
585 silc_server_command_reply_free(cmd);