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,
179 cmd->sock->user_data, NULL);
181 /* We have the client already, update the data */
183 SILC_LOG_DEBUG(("Updating client data"));
185 /* Take hostname out of nick string if it includes it. */
186 if (strchr(nickname, '@')) {
187 int len = strcspn(nickname, "@");
188 nick = silc_calloc(len + 1, sizeof(char));
189 memcpy(nick, nickname, len);
191 nick = strdup(nickname);
194 if (client->nickname)
195 silc_free(client->nickname);
196 if (client->username)
197 silc_free(client->username);
198 if (client->userinfo)
199 silc_free(client->userinfo);
201 client->nickname = nick;
202 client->username = strdup(username);
203 client->userinfo = strdup(realname);
207 silc_idcache_sort_by_data(global ? server->global_list->clients :
208 server->local_list->clients);
211 silc_free(client_id);
217 /* Reiceved reply for WHOIS command. We sent the whois request to our
218 primary router, if we are normal server, and thus has now received reply
219 to the command. We will figure out what client originally sent us the
220 command and will send the reply to it. If we are router we will figure
221 out who server sent us the command and send reply to that one. */
223 SILC_SERVER_CMD_REPLY_FUNC(whois)
225 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
226 SilcCommandStatus status;
228 COMMAND_CHECK_STATUS_LIST;
230 if (!silc_server_command_reply_whois_save(cmd))
233 /* Execute any pending commands */
234 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
237 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
238 silc_server_command_reply_free(cmd);
241 /* Caches the received IDENTIFY information. */
244 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
246 SilcServer server = cmd->server;
248 unsigned char *id_data;
249 char *nickname, *username;
250 SilcClientID *client_id;
251 SilcClientEntry client;
252 SilcIDCacheEntry cache = NULL;
256 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
257 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
258 username = silc_argument_get_arg_type(cmd->args, 4, &len);
262 client_id = silc_id_payload_parse_id(id_data, id_len);
266 /* Check if we have this client cached already. */
268 client = silc_idlist_find_client_by_id(server->local_list, client_id,
271 client = silc_idlist_find_client_by_id(server->global_list,
277 /* If router did not find such Client ID in its lists then this must
278 be bogus client or some router in the net is buggy. */
279 if (server->server_type == SILC_ROUTER)
282 /* Take hostname out of nick string if it includes it. */
284 if (strchr(nickname, '@')) {
285 int len = strcspn(nickname, "@");
286 nick = silc_calloc(len + 1, sizeof(char));
287 memcpy(nick, nickname, len);
289 nick = strdup(nickname);
293 /* We don't have that client anywhere, add it. The client is added
294 to global list since server didn't have it in the lists so it must be
296 silc_idlist_add_client(server->global_list, nick,
297 username ? strdup(username) : NULL, NULL,
298 client_id, cmd->sock->user_data, NULL);
300 /* We have the client already, update the data */
302 SILC_LOG_DEBUG(("Updating client data"));
304 /* Take hostname out of nick string if it includes it. */
306 if (strchr(nickname, '@')) {
307 int len = strcspn(nickname, "@");
308 nick = silc_calloc(len + 1, sizeof(char));
309 memcpy(nick, nickname, len);
311 nick = strdup(nickname);
315 if (nickname && client->nickname)
316 silc_free(client->nickname);
319 client->nickname = nick;
321 if (username && client->username) {
322 silc_free(client->username);
323 client->username = strdup(username);
326 if (nickname && cache) {
328 silc_idcache_sort_by_data(global ? server->global_list->clients :
329 server->local_list->clients);
332 silc_free(client_id);
338 /* Received reply for forwarded IDENTIFY command. We have received the
339 requested identify information now and we will cache it. After this we
340 will call the pending command so that the requestee gets the information
343 SILC_SERVER_CMD_REPLY_FUNC(identify)
345 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
346 SilcCommandStatus status;
348 COMMAND_CHECK_STATUS_LIST;
350 if (!silc_server_command_reply_identify_save(cmd))
353 /* Execute any pending commands */
354 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
357 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
358 silc_server_command_reply_free(cmd);
361 /* Received reply for forwarded JOIN command. Router has created or joined
362 the client to the channel. We save some channel information locally
365 SILC_SERVER_CMD_REPLY_FUNC(join)
367 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
368 SilcServer server = cmd->server;
369 SilcCommandStatus status;
371 SilcChannelEntry entry;
372 unsigned int id_len, len;
373 unsigned char *id_string;
374 char *channel_name, *tmp;
375 unsigned int mode, created;
378 COMMAND_CHECK_STATUS;
380 /* Get channel name */
381 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
386 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
391 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
394 SILC_GET32_MSB(mode, tmp);
396 /* Get created boolean value */
397 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
400 SILC_GET32_MSB(created, tmp);
401 if (created != 0 && created != 1)
404 /* Get channel key */
405 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
408 keyp = silc_buffer_alloc(len);
409 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
410 silc_buffer_put(keyp, tmp, len);
412 id = silc_id_payload_parse_id(id_string, id_len);
416 /* See whether we already have the channel. */
417 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
419 /* Add new channel */
421 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
422 (created == 0 ? "existing" : "created"), channel_name,
423 silc_id_render(id, SILC_ID_CHANNEL)));
425 /* Add the channel to our local list. */
426 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
427 SILC_CHANNEL_MODE_NONE, id,
428 server->router, NULL);
437 /* If channel was not created we know there is global users on the
439 entry->global_users = (created == 0 ? TRUE : FALSE);
441 /* If channel was just created the mask must be zero */
442 if (!entry->global_users && mode) {
443 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
444 "new channel, forcing it to zero", cmd->sock->hostname));
448 /* Save channel mode */
451 /* Save channel key */
452 silc_server_save_channel_key(server, keyp, entry);
453 silc_buffer_free(keyp);
455 /* Execute any pending commands */
456 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
459 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
460 silc_server_command_reply_free(cmd);
463 SILC_SERVER_CMD_REPLY_FUNC(users)
465 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
466 SilcServer server = cmd->server;
467 SilcCommandStatus status;
468 SilcChannelEntry channel;
469 SilcChannelID *channel_id = NULL;
470 SilcBuffer client_id_list;
471 SilcBuffer client_mode_list;
473 unsigned int tmp_len;
474 unsigned int list_count, i;
476 COMMAND_CHECK_STATUS;
479 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
482 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
486 /* Get the list count */
487 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
490 SILC_GET32_MSB(list_count, tmp);
492 /* Get Client ID list */
493 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
497 client_id_list = silc_buffer_alloc(tmp_len);
498 silc_buffer_pull_tail(client_id_list, tmp_len);
499 silc_buffer_put(client_id_list, tmp, tmp_len);
501 /* Get client mode list */
502 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
506 client_mode_list = silc_buffer_alloc(tmp_len);
507 silc_buffer_pull_tail(client_mode_list, tmp_len);
508 silc_buffer_put(client_mode_list, tmp, tmp_len);
510 /* Get channel entry */
511 channel = silc_idlist_find_channel_by_id(server->local_list,
514 channel = silc_idlist_find_channel_by_id(server->global_list,
520 /* Cache the received Client ID's and modes. This cache expires
521 whenever server sends notify message to channel. It means two things;
522 some user has joined or leaved the channel. XXX! */
523 for (i = 0; i < list_count; i++) {
524 unsigned short idp_len;
526 SilcClientID *client_id;
527 SilcClientEntry client;
530 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
532 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
535 silc_buffer_pull(client_id_list, idp_len);
538 SILC_GET32_MSB(mode, client_mode_list->data);
539 silc_buffer_pull(client_mode_list, 4);
541 /* Check if we have this client cached already. */
542 client = silc_idlist_find_client_by_id(server->local_list, client_id,
545 client = silc_idlist_find_client_by_id(server->global_list,
548 /* If router did not find such Client ID in its lists then this must
549 be bogus client or some router in the net is buggy. */
550 if (server->server_type == SILC_ROUTER)
553 /* We don't have that client anywhere, add it. The client is added
554 to global list since server didn't have it in the lists so it must be
556 client = silc_idlist_add_client(server->global_list, NULL, NULL,
557 NULL, client_id, cmd->sock->user_data,
560 silc_free(client_id);
564 /* We have the client already. */
565 silc_free(client_id);
568 if (!silc_server_client_on_channel(client, channel)) {
569 /* Client was not on the channel, add it. */
570 SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
571 chl->client = client;
573 chl->channel = channel;
574 silc_list_add(channel->user_list, chl);
575 silc_list_add(client->channels, chl);
579 silc_buffer_free(client_id_list);
580 silc_buffer_free(client_mode_list);
582 /* Execute any pending commands */
583 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
586 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
588 silc_free(channel_id);
589 silc_server_command_reply_free(cmd);