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.
23 * Revision 1.10 2000/07/17 11:47:30 priikone
24 * Added command lagging support. Added idle counting support.
26 * Revision 1.9 2000/07/12 05:59:41 priikone
27 * Major rewrite of ID Cache system. Support added for the new
28 * ID cache system. Major rewrite of ID List stuff on server. All
29 * SilcXXXList's are now called SilcXXXEntry's and they are pointers
30 * by default. A lot rewritten ID list functions.
32 * Revision 1.8 2000/07/10 05:42:59 priikone
33 * Removed command packet processing from server.c and added it to
35 * Implemented INFO command. Added support for testing that
36 * connections are registered before executing commands.
38 * Revision 1.7 2000/07/07 06:55:24 priikone
39 * Do not allow client to join twice on same channel.
41 * Revision 1.6 2000/07/06 10:20:59 priikone
42 * Cipher name in joining is not mandatory, removed check.
44 * Revision 1.5 2000/07/06 07:16:43 priikone
45 * Fixed a wrong way of sending command replies. The fixed way
46 * does comply with the protocol.
48 * Revision 1.4 2000/07/05 06:13:38 priikone
49 * Added PING, INVITE and NAMES command.
51 * Revision 1.3 2000/07/03 05:52:22 priikone
52 * Implemented LEAVE command.
54 * Revision 1.2 2000/06/28 05:06:38 priikone
55 * Shorter timeout for channel joining notify.
57 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
58 * Imported from internal CVS/Added Log headers.
63 #include "serverincludes.h"
64 #include "server_internal.h"
66 static int silc_server_is_registered(SilcServer server,
67 SilcSocketConnection sock,
68 SilcServerCommandContext cmd,
71 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
73 SilcCommandStatus status);
75 silc_server_command_send_status_data(SilcServerCommandContext cmd,
77 SilcCommandStatus status,
78 unsigned int arg_type,
80 unsigned int arg_len);
81 static void silc_server_command_free(SilcServerCommandContext cmd);
83 /* Server command list. */
84 SilcServerCommand silc_command_list[] =
86 SILC_SERVER_CMD(whois, WHOIS, SILC_CF_LAG | SILC_CF_REG),
87 SILC_SERVER_CMD(whowas, WHOWAS, SILC_CF_LAG | SILC_CF_REG),
88 SILC_SERVER_CMD(identify, IDENTIFY, SILC_CF_LAG | SILC_CF_REG),
89 SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG | SILC_CF_REG),
90 SILC_SERVER_CMD(list, LIST, SILC_CF_LAG | SILC_CF_REG),
91 SILC_SERVER_CMD(topic, TOPIC, SILC_CF_LAG | SILC_CF_REG),
92 SILC_SERVER_CMD(invite, INVITE, SILC_CF_LAG | SILC_CF_REG),
93 SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
94 SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
95 SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
96 SILC_SERVER_CMD(connect, CONNECT,
97 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
98 SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
99 SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
100 SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG | SILC_CF_REG),
101 SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
102 SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
103 SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG),
104 SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG | SILC_CF_REG),
105 SILC_SERVER_CMD(restart, RESTART,
106 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
107 SILC_SERVER_CMD(close, CLOSE,
108 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
109 SILC_SERVER_CMD(die, DIE, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
110 SILC_SERVER_CMD(silcoper, SILCOPER,
111 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
112 SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG | SILC_CF_REG),
113 SILC_SERVER_CMD(names, NAMES, SILC_CF_LAG | SILC_CF_REG),
118 /* List of pending commands. */
119 SilcServerCommandPending *silc_command_pending = NULL;
121 /* Returns TRUE if the connection is registered. Unregistered connections
122 usually cannot send commands hence the check. */
124 static int silc_server_is_registered(SilcServer server,
125 SilcSocketConnection sock,
126 SilcServerCommandContext cmd,
130 case SILC_SOCKET_TYPE_CLIENT:
132 SilcClientEntry client = (SilcClientEntry)sock->user_data;
133 if (client->registered)
137 case SILC_SOCKET_TYPE_SERVER:
138 case SILC_SOCKET_TYPE_ROUTER:
140 SilcServerEntry serv = (SilcServerEntry)sock->user_data;
141 if (serv->registered)
149 silc_server_command_send_status_reply(cmd, command,
150 SILC_STATUS_ERR_NOT_REGISTERED);
151 silc_server_command_free(cmd);
155 /* Processes received command packet. */
157 void silc_server_command_process(SilcServer server,
158 SilcSocketConnection sock,
159 SilcPacketContext *packet)
161 SilcServerCommandContext ctx;
162 SilcServerCommand *cmd;
164 /* Check whether it is allowed for this connection to execute any
166 if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
168 SilcClientEntry client = (SilcClientEntry)sock->user_data;
173 /* Allow only one command executed in 2 seconds. */
174 curtime = time(NULL);
175 if (client->last_command && (curtime - client->last_command) < 2)
178 /* Update access time */
179 client->last_command = curtime;
182 /* Allocate command context. This must be free'd by the
183 command routine receiving it. */
184 ctx = silc_calloc(1, sizeof(*ctx));
185 ctx->server = server;
187 ctx->packet = packet; /* Save original packet */
189 /* Parse the command payload in the packet */
190 ctx->payload = silc_command_parse_payload(packet->buffer);
192 SILC_LOG_ERROR(("Bad command payload, packet dropped"));
193 silc_buffer_free(packet->buffer);
198 /* Execute command. If this fails the packet is dropped. */
199 for (cmd = silc_command_list; cmd->cb; cmd++)
200 if (cmd->cmd == silc_command_get(ctx->payload)) {
202 if (!(cmd->flags & SILC_CF_REG)) {
207 if (silc_server_is_registered(server, sock, ctx, cmd->cmd)) {
214 SILC_LOG_ERROR(("Unknown command, packet dropped"));
220 silc_buffer_free(packet->buffer);
223 /* Add new pending command to the list of pending commands. Currently
224 pending commands are executed from command replies, thus we can
225 execute any command after receiving some specific command reply.
227 The argument `reply_cmd' is the command reply from where the callback
228 function is to be called, thus, it IS NOT the command to be executed. */
230 void silc_server_command_pending(SilcCommand reply_cmd,
231 SilcCommandCb callback,
234 SilcServerCommandPending *reply, *r;
236 reply = silc_calloc(1, sizeof(*reply));
237 reply->reply_cmd = reply_cmd;
238 reply->context = context;
239 reply->callback = callback;
241 if (silc_command_pending == NULL) {
242 silc_command_pending = reply;
246 for (r = silc_command_pending; r; r = r->next) {
247 if (r->next == NULL) {
254 /* Deletes pending command by reply command type. */
256 void silc_server_command_pending_del(SilcCommand reply_cmd)
258 SilcServerCommandPending *r, *tmp;
260 if (silc_command_pending) {
261 if (silc_command_pending->reply_cmd == reply_cmd) {
262 silc_free(silc_command_pending);
263 silc_command_pending = NULL;
267 for (r = silc_command_pending; r; r = r->next) {
268 if (r->next && r->next->reply_cmd == reply_cmd) {
270 r->next = r->next->next;
278 /* Free's the command context allocated before executing the command */
280 static void silc_server_command_free(SilcServerCommandContext cmd)
283 silc_command_free_payload(cmd->payload);
288 #define SILC_COMMAND_STATUS_DATA(x) \
291 /* Sends simple status message as command reply packet */
294 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
296 SilcCommandStatus status)
300 SILC_LOG_DEBUG(("Sending command status %d", status));
302 buffer = silc_command_encode_reply_payload_va(command, status, 0);
303 silc_server_packet_send(cmd->server, cmd->sock,
304 SILC_PACKET_COMMAND_REPLY, 0,
305 buffer->data, buffer->len, FALSE);
306 silc_buffer_free(buffer);
309 /* Sends command status reply with one extra argument. The argument
310 type must be sent as argument. */
313 silc_server_command_send_status_data(SilcServerCommandContext cmd,
315 SilcCommandStatus status,
316 unsigned int arg_type,
318 unsigned int arg_len)
322 SILC_LOG_DEBUG(("Sending command status %d", status));
324 buffer = silc_command_encode_reply_payload_va(command, status, 1,
325 arg_type, arg, arg_len);
326 silc_server_packet_send(cmd->server, cmd->sock,
327 SILC_PACKET_COMMAND_REPLY, 0,
328 buffer->data, buffer->len, FALSE);
329 silc_buffer_free(buffer);
332 /* Server side of command WHOIS. Processes user's query and sends found
333 results as command replies back to the client. */
335 SILC_SERVER_CMD_FUNC(whois)
337 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
338 SilcServer server = cmd->server;
339 char *tmp, *nick = NULL, *server_name = NULL;
340 unsigned int argc, count = 0, len;
341 SilcClientEntry entry;
343 unsigned char *id_string;
345 SILC_LOG_DEBUG(("Start"));
347 argc = silc_command_get_arg_num(cmd->payload);
349 silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
350 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
354 silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
355 SILC_STATUS_ERR_TOO_MANY_PARAMS);
359 /* Get the nickname@server string and parse it. */
360 tmp = silc_command_get_first_arg(cmd->payload, NULL);
362 if (strchr(tmp, '@')) {
363 len = strcspn(tmp, "@");
364 nick = silc_calloc(len + 1, sizeof(char));
365 memcpy(nick, tmp, len);
366 server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
367 memcpy(server_name, tmp + len + 1, strlen(tmp) - len - 1);
372 silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
373 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
377 /* Get the max count of reply messages allowed */
379 tmp = silc_command_get_next_arg(cmd->payload, NULL);
381 silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
382 SILC_STATUS_ERR_TOO_MANY_PARAMS);
386 silc_free(server_name);
392 /* Then, make the query from our local client list */
393 entry = silc_idlist_find_client_by_nickname(server->local_list,
397 /* If we are normal server and are connected to a router we will
398 make global query from the router. */
399 if (server->server_type == SILC_SERVER && !server->standalone) {
404 /* If we are router then we will check our global list as well. */
405 if (server->server_type == SILC_ROUTER) {
407 silc_idlist_find_client_by_nickname(server->global_list,
410 silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
411 SILC_STATUS_ERR_NO_SUCH_NICK,
412 3, tmp, strlen(tmp));
418 silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
419 SILC_STATUS_ERR_NO_SUCH_NICK,
420 3, tmp, strlen(tmp));
425 /* XXX, works only for local server info */
427 /* Send WHOIS reply */
428 id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
429 tmp = silc_command_get_first_arg(cmd->payload, NULL);
432 if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
433 char nh[256], uh[256];
434 unsigned char idle[4];
435 SilcSocketConnection hsock;
437 memset(uh, 0, sizeof(uh));
438 memset(nh, 0, sizeof(nh));
440 strncat(nh, entry->nickname, strlen(entry->nickname));
442 len = entry->router ? strlen(entry->router->server_name) :
443 strlen(server->server_name);
444 strncat(nh, entry->router ? entry->router->server_name :
445 server->server_name, len);
447 strncat(uh, entry->username, strlen(entry->username));
449 hsock = (SilcSocketConnection)entry->connection;
450 len = hsock->hostname ? strlen(hsock->hostname) : strlen(hsock->ip);
451 strncat(uh, hsock->hostname ? hsock->hostname : hsock->ip, len);
453 SILC_PUT32_MSB((time(NULL) - entry->last_receive), idle);
458 silc_command_encode_reply_payload_va(SILC_COMMAND_WHOIS,
460 2, id_string, SILC_ID_CLIENT_LEN,
464 strlen(entry->userinfo),
468 silc_command_encode_reply_payload_va(SILC_COMMAND_WHOIS,
470 2, id_string, SILC_ID_CLIENT_LEN,
478 silc_command_encode_reply_payload_va(SILC_COMMAND_WHOIS,
480 2, id_string, SILC_ID_CLIENT_LEN,
482 strlen(entry->nickname),
483 4, tmp, strlen(tmp)); /* XXX */
485 silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
486 0, packet->data, packet->len, FALSE);
488 silc_free(id_string);
489 silc_buffer_free(packet);
492 silc_server_command_free(cmd);
495 SILC_SERVER_CMD_FUNC(whowas)
499 SILC_SERVER_CMD_FUNC(identify)
501 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
502 SilcServer server = cmd->server;
503 char *tmp, *nick = NULL, *server_name = NULL;
504 unsigned int argc, count = 0, len;
505 SilcClientEntry entry;
507 unsigned char *id_string;
509 SILC_LOG_DEBUG(("Start"));
511 argc = silc_command_get_arg_num(cmd->payload);
513 silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
514 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
518 silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
519 SILC_STATUS_ERR_TOO_MANY_PARAMS);
523 /* Get the nickname@server string and parse it. */
524 tmp = silc_command_get_first_arg(cmd->payload, NULL);
526 if (strchr(tmp, '@')) {
527 len = strcspn(tmp, "@");
528 nick = silc_calloc(len + 1, sizeof(char));
529 memcpy(nick, tmp, len);
530 server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
531 memcpy(server_name, tmp + len + 1, strlen(tmp) - len - 1);
536 silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
537 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
541 /* Get the max count of reply messages allowed */
543 tmp = silc_command_get_next_arg(cmd->payload, NULL);
545 silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
546 SILC_STATUS_ERR_TOO_MANY_PARAMS);
552 /* Then, make the query from our local client list */
553 entry = silc_idlist_find_client_by_nickname(server->local_list,
557 /* If we are normal server and are connected to a router we will
558 make global query from the router. */
559 if (server->server_type == SILC_SERVER && !server->standalone) {
560 SilcBuffer buffer = cmd->packet->buffer;
562 SILC_LOG_DEBUG(("Requesting identify from router"));
564 /* Send IDENTIFY command to our router */
565 silc_buffer_push(buffer, buffer->data - buffer->head);
566 silc_server_packet_forward(server, (SilcSocketConnection)
567 server->id_entry->router->connection,
568 buffer->data, buffer->len, TRUE);
572 /* If we are router then we will check our global list as well. */
573 if (server->server_type == SILC_ROUTER) {
575 silc_idlist_find_client_by_nickname(server->global_list,
578 silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
579 SILC_STATUS_ERR_NO_SUCH_NICK,
580 3, tmp, strlen(tmp));
586 silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
587 SILC_STATUS_ERR_NO_SUCH_NICK,
588 3, tmp, strlen(tmp));
593 /* Send IDENTIFY reply */
594 id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
595 tmp = silc_command_get_first_arg(cmd->payload, NULL);
596 packet = silc_command_encode_reply_payload_va(SILC_COMMAND_IDENTIFY,
600 3, nick, strlen(nick));
602 if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
603 void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
604 silc_server_packet_send_dest(server, cmd->sock,
605 SILC_PACKET_COMMAND_REPLY, 0,
606 id, cmd->packet->src_id_type,
607 packet->data, packet->len, FALSE);
611 silc_server_packet_send(server, cmd->sock,
612 SILC_PACKET_COMMAND_REPLY, 0,
613 packet->data, packet->len, FALSE);
615 silc_free(id_string);
616 silc_buffer_free(packet);
622 silc_free(server_name);
623 silc_server_command_free(cmd);
626 /* Checks string for bad characters and returns TRUE if they are found. */
628 static int silc_server_command_bad_chars(char *nick)
630 if (strchr(nick, '\\')) return TRUE;
631 if (strchr(nick, '\"')) return TRUE;
632 if (strchr(nick, '´')) return TRUE;
633 if (strchr(nick, '`')) return TRUE;
634 if (strchr(nick, '\'')) return TRUE;
635 if (strchr(nick, '*')) return TRUE;
636 if (strchr(nick, '/')) return TRUE;
637 if (strchr(nick, '@')) return TRUE;
642 /* Server side of command NICK. Sets nickname for user. Setting
643 nickname causes generation of a new client ID for the client. The
644 new client ID is sent to the client after changing the nickname. */
646 SILC_SERVER_CMD_FUNC(nick)
648 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
649 SilcClientEntry id_entry = (SilcClientEntry)cmd->sock->user_data;
650 SilcServer server = cmd->server;
652 SilcClientID *new_id;
656 SILC_LOG_DEBUG(("Start"));
658 /* Check number of arguments */
659 if (silc_command_get_arg_num(cmd->payload) < 1) {
660 silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
661 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
666 nick = silc_command_get_arg_type(cmd->payload, 1, NULL);
667 if (silc_server_command_bad_chars(nick) == TRUE) {
668 silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
669 SILC_STATUS_ERR_BAD_NICKNAME);
673 /* Create new Client ID */
674 silc_id_create_client_id(cmd->server->id, cmd->server->rng,
675 cmd->server->md5hash, nick,
678 /* Send notify about nickname change to our router. We send the new
679 ID and ask to replace it with the old one. */
680 if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone)
681 silc_server_send_replace_id(server, server->id_entry->router->connection,
683 SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
684 new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
686 /* If we are router we have to distribute the new Client ID to all
688 if (cmd->server->server_type == SILC_ROUTER && !cmd->server->standalone)
689 silc_server_send_replace_id(server, server->id_entry->router->connection,
691 SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
692 new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
694 /* Remove old cache entry */
695 silc_idcache_del_by_id(server->local_list->clients, SILC_ID_CLIENT,
700 memset(id_entry->id, 0, SILC_ID_CLIENT_LEN);
701 silc_free(id_entry->id);
704 /* Save the nickname as this client is our local client */
705 if (id_entry->nickname)
706 silc_free(id_entry->nickname);
708 id_entry->nickname = strdup(nick);
709 id_entry->id = new_id;
711 /* Update client cache */
712 silc_idcache_add(server->local_list->clients, id_entry->nickname,
713 SILC_ID_CLIENT, id_entry->id, (void *)id_entry, TRUE);
715 /* Send the new Client ID as reply command back to client */
716 id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT);
717 packet = silc_command_encode_reply_payload_va(SILC_COMMAND_NICK,
721 silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
722 0, packet->data, packet->len, FALSE);
724 silc_free(id_string);
725 silc_buffer_free(packet);
728 silc_server_command_free(cmd);
731 SILC_SERVER_CMD_FUNC(list)
735 SILC_SERVER_CMD_FUNC(topic)
739 /* Server side of INVITE command. Invites some client to join some channel. */
741 SILC_SERVER_CMD_FUNC(invite)
743 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
744 SilcServer server = cmd->server;
745 SilcSocketConnection sock = cmd->sock, dest_sock;
746 SilcClientEntry sender, dest;
747 SilcClientID *dest_id;
748 SilcChannelEntry channel;
749 SilcChannelID *channel_id;
750 unsigned int argc, len;
751 unsigned char *id_string;
753 /* Check number of arguments */
754 argc = silc_command_get_arg_num(cmd->payload);
756 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
757 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
761 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
762 SILC_STATUS_ERR_TOO_MANY_PARAMS);
766 /* Get destination ID */
767 id_string = silc_command_get_arg_type(cmd->payload, 1, &len);
769 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
770 SILC_STATUS_ERR_NO_CLIENT_ID);
773 dest_id = silc_id_str2id(id_string, SILC_ID_CLIENT);
776 id_string = silc_command_get_arg_type(cmd->payload, 2, &len);
778 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
779 SILC_STATUS_ERR_NO_CHANNEL_ID);
782 channel_id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
784 /* Check whether the channel exists */
785 channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
787 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
788 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
792 /* Check whether the sender of this command is on the channel. */
793 sender = (SilcClientEntry )sock->user_data;
794 if (!silc_server_client_on_channel(sender, channel)) {
795 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
796 SILC_STATUS_ERR_NOT_ON_CHANNEL);
800 /* Check whether the channel is invite-only channel. If yes then the
801 sender of this command must be at least channel operator. */
804 /* Find the connection data for the destination. If it is local we will
805 send it directly otherwise we will send it to router for routing. */
806 dest = silc_idlist_find_client_by_id(server->local_list, dest_id);
808 dest_sock = (SilcSocketConnection)dest->connection;
810 dest_sock = silc_server_get_route(server, dest_id, SILC_ID_CLIENT);
812 /* Check whether the requested client is already on the channel. */
813 /* XXX if we are normal server we don't know about global clients on
814 the channel thus we must request it (NAMES command), check from
815 local cache as well. */
816 if (silc_server_client_on_channel(dest, channel)) {
817 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
818 SILC_STATUS_ERR_USER_ON_CHANNEL);
822 /* Send notify to the client that is invited to the channel */
823 silc_server_send_notify_dest(server, dest_sock, dest_id, SILC_ID_CLIENT,
824 "%s invites you to channel %s",
825 sender->nickname, channel->channel_name);
827 /* Send command reply */
828 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
832 silc_server_command_free(cmd);
835 /* Quits connection to client. This gets called if client won't
836 close the connection even when it has issued QUIT command. */
838 SILC_TASK_CALLBACK(silc_server_command_quit_cb)
840 SilcServer server = (SilcServer)context;
841 SilcSocketConnection sock = server->sockets[fd];
843 /* Free all client specific data, such as client entry and entires
844 on channels this client may be on. */
845 silc_server_free_sock_user_data(server, sock);
847 /* Close the connection on our side */
848 silc_server_close_connection(server, sock);
851 /* Quits SILC session. This is the normal way to disconnect client. */
853 SILC_SERVER_CMD_FUNC(quit)
855 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
856 SilcServer server = cmd->server;
857 SilcSocketConnection sock = cmd->sock;
859 SILC_LOG_DEBUG(("Start"));
861 /* We quit the connection with little timeout */
862 silc_task_register(server->timeout_queue, sock->sock,
863 silc_server_command_quit_cb, server,
864 0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
866 silc_server_command_free(cmd);
869 SILC_SERVER_CMD_FUNC(kill)
873 /* Server side of command INFO. This sends information about us to
874 the client. If client requested specific server we will send the
875 command to that server. */
877 SILC_SERVER_CMD_FUNC(info)
879 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
880 SilcServer server = cmd->server;
883 unsigned char *id_string;
884 char info_string[256], *dest_server;
886 argc = silc_command_get_arg_num(cmd->payload);
888 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
889 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
893 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
894 SILC_STATUS_ERR_TOO_MANY_PARAMS);
898 /* Get server name */
899 dest_server = silc_command_get_arg_type(cmd->payload, 1, NULL);
901 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
902 SILC_STATUS_ERR_NO_SUCH_SERVER);
906 if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
908 memset(info_string, 0, sizeof(info_string));
909 snprintf(info_string, sizeof(info_string),
910 "location: %s server: %s admin: %s <%s>",
911 server->config->admin_info->location,
912 server->config->admin_info->server_type,
913 server->config->admin_info->admin_name,
914 server->config->admin_info->admin_email);
916 id_string = silc_id_id2str(server->id, SILC_ID_SERVER);
919 silc_command_encode_reply_payload_va(SILC_COMMAND_INFO,
921 2, id_string, SILC_ID_SERVER_LEN,
923 strlen(info_string));
924 silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
925 packet->data, packet->len, FALSE);
927 silc_free(id_string);
928 silc_buffer_free(packet);
930 /* Send this command to the requested server */
932 if (server->server_type == SILC_SERVER && !server->standalone) {
936 if (server->server_type == SILC_ROUTER) {
942 silc_server_command_free(cmd);
945 SILC_SERVER_CMD_FUNC(connect)
949 /* Server side of command PING. This just replies to the ping. */
951 SILC_SERVER_CMD_FUNC(ping)
953 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
954 SilcServer server = cmd->server;
957 unsigned char *id_string;
959 argc = silc_command_get_arg_num(cmd->payload);
961 silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
962 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
966 silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
967 SILC_STATUS_ERR_TOO_MANY_PARAMS);
972 id_string = silc_command_get_arg_type(cmd->payload, 1, NULL);
974 silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
975 SILC_STATUS_ERR_NO_SERVER_ID);
978 id = silc_id_str2id(id_string, SILC_ID_SERVER);
980 if (!SILC_ID_SERVER_COMPARE(id, server->id)) {
982 silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
985 silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
986 SILC_STATUS_ERR_NO_SUCH_SERVER);
993 silc_server_command_free(cmd);
996 SILC_SERVER_CMD_FUNC(oper)
1005 SilcChannelEntry channel;
1007 } JoinInternalContext;
1009 SILC_TASK_CALLBACK(silc_server_command_join_notify)
1011 JoinInternalContext *ctx = (JoinInternalContext *)context;
1013 if (ctx->channel->key && ctx->channel->key_len) {
1014 silc_server_send_notify_to_channel(ctx->server, ctx->channel,
1015 "%s (%s@%s) has joined channel %s",
1016 ctx->nickname, ctx->username,
1017 ctx->hostname, ctx->channel_name);
1020 silc_task_register(ctx->server->timeout_queue, fd,
1021 silc_server_command_join_notify, context,
1022 0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
1026 /* Assembles NAMES command and executes it. This is called when client
1027 joins to a channel and we wan't to send NAMES command reply to the
1030 void silc_server_command_send_names(SilcServer server,
1031 SilcSocketConnection sock,
1032 SilcChannelEntry channel)
1034 SilcServerCommandContext cmd;
1036 unsigned char *id_string;
1038 id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
1039 buffer = silc_command_encode_payload_va(SILC_COMMAND_NAMES, 1,
1040 1, id_string, SILC_ID_CHANNEL_LEN);
1042 cmd = silc_calloc(1, sizeof(*cmd));
1043 cmd->payload = silc_command_parse_payload(buffer);
1044 cmd->server = server;
1046 cmd->pending = FALSE;
1048 silc_server_command_names((void *)cmd);
1049 silc_free(id_string);
1053 /* Server side of command JOIN. Joins client into requested channel. If
1054 the channel does not exist it will be created. */
1056 SILC_SERVER_CMD_FUNC(join)
1058 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
1059 SilcServer server = cmd->server;
1060 SilcSocketConnection sock = cmd->sock;
1061 SilcBuffer buffer = cmd->packet->buffer;
1062 int argc, i, tmp_len;
1063 char *tmp, *channel_name = NULL, *cipher = NULL, *id_string = NULL;
1064 unsigned char *passphrase, mode[4];
1065 SilcChannelEntry channel;
1066 SilcServerID *router_id;
1068 SilcClientEntry client;
1070 SILC_LOG_DEBUG(("Start"));
1072 /* Check number of parameters */
1073 argc = silc_command_get_arg_num(cmd->payload);
1075 silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
1076 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1080 silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
1081 SILC_STATUS_ERR_TOO_MANY_PARAMS);
1085 /* Get channel name */
1086 tmp = silc_command_get_arg_type(cmd->payload, 1, &tmp_len);
1087 channel_name = silc_calloc(tmp_len + 1, sizeof(*channel_name));
1088 memcpy(channel_name, tmp, tmp_len);
1089 if (silc_server_command_bad_chars(tmp) == TRUE) {
1090 silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
1091 SILC_STATUS_ERR_BAD_CHANNEL);
1092 silc_free(channel_name);
1096 /* Get passphrase */
1097 tmp = silc_command_get_arg_type(cmd->payload, 2, &tmp_len);
1099 passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
1100 memcpy(passphrase, tmp, tmp_len);
1103 /* Get cipher name */
1104 cipher = silc_command_get_arg_type(cmd->payload, 3, NULL);
1106 /* See if the channel exists */
1108 silc_idlist_find_channel_by_name(server->local_list, channel_name);
1110 /* Channel not found */
1112 /* If we are standalone server we don't have a router, we just create
1113 the channel by ourselves. */
1114 if (server->standalone) {
1115 router_id = server->id;
1116 channel = silc_server_new_channel(server, router_id,
1117 cipher, channel_name);
1124 /* No channel ID found, the channel does not exist on our server.
1125 We send JOIN command to our router which will handle the joining
1126 procedure (either creates the channel if it doesn't exist or
1127 joins the client to it) - if we are normal server. */
1128 if (server->server_type == SILC_SERVER) {
1130 /* Forward the original JOIN command to the router */
1131 silc_buffer_push(buffer, buffer->data - buffer->head);
1132 silc_server_packet_forward(server, (SilcSocketConnection)
1133 server->id_entry->router->connection,
1134 buffer->data, buffer->len, TRUE);
1136 /* Add the command to be pending. It will be re-executed after
1137 router has replied back to us. */
1138 cmd->pending = TRUE;
1139 silc_server_command_pending(SILC_COMMAND_JOIN,
1140 silc_server_command_join, context);
1145 /* If we are router and the channel does not exist we will check our
1146 global list for the channel. */
1147 if (!channel && server->server_type == SILC_ROUTER) {
1149 /* Notify all routers about the new channel in SILC network. */
1150 if (!server->standalone) {
1152 silc_server_send_new_id(server, server->id_entry->router->connection,
1154 xxx, SILC_ID_CHANNEL, SILC_ID_CHANNEL_LEN);
1162 /* If the JOIN request was forwarded to us we will make a bit slower
1163 query to get the client pointer. Otherwise, we get the client pointer
1165 if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
1166 client = (SilcClientEntry)sock->user_data;
1168 void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
1169 client = silc_idlist_find_client_by_id(server->local_list, id);
1177 /* Check whether the client already is on the channel */
1178 if (silc_server_client_on_channel(client, channel)) {
1179 silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
1180 SILC_STATUS_ERR_USER_ON_CHANNEL);
1181 silc_free(channel_name);
1185 /* Join the client to the channel */
1186 i = channel->user_list_count;
1187 channel->user_list = silc_realloc(channel->user_list,
1188 sizeof(*channel->user_list) * (i + 1));
1189 channel->user_list[i].mode = SILC_CHANNEL_UMODE_NONE;
1190 channel->user_list[i].client = client;
1191 channel->user_list_count++;
1193 /* Add the channel to client's channel list */
1194 i = client->channel_count;
1195 client->channel = silc_realloc(client->channel,
1196 sizeof(*client->channel) * (i + 1));
1197 client->channel[i] = channel;
1198 client->channel_count++;
1200 /* Notify router about new user on channel. If we are normal server
1201 we send it to our router, if we are router we send it to our
1203 if (!server->standalone) {
1207 /* Send command reply to the client. Client receives the Channe ID,
1208 channel mode and possibly other information in this reply packet. */
1209 if (!cmd->pending) {
1210 id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
1211 SILC_PUT32_MSB(channel->mode, mode);
1213 if (!channel->topic)
1215 silc_command_encode_reply_payload_va(SILC_COMMAND_JOIN,
1218 strlen(channel_name),
1219 3, id_string, SILC_ID_CHANNEL_LEN,
1223 silc_command_encode_reply_payload_va(SILC_COMMAND_JOIN,
1226 strlen(channel_name),
1227 3, id_string, SILC_ID_CHANNEL_LEN,
1230 strlen(channel->topic));
1232 if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
1233 void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
1234 silc_server_packet_send_dest(cmd->server, cmd->sock,
1235 SILC_PACKET_COMMAND_REPLY, 0,
1236 id, cmd->packet->src_id_type,
1237 packet->data, packet->len, FALSE);
1240 silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
1241 packet->data, packet->len, FALSE);
1243 silc_buffer_free(packet);
1246 /* Send channel key to the client. Client cannot start transmitting
1247 to the channel until we have sent the key. */
1248 if (!cmd->pending) {
1249 tmp_len = strlen(channel->channel_key->cipher->name);
1251 silc_channel_key_encode_payload(SILC_ID_CHANNEL_LEN,
1253 channel->channel_key->cipher->name,
1254 channel->key_len / 8, channel->key);
1256 silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
1257 packet->data, packet->len, FALSE);
1258 silc_buffer_free(packet);
1262 silc_free(id_string);
1264 /* Finally, send notify message to all clients on the channel about
1265 new user on the channel. */
1266 if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
1267 if (!cmd->pending) {
1268 silc_server_send_notify_to_channel(server, channel,
1269 "%s (%s@%s) has joined channel %s",
1270 client->nickname, client->username,
1271 sock->hostname ? sock->hostname :
1272 sock->ip, channel_name);
1274 /* This is pending command request. Send the notify after we have
1275 received the key for the channel from the router. */
1276 JoinInternalContext *ctx = silc_calloc(1, sizeof(*ctx));
1277 ctx->channel_name = channel_name;
1278 ctx->nickname = client->nickname;
1279 ctx->username = client->username;
1280 ctx->hostname = sock->hostname ? sock->hostname : sock->ip;
1281 ctx->channel = channel;
1282 ctx->server = server;
1283 silc_task_register(server->timeout_queue, sock->sock,
1284 silc_server_command_join_notify, ctx,
1285 0, 10000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
1289 /* Send NAMES command reply to the joined channel so the user sees who
1290 is currently on the channel. */
1291 silc_server_command_send_names(server, sock, channel);
1294 silc_server_command_free(cmd);
1297 /* Server side of command MOTD. Sends servers current "message of the
1298 day" to the client. */
1300 SILC_SERVER_CMD_FUNC(motd)
1303 SILC_LOG_DEBUG(("Start"));
1307 SILC_SERVER_CMD_FUNC(umode)
1311 SILC_SERVER_CMD_FUNC(cmode)
1315 SILC_SERVER_CMD_FUNC(kick)
1319 SILC_SERVER_CMD_FUNC(restart)
1323 SILC_SERVER_CMD_FUNC(close)
1327 SILC_SERVER_CMD_FUNC(die)
1331 SILC_SERVER_CMD_FUNC(silcoper)
1335 /* Server side command of LEAVE. Removes client from a channel. */
1337 SILC_SERVER_CMD_FUNC(leave)
1339 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
1340 SilcServer server = cmd->server;
1341 SilcSocketConnection sock = cmd->sock;
1342 SilcClientEntry id_entry = (SilcClientEntry)cmd->sock->user_data;
1344 SilcChannelEntry channel;
1346 unsigned int i, argc, key_len;
1347 unsigned char *tmp, channel_key[32];
1349 SILC_LOG_DEBUG(("Start"));
1351 argc = silc_command_get_arg_num(cmd->payload);
1353 silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
1354 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1358 silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
1359 SILC_STATUS_ERR_TOO_MANY_PARAMS);
1363 /* Get Channel ID */
1364 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
1366 silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
1367 SILC_STATUS_ERR_NO_CHANNEL_ID);
1370 id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
1372 /* Get channel entry */
1373 channel = silc_idlist_find_channel_by_id(server->local_list, id);
1375 silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
1376 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
1380 /* Check whether this client is on the channel */
1381 if (!silc_server_client_on_channel(id_entry, channel)) {
1382 silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
1383 SILC_STATUS_ERR_NOT_ON_CHANNEL);
1387 /* Remove client from channel */
1388 i = silc_server_remove_from_one_channel(server, sock, channel, id_entry);
1389 silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
1392 /* If the channel does not exist anymore we won't send anything */
1396 /* Re-generate channel key */
1397 key_len = channel->key_len / 8;
1398 for (i = 0; i < key_len; i++)
1399 channel_key[i] = silc_rng_get_byte(server->rng);
1400 channel->channel_key->cipher->set_key(channel->channel_key->context,
1401 channel_key, key_len);
1402 memset(channel->key, 0, key_len);
1403 silc_free(channel->key);
1404 channel->key = silc_calloc(key_len, sizeof(*channel->key));
1405 memcpy(channel->key, channel_key, key_len);
1406 memset(channel_key, 0, sizeof(channel_key));
1408 /* Encode channel key payload to be distributed on the channel */
1410 silc_channel_key_encode_payload(SILC_ID_CHANNEL_LEN, tmp,
1411 strlen(channel->channel_key->cipher->name),
1412 channel->channel_key->cipher->name,
1413 key_len, channel->key);
1415 /* If we are normal server then we will send it to our router. If we
1416 are router we will send it to all local servers that has clients on
1418 if (server->server_type == SILC_SERVER) {
1419 if (!server->standalone)
1420 silc_server_packet_send(server,
1421 cmd->server->id_entry->router->connection,
1422 SILC_PACKET_CHANNEL_KEY, 0, packet->data,
1428 /* Send to locally connected clients on the channel */
1429 silc_server_packet_send_local_channel(server, channel,
1430 SILC_PACKET_CHANNEL_KEY, 0,
1431 packet->data, packet->len, FALSE);
1433 silc_buffer_free(packet);
1437 silc_server_command_free(cmd);
1440 /* Server side of command NAMES. Resolves clients and their names currently
1441 joined on the requested channel. The name list is sent back to the
1444 SILC_SERVER_CMD_FUNC(names)
1446 SilcServerCommandContext cmd = (SilcServerCommandContext)context;
1447 SilcServer server = cmd->server;
1448 SilcChannelEntry channel;
1451 unsigned int i, len, len2, argc;
1453 char *name_list = NULL, *n;
1454 SilcBuffer client_id_list;
1456 SILC_LOG_DEBUG(("Start"));
1458 argc = silc_command_get_arg_num(cmd->payload);
1460 silc_server_command_send_status_reply(cmd, SILC_COMMAND_NAMES,
1461 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1465 silc_server_command_send_status_reply(cmd, SILC_COMMAND_NAMES,
1466 SILC_STATUS_ERR_TOO_MANY_PARAMS);
1470 /* Get Channel ID */
1471 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
1473 silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
1474 SILC_STATUS_ERR_NO_CHANNEL_ID);
1477 id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
1479 /* Check whether the channel exists. If we are normal server and the
1480 channel does not exist we will send this same command to our router
1481 which will know if the channel exists. */
1482 channel = silc_idlist_find_channel_by_id(server->local_list, id);
1484 if (server->server_type == SILC_SERVER && !server->standalone) {
1485 /* XXX Send names command */
1487 cmd->pending = TRUE;
1488 silc_server_command_pending(SILC_COMMAND_NAMES,
1489 silc_server_command_names, context);
1493 silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
1494 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
1498 /* Assemble the name list now */
1501 for (i = 0; i < channel->user_list_count; i++) {
1502 if (!channel->user_list[i].client)
1505 n = channel->user_list[i].client->nickname;
1509 name_list = silc_realloc(name_list, sizeof(*name_list) * (len + 1));
1510 memcpy(name_list + (len - len2), n, len2);
1513 if (i == channel->user_list_count - 1)
1515 memcpy(name_list + len, ",", 1);
1520 /* Assemble the Client ID list now */
1521 client_id_list = silc_buffer_alloc(SILC_ID_CLIENT_LEN *
1522 channel->user_list_count);
1523 silc_buffer_pull_tail(client_id_list, (SILC_ID_CLIENT_LEN *
1524 channel->user_list_count));
1525 for (i = 0; i < channel->user_list_count; i++) {
1526 unsigned char *id_string;
1528 if (!channel->user_list[i].client)
1531 id_string = silc_id_id2str(channel->user_list[i].client->id,
1533 silc_buffer_format(client_id_list,
1534 SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
1536 silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
1537 silc_free(id_string);
1539 silc_buffer_push(client_id_list,
1540 client_id_list->data - client_id_list->head);
1543 packet = silc_command_encode_reply_payload_va(SILC_COMMAND_NAMES,
1545 2, tmp, SILC_ID_CHANNEL_LEN,
1548 4, client_id_list->data,
1549 client_id_list->len);
1550 silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
1551 packet->data, packet->len, FALSE);
1553 silc_buffer_free(packet);
1554 silc_free(name_list);
1555 silc_buffer_free(client_id_list);
1559 silc_server_command_free(cmd);