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) {
144 SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
145 nickname ? nickname : "",
146 username ? username : "",
147 realname ? realname : ""));
151 client_id = silc_id_payload_parse_id(id_data, id_len);
155 /* Check if we have this client cached already. */
157 client = silc_idlist_find_client_by_id(server->local_list, client_id,
160 client = silc_idlist_find_client_by_id(server->global_list,
166 /* If router did not find such Client ID in its lists then this must
167 be bogus client or some router in the net is buggy. */
168 if (server->server_type == SILC_ROUTER)
171 /* Take hostname out of nick string if it includes it. */
172 if (strchr(nickname, '@')) {
173 int len = strcspn(nickname, "@");
174 nick = silc_calloc(len + 1, sizeof(char));
175 memcpy(nick, nickname, len);
177 nick = strdup(nickname);
180 /* We don't have that client anywhere, add it. The client is added
181 to global list since server didn't have it in the lists so it must be
183 silc_idlist_add_client(server->global_list, nick,
185 strdup(realname), client_id,
186 cmd->sock->user_data, NULL);
188 /* We have the client already, update the data */
190 SILC_LOG_DEBUG(("Updating client data"));
192 /* Take hostname out of nick string if it includes it. */
193 if (strchr(nickname, '@')) {
194 int len = strcspn(nickname, "@");
195 nick = silc_calloc(len + 1, sizeof(char));
196 memcpy(nick, nickname, len);
198 nick = strdup(nickname);
201 if (client->nickname)
202 silc_free(client->nickname);
203 if (client->username)
204 silc_free(client->username);
205 if (client->userinfo)
206 silc_free(client->userinfo);
208 client->nickname = nick;
209 client->username = strdup(username);
210 client->userinfo = strdup(realname);
214 silc_idcache_sort_by_data(global ? server->global_list->clients :
215 server->local_list->clients);
218 silc_free(client_id);
224 /* Reiceved reply for WHOIS command. We sent the whois request to our
225 primary router, if we are normal server, and thus has now received reply
226 to the command. We will figure out what client originally sent us the
227 command and will send the reply to it. If we are router we will figure
228 out who server sent us the command and send reply to that one. */
230 SILC_SERVER_CMD_REPLY_FUNC(whois)
232 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
233 SilcCommandStatus status;
235 COMMAND_CHECK_STATUS_LIST;
237 if (!silc_server_command_reply_whois_save(cmd))
240 /* Execute any pending commands */
241 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
244 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
245 silc_server_command_reply_free(cmd);
248 /* Caches the received IDENTIFY information. */
251 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
253 SilcServer server = cmd->server;
255 unsigned char *id_data;
256 char *nickname, *username;
257 SilcClientID *client_id;
258 SilcClientEntry client;
259 SilcIDCacheEntry cache = NULL;
263 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
264 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
265 username = silc_argument_get_arg_type(cmd->args, 4, &len);
269 client_id = silc_id_payload_parse_id(id_data, id_len);
273 /* Check if we have this client cached already. */
275 client = silc_idlist_find_client_by_id(server->local_list, client_id,
278 client = silc_idlist_find_client_by_id(server->global_list,
284 /* If router did not find such Client ID in its lists then this must
285 be bogus client or some router in the net is buggy. */
286 if (server->server_type == SILC_ROUTER)
289 /* Take hostname out of nick string if it includes it. */
291 if (strchr(nickname, '@')) {
292 int len = strcspn(nickname, "@");
293 nick = silc_calloc(len + 1, sizeof(char));
294 memcpy(nick, nickname, len);
296 nick = strdup(nickname);
300 /* We don't have that client anywhere, add it. The client is added
301 to global list since server didn't have it in the lists so it must be
303 silc_idlist_add_client(server->global_list, nick,
304 username ? strdup(username) : NULL, NULL,
305 client_id, cmd->sock->user_data, NULL);
307 /* We have the client already, update the data */
309 SILC_LOG_DEBUG(("Updating client data"));
311 /* Take hostname out of nick string if it includes it. */
313 if (strchr(nickname, '@')) {
314 int len = strcspn(nickname, "@");
315 nick = silc_calloc(len + 1, sizeof(char));
316 memcpy(nick, nickname, len);
318 nick = strdup(nickname);
322 if (nickname && client->nickname)
323 silc_free(client->nickname);
326 client->nickname = nick;
328 if (username && client->username) {
329 silc_free(client->username);
330 client->username = strdup(username);
333 if (nickname && cache) {
335 silc_idcache_sort_by_data(global ? server->global_list->clients :
336 server->local_list->clients);
339 silc_free(client_id);
345 /* Received reply for forwarded IDENTIFY command. We have received the
346 requested identify information now and we will cache it. After this we
347 will call the pending command so that the requestee gets the information
350 SILC_SERVER_CMD_REPLY_FUNC(identify)
352 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
353 SilcCommandStatus status;
355 COMMAND_CHECK_STATUS_LIST;
357 if (!silc_server_command_reply_identify_save(cmd))
360 /* Execute any pending commands */
361 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
364 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
365 silc_server_command_reply_free(cmd);
368 /* Received reply for forwarded JOIN command. Router has created or joined
369 the client to the channel. We save some channel information locally
372 SILC_SERVER_CMD_REPLY_FUNC(join)
374 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
375 SilcServer server = cmd->server;
376 SilcCommandStatus status;
378 SilcChannelEntry entry;
379 SilcHmac hmac = NULL;
380 unsigned int id_len, len;
381 unsigned char *id_string;
382 char *channel_name, *tmp;
383 unsigned int mode, created;
386 COMMAND_CHECK_STATUS;
388 /* Get channel name */
389 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
394 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
399 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
402 SILC_GET32_MSB(mode, tmp);
404 /* Get created boolean value */
405 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
408 SILC_GET32_MSB(created, tmp);
409 if (created != 0 && created != 1)
412 /* Get channel key */
413 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
416 keyp = silc_buffer_alloc(len);
417 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
418 silc_buffer_put(keyp, tmp, len);
420 id = silc_id_payload_parse_id(id_string, id_len);
425 tmp = silc_argument_get_arg_type(cmd->args, 10, NULL);
427 if (!silc_hmac_alloc(tmp, NULL, &hmac))
431 /* See whether we already have the channel. */
432 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
434 /* Add new channel */
436 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
437 (created == 0 ? "existing" : "created"), channel_name,
438 silc_id_render(id, SILC_ID_CHANNEL)));
440 /* Add the channel to our local list. */
441 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
442 SILC_CHANNEL_MODE_NONE, id,
443 server->router, NULL, hmac);
452 /* If channel was not created we know there is global users on the
454 entry->global_users = (created == 0 ? TRUE : FALSE);
456 /* If channel was just created the mask must be zero */
457 if (!entry->global_users && mode) {
458 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
459 "new channel, forcing it to zero", cmd->sock->hostname));
463 /* Save channel mode */
466 /* Save channel key */
467 silc_server_save_channel_key(server, keyp, entry);
468 silc_buffer_free(keyp);
470 /* Execute any pending commands */
471 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
474 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
475 silc_server_command_reply_free(cmd);
478 SILC_SERVER_CMD_REPLY_FUNC(users)
480 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
481 SilcServer server = cmd->server;
482 SilcCommandStatus status;
483 SilcChannelEntry channel;
484 SilcChannelID *channel_id = NULL;
485 SilcBuffer client_id_list;
486 SilcBuffer client_mode_list;
488 unsigned int tmp_len;
489 unsigned int list_count, i;
491 COMMAND_CHECK_STATUS;
494 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
497 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
501 /* Get the list count */
502 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
505 SILC_GET32_MSB(list_count, tmp);
507 /* Get Client ID list */
508 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
512 client_id_list = silc_buffer_alloc(tmp_len);
513 silc_buffer_pull_tail(client_id_list, tmp_len);
514 silc_buffer_put(client_id_list, tmp, tmp_len);
516 /* Get client mode list */
517 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
521 client_mode_list = silc_buffer_alloc(tmp_len);
522 silc_buffer_pull_tail(client_mode_list, tmp_len);
523 silc_buffer_put(client_mode_list, tmp, tmp_len);
525 /* Get channel entry */
526 channel = silc_idlist_find_channel_by_id(server->local_list,
529 channel = silc_idlist_find_channel_by_id(server->global_list,
535 /* Cache the received Client ID's and modes. This cache expires
536 whenever server sends notify message to channel. It means two things;
537 some user has joined or leaved the channel. XXX! */
538 for (i = 0; i < list_count; i++) {
539 unsigned short idp_len;
541 SilcClientID *client_id;
542 SilcClientEntry client;
545 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
547 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
550 silc_buffer_pull(client_id_list, idp_len);
553 SILC_GET32_MSB(mode, client_mode_list->data);
554 silc_buffer_pull(client_mode_list, 4);
556 /* Check if we have this client cached already. */
557 client = silc_idlist_find_client_by_id(server->local_list, client_id,
560 client = silc_idlist_find_client_by_id(server->global_list,
563 /* If router did not find such Client ID in its lists then this must
564 be bogus client or some router in the net is buggy. */
565 if (server->server_type == SILC_ROUTER)
568 /* We don't have that client anywhere, add it. The client is added
569 to global list since server didn't have it in the lists so it must be
571 client = silc_idlist_add_client(server->global_list, NULL, NULL,
572 NULL, client_id, cmd->sock->user_data,
575 silc_free(client_id);
579 /* We have the client already. */
580 silc_free(client_id);
583 if (!silc_server_client_on_channel(client, channel)) {
584 /* Client was not on the channel, add it. */
585 SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
586 chl->client = client;
588 chl->channel = channel;
589 silc_list_add(channel->user_list, chl);
590 silc_list_add(client->channels, chl);
594 silc_buffer_free(client_id_list);
595 silc_buffer_free(client_mode_list);
597 /* Execute any pending commands */
598 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
601 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
603 silc_free(channel_id);
604 silc_server_command_reply_free(cmd);