5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 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);
146 /* Check if we have this client cached already. */
148 client = silc_idlist_find_client_by_id(server->local_list, client_id,
151 client = silc_idlist_find_client_by_id(server->global_list,
157 /* If router did not find such Client ID in its lists then this must
158 be bogus client or some router in the net is buggy. */
159 if (server->server_type == SILC_ROUTER)
162 /* Take hostname out of nick string if it includes it. */
163 if (strchr(nickname, '@')) {
164 int len = strcspn(nickname, "@");
165 nick = silc_calloc(len + 1, sizeof(char));
166 memcpy(nick, nickname, len);
168 nick = strdup(nickname);
171 /* We don't have that client anywhere, add it. The client is added
172 to global list since server didn't have it in the lists so it must be
174 silc_idlist_add_client(server->global_list, nick,
176 strdup(realname), client_id, NULL, NULL);
178 /* We have the client already, update the data */
180 SILC_LOG_DEBUG(("Updating client data"));
182 /* Take hostname out of nick string if it includes it. */
183 if (strchr(nickname, '@')) {
184 int len = strcspn(nickname, "@");
185 nick = silc_calloc(len + 1, sizeof(char));
186 memcpy(nick, nickname, len);
188 nick = strdup(nickname);
191 if (client->nickname)
192 silc_free(client->nickname);
193 if (client->username)
194 silc_free(client->username);
195 if (client->userinfo)
196 silc_free(client->userinfo);
198 client->nickname = nick;
199 client->username = strdup(username);
200 client->userinfo = strdup(realname);
204 silc_idcache_sort_by_data(global ? server->global_list->clients :
205 server->local_list->clients);
208 silc_free(client_id);
214 /* Reiceved reply for WHOIS command. We sent the whois request to our
215 primary router, if we are normal server, and thus has now received reply
216 to the command. We will figure out what client originally sent us the
217 command and will send the reply to it. If we are router we will figure
218 out who server sent us the command and send reply to that one. */
220 SILC_SERVER_CMD_REPLY_FUNC(whois)
222 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
223 SilcCommandStatus status;
225 COMMAND_CHECK_STATUS_LIST;
227 if (!silc_server_command_reply_whois_save(cmd))
232 /* Process one identify reply */
233 if (status == SILC_STATUS_OK) {
237 if (status == SILC_STATUS_LIST_START) {
241 if (status == SILC_STATUS_LIST_ITEM) {
245 if (status == SILC_STATUS_LIST_END) {
249 /* Execute any pending commands */
250 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
253 silc_server_command_reply_free(cmd);
256 /* Caches the received IDENTIFY information. */
259 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
261 SilcServer server = cmd->server;
263 unsigned char *id_data;
264 char *nickname, *username;
265 SilcClientID *client_id;
266 SilcClientEntry client;
267 SilcIDCacheEntry cache = NULL;
271 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
272 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
273 username = silc_argument_get_arg_type(cmd->args, 4, &len);
277 client_id = silc_id_payload_parse_id(id_data, id_len);
279 /* Check if we have this client cached already. */
281 client = silc_idlist_find_client_by_id(server->local_list, client_id,
284 client = silc_idlist_find_client_by_id(server->global_list,
290 /* If router did not find such Client ID in its lists then this must
291 be bogus client or some router in the net is buggy. */
292 if (server->server_type == SILC_ROUTER)
295 /* Take hostname out of nick string if it includes it. */
297 if (strchr(nickname, '@')) {
298 int len = strcspn(nickname, "@");
299 nick = silc_calloc(len + 1, sizeof(char));
300 memcpy(nick, nickname, len);
302 nick = strdup(nickname);
306 /* We don't have that client anywhere, add it. The client is added
307 to global list since server didn't have it in the lists so it must be
309 silc_idlist_add_client(server->global_list, nick,
310 username ? strdup(username) : NULL, NULL,
311 client_id, NULL, NULL);
313 /* We have the client already, update the data */
315 SILC_LOG_DEBUG(("Updating client data"));
317 /* Take hostname out of nick string if it includes it. */
319 if (strchr(nickname, '@')) {
320 int len = strcspn(nickname, "@");
321 nick = silc_calloc(len + 1, sizeof(char));
322 memcpy(nick, nickname, len);
324 nick = strdup(nickname);
328 if (nickname && client->nickname) {
329 silc_free(client->nickname);
330 client->nickname = nick;
333 if (username && client->username) {
334 silc_free(client->username);
335 client->username = strdup(username);
338 if (nickname && cache) {
340 silc_idcache_sort_by_data(global ? server->global_list->clients :
341 server->local_list->clients);
344 silc_free(client_id);
350 /* Received reply for forwarded IDENTIFY command. We have received the
351 requested identify information now and we will cache it. After this we
352 will call the pending command so that the requestee gets the information
355 SILC_SERVER_CMD_REPLY_FUNC(identify)
357 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
358 SilcCommandStatus status;
360 COMMAND_CHECK_STATUS_LIST;
362 if (!silc_server_command_reply_identify_save(cmd))
367 if (status == SILC_STATUS_OK) {
371 if (status == SILC_STATUS_LIST_START) {
375 if (status == SILC_STATUS_LIST_ITEM) {
379 if (status == SILC_STATUS_LIST_END) {
383 /* Execute any pending commands */
384 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
387 silc_server_command_reply_free(cmd);
390 /* Received reply for forwarded JOIN command. Router has created or joined
391 the client to the channel. We save some channel information locally
394 SILC_SERVER_CMD_REPLY_FUNC(join)
396 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
397 SilcServer server = cmd->server;
398 SilcCommandStatus status;
400 SilcChannelEntry entry;
402 unsigned char *id_string;
403 char *channel_name, *tmp;
404 unsigned int mode, created;
407 COMMAND_CHECK_STATUS;
409 /* Get channel name */
410 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
415 id_string = silc_argument_get_arg_type(cmd->args, 3, &len);
420 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
423 SILC_GET32_MSB(mode, tmp);
425 /* Get created boolean value */
426 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
429 SILC_GET32_MSB(created, tmp);
431 /* Get channel key */
432 tmp = silc_argument_get_arg_type(cmd->args, 6, &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, len);
441 /* See whether we already have the channel. */
442 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
444 /* Add new channel */
446 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
447 (created == 0 ? "existing" : "created"), channel_name,
448 silc_id_render(id, SILC_ID_CHANNEL)));
450 /* Add the channel to our local list. */
451 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
452 SILC_CHANNEL_MODE_NONE, id,
453 server->router, NULL);
462 /* If channel was not created we know there is global users on the
464 entry->global_users = (created == 0 ? TRUE : FALSE);
466 /* If channel was just created the mask must be zero */
467 if (!entry->global_users && mode) {
468 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
469 "new channel, forcing it to zero", cmd->sock->hostname));
473 /* Save channel mode */
476 /* Save channel key */
477 silc_server_save_channel_key(server, keyp, entry);
478 silc_buffer_free(keyp);
480 /* Execute any pending commands */
481 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
484 silc_server_command_reply_free(cmd);
487 SILC_SERVER_CMD_REPLY_FUNC(users)
489 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
490 SilcServer server = cmd->server;
491 SilcCommandStatus status;
492 SilcChannelEntry channel;
493 SilcChannelID *channel_id = NULL;
494 SilcBuffer client_id_list;
495 SilcBuffer client_mode_list;
497 unsigned int tmp_len;
498 unsigned int list_count, i;
500 COMMAND_CHECK_STATUS;
503 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
506 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
508 /* Get the list count */
509 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
512 SILC_GET32_MSB(list_count, tmp);
514 /* Get Client ID list */
515 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
519 client_id_list = silc_buffer_alloc(tmp_len);
520 silc_buffer_pull_tail(client_id_list, tmp_len);
521 silc_buffer_put(client_id_list, tmp, tmp_len);
523 /* Get client mode list */
524 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
528 client_mode_list = silc_buffer_alloc(tmp_len);
529 silc_buffer_pull_tail(client_mode_list, tmp_len);
530 silc_buffer_put(client_mode_list, tmp, tmp_len);
532 /* Get channel entry */
533 channel = silc_idlist_find_channel_by_id(server->local_list,
536 channel = silc_idlist_find_channel_by_id(server->global_list,
542 /* Cache the received Client ID's and modes. This cache expires
543 whenever server sends notify message to channel. It means two things;
544 some user has joined or leaved the channel. XXX! */
545 for (i = 0; i < list_count; i++) {
546 unsigned short idp_len;
548 SilcClientID *client_id;
549 SilcClientEntry client;
552 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
554 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
555 silc_buffer_pull(client_id_list, idp_len);
558 SILC_GET32_MSB(mode, client_mode_list->data);
559 silc_buffer_pull(client_mode_list, 4);
561 /* Check if we have this client cached already. */
562 client = silc_idlist_find_client_by_id(server->local_list, client_id,
565 client = silc_idlist_find_client_by_id(server->global_list,
568 /* If router did not find such Client ID in its lists then this must
569 be bogus client or some router in the net is buggy. */
570 if (server->server_type == SILC_ROUTER)
573 /* We don't have that client anywhere, add it. The client is added
574 to global list since server didn't have it in the lists so it must be
576 silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
577 client_id, NULL, NULL);
579 /* We have the client already. */
580 silc_free(client_id);
584 silc_buffer_free(client_id_list);
585 silc_buffer_free(client_mode_list);
587 /* Execute any pending commands */
588 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
592 silc_free(channel_id);
593 silc_server_command_reply_free(cmd);