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 *id_data;
132 char *nickname, *username, *realname;
133 SilcClientID *client_id;
134 SilcClientEntry client;
135 SilcIDCacheEntry cache = NULL;
139 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
140 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
141 username = silc_argument_get_arg_type(cmd->args, 4, &len);
142 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
143 if (!id_data || !nickname || !username || !realname)
146 client_id = silc_id_payload_parse_id(id_data, id_len);
150 /* Check if we have this client cached already. */
152 client = silc_idlist_find_client_by_id(server->local_list, client_id,
155 client = silc_idlist_find_client_by_id(server->global_list,
161 /* If router did not find such Client ID in its lists then this must
162 be bogus client or some router in the net is buggy. */
163 if (server->server_type == SILC_ROUTER)
166 /* Take hostname out of nick string if it includes it. */
167 if (strchr(nickname, '@')) {
168 int len = strcspn(nickname, "@");
169 nick = silc_calloc(len + 1, sizeof(char));
170 memcpy(nick, nickname, len);
172 nick = strdup(nickname);
175 /* We don't have that client anywhere, add it. The client is added
176 to global list since server didn't have it in the lists so it must be
178 silc_idlist_add_client(server->global_list, nick,
180 strdup(realname), client_id,
181 cmd->sock->user_data, NULL);
183 /* We have the client already, update the data */
185 SILC_LOG_DEBUG(("Updating client data"));
187 /* Take hostname out of nick string if it includes it. */
188 if (strchr(nickname, '@')) {
189 int len = strcspn(nickname, "@");
190 nick = silc_calloc(len + 1, sizeof(char));
191 memcpy(nick, nickname, len);
193 nick = strdup(nickname);
196 if (client->nickname)
197 silc_free(client->nickname);
198 if (client->username)
199 silc_free(client->username);
200 if (client->userinfo)
201 silc_free(client->userinfo);
203 client->nickname = nick;
204 client->username = strdup(username);
205 client->userinfo = strdup(realname);
209 silc_idcache_sort_by_data(global ? server->global_list->clients :
210 server->local_list->clients);
213 silc_free(client_id);
219 /* Reiceved reply for WHOIS command. We sent the whois request to our
220 primary router, if we are normal server, and thus has now received reply
221 to the command. We will figure out what client originally sent us the
222 command and will send the reply to it. If we are router we will figure
223 out who server sent us the command and send reply to that one. */
225 SILC_SERVER_CMD_REPLY_FUNC(whois)
227 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
228 SilcCommandStatus status;
230 COMMAND_CHECK_STATUS_LIST;
232 if (!silc_server_command_reply_whois_save(cmd))
235 /* Execute any pending commands */
236 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
239 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
240 silc_server_command_reply_free(cmd);
243 /* Caches the received IDENTIFY information. */
246 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
248 SilcServer server = cmd->server;
250 unsigned char *id_data;
251 char *nickname, *username;
252 SilcClientID *client_id;
253 SilcClientEntry client;
254 SilcIDCacheEntry cache = NULL;
258 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
259 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
260 username = silc_argument_get_arg_type(cmd->args, 4, &len);
264 client_id = silc_id_payload_parse_id(id_data, id_len);
268 /* Check if we have this client cached already. */
270 client = silc_idlist_find_client_by_id(server->local_list, client_id,
273 client = silc_idlist_find_client_by_id(server->global_list,
279 /* If router did not find such Client ID in its lists then this must
280 be bogus client or some router in the net is buggy. */
281 if (server->server_type == SILC_ROUTER)
284 /* Take hostname out of nick string if it includes it. */
286 if (strchr(nickname, '@')) {
287 int len = strcspn(nickname, "@");
288 nick = silc_calloc(len + 1, sizeof(char));
289 memcpy(nick, nickname, len);
291 nick = strdup(nickname);
295 /* We don't have that client anywhere, add it. The client is added
296 to global list since server didn't have it in the lists so it must be
298 silc_idlist_add_client(server->global_list, nick,
299 username ? strdup(username) : NULL, NULL,
300 client_id, cmd->sock->user_data, NULL);
302 /* We have the client already, update the data */
304 SILC_LOG_DEBUG(("Updating client data"));
306 /* Take hostname out of nick string if it includes it. */
308 if (strchr(nickname, '@')) {
309 int len = strcspn(nickname, "@");
310 nick = silc_calloc(len + 1, sizeof(char));
311 memcpy(nick, nickname, len);
313 nick = strdup(nickname);
317 if (nickname && client->nickname)
318 silc_free(client->nickname);
321 client->nickname = nick;
323 if (username && client->username) {
324 silc_free(client->username);
325 client->username = strdup(username);
328 if (nickname && cache) {
330 silc_idcache_sort_by_data(global ? server->global_list->clients :
331 server->local_list->clients);
334 silc_free(client_id);
340 /* Received reply for forwarded IDENTIFY command. We have received the
341 requested identify information now and we will cache it. After this we
342 will call the pending command so that the requestee gets the information
345 SILC_SERVER_CMD_REPLY_FUNC(identify)
347 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
348 SilcCommandStatus status;
350 COMMAND_CHECK_STATUS_LIST;
352 if (!silc_server_command_reply_identify_save(cmd))
355 /* Execute any pending commands */
356 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
359 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
360 silc_server_command_reply_free(cmd);
363 /* Received reply for forwarded JOIN command. Router has created or joined
364 the client to the channel. We save some channel information locally
367 SILC_SERVER_CMD_REPLY_FUNC(join)
369 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
370 SilcServer server = cmd->server;
371 SilcCommandStatus status;
373 SilcChannelEntry entry;
374 unsigned int id_len, len;
375 unsigned char *id_string;
376 char *channel_name, *tmp;
377 unsigned int mode, created;
380 COMMAND_CHECK_STATUS;
382 /* Get channel name */
383 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
388 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
393 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
396 SILC_GET32_MSB(mode, tmp);
398 /* Get created boolean value */
399 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
402 SILC_GET32_MSB(created, tmp);
403 if (created != 0 && created != 1)
406 /* Get channel key */
407 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
410 keyp = silc_buffer_alloc(len);
411 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
412 silc_buffer_put(keyp, tmp, len);
414 id = silc_id_payload_parse_id(id_string, id_len);
418 /* See whether we already have the channel. */
419 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
421 /* Add new channel */
423 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
424 (created == 0 ? "existing" : "created"), channel_name,
425 silc_id_render(id, SILC_ID_CHANNEL)));
427 /* Add the channel to our local list. */
428 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
429 SILC_CHANNEL_MODE_NONE, id,
430 server->router, NULL);
439 /* If channel was not created we know there is global users on the
441 entry->global_users = (created == 0 ? TRUE : FALSE);
443 /* If channel was just created the mask must be zero */
444 if (!entry->global_users && mode) {
445 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
446 "new channel, forcing it to zero", cmd->sock->hostname));
450 /* Save channel mode */
453 /* Save channel key */
454 silc_server_save_channel_key(server, keyp, entry);
455 silc_buffer_free(keyp);
457 /* Execute any pending commands */
458 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
461 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
462 silc_server_command_reply_free(cmd);
465 SILC_SERVER_CMD_REPLY_FUNC(users)
467 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
468 SilcServer server = cmd->server;
469 SilcCommandStatus status;
470 SilcChannelEntry channel;
471 SilcChannelID *channel_id = NULL;
472 SilcBuffer client_id_list;
473 SilcBuffer client_mode_list;
475 unsigned int tmp_len;
476 unsigned int list_count, i;
478 COMMAND_CHECK_STATUS;
481 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
484 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
488 /* Get the list count */
489 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
492 SILC_GET32_MSB(list_count, tmp);
494 /* Get Client ID list */
495 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
499 client_id_list = silc_buffer_alloc(tmp_len);
500 silc_buffer_pull_tail(client_id_list, tmp_len);
501 silc_buffer_put(client_id_list, tmp, tmp_len);
503 /* Get client mode list */
504 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
508 client_mode_list = silc_buffer_alloc(tmp_len);
509 silc_buffer_pull_tail(client_mode_list, tmp_len);
510 silc_buffer_put(client_mode_list, tmp, tmp_len);
512 /* Get channel entry */
513 channel = silc_idlist_find_channel_by_id(server->local_list,
516 channel = silc_idlist_find_channel_by_id(server->global_list,
522 /* Cache the received Client ID's and modes. This cache expires
523 whenever server sends notify message to channel. It means two things;
524 some user has joined or leaved the channel. XXX! */
525 for (i = 0; i < list_count; i++) {
526 unsigned short idp_len;
528 SilcClientID *client_id;
529 SilcClientEntry client;
532 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
534 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
537 silc_buffer_pull(client_id_list, idp_len);
540 SILC_GET32_MSB(mode, client_mode_list->data);
541 silc_buffer_pull(client_mode_list, 4);
543 /* Check if we have this client cached already. */
544 client = silc_idlist_find_client_by_id(server->local_list, client_id,
547 client = silc_idlist_find_client_by_id(server->global_list,
550 /* If router did not find such Client ID in its lists then this must
551 be bogus client or some router in the net is buggy. */
552 if (server->server_type == SILC_ROUTER)
555 /* We don't have that client anywhere, add it. The client is added
556 to global list since server didn't have it in the lists so it must be
558 client = silc_idlist_add_client(server->global_list, NULL, NULL,
559 NULL, client_id, cmd->sock->user_data,
562 silc_free(client_id);
566 /* We have the client already. */
567 silc_free(client_id);
570 if (!silc_server_client_on_channel(client, channel)) {
571 /* Client was not on the channel, add it. */
572 SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
573 chl->client = client;
575 chl->channel = channel;
576 silc_list_add(channel->user_list, chl);
577 silc_list_add(client->channels, chl);
581 silc_buffer_free(client_id_list);
582 silc_buffer_free(client_mode_list);
584 /* Execute any pending commands */
585 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
588 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
590 silc_free(channel_id);
591 silc_server_command_reply_free(cmd);