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, NULL, NULL);
180 /* We have the client already, update the data */
182 SILC_LOG_DEBUG(("Updating client data"));
184 /* Take hostname out of nick string if it includes it. */
185 if (strchr(nickname, '@')) {
186 int len = strcspn(nickname, "@");
187 nick = silc_calloc(len + 1, sizeof(char));
188 memcpy(nick, nickname, len);
190 nick = strdup(nickname);
193 if (client->nickname)
194 silc_free(client->nickname);
195 if (client->username)
196 silc_free(client->username);
197 if (client->userinfo)
198 silc_free(client->userinfo);
200 client->nickname = nick;
201 client->username = strdup(username);
202 client->userinfo = strdup(realname);
206 silc_idcache_sort_by_data(global ? server->global_list->clients :
207 server->local_list->clients);
210 silc_free(client_id);
216 /* Reiceved reply for WHOIS command. We sent the whois request to our
217 primary router, if we are normal server, and thus has now received reply
218 to the command. We will figure out what client originally sent us the
219 command and will send the reply to it. If we are router we will figure
220 out who server sent us the command and send reply to that one. */
222 SILC_SERVER_CMD_REPLY_FUNC(whois)
224 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
225 SilcCommandStatus status;
227 COMMAND_CHECK_STATUS_LIST;
229 if (!silc_server_command_reply_whois_save(cmd))
232 /* Execute any pending commands */
233 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
236 silc_server_command_reply_free(cmd);
239 /* Caches the received IDENTIFY information. */
242 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
244 SilcServer server = cmd->server;
246 unsigned char *id_data;
247 char *nickname, *username;
248 SilcClientID *client_id;
249 SilcClientEntry client;
250 SilcIDCacheEntry cache = NULL;
254 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
255 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
256 username = silc_argument_get_arg_type(cmd->args, 4, &len);
260 client_id = silc_id_payload_parse_id(id_data, id_len);
264 /* Check if we have this client cached already. */
266 client = silc_idlist_find_client_by_id(server->local_list, client_id,
269 client = silc_idlist_find_client_by_id(server->global_list,
275 /* If router did not find such Client ID in its lists then this must
276 be bogus client or some router in the net is buggy. */
277 if (server->server_type == SILC_ROUTER)
280 /* Take hostname out of nick string if it includes it. */
282 if (strchr(nickname, '@')) {
283 int len = strcspn(nickname, "@");
284 nick = silc_calloc(len + 1, sizeof(char));
285 memcpy(nick, nickname, len);
287 nick = strdup(nickname);
291 /* We don't have that client anywhere, add it. The client is added
292 to global list since server didn't have it in the lists so it must be
294 silc_idlist_add_client(server->global_list, nick,
295 username ? strdup(username) : NULL, NULL,
296 client_id, NULL, NULL);
298 /* We have the client already, update the data */
300 SILC_LOG_DEBUG(("Updating client data"));
302 /* Take hostname out of nick string if it includes it. */
304 if (strchr(nickname, '@')) {
305 int len = strcspn(nickname, "@");
306 nick = silc_calloc(len + 1, sizeof(char));
307 memcpy(nick, nickname, len);
309 nick = strdup(nickname);
313 if (nickname && client->nickname)
314 silc_free(client->nickname);
317 client->nickname = nick;
319 if (username && client->username) {
320 silc_free(client->username);
321 client->username = strdup(username);
324 if (nickname && cache) {
326 silc_idcache_sort_by_data(global ? server->global_list->clients :
327 server->local_list->clients);
330 silc_free(client_id);
336 /* Received reply for forwarded IDENTIFY command. We have received the
337 requested identify information now and we will cache it. After this we
338 will call the pending command so that the requestee gets the information
341 SILC_SERVER_CMD_REPLY_FUNC(identify)
343 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
344 SilcCommandStatus status;
346 COMMAND_CHECK_STATUS_LIST;
348 if (!silc_server_command_reply_identify_save(cmd))
351 /* Execute any pending commands */
352 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
355 silc_server_command_reply_free(cmd);
358 /* Received reply for forwarded JOIN command. Router has created or joined
359 the client to the channel. We save some channel information locally
362 SILC_SERVER_CMD_REPLY_FUNC(join)
364 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
365 SilcServer server = cmd->server;
366 SilcCommandStatus status;
368 SilcChannelEntry entry;
369 unsigned int id_len, len;
370 unsigned char *id_string;
371 char *channel_name, *tmp;
372 unsigned int mode, created;
375 COMMAND_CHECK_STATUS;
377 /* Get channel name */
378 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
383 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
388 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
391 SILC_GET32_MSB(mode, tmp);
393 /* Get created boolean value */
394 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
397 SILC_GET32_MSB(created, tmp);
398 if (created != 0 && created != 1)
401 /* Get channel key */
402 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
405 keyp = silc_buffer_alloc(len);
406 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
407 silc_buffer_put(keyp, tmp, len);
409 id = silc_id_payload_parse_id(id_string, id_len);
413 /* See whether we already have the channel. */
414 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
416 /* Add new channel */
418 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
419 (created == 0 ? "existing" : "created"), channel_name,
420 silc_id_render(id, SILC_ID_CHANNEL)));
422 /* Add the channel to our local list. */
423 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
424 SILC_CHANNEL_MODE_NONE, id,
425 server->router, NULL);
434 /* If channel was not created we know there is global users on the
436 entry->global_users = (created == 0 ? TRUE : FALSE);
438 /* If channel was just created the mask must be zero */
439 if (!entry->global_users && mode) {
440 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
441 "new channel, forcing it to zero", cmd->sock->hostname));
445 /* Save channel mode */
448 /* Save channel key */
449 silc_server_save_channel_key(server, keyp, entry);
450 silc_buffer_free(keyp);
452 /* Execute any pending commands */
453 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
456 silc_server_command_reply_free(cmd);
459 SILC_SERVER_CMD_REPLY_FUNC(users)
461 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
462 SilcServer server = cmd->server;
463 SilcCommandStatus status;
464 SilcChannelEntry channel;
465 SilcChannelID *channel_id = NULL;
466 SilcBuffer client_id_list;
467 SilcBuffer client_mode_list;
469 unsigned int tmp_len;
470 unsigned int list_count, i;
472 COMMAND_CHECK_STATUS;
475 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
478 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
482 /* Get the list count */
483 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
486 SILC_GET32_MSB(list_count, tmp);
488 /* Get Client ID list */
489 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
493 client_id_list = silc_buffer_alloc(tmp_len);
494 silc_buffer_pull_tail(client_id_list, tmp_len);
495 silc_buffer_put(client_id_list, tmp, tmp_len);
497 /* Get client mode list */
498 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
502 client_mode_list = silc_buffer_alloc(tmp_len);
503 silc_buffer_pull_tail(client_mode_list, tmp_len);
504 silc_buffer_put(client_mode_list, tmp, tmp_len);
506 /* Get channel entry */
507 channel = silc_idlist_find_channel_by_id(server->local_list,
510 channel = silc_idlist_find_channel_by_id(server->global_list,
516 /* Cache the received Client ID's and modes. This cache expires
517 whenever server sends notify message to channel. It means two things;
518 some user has joined or leaved the channel. XXX! */
519 for (i = 0; i < list_count; i++) {
520 unsigned short idp_len;
522 SilcClientID *client_id;
523 SilcClientEntry client;
526 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
528 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
531 silc_buffer_pull(client_id_list, idp_len);
534 SILC_GET32_MSB(mode, client_mode_list->data);
535 silc_buffer_pull(client_mode_list, 4);
537 /* Check if we have this client cached already. */
538 client = silc_idlist_find_client_by_id(server->local_list, client_id,
541 client = silc_idlist_find_client_by_id(server->global_list,
544 /* If router did not find such Client ID in its lists then this must
545 be bogus client or some router in the net is buggy. */
546 if (server->server_type == SILC_ROUTER)
549 /* We don't have that client anywhere, add it. The client is added
550 to global list since server didn't have it in the lists so it must be
552 client = silc_idlist_add_client(server->global_list, NULL, NULL,
553 NULL, client_id, cmd->sock->user_data,
556 silc_free(client_id);
560 /* We have the client already. */
561 silc_free(client_id);
564 if (!silc_server_client_on_channel(client, channel)) {
565 /* Client was not on the channel, add it. */
566 SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
567 chl->client = client;
569 chl->channel = channel;
570 silc_list_add(channel->user_list, chl);
571 silc_list_add(client->channels, chl);
575 silc_buffer_free(client_id_list);
576 silc_buffer_free(client_mode_list);
578 /* Execute any pending commands */
579 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
583 silc_free(channel_id);
584 silc_server_command_reply_free(cmd);