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);
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);
199 /* We have the client already, update the data */
201 SILC_LOG_DEBUG(("Updating client data"));
203 /* Take hostname out of nick string if it includes it. */
204 if (strchr(nickname, '@')) {
205 int len = strcspn(nickname, "@");
206 nick = silc_calloc(len + 1, sizeof(char));
207 memcpy(nick, nickname, len);
209 nick = strdup(nickname);
212 if (client->nickname)
213 silc_free(client->nickname);
214 if (client->username)
215 silc_free(client->username);
216 if (client->userinfo)
217 silc_free(client->userinfo);
219 client->nickname = nick;
220 client->username = strdup(username);
221 client->userinfo = strdup(realname);
226 silc_idcache_sort_by_data(global ? server->global_list->clients :
227 server->local_list->clients);
230 silc_free(client_id);
236 /* Reiceved reply for WHOIS command. We sent the whois request to our
237 primary router, if we are normal server, and thus has now received reply
238 to the command. We will figure out what client originally sent us the
239 command and will send the reply to it. If we are router we will figure
240 out who server sent us the command and send reply to that one. */
242 SILC_SERVER_CMD_REPLY_FUNC(whois)
244 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
245 SilcCommandStatus status;
247 COMMAND_CHECK_STATUS_LIST;
249 if (!silc_server_command_reply_whois_save(cmd))
252 /* Execute any pending commands */
253 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
256 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
257 silc_server_command_reply_free(cmd);
260 /* Caches the received IDENTIFY information. */
263 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
265 SilcServer server = cmd->server;
267 unsigned char *id_data;
268 char *nickname, *username;
269 SilcClientID *client_id;
270 SilcClientEntry client;
271 SilcIDCacheEntry cache = NULL;
275 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
276 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
277 username = silc_argument_get_arg_type(cmd->args, 4, &len);
281 client_id = silc_id_payload_parse_id(id_data, id_len);
285 /* Check if we have this client cached already. */
287 client = silc_idlist_find_client_by_id(server->local_list, client_id,
290 client = silc_idlist_find_client_by_id(server->global_list,
296 /* If router did not find such Client ID in its lists then this must
297 be bogus client or some router in the net is buggy. */
298 if (server->server_type == SILC_ROUTER)
301 /* Take hostname out of nick string if it includes it. */
303 if (strchr(nickname, '@')) {
304 int len = strcspn(nickname, "@");
305 nick = silc_calloc(len + 1, sizeof(char));
306 memcpy(nick, nickname, len);
308 nick = strdup(nickname);
312 /* We don't have that client anywhere, add it. The client is added
313 to global list since server didn't have it in the lists so it must be
315 silc_idlist_add_client(server->global_list, nick,
316 username ? strdup(username) : NULL, NULL,
317 client_id, cmd->sock->user_data, NULL);
319 /* We have the client already, update the data */
321 SILC_LOG_DEBUG(("Updating client data"));
323 /* Take hostname out of nick string if it includes it. */
325 if (strchr(nickname, '@')) {
326 int len = strcspn(nickname, "@");
327 nick = silc_calloc(len + 1, sizeof(char));
328 memcpy(nick, nickname, len);
330 nick = strdup(nickname);
334 if (nickname && client->nickname)
335 silc_free(client->nickname);
338 client->nickname = nick;
340 if (username && client->username) {
341 silc_free(client->username);
342 client->username = strdup(username);
345 if (nickname && cache) {
347 silc_idcache_sort_by_data(global ? server->global_list->clients :
348 server->local_list->clients);
351 silc_free(client_id);
357 /* Received reply for forwarded IDENTIFY command. We have received the
358 requested identify information now and we will cache it. After this we
359 will call the pending command so that the requestee gets the information
362 SILC_SERVER_CMD_REPLY_FUNC(identify)
364 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
365 SilcCommandStatus status;
367 COMMAND_CHECK_STATUS_LIST;
369 if (!silc_server_command_reply_identify_save(cmd))
372 /* Execute any pending commands */
373 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
376 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
377 silc_server_command_reply_free(cmd);
380 /* Received reply for forwarded JOIN command. Router has created or joined
381 the client to the channel. We save some channel information locally
384 SILC_SERVER_CMD_REPLY_FUNC(join)
386 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
387 SilcServer server = cmd->server;
388 SilcCommandStatus status;
390 SilcClientID *client_id = NULL;
391 SilcChannelEntry entry;
392 SilcHmac hmac = NULL;
393 unsigned int id_len, len, list_count;
394 unsigned char *id_string;
395 char *channel_name, *tmp;
396 unsigned int mode, created;
397 SilcBuffer keyp, client_id_list, client_mode_list;
399 COMMAND_CHECK_STATUS;
401 /* Get channel name */
402 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
407 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
412 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
415 client_id = silc_id_payload_parse_id(tmp, len);
420 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
423 SILC_GET32_MSB(mode, tmp);
425 /* Get created boolean value */
426 tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
429 SILC_GET32_MSB(created, tmp);
430 if (created != 0 && created != 1)
433 /* Get channel key */
434 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
437 keyp = silc_buffer_alloc(len);
438 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
439 silc_buffer_put(keyp, tmp, len);
441 id = silc_id_payload_parse_id(id_string, id_len);
446 tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
448 if (!silc_hmac_alloc(tmp, NULL, &hmac))
452 /* Get the list count */
453 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
456 SILC_GET32_MSB(list_count, tmp);
458 /* Get Client ID list */
459 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
463 client_id_list = silc_buffer_alloc(len);
464 silc_buffer_pull_tail(client_id_list, len);
465 silc_buffer_put(client_id_list, tmp, len);
467 /* Get client mode list */
468 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
472 client_mode_list = silc_buffer_alloc(len);
473 silc_buffer_pull_tail(client_mode_list, len);
474 silc_buffer_put(client_mode_list, tmp, len);
476 /* See whether we already have the channel. */
477 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
479 /* Add new channel */
481 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
482 (created == 0 ? "existing" : "created"), channel_name,
483 silc_id_render(id, SILC_ID_CHANNEL)));
485 /* Add the channel to our local list. */
486 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
487 SILC_CHANNEL_MODE_NONE, id,
488 server->router, NULL, hmac);
497 /* If channel was not created we know there is global users on the
499 entry->global_users = (created == 0 ? TRUE : FALSE);
501 /* If channel was just created the mask must be zero */
502 if (!entry->global_users && mode) {
503 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
504 "new channel, forcing it to zero", cmd->sock->hostname));
508 /* Save channel mode */
511 /* Save channel key */
512 silc_server_save_channel_key(server, keyp, entry);
513 silc_buffer_free(keyp);
515 /* Save the users to the channel */
516 silc_server_save_users_on_channel(server, cmd->sock, entry,
517 client_id, client_id_list,
518 client_mode_list, list_count);
520 silc_buffer_free(client_id_list);
521 silc_buffer_free(client_mode_list);
523 /* Execute any pending commands */
524 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
527 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
529 silc_free(client_id);
530 silc_server_command_reply_free(cmd);
533 SILC_SERVER_CMD_REPLY_FUNC(users)
535 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
536 SilcServer server = cmd->server;
537 SilcCommandStatus status;
538 SilcChannelEntry channel;
539 SilcChannelID *channel_id = NULL;
540 SilcBuffer client_id_list;
541 SilcBuffer client_mode_list;
543 unsigned int tmp_len;
544 unsigned int list_count;
546 COMMAND_CHECK_STATUS;
549 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
552 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
556 /* Get channel entry */
557 channel = silc_idlist_find_channel_by_id(server->local_list,
560 channel = silc_idlist_find_channel_by_id(server->global_list,
566 /* Get the list count */
567 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
570 SILC_GET32_MSB(list_count, tmp);
572 /* Get Client ID list */
573 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
577 client_id_list = silc_buffer_alloc(tmp_len);
578 silc_buffer_pull_tail(client_id_list, tmp_len);
579 silc_buffer_put(client_id_list, tmp, tmp_len);
581 /* Get client mode list */
582 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
586 client_mode_list = silc_buffer_alloc(tmp_len);
587 silc_buffer_pull_tail(client_mode_list, tmp_len);
588 silc_buffer_put(client_mode_list, tmp, tmp_len);
590 /* Save the users to the channel */
591 silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
592 client_id_list, client_mode_list,
595 silc_buffer_free(client_id_list);
596 silc_buffer_free(client_mode_list);
598 /* Execute any pending commands */
599 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
602 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
604 silc_free(channel_id);
605 silc_server_command_reply_free(cmd);