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);
332 client->nickname = nick;
334 if (username && client->username) {
335 silc_free(client->username);
336 client->username = strdup(username);
339 if (nickname && cache) {
341 silc_idcache_sort_by_data(global ? server->global_list->clients :
342 server->local_list->clients);
345 silc_free(client_id);
351 /* Received reply for forwarded IDENTIFY command. We have received the
352 requested identify information now and we will cache it. After this we
353 will call the pending command so that the requestee gets the information
356 SILC_SERVER_CMD_REPLY_FUNC(identify)
358 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
359 SilcCommandStatus status;
361 COMMAND_CHECK_STATUS_LIST;
363 if (!silc_server_command_reply_identify_save(cmd))
368 if (status == SILC_STATUS_OK) {
372 if (status == SILC_STATUS_LIST_START) {
376 if (status == SILC_STATUS_LIST_ITEM) {
380 if (status == SILC_STATUS_LIST_END) {
384 /* Execute any pending commands */
385 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
388 silc_server_command_reply_free(cmd);
391 /* Received reply for forwarded JOIN command. Router has created or joined
392 the client to the channel. We save some channel information locally
395 SILC_SERVER_CMD_REPLY_FUNC(join)
397 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
398 SilcServer server = cmd->server;
399 SilcCommandStatus status;
401 SilcChannelEntry entry;
402 unsigned int id_len, len;
403 unsigned char *id_string;
404 char *channel_name, *tmp;
405 unsigned int mode, created;
408 COMMAND_CHECK_STATUS;
410 /* Get channel name */
411 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
416 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
421 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
424 SILC_GET32_MSB(mode, tmp);
426 /* Get created boolean value */
427 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
430 SILC_GET32_MSB(created, tmp);
432 /* Get channel key */
433 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
436 keyp = silc_buffer_alloc(len);
437 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
438 silc_buffer_put(keyp, tmp, len);
440 id = silc_id_payload_parse_id(id_string, id_len);
442 /* See whether we already have the channel. */
443 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
445 /* Add new channel */
447 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
448 (created == 0 ? "existing" : "created"), channel_name,
449 silc_id_render(id, SILC_ID_CHANNEL)));
451 /* Add the channel to our local list. */
452 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
453 SILC_CHANNEL_MODE_NONE, id,
454 server->router, NULL);
463 /* If channel was not created we know there is global users on the
465 entry->global_users = (created == 0 ? TRUE : FALSE);
467 /* If channel was just created the mask must be zero */
468 if (!entry->global_users && mode) {
469 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
470 "new channel, forcing it to zero", cmd->sock->hostname));
474 /* Save channel mode */
477 /* Save channel key */
478 silc_server_save_channel_key(server, keyp, entry);
479 silc_buffer_free(keyp);
481 /* Execute any pending commands */
482 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
485 silc_server_command_reply_free(cmd);
488 SILC_SERVER_CMD_REPLY_FUNC(users)
490 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
491 SilcServer server = cmd->server;
492 SilcCommandStatus status;
493 SilcChannelEntry channel;
494 SilcChannelID *channel_id = NULL;
495 SilcBuffer client_id_list;
496 SilcBuffer client_mode_list;
498 unsigned int tmp_len;
499 unsigned int list_count, i;
501 COMMAND_CHECK_STATUS;
504 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
507 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
509 /* Get the list count */
510 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
513 SILC_GET32_MSB(list_count, tmp);
515 /* Get Client ID list */
516 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
520 client_id_list = silc_buffer_alloc(tmp_len);
521 silc_buffer_pull_tail(client_id_list, tmp_len);
522 silc_buffer_put(client_id_list, tmp, tmp_len);
524 /* Get client mode list */
525 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
529 client_mode_list = silc_buffer_alloc(tmp_len);
530 silc_buffer_pull_tail(client_mode_list, tmp_len);
531 silc_buffer_put(client_mode_list, tmp, tmp_len);
533 /* Get channel entry */
534 channel = silc_idlist_find_channel_by_id(server->local_list,
537 channel = silc_idlist_find_channel_by_id(server->global_list,
543 /* Cache the received Client ID's and modes. This cache expires
544 whenever server sends notify message to channel. It means two things;
545 some user has joined or leaved the channel. XXX! */
546 for (i = 0; i < list_count; i++) {
547 unsigned short idp_len;
549 SilcClientID *client_id;
550 SilcClientEntry client;
553 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
555 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
556 silc_buffer_pull(client_id_list, idp_len);
559 SILC_GET32_MSB(mode, client_mode_list->data);
560 silc_buffer_pull(client_mode_list, 4);
562 /* Check if we have this client cached already. */
563 client = silc_idlist_find_client_by_id(server->local_list, client_id,
566 client = silc_idlist_find_client_by_id(server->global_list,
569 /* If router did not find such Client ID in its lists then this must
570 be bogus client or some router in the net is buggy. */
571 if (server->server_type == SILC_ROUTER)
574 /* We don't have that client anywhere, add it. The client is added
575 to global list since server didn't have it in the lists so it must be
577 client = silc_idlist_add_client(server->global_list, NULL, NULL,
578 NULL, client_id, cmd->sock->user_data,
581 /* We have the client already. */
582 silc_free(client_id);
585 if (!silc_server_client_on_channel(client, channel)) {
586 /* Client was not on the channel, add it. */
587 SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
588 chl->client = client;
590 chl->channel = channel;
591 silc_list_add(channel->user_list, chl);
592 silc_list_add(client->channels, chl);
596 silc_buffer_free(client_id_list);
597 silc_buffer_free(client_mode_list);
599 /* Execute any pending commands */
600 SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
604 silc_free(channel_id);
605 silc_server_command_reply_free(cmd);