command.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2001 Pekka Riikonen
+ Copyright (C) 1997 - 2007 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
+ the Free Software Foundation; version 2 of the License.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#include "server_internal.h"
static int silc_server_is_registered(SilcServer server,
- SilcSocketConnection sock,
+ SilcPacketStream sock,
SilcServerCommandContext cmd,
SilcCommand command);
-static void
+static void
silc_server_command_send_status_reply(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status);
-static void
+ SilcStatus status,
+ SilcStatus error);
+static void
silc_server_command_send_status_data(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status,
- unsigned int arg_type,
- unsigned char *arg,
- unsigned int arg_len);
+ SilcStatus status,
+ SilcStatus error,
+ SilcUInt32 arg_type,
+ const unsigned char *arg,
+ SilcUInt32 arg_len);
+static bool
+silc_server_command_pending_error_check(SilcServerCommandContext cmd,
+ SilcServerCommandReplyContext cmdr,
+ SilcCommand command);
SILC_TASK_CALLBACK(silc_server_command_process_timeout);
/* Server command list. */
SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
- SILC_SERVER_CMD(connect, CONNECT,
- SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+ SILC_SERVER_CMD(stats, STATS, SILC_CF_LAG | SILC_CF_REG),
SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG_STRICT | SILC_CF_REG),
SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
- SILC_SERVER_CMD(restart, RESTART,
- SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
- SILC_SERVER_CMD(close, CLOSE,
- SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
- SILC_SERVER_CMD(shutdown, SHUTDOWN, SILC_CF_LAG | SILC_CF_REG |
- SILC_CF_OPER),
+ SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
+ SILC_SERVER_CMD(detach, DETACH, SILC_CF_LAG_STRICT | SILC_CF_REG),
+ SILC_SERVER_CMD(watch, WATCH, SILC_CF_LAG | SILC_CF_REG),
SILC_SERVER_CMD(silcoper, SILCOPER,
SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
+ SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
+ SILC_SERVER_CMD(service, SERVICE, SILC_CF_LAG_STRICT | SILC_CF_REG),
+
+ SILC_SERVER_CMD(connect, PRIV_CONNECT,
+ SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+ SILC_SERVER_CMD(close, PRIV_CLOSE,
+ SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+ SILC_SERVER_CMD(shutdown, PRIV_SHUTDOWN, SILC_CF_LAG | SILC_CF_REG |
+ SILC_CF_OPER),
{ NULL, 0 },
};
-#define SILC_SERVER_COMMAND_CHECK_ARGC(command, context, min, max) \
-do { \
- unsigned int _argc = silc_argument_get_arg_num(cmd->args); \
- \
- SILC_LOG_DEBUG(("Start")); \
- \
- if (_argc < min) { \
- silc_server_command_send_status_reply(cmd, command, \
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \
- silc_server_command_free(cmd); \
- return; \
- } \
- if (_argc > max) { \
- silc_server_command_send_status_reply(cmd, command, \
- SILC_STATUS_ERR_TOO_MANY_PARAMS); \
- silc_server_command_free(cmd); \
- return; \
- } \
+/* Performs several checks to the command. It first checks whether this
+ command was called as pending command callback. If it was then it checks
+ whether error occurred in the command reply where the pending command
+ callback was called.
+
+ It also checks that the requested command includes correct amount
+ of arguments. */
+#define SILC_SERVER_COMMAND_CHECK(command, context, min, max) \
+do { \
+ SilcUInt32 _argc; \
+ \
+ if (silc_server_command_pending_error_check(cmd, context2, command)) { \
+ SILC_LOG_DEBUG(("Error occurred in command reply, command not called")); \
+ silc_server_command_free(cmd); \
+ return; \
+ } \
+ \
+ _argc = silc_argument_get_arg_num(cmd->args); \
+ if (_argc < min) { \
+ SILC_LOG_DEBUG(("Not enough parameters in command")); \
+ silc_server_command_send_status_reply(cmd, command, \
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \
+ 0); \
+ silc_server_command_free(cmd); \
+ return; \
+ } \
+ if (_argc > max) { \
+ SILC_LOG_DEBUG(("Too many parameters in command")); \
+ silc_server_command_send_status_reply(cmd, command, \
+ SILC_STATUS_ERR_TOO_MANY_PARAMS, \
+ 0); \
+ silc_server_command_free(cmd); \
+ return; \
+ } \
} while(0)
/* Returns TRUE if the connection is registered. Unregistered connections
usually cannot send commands hence the check. */
static int silc_server_is_registered(SilcServer server,
- SilcSocketConnection sock,
+ SilcPacketStream sock,
SilcServerCommandContext cmd,
SilcCommand command)
{
- SilcIDListData idata = (SilcIDListData)sock->user_data;
- if (idata->registered)
+ SilcIDListData idata = silc_packet_get_context(sock);
+
+ if (!idata)
+ return FALSE;
+
+ if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
return TRUE;
silc_server_command_send_status_reply(cmd, command,
- SILC_STATUS_ERR_NOT_REGISTERED);
- silc_server_command_free(cmd);
+ SILC_STATUS_ERR_NOT_REGISTERED, 0);
return FALSE;
}
SILC_TASK_CALLBACK(silc_server_command_process_timeout)
{
SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
- SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
+ SilcClientEntry client = silc_packet_get_context(timeout->ctx->sock);
+
+ if (!client) {
+ SILC_LOG_DEBUG(("Client entry is invalid"));
+ silc_server_command_free(timeout->ctx);
+ silc_free(timeout);
+ return;
+ }
/* Update access time */
client->last_command = time(NULL);
- if (!(timeout->cmd->flags & SILC_CF_REG))
- timeout->cmd->cb(timeout->ctx);
- else if (silc_server_is_registered(timeout->ctx->server,
- timeout->ctx->sock,
- timeout->ctx,
- timeout->cmd->cmd))
- timeout->cmd->cb(timeout->ctx);
+ if (!(timeout->cmd->flags & SILC_CF_REG)) {
+ SILC_LOG_DEBUG(("Calling %s command",
+ silc_get_command_name(timeout->cmd->cmd)));
+ timeout->cmd->cb(timeout->ctx, NULL);
+ } else if (silc_server_is_registered(timeout->ctx->server,
+ timeout->ctx->sock,
+ timeout->ctx,
+ timeout->cmd->cmd)) {
+ SILC_LOG_DEBUG(("Calling %s command",
+ silc_get_command_name(timeout->cmd->cmd)));
+ timeout->cmd->cb(timeout->ctx, NULL);
+ } else {
+ SILC_LOG_DEBUG(("Client is not registered"));
+ silc_server_command_free(timeout->ctx);
+ }
silc_free(timeout);
}
/* Processes received command packet. */
void silc_server_command_process(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+ SilcPacketStream sock,
+ SilcPacket packet)
{
+ SilcIDListData idata = silc_packet_get_context(sock);
SilcServerCommandContext ctx;
SilcServerCommand *cmd;
+ SilcCommand command;
+
+ if (!idata)
+ return;
/* Allocate command context. This must be free'd by the
command routine receiving it. */
ctx = silc_server_command_alloc();
ctx->server = server;
- ctx->sock = silc_socket_dup(sock);
- ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
-
+ ctx->sock = sock;
+ ctx->packet = packet; /* Save original packet */
+ silc_packet_stream_ref(sock);
+
/* Parse the command payload in the packet */
- ctx->payload = silc_command_payload_parse(packet->buffer);
+ ctx->payload = silc_command_payload_parse(packet->buffer.data,
+ silc_buffer_len(&packet->buffer));
if (!ctx->payload) {
- SILC_LOG_ERROR(("Bad command payload, packet dropped"));
- silc_buffer_free(packet->buffer);
- silc_packet_context_free(packet);
- silc_socket_free(ctx->sock);
+ SILC_LOG_ERROR(("Bad command payload"));
+ silc_packet_free(packet);
+ silc_packet_stream_unref(ctx->sock);
silc_free(ctx);
return;
}
ctx->args = silc_command_get_args(ctx->payload);
-
+
/* Get the command */
+ command = silc_command_get(ctx->payload);
for (cmd = silc_command_list; cmd->cb; cmd++)
- if (cmd->cmd == silc_command_get(ctx->payload))
+ if (cmd->cmd == command)
break;
- if (cmd == NULL) {
- SILC_LOG_ERROR(("Unknown command, packet dropped"));
- silc_server_command_free(ctx);
+ if (!cmd || !cmd->cb) {
+ SILC_LOG_DEBUG(("Unknown command %d", command));
+ silc_server_command_send_status_reply(ctx, command,
+ SILC_STATUS_ERR_UNKNOWN_COMMAND, 0);
+ silc_packet_free(packet);
+ silc_packet_stream_unref(ctx->sock);
+ silc_free(ctx);
return;
}
executed with zero (0) timeout but if client is sending command more
frequently than once in 2 seconds, then the timeout may be 0 to 2
seconds. */
- if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
- SilcClientEntry client = (SilcClientEntry)sock->user_data;
- SilcServerCommandTimeout timeout = silc_calloc(1, sizeof(*timeout));
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ SilcClientEntry client = silc_packet_get_context(sock);
+ SilcServerCommandTimeout timeout;
int fast;
+ timeout = silc_calloc(1, sizeof(*timeout));
timeout->ctx = ctx;
timeout->cmd = cmd;
client->fast_command++;
fast = FALSE;
} else {
- client->fast_command = ((client->fast_command - 1) <= 0 ? 0 :
- client->fast_command--);
+ if (client->fast_command - 2 <= 0)
+ client->fast_command = 0;
+ else
+ client->fast_command -= 2;
fast = TRUE;
}
if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
(client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_command_process_timeout,
- (void *)timeout,
- 2 - (time(NULL) - client->last_command), 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add_timeout(
+ server->schedule,
+ silc_server_command_process_timeout, timeout,
+ (client->fast_command < 3 ? 0 :
+ 2 - (time(NULL) - client->last_command)),
+ (client->fast_command < 3 ? 200000 : 0));
else
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_command_process_timeout,
- (void *)timeout,
- 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_command_process_timeout,
+ timeout, 0, 0);
return;
}
/* Execute for server */
- if (!(cmd->flags & SILC_CF_REG))
- cmd->cb(ctx);
- else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
- cmd->cb(ctx);
+ if (!(cmd->flags & SILC_CF_REG)) {
+ SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
+ cmd->cb(ctx, NULL);
+ } else if (silc_server_is_registered(server, sock, ctx, cmd->cmd)) {
+ SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
+ cmd->cb(ctx, NULL);
+ } else {
+ SILC_LOG_DEBUG(("Server is not registered"));
+ silc_server_command_free(ctx);
+ }
}
/* Allocate Command Context */
ctx->users));
if (ctx->users < 1) {
if (ctx->payload)
- silc_command_free_payload(ctx->payload);
+ silc_command_payload_free(ctx->payload);
if (ctx->packet)
- silc_packet_context_free(ctx->packet);
+ silc_packet_free(ctx->packet);
if (ctx->sock)
- silc_socket_free(ctx->sock); /* Decrease reference counter */
+ silc_packet_stream_unref(ctx->sock);
silc_free(ctx);
}
}
/* Duplicate Command Context by adding reference counter. The context won't
be free'd untill it hits zero. */
-SilcServerCommandContext
+SilcServerCommandContext
silc_server_command_dup(SilcServerCommandContext ctx)
{
ctx->users++;
return ctx;
}
+/* Timeout for pending command. If reply to pending command never arrives
+ this is called to free resources. */
+
+SILC_TASK_CALLBACK(silc_server_command_pending_timeout)
+{
+ SilcServer server = app_context;
+ SilcServerCommandPending *reply = context;
+ SilcServerCommandReplyContext cmdr;
+ SilcBuffer tmpreply;
+ int i;
+
+ SILC_LOG_DEBUG(("Timeout pending command"));
+
+ /* Allocate temporary and bogus command reply context */
+ cmdr = silc_calloc(1, sizeof(*cmdr));
+ cmdr->server = server;
+ cmdr->ident = reply->ident;
+
+ /* Check for pending commands and mark to be exeucted */
+ cmdr->callbacks =
+ silc_server_command_pending_check(server, reply->reply_cmd,
+ reply->ident, &cmdr->callbacks_count);
+
+ /* Create bogus command reply with an error inside */
+ tmpreply =
+ silc_command_reply_payload_encode_va(reply->reply_cmd ? reply->reply_cmd :
+ SILC_COMMAND_RESERVED,
+ SILC_STATUS_ERR_TIMEDOUT, 0,
+ reply->ident, 0);
+ cmdr->payload = silc_command_payload_parse(tmpreply->data,
+ silc_buffer_len(tmpreply));
+ silc_buffer_free(tmpreply);
+
+ /* Call all callbacks. Same as SILC_SERVER_PENDING_EXEC macro. */
+ for (i = 0; i < cmdr->callbacks_count; i++)
+ if (cmdr->callbacks[i].callback)
+ (*cmdr->callbacks[i].callback)(cmdr->callbacks[i].context, cmdr);
+
+ silc_server_command_pending_del(server, reply->reply_cmd, reply->ident);
+ silc_server_command_reply_free(cmdr);
+}
+
/* Add new pending command to be executed when reply to a command has been
received. The `reply_cmd' is the command that will call the `callback'
- with `context' when reply has been received. If `ident' is non-zero
+ with `context' when reply has been received. It can be SILC_COMMAND_NONE
+ to match any command with the `ident'. If `ident' is non-zero
the `callback' will be executed when received reply with command
- identifier `ident'. */
+ identifier `ident'. If there already exists pending command for the
+ specified command, ident, callback and context this function has no
+ effect. */
-void silc_server_command_pending(SilcServer server,
+SilcBool silc_server_command_pending(SilcServer server,
SilcCommand reply_cmd,
- unsigned short ident,
- SilcServerPendingDestructor destructor,
+ SilcUInt16 ident,
SilcCommandCb callback,
void *context)
+{
+ return silc_server_command_pending_timed(server, reply_cmd, ident, callback,
+ context, 0);
+}
+
+/* Same as silc_server_command_pending with specific timeout for pending
+ commands. If the `timeout' is zero default timeout is used. */
+
+SilcBool silc_server_command_pending_timed(SilcServer server,
+ SilcCommand reply_cmd,
+ SilcUInt16 ident,
+ SilcCommandCb callback,
+ void *context,
+ SilcUInt16 timeout)
{
SilcServerCommandPending *reply;
+ /* Check whether identical pending already exists for same command,
+ ident, callback and callback context. If it does then it would be
+ error to register it again. */
+ silc_dlist_start(server->pending_commands);
+ while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+ if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
+ reply->callback == callback && reply->context == context)
+ return FALSE;
+ }
+
reply = silc_calloc(1, sizeof(*reply));
reply->reply_cmd = reply_cmd;
reply->ident = ident;
reply->context = context;
reply->callback = callback;
- reply->destructor = destructor;
+ reply->timeout =
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_command_pending_timeout, reply,
+ timeout ? timeout : 12, 0);
silc_dlist_add(server->pending_commands, reply);
+
+ return TRUE;
}
/* Deletes pending command by reply command type. */
void silc_server_command_pending_del(SilcServer server,
SilcCommand reply_cmd,
- unsigned short ident)
+ SilcUInt16 ident)
{
SilcServerCommandPending *r;
silc_dlist_start(server->pending_commands);
while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
- if (r->reply_cmd == reply_cmd && r->ident == ident) {
+ if ((r->reply_cmd == reply_cmd || (r->reply_cmd == SILC_COMMAND_NONE &&
+ r->reply_check))
+ && r->ident == ident) {
silc_dlist_del(server->pending_commands, r);
- break;
+ if (r->timeout)
+ silc_schedule_task_del(server->schedule, r->timeout);
+ silc_free(r);
}
}
}
/* Checks for pending commands and marks callbacks to be called from
the command reply function. Returns TRUE if there were pending command. */
-int silc_server_command_pending_check(SilcServer server,
- SilcServerCommandReplyContext ctx,
- SilcCommand command,
- unsigned short ident)
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+ SilcCommand command,
+ SilcUInt16 ident,
+ SilcUInt32 *callbacks_count)
{
SilcServerCommandPending *r;
+ SilcServerCommandPendingCallbacks callbacks = NULL;
+ int i = 0;
silc_dlist_start(server->pending_commands);
while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
- if (r->reply_cmd == command && r->ident == ident) {
- ctx->context = r->context;
- ctx->callback = r->callback;
- ctx->destructor = r->destructor;
- ctx->ident = ident;
- return TRUE;
+ if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
+ && r->ident == ident) {
+ callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
+ callbacks[i].context = r->context;
+ callbacks[i].callback = r->callback;
+ r->reply_check = TRUE;
+ i++;
}
}
- return FALSE;
-}
-
-/* Destructor function for pending callbacks. This is called when using
- pending commands to free the context given for the pending command. */
-
-static void silc_server_command_destructor(void *context)
-{
- silc_server_command_free((SilcServerCommandContext)context);
+ *callbacks_count = i;
+ return callbacks;
}
/* Sends simple status message as command reply packet */
-static void
+static void
silc_server_command_send_status_reply(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status)
+ SilcStatus status,
+ SilcStatus error)
{
SilcBuffer buffer;
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
SILC_LOG_DEBUG(("Sending command status %d", status));
- buffer = silc_command_reply_payload_encode_va(command, status, 0, 0);
+ buffer =
+ silc_command_reply_payload_encode_va(command, status, error,
+ silc_command_get_ident(cmd->payload),
+ 0);
silc_server_packet_send(cmd->server, cmd->sock,
- SILC_PACKET_COMMAND_REPLY, 0,
- buffer->data, buffer->len, FALSE);
+ SILC_PACKET_COMMAND_REPLY, 0,
+ buffer->data, silc_buffer_len(buffer));
silc_buffer_free(buffer);
}
/* Sends command status reply with one extra argument. The argument
type must be sent as argument. */
-static void
+static void
silc_server_command_send_status_data(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status,
- unsigned int arg_type,
- unsigned char *arg,
- unsigned int arg_len)
+ SilcStatus status,
+ SilcStatus error,
+ SilcUInt32 arg_type,
+ const unsigned char *arg,
+ SilcUInt32 arg_len)
{
SilcBuffer buffer;
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
SILC_LOG_DEBUG(("Sending command status %d", status));
- buffer = silc_command_reply_payload_encode_va(command, status, 0, 1,
- arg_type, arg, arg_len);
+ buffer =
+ silc_command_reply_payload_encode_va(command, status, 0,
+ silc_command_get_ident(cmd->payload),
+ 1, arg_type, arg, arg_len);
silc_server_packet_send(cmd->server, cmd->sock,
- SILC_PACKET_COMMAND_REPLY, 0,
- buffer->data, buffer->len, FALSE);
+ SILC_PACKET_COMMAND_REPLY, 0,
+ buffer->data, silc_buffer_len(buffer));
silc_buffer_free(buffer);
}
-/******************************************************************************
+static void
+silc_server_command_send_status_data2(SilcServerCommandContext cmd,
+ SilcCommand command,
+ SilcStatus status,
+ SilcStatus error,
+ SilcUInt32 arg_type1,
+ const unsigned char *arg1,
+ SilcUInt32 arg_len1,
+ SilcUInt32 arg_type2,
+ const unsigned char *arg2,
+ SilcUInt32 arg_len2)
+{
+ SilcBuffer buffer;
- WHOIS Functions
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
-******************************************************************************/
+ SILC_LOG_DEBUG(("Sending command status %d", status));
-static int
-silc_server_command_whois_parse(SilcServerCommandContext cmd,
- SilcClientID ***client_id,
- unsigned int *client_id_count,
- char **nickname,
- char **server_name,
- int *count,
- SilcCommand command)
-{
- unsigned char *tmp;
- unsigned int len;
- unsigned int argc = silc_argument_get_arg_num(cmd->args);
- int i, k;
+ buffer =
+ silc_command_reply_payload_encode_va(command, status, 0,
+ silc_command_get_ident(cmd->payload),
+ 2, arg_type1, arg1, arg_len1,
+ arg_type2, arg2, arg_len2);
+ silc_server_packet_send(cmd->server, cmd->sock,
+ SILC_PACKET_COMMAND_REPLY, 0,
+ buffer->data, silc_buffer_len(buffer));
+ silc_buffer_free(buffer);
+}
- /* If client ID is in the command it must be used instead of nickname */
- tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
- if (!tmp) {
- /* No ID, get the nickname@server string and parse it. */
- tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
- if (tmp) {
- if (strchr(tmp, '@')) {
- len = strcspn(tmp, "@");
- *nickname = silc_calloc(len + 1, sizeof(char));
- memcpy(*nickname, tmp, len);
- *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
- memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
- } else {
- *nickname = strdup(tmp);
- }
- } else {
- silc_server_command_send_status_reply(cmd, command,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return FALSE;
- }
- } else {
- /* Command includes ID, we must use that. Also check whether the command
- has more than one ID set - take them all. */
+/* This function can be called to check whether in the command reply
+ an error occurred. This function has no effect if this is called
+ when the command function was not called as pending command callback.
+ This returns TRUE if error had occurred. */
- *client_id = silc_calloc(1, sizeof(**client_id));
- (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
- if ((*client_id)[0] == NULL) {
- silc_free(*client_id);
- return FALSE;
- }
- *client_id_count = 1;
-
- /* Take all ID's from the command packet */
- if (argc > 1) {
- for (k = 1, i = 1; i < argc; i++) {
- tmp = silc_argument_get_arg_type(cmd->args, i + 3, &len);
- if (tmp) {
- *client_id = silc_realloc(*client_id, sizeof(**client_id) *
- (*client_id_count + 1));
- (*client_id)[k] = silc_id_payload_parse_id(tmp, len);
- if ((*client_id)[k] == NULL) {
- /* Cleanup all and fail */
- for (i = 0; i < *client_id_count; i++)
- silc_free((*client_id)[i]);
- silc_free(*client_id);
- return FALSE;
- }
- (*client_id_count)++;
- k++;
- }
- }
- }
+static bool
+silc_server_command_pending_error_check(SilcServerCommandContext cmd,
+ SilcServerCommandReplyContext cmdr,
+ SilcCommand command)
+{
+ if (!cmd->pending || !cmdr)
+ return FALSE;
- /* Command includes ID, use that */
+ if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
+ SilcBuffer buffer;
+
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
+ /* Send the same command reply payload */
+ silc_command_set_command(cmdr->payload, silc_command_get(cmd->payload));
+ silc_command_set_ident(cmdr->payload,
+ silc_command_get_ident(cmd->payload));
+ buffer = silc_command_payload_encode_payload(cmdr->payload);
+ silc_server_packet_send(cmd->server, cmd->sock,
+ SILC_PACKET_COMMAND_REPLY, 0,
+ buffer->data, silc_buffer_len(buffer));
+ silc_buffer_free(buffer);
+ return TRUE;
}
- /* Get the max count of reply messages allowed */
- tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (tmp)
- *count = atoi(tmp);
- else
- *count = 0;
+ return FALSE;
+}
- return TRUE;
+/* Server side of command WHOIS. */
+
+SILC_SERVER_CMD_FUNC(whois)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 256);
+ silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd, NULL);
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command WHOWAS. */
+
+SILC_SERVER_CMD_FUNC(whowas)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+ silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd, NULL);
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command IDENTIFY. */
+
+SILC_SERVER_CMD_FUNC(identify)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 256);
+ silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd, NULL);
+ silc_server_command_free(cmd);
}
-static char
-silc_server_command_whois_check(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- unsigned int clients_count)
+/* Server side of command NICK. Sets nickname for user. Setting
+ nickname causes generation of a new client ID for the client. The
+ new client ID is sent to the client after changing the nickname. */
+
+SILC_SERVER_CMD_FUNC(nick)
{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
SilcServer server = cmd->server;
- int i;
- SilcClientEntry entry;
+ SilcBuffer nidp, oidp = NULL;
+ SilcClientID *new_id;
+ SilcUInt32 nick_len;
+ unsigned char *nick, *nickc = NULL;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+ goto out;
- if (!entry->nickname || !entry->username) {
- SilcBuffer tmpbuf;
- unsigned short old_ident;
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
- if (!entry->router)
- continue;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+ /* Get nickname */
+ nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
+ if (!nick) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+ SILC_STATUS_ERR_BAD_NICKNAME, 0);
+ goto out;
+ }
- /* Send WHOIS command */
- silc_server_packet_send(server, entry->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
- silc_command_get_ident(cmd->payload),
- silc_server_command_destructor,
- silc_server_command_whois,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
-
- silc_command_set_ident(cmd->payload, old_ident);
+ /* Truncate over long nicks */
+ if (nick_len > 128) {
+ nick_len = 128;
+ nick[nick_len - 1] = '\0';
+ }
- silc_buffer_free(tmpbuf);
- return FALSE;
- }
+ /* Check for valid nickname string. This is cached, original is saved
+ in the client context. */
+ nickc = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, NULL);
+ if (!nickc) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+ SILC_STATUS_ERR_BAD_NICKNAME, 0);
+ goto out;
}
- return TRUE;
+ /* Check for same nickname */
+ if (strlen(client->nickname) == nick_len &&
+ !memcmp(client->nickname, nick, nick_len)) {
+ nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ silc_free(nickc);
+ goto send_reply;
+ }
+
+ /* Create new Client ID */
+ if (!silc_id_create_client_id(cmd->server, cmd->server->id,
+ cmd->server->rng,
+ cmd->server->md5hash,
+ nickc, strlen(nickc), &new_id)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+ SILC_STATUS_ERR_BAD_NICKNAME, 0);
+ silc_free(nickc);
+ goto out;
+ }
+
+ /* Send notify about nickname change to our router. We send the new
+ ID and ask to replace it with the old one. If we are router the
+ packet is broadcasted. Send NICK_CHANGE notify. */
+ silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), client->id,
+ new_id, nick);
+
+ /* Check if anyone is watching the old nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, nick,
+ SILC_NOTIFY_TYPE_NICK_CHANGE);
+
+ oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+ /* Update client entry */
+ silc_idcache_update_by_context(server->local_list->clients, client,
+ new_id, nickc, TRUE);
+ silc_free(new_id);
+ silc_free(client->nickname);
+ client->nickname = strdup(nick);
+
+ nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+ /* Send NICK_CHANGE notify to the client's channels */
+ silc_server_send_notify_on_channels(server, NULL, client,
+ SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+ oidp->data, silc_buffer_len(oidp),
+ nidp->data, silc_buffer_len(nidp),
+ client->nickname,
+ strlen(client->nickname));
+
+ /* Check if anyone is watching the new nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_NICK_CHANGE);
+
+ send_reply:
+ /* Send the new Client ID as reply command back to client */
+ silc_server_send_command_reply(cmd->server, cmd->sock,
+ SILC_COMMAND_NICK,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, nidp->data, silc_buffer_len(nidp),
+ 3, nick, nick_len);
+ silc_buffer_free(nidp);
+ if (oidp)
+ silc_buffer_free(oidp);
+
+ out:
+ silc_server_command_free(cmd);
}
+/* Sends the LIST command reply */
+
static void
-silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- unsigned int clients_count)
+silc_server_command_list_send_reply(SilcServerCommandContext cmd,
+ SilcChannelEntry *lch,
+ SilcUInt32 lch_count,
+ SilcChannelEntry *gch,
+ SilcUInt32 gch_count)
{
- SilcServer server = cmd->server;
- char *tmp;
- int i, count = 0, len;
- SilcBuffer packet, idp;
- SilcClientEntry entry;
- SilcCommandStatus status;
- unsigned short ident = silc_command_get_ident(cmd->payload);
+ int i, k;
+ SilcBuffer idp;
+ SilcChannelEntry entry;
+ SilcStatus status;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ char *topic;
+ unsigned char usercount[4];
+ SilcUInt32 users;
+ int valid_lcount = 0, valid_rcount = 0;
+
+ for (i = 0; i < lch_count; i++) {
+ if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+ lch[i] = NULL;
+ else
+ valid_lcount++;
+ }
+ for (i = 0; i < gch_count; i++) {
+ if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+ gch[i] = NULL;
+ else
+ valid_rcount++;
+ }
+
+ if (!lch_count && !gch_count) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
+ SILC_STATUS_OK, 0);
+ return;
+ }
status = SILC_STATUS_OK;
- if (clients_count > 1)
+ if ((lch_count + gch_count) > 1)
status = SILC_STATUS_LIST_START;
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
-
- if (entry->connection && entry->data.registered == FALSE) {
- if (clients_count == 1)
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, entry->nickname,
- strlen(entry->nickname));
+ /* Local list */
+ for (i = 0, k = 0; i < lch_count; i++) {
+ entry = lch[i];
+ if (!entry)
continue;
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
+ status = SILC_STATUS_LIST_END;
+
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+
+ if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
+ topic = "*private*";
+ memset(usercount, 0, sizeof(usercount));
+ } else {
+ topic = entry->topic;
+ users = silc_hash_table_count(entry->user_list);
+ SILC_PUT32_MSB(users, usercount);
}
- if (count && i - 1 == count)
- break;
+ /* Send the reply */
+ silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_LIST,
+ status, 0, ident, 4,
+ 2, idp->data, silc_buffer_len(idp),
+ 3, entry->channel_name,
+ strlen(entry->channel_name),
+ 4, topic, topic ? strlen(topic) : 0,
+ 5, usercount, 4);
+ silc_buffer_free(idp);
+ k++;
+ }
- if (clients_count > 2)
- status = SILC_STATUS_LIST_ITEM;
+ /* Global list */
+ for (i = 0, k = 0; i < gch_count; i++) {
+ entry = gch[i];
+ if (!entry)
+ continue;
- if (clients_count > 1 && i == clients_count - 1)
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (valid_rcount > 1 && k == valid_rcount - 1)
status = SILC_STATUS_LIST_END;
- /* Sanity check, however these should never fail. However, as
- this sanity check has been added here they have failed. */
- if (!entry->nickname || !entry->username)
- continue;
-
- /* Send WHOIS reply */
- idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
- tmp = silc_argument_get_first_arg(cmd->args, NULL);
-
- {
- char nh[256], uh[256];
- unsigned char idle[4];
- SilcSocketConnection hsock;
-
- memset(uh, 0, sizeof(uh));
- memset(nh, 0, sizeof(nh));
-
- strncat(nh, entry->nickname, strlen(entry->nickname));
- if (!strchr(entry->nickname, '@')) {
- strncat(nh, "@", 1);
- len = entry->router ? strlen(entry->router->server_name) :
- strlen(server->server_name);
- strncat(nh, entry->router ? entry->router->server_name :
- server->server_name, len);
- }
-
- strncat(uh, entry->username, strlen(entry->username));
- if (!strchr(entry->username, '@')) {
- strncat(uh, "@", 1);
- hsock = (SilcSocketConnection)entry->connection;
- len = strlen(hsock->hostname);
- strncat(uh, hsock->hostname, len);
- }
-
- SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
-
- packet =
- silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
- status, ident, 5,
- 2, idp->data, idp->len,
- 3, nh, strlen(nh),
- 4, uh, strlen(uh),
- 5, entry->userinfo,
- strlen(entry->userinfo),
- 7, idle, 4);
- }
-
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
- 0, packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+
+ if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
+ topic = "*private*";
+ memset(usercount, 0, sizeof(usercount));
+ } else {
+ topic = entry->topic;
+ users = entry->user_count;
+ SILC_PUT32_MSB(users, usercount);
+ }
+
+ /* Send the reply */
+ silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_LIST,
+ status, 0, ident, 4,
+ 2, idp->data, silc_buffer_len(idp),
+ 3, entry->channel_name,
+ strlen(entry->channel_name),
+ 4, topic, topic ? strlen(topic) : 0,
+ 5, usercount, 4);
silc_buffer_free(idp);
+ k++;
}
}
-static int
-silc_server_command_whois_from_client(SilcServerCommandContext cmd)
+/* Server side of LIST command. This lists the channel of the requested
+ server. Secret channels are not listed. */
+
+SILC_SERVER_CMD_FUNC(list)
{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0, clients_count = 0;
- SilcClientEntry *clients = NULL, entry;
- SilcClientID **client_id = NULL;
- unsigned int client_id_count = 0;
- int i, ret = 0;
-
- /* Protocol dictates that we must always send the received WHOIS request
- to our router if we are normal server, so let's do it now unless we
- are standalone. We will not send any replies to the client until we
- have received reply from the router. */
- if (server->server_type == SILC_SERVER &&
- !cmd->pending && !server->standalone) {
+ SilcID id;
+ SilcChannelID *channel_id = NULL;
+ SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
+ SilcUInt32 lch_count = 0, gch_count = 0;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
+
+ /* If we are normal server, send the command to router, since we
+ want to know all channels in the network. */
+ if (!cmd->pending && server->server_type != SILC_ROUTER &&
+ !server->standalone) {
SilcBuffer tmpbuf;
- unsigned short old_ident;
+ SilcUInt16 old_ident;
+
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send WHOIS command to our router */
- silc_server_packet_send(server, (SilcSocketConnection)
- server->router->connection,
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
+ tmpbuf->data, silc_buffer_len(tmpbuf));
/* Reprocess this packet after received reply from router */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ silc_server_command_pending(server, SILC_COMMAND_LIST,
silc_command_get_ident(cmd->payload),
- silc_server_command_destructor,
- silc_server_command_whois,
+ silc_server_command_list,
silc_server_command_dup(cmd));
cmd->pending = TRUE;
-
silc_command_set_ident(cmd->payload, old_ident);
-
silc_buffer_free(tmpbuf);
- ret = -1;
goto out;
}
- /* We are ready to process the command request. Let's search for the
- requested client and send reply to the requesting client. */
+ /* Get Channel ID */
+ if (silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL))
+ channel_id = SILC_ID_GET_ID(id);
- /* Parse the whois request */
- if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
- &nick, &server_name, &count,
- SILC_COMMAND_WHOIS))
- return 0;
+ /* Get the channels from local list */
+ lchannels = silc_idlist_get_channels(server->local_list, channel_id,
+ &lch_count);
- /* Get all clients matching that ID or nickname from local list */
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->local_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
- }
- } else {
- clients = silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients_count);
- }
-
- /* Check global list as well */
- if (!clients) {
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->global_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
- }
- } else {
- clients = silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients_count);
- }
- }
-
- if (!clients) {
- /* Such client(s) really does not exist in the SILC network. */
- if (!client_id_count) {
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
- } else {
- SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
- 2, idp->data, idp->len);
- silc_buffer_free(idp);
- }
- goto out;
- }
+ /* Get the channels from global list */
+ gchannels = silc_idlist_get_channels(server->global_list, channel_id,
+ &gch_count);
- /* Router always finds the client entry if it exists in the SILC network.
- However, it might be incomplete entry and does not include all the
- mandatory fields that WHOIS command reply requires. Check for these and
- make query from the server who owns the client if some fields are
- missing. */
- if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
- ret = -1;
- goto out;
- }
+ /* Send the reply */
+ silc_server_command_list_send_reply(cmd, lchannels, lch_count,
+ gchannels, gch_count);
- /* Send the command reply to the client */
- silc_server_command_whois_send_reply(cmd, clients, clients_count);
+ silc_free(lchannels);
+ silc_free(gchannels);
out:
- if (client_id_count) {
- for (i = 0; i < client_id_count; i++)
- silc_free(client_id[i]);
- silc_free(client_id);
- }
- if (clients)
- silc_free(clients);
- if (nick)
- silc_free(nick);
- if (server_name)
- silc_free(server_name);
-
- return ret;
+ silc_server_command_free(cmd);
}
-static int
-silc_server_command_whois_from_server(SilcServerCommandContext cmd)
+/* Server side of TOPIC command. Sets topic for channel and/or returns
+ current topic to client. */
+
+SILC_SERVER_CMD_FUNC(topic)
{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0, clients_count = 0;
- SilcClientEntry *clients = NULL, entry;
- SilcClientID **client_id = NULL;
- unsigned int client_id_count = 0;
- int i, ret = 0;
-
- /* Parse the whois request */
- if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
- &nick, &server_name, &count,
- SILC_COMMAND_WHOIS))
- return 0;
-
- /* Process the command request. Let's search for the requested client and
- send reply to the requesting server. */
-
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->local_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
- }
- } else {
- clients = silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients_count);
- }
-
- /* If we are router we will check our global list as well. */
- if (!clients && server->server_type == SILC_ROUTER) {
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->global_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
- }
- } else {
- clients = silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients_count);
- }
- }
-
- if (!clients) {
- /* Such a client really does not exist in the SILC network. */
- if (!client_id_count) {
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
- } else {
- SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
- 2, idp->data, idp->len);
- silc_buffer_free(idp);
- }
- goto out;
- }
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ SilcID id;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ SilcBuffer idp;
+ unsigned char *tmp;
+ SilcUInt32 argc, tmp_len;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
- /* Router always finds the client entry if it exists in the SILC network.
- However, it might be incomplete entry and does not include all the
- mandatory fields that WHOIS command reply requires. Check for these and
- make query from the server who owns the client if some fields are
- missing. */
- if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
- ret = -1;
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
goto out;
- }
- /* Send the command reply to the client */
- silc_server_command_whois_send_reply(cmd, clients, clients_count);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
- out:
- if (client_id_count) {
- for (i = 0; i < client_id_count; i++)
- silc_free(client_id[i]);
- silc_free(client_id);
- }
- if (clients)
- silc_free(clients);
- if (nick)
- silc_free(nick);
- if (server_name)
- silc_free(server_name);
+ argc = silc_argument_get_arg_num(cmd->args);
- return ret;
-}
+ /* Get Channel ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+ goto out;
+ }
-/* Server side of command WHOIS. Processes user's query and sends found
- results as command replies back to the client. */
+ /* Check whether the channel exists */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
+ }
-SILC_SERVER_CMD_FUNC(whois)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- int ret = 0;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOIS, cmd, 1, 3328);
-
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
- ret = silc_server_command_whois_from_client(cmd);
- else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
- (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
- ret = silc_server_command_whois_from_server(cmd);
-
- if (!ret)
- silc_server_command_free(cmd);
-}
-
-/******************************************************************************
+ if (argc > 1) {
+ /* Get the topic */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
+ if (tmp_len > 256) {
+ tmp_len = 256;
+ tmp[tmp_len - 1] = '\0';
+ }
- WHOWAS Functions
+ if (!silc_utf8_valid(tmp, tmp_len)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
-******************************************************************************/
+ /* See whether the client is on channel and has rights to change topic */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
-static int
-silc_server_command_whowas_parse(SilcServerCommandContext cmd,
- char **nickname,
- char **server_name,
- int *count)
-{
- unsigned char *tmp;
- unsigned int len;
+ if (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
- tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return FALSE;
+ if (!channel->topic || strcmp(channel->topic, tmp)) {
+ /* Set the topic for channel */
+ silc_free(channel->topic);
+ channel->topic = strdup(tmp);
+
+ /* Send TOPIC_SET notify type to the network */
+ silc_server_send_notify_topic_set(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel,
+ client->id, SILC_ID_CLIENT,
+ channel->topic);
+
+ /* Send notify about topic change to all clients on the channel */
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+ idp->data, silc_buffer_len(idp),
+ channel->topic,
+ strlen(channel->topic));
+ silc_buffer_free(idp);
+ }
}
- /* Get the nickname@server string and parse it. */
- if (strchr(tmp, '@')) {
- len = strcspn(tmp, "@");
- *nickname = silc_calloc(len + 1, sizeof(char));
- memcpy(*nickname, tmp, len);
- *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
- memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
- } else {
- *nickname = strdup(tmp);
- }
- /* Get the max count of reply messages allowed */
- tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (tmp)
- *count = atoi(tmp);
- else
- *count = 0;
+ /* Send the topic to client as reply packet */
+ idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL);
+ silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_TOPIC,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, idp->data, silc_buffer_len(idp),
+ 3, channel->topic,
+ channel->topic ?
+ strlen(channel->topic) : 0);
+ silc_buffer_free(idp);
- return TRUE;
+ out:
+ silc_server_command_free(cmd);
}
-static void
-silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- unsigned int clients_count)
+/* Server side of INVITE command. Invites some client to join some channel.
+ This command is also used to manage the invite list of the channel. */
+
+SILC_SERVER_CMD_FUNC(invite)
{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- char *tmp;
- int i, count = 0, len;
- SilcBuffer packet, idp;
- SilcClientEntry entry = NULL;
- SilcCommandStatus status;
- unsigned short ident = silc_command_get_ident(cmd->payload);
- char found = FALSE;
+ SilcPacketStream sock = cmd->sock, dest_sock;
+ SilcChannelClientEntry chl;
+ SilcClientEntry sender, dest;
+ SilcChannelEntry channel;
+ SilcID id, id2;
+ SilcIDListData idata;
+ SilcArgumentPayload args;
+ SilcHashTableList htl;
+ SilcBuffer list, tmp2;
+ SilcBufferStruct alist;
+ unsigned char *tmp, *atype = NULL;
+ SilcUInt32 len, type, len2;
+ SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
- status = SILC_STATUS_OK;
- if (clients_count > 1)
- status = SILC_STATUS_LIST_START;
+ /* Get Channel ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+ goto out;
+ }
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
+ /* Get the channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 0, 2, tmp, len);
+ goto out;
+ }
+ }
- /* We will take only clients that are not valid anymore. They are the
- ones that are not registered anymore but still have a ID. They
- have disconnected us, and thus valid for WHOWAS. */
- if (entry->data.registered == TRUE)
- continue;
- if (entry->id == NULL)
- continue;
+ /* Check whether the sender of this command is on the channel. */
+ sender = silc_packet_get_context(sock);
+ if (!sender || !silc_server_client_on_channel(sender, channel, &chl)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+ 2, tmp, len);
+ goto out;
+ }
- if (count && i - 1 == count)
- break;
+ /* Check whether the channel is invite-only channel. If yes then the
+ sender of this command must be at least channel operator. */
+ if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+ 0, 2, tmp, len);
+ goto out;
+ }
- found = TRUE;
+ /* Get destination client ID */
+ if (silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id2, NULL)) {
+ SilcBool resolve;
+
+ /* Get the client entry */
+ dest = silc_server_query_client(server, SILC_ID_GET_ID(id2),
+ FALSE, &resolve);
+ if (!dest) {
+ if (server->server_type != SILC_SERVER || !resolve || cmd->pending) {
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0,
+ 2, tmp, len);
+ goto out;
+ }
- if (clients_count > 2)
- status = SILC_STATUS_LIST_ITEM;
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_invite,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ goto out;
+ }
- if (clients_count > 1 && i == clients_count - 1)
- status = SILC_STATUS_LIST_END;
+ /* Check whether the requested client is already on the channel. */
+ if (silc_server_client_on_channel(dest, channel, NULL)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+ atype = silc_argument_get_arg_type(cmd->args, 1, &len2);
+ silc_server_command_send_status_data2(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_USER_ON_CHANNEL,
+ 0, 2, tmp, len,
+ 3, atype, len2);
+ goto out;
+ }
- /* Sanity check, however these should never fail. However, as
- this sanity check has been added here they have failed. */
- if (!entry->nickname || !entry->username)
- continue;
-
- /* Send WHOWAS reply */
- idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
- tmp = silc_argument_get_first_arg(cmd->args, NULL);
-
- {
- char nh[256], uh[256];
-
- memset(uh, 0, sizeof(uh));
- memset(nh, 0, sizeof(nh));
-
- strncat(nh, entry->nickname, strlen(entry->nickname));
- if (!strchr(entry->nickname, '@')) {
- strncat(nh, "@", 1);
- len = entry->router ? strlen(entry->router->server_name) :
- strlen(server->server_name);
- strncat(nh, entry->router ? entry->router->server_name :
- server->server_name, len);
- }
-
- strncat(uh, entry->username, strlen(entry->username));
- if (!strchr(entry->username, '@')) {
- strncat(uh, "@", 1);
- strcat(uh, "*private*");
- }
-
- packet =
- silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
- status, ident, 4,
- 2, idp->data, idp->len,
- 3, nh, strlen(nh),
- 4, uh, strlen(uh),
- 5, entry->userinfo,
- strlen(entry->userinfo));
- }
-
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
- 0, packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_buffer_free(idp);
- }
+ /* Get route to the client */
+ dest_sock = silc_server_get_client_route(server, NULL, 0,
+ SILC_ID_GET_ID(id2),
+ &idata, NULL);
+ if (!dest_sock) {
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 0, 2, tmp, len);
+ goto out;
+ }
- if (found == FALSE && entry)
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, entry->nickname,
- strlen(entry->nickname));
-}
+ /* Add the client to the invite list */
+
+ /* Allocate hash table for invite list if it doesn't exist yet */
+ if (!channel->invite_list)
+ channel->invite_list =
+ silc_hash_table_alloc(0, silc_hash_ptr,
+ NULL, NULL, NULL,
+ silc_server_inviteban_destruct, channel, TRUE);
+
+ /* Check if the ID is in the list already */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+ silc_hash_table_list(channel->invite_list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2)) {
+ if (type == 3 && !memcmp(tmp2->data, tmp, len)) {
+ tmp = NULL;
+ break;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
-static int
-silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
-{
- SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0, clients_count = 0;
- SilcClientEntry *clients = NULL;
- int ret = 0;
-
- /* Protocol dictates that we must always send the received WHOWAS request
- to our router if we are normal server, so let's do it now unless we
- are standalone. We will not send any replies to the client until we
- have received reply from the router. */
- if (server->server_type == SILC_SERVER &&
- !cmd->pending && !server->standalone) {
- SilcBuffer tmpbuf;
- unsigned short old_ident;
+ /* Add new Client ID to invite list */
+ if (tmp) {
+ list = silc_buffer_alloc_size(len);
+ silc_buffer_put(list, tmp, len);
+ silc_hash_table_add(channel->invite_list, (void *)3, list);
+ }
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+ if (!(dest->mode & SILC_UMODE_BLOCK_INVITE)) {
+ /* Send notify to the client that is invited to the channel */
+ SilcBuffer idp, idp2;
+ idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL);
+ idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+ silc_server_send_notify_dest(server, dest_sock, FALSE,
+ SILC_ID_GET_ID(id2), SILC_ID_CLIENT,
+ SILC_NOTIFY_TYPE_INVITE, 3,
+ idp->data, silc_buffer_len(idp),
+ channel->channel_name,
+ strlen(channel->channel_name),
+ idp2->data, silc_buffer_len(idp2));
+ silc_buffer_free(idp);
+ silc_buffer_free(idp2);
+ }
+ }
- /* Send WHOWAS command to our router */
- silc_server_packet_send(server, (SilcSocketConnection)
- server->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
+ /* Get the invite information */
+ tmp = silc_argument_get_arg_type(cmd->args, 4, &len2);
+ if (tmp && len2 > 2) {
+ /* Parse the arguments to see they are constructed correctly */
+ SILC_GET16_MSB(argc, tmp);
+ args = silc_argument_payload_parse(tmp + 2, len2 - 2, argc);
+ if (!args) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
- /* Reprocess this packet after received reply from router */
- silc_server_command_pending(server, SILC_COMMAND_WHOWAS,
- silc_command_get_ident(cmd->payload),
- silc_server_command_destructor,
- silc_server_command_whois,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
+ /* Get the type of action */
+ atype = silc_argument_get_arg_type(cmd->args, 3, &len);
+ if (atype && len == 1) {
+ if (atype[0] == 0x00) {
+ /* Allocate hash table for invite list if it doesn't exist yet */
+ if (!channel->invite_list)
+ channel->invite_list =
+ silc_hash_table_alloc(0, silc_hash_ptr,
+ NULL, NULL, NULL,
+ silc_server_inviteban_destruct, channel,
+ TRUE);
+
+ /* Check for resource limit */
+ if (silc_hash_table_count(channel->invite_list) > 64) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ goto out;
+ }
+ }
- silc_command_set_ident(cmd->payload, old_ident);
+ /* Now add or delete the information. */
+ if (!silc_server_inviteban_process(server, channel->invite_list,
+ (SilcUInt8)atype[0], args)) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
+ }
+ silc_argument_payload_free(args);
+ }
- silc_buffer_free(tmpbuf);
- ret = -1;
- goto out;
+ /* Encode invite list */
+ list = NULL;
+ if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+ list = silc_buffer_alloc_size(2);
+ silc_buffer_format(list,
+ SILC_STR_UI_SHORT(silc_hash_table_count(
+ channel->invite_list)),
+ SILC_STR_END);
+ silc_hash_table_list(channel->invite_list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
+ list = silc_argument_payload_encode_one(list, tmp2->data,
+ silc_buffer_len(tmp2), type);
+ silc_hash_table_list_reset(&htl);
}
- /* We are ready to process the command request. Let's search for the
- requested client and send reply to the requesting client. */
+ /* The notify is sent to local servers (not clients), and to network. */
+ if (atype && tmp && len2) {
+ silc_buffer_set(&alist, tmp, len2);
- /* Parse the whowas request */
- if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
- return 0;
+ /* Send to local servers if we are router */
+ if (server->server_type == SILC_ROUTER) {
+ SilcBuffer idp, idp2;
+ idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL);
+ idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, FALSE,
+ SILC_NOTIFY_TYPE_INVITE, 5,
+ idp->data, silc_buffer_len(idp),
+ channel->channel_name,
+ strlen(channel->channel_name),
+ idp2->data, silc_buffer_len(idp2),
+ atype, 1,
+ tmp ? alist.data : NULL,
+ tmp ? silc_buffer_len(&alist) : 0);
+ silc_buffer_free(idp);
+ silc_buffer_free(idp2);
+ }
- /* Get all clients matching that nickname from local list */
- clients = silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients_count);
-
- /* Check global list as well */
- if (!clients) {
- clients = silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients_count);
- }
-
- if (!clients) {
- /* Such client(s) really does not exist in the SILC network. */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
- goto out;
+ /* Send to network */
+ silc_server_send_notify_invite(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel,
+ sender->id, atype,
+ tmp ? &alist : NULL);
}
- /* Send the command reply to the client */
- silc_server_command_whowas_send_reply(cmd, clients, clients_count);
+ /* Send invite list back only if the list was modified, or no arguments
+ was given. */
+ type = 0;
+ argc = silc_argument_get_arg_num(cmd->args);
+ if (argc == 1)
+ type = 1;
+ if (silc_argument_get_arg_type(cmd->args, 3, &len))
+ type = 1;
- out:
- if (clients)
- silc_free(clients);
- if (nick)
- silc_free(nick);
- if (server_name)
- silc_free(server_name);
+ /* Send command reply */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_INVITE,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, tmp, len,
+ 3, type && list ?
+ list->data : NULL,
+ type && list ? silc_buffer_len(list) : 0);
+ silc_buffer_free(list);
- return ret;
+ out:
+ silc_server_command_free(cmd);
}
-static int
-silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
+typedef struct {
+ SilcPacketStream sock;
+ char *signoff;
+} *QuitInternal;
+
+/* Quits connection to client. This gets called if client won't
+ close the connection even when it has issued QUIT command. */
+
+SILC_TASK_CALLBACK(silc_server_command_quit_cb)
{
- SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0, clients_count = 0;
- SilcClientEntry *clients = NULL;
- int ret = 0;
-
- /* Parse the whowas request */
- if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
- return 0;
-
- /* Process the command request. Let's search for the requested client and
- send reply to the requesting server. */
-
- clients = silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients_count);
-
- /* If we are router we will check our global list as well. */
- if (!clients && server->server_type == SILC_ROUTER) {
- clients = silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients_count);
- }
-
- if (!clients) {
- /* Such a client really does not exist in the SILC network. */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
- goto out;
- }
-
- /* Send the command reply to the client */
- silc_server_command_whowas_send_reply(cmd, clients, clients_count);
+ SilcServer server = app_context;
+ QuitInternal q = (QuitInternal)context;
+ SilcClientEntry client = silc_packet_get_context(q->sock);
- out:
- if (clients)
- silc_free(clients);
- if (nick)
- silc_free(nick);
- if (server_name)
- silc_free(server_name);
+ if (client) {
+ /* Free all client specific data, such as client entry and entires
+ on channels this client may be on. */
+ silc_server_free_sock_user_data(server, q->sock, q->signoff);
+ silc_server_close_connection(server, q->sock);
+ }
- return ret;
+ silc_packet_stream_unref(q->sock);
+ silc_free(q->signoff);
+ silc_free(q);
}
-/* Server side of command WHOWAS. */
+/* Quits SILC session. This is the normal way to disconnect client. */
-SILC_SERVER_CMD_FUNC(whowas)
+SILC_SERVER_CMD_FUNC(quit)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- int ret = 0;
+ SilcServer server = cmd->server;
+ SilcPacketStream sock = cmd->sock;
+ SilcClientEntry client = silc_packet_get_context(sock);
+ QuitInternal q;
+ unsigned char *tmp = NULL;
+ SilcUInt32 len = 0;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
- ret = silc_server_command_whowas_from_client(cmd);
- else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
- (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
- ret = silc_server_command_whowas_from_server(cmd);
+ if (client->data.conn_type != SILC_CONN_CLIENT)
+ goto out;
- if (!ret)
- silc_server_command_free(cmd);
-}
+ /* Get message */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ if (len > 128)
+ tmp = NULL;
-/******************************************************************************
+ q = silc_calloc(1, sizeof(*q));
+ q->sock = sock;
+ q->signoff = tmp ? strdup(tmp) : NULL;
+ silc_packet_stream_ref(q->sock);
- IDENTIFY Functions
+ /* We quit the connection with little timeout */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_command_quit_cb, (void *)q,
+ 0, 200000);
-******************************************************************************/
+ out:
+ silc_server_command_free(cmd);
+}
-/* Checks that all mandatory fields are present. If not then send WHOIS
- request to the server who owns the client. We use WHOIS because we want
- to get as much information as possible at once. */
+/* Server side of command KILL. This command is used by router operator
+ to remove an client from the SILC Network temporarily. */
-static char
-silc_server_command_identify_check(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- unsigned int clients_count)
+SILC_SERVER_CMD_FUNC(kill)
{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- int i;
- SilcClientEntry entry;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ SilcClientEntry remote_client;
+ SilcID id;
+ unsigned char *tmp, *comment, *auth;
+ SilcUInt32 tmp_len, tmp_len2, auth_len;
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 3);
- if (!entry->nickname) {
- SilcBuffer tmpbuf;
- unsigned short old_ident;
-
- if (!entry->router)
- continue;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- silc_command_set_command(cmd->payload, SILC_COMMAND_WHOIS);
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send WHOIS request. We send WHOIS since we're doing the requesting
- now anyway so make it a good one. */
- silc_server_packet_send(server, entry->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
- silc_command_get_ident(cmd->payload),
- silc_server_command_destructor,
- silc_server_command_identify,
- silc_server_command_dup(cmd));
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+ goto out;
- cmd->pending = TRUE;
-
- /* Put old data back to the Command Payload we just changed */
- silc_command_set_ident(cmd->payload, old_ident);
- silc_command_set_command(cmd->payload, SILC_COMMAND_IDENTIFY);
+ /* Get authentication payload if present */
+ auth = silc_argument_get_arg_type(cmd->args, 3, &auth_len);
- silc_buffer_free(tmpbuf);
- return FALSE;
+ if (!auth) {
+ /* Router operator killing */
+
+ /* KILL command works only on router */
+ if (server->server_type != SILC_ROUTER) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
+ goto out;
+ }
+
+ /* Check whether client has the permissions. */
+ if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
+ goto out;
}
}
- return TRUE;
-}
+ /* Get the client ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_CLIENT_ID, 0);
+ goto out;
+ }
-static void
-silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- unsigned int clients_count)
-{
- SilcServer server = cmd->server;
- char *tmp;
- int i, count = 0, len;
- SilcBuffer packet, idp;
- SilcClientEntry entry;
- SilcCommandStatus status;
- unsigned short ident = silc_command_get_ident(cmd->payload);
+ /* Get the client entry */
+ remote_client = silc_idlist_find_client_by_id(server->local_list,
+ SILC_ID_GET_ID(id),
+ TRUE, NULL);
+ if (!remote_client) {
+ remote_client = silc_idlist_find_client_by_id(server->global_list,
+ SILC_ID_GET_ID(id),
+ TRUE, NULL);
+ if (!remote_client) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
+ }
- status = SILC_STATUS_OK;
- if (clients_count > 1)
- status = SILC_STATUS_LIST_START;
+ /* Get comment */
+ comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
+ if (comment && tmp_len2 > 128) {
+ tmp_len2 = 128;
+ comment[tmp_len2 - 1] = '\0';
+ }
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
+ /* If authentication data is provided then verify that killing is
+ actually allowed */
+ if (auth && auth_len) {
+ SilcPacketStream sock;
- if (entry->connection && entry->data.registered == FALSE) {
- if (clients_count == 1)
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, entry->nickname,
- strlen(entry->nickname));
- continue;
+ if (!SILC_IS_LOCAL(remote_client) || !remote_client->data.public_key) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_OPERATION_ALLOWED,
+ 0);
+ goto out;
}
- if (count && i - 1 == count)
- break;
+ /* Verify the signature */
+ if (!silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+ remote_client->data.public_key, 0,
+ server->sha1hash, remote_client->id,
+ SILC_ID_CLIENT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ goto out;
+ }
- if (clients_count > 2)
- status = SILC_STATUS_LIST_ITEM;
+ /* Send reply to the sender */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_OK, 0,
+ 2, tmp, tmp_len);
+
+ /* Do normal signoff for the destination client */
+ sock = remote_client->connection;
+ silc_server_remove_from_channels(server, NULL, remote_client,
+ TRUE, (char *)"Killed", TRUE, TRUE);
+ silc_server_free_client_data(server, NULL, remote_client, TRUE,
+ comment ? comment :
+ (unsigned char *)"Killed");
+ if (sock)
+ silc_server_close_connection(server, sock);
+ } else {
+ /* Router operator killing */
- if (clients_count > 1 && i == clients_count - 1)
- status = SILC_STATUS_LIST_END;
+ /* Send reply to the sender */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_OK, 0,
+ 2, tmp, tmp_len);
- /* Send IDENTIFY reply */
- idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
- tmp = silc_argument_get_first_arg(cmd->args, NULL);
-
- /* XXX */
- {
- char nh[256], uh[256];
- SilcSocketConnection hsock;
-
- memset(uh, 0, sizeof(uh));
- memset(nh, 0, sizeof(nh));
-
- strncat(nh, entry->nickname, strlen(entry->nickname));
- if (!strchr(entry->nickname, '@')) {
- strncat(nh, "@", 1);
- len = entry->router ? strlen(entry->router->server_name) :
- strlen(server->server_name);
- strncat(nh, entry->router ? entry->router->server_name :
- server->server_name, len);
- }
-
- if (!entry->username) {
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
- status, ident, 2,
- 2, idp->data, idp->len,
- 3, nh, strlen(nh));
- } else {
- strncat(uh, entry->username, strlen(entry->username));
- if (!strchr(entry->username, '@')) {
- strncat(uh, "@", 1);
- hsock = (SilcSocketConnection)entry->connection;
- len = strlen(hsock->hostname);
- strncat(uh, hsock->hostname, len);
- }
-
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
- status, ident, 3,
- 2, idp->data, idp->len,
- 3, nh, strlen(nh),
- 4, uh, strlen(uh));
- }
-
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
- 0, packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_buffer_free(idp);
- }
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_KILLED);
+
+ /* Now do the killing */
+ silc_server_kill_client(server, remote_client, comment, client->id,
+ SILC_ID_CLIENT);
}
+
+ out:
+ silc_server_command_free(cmd);
}
-static int
-silc_server_command_identify_from_client(SilcServerCommandContext cmd)
+/* Server side of command INFO. This sends information about us to
+ the client. If client requested specific server we will send the
+ command to that server. */
+
+SILC_SERVER_CMD_FUNC(info)
{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0, clients_count = 0;
- SilcClientEntry *clients = NULL, entry;
- SilcClientID **client_id = NULL;
- unsigned int client_id_count = 0;
- int i, ret = 0;
-
- /* Protocol dictates that we must always send the received IDENTIFY request
- to our router if we are normal server, so let's do it now unless we
- are standalone. We will not send any replies to the client until we
- have received reply from the router. */
- if (server->server_type == SILC_SERVER &&
- !cmd->pending && !server->standalone) {
- SilcBuffer tmpbuf;
- unsigned short old_ident;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+ SilcBuffer idp;
+ unsigned char *tmp;
+ SilcUInt32 tmp_len;
+ char *dest_server = NULL, *server_info = NULL, *server_name;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ SilcServerEntry entry = NULL;
+ SilcID id;
- /* Send IDENTIFY command to our router */
- silc_server_packet_send(server, (SilcSocketConnection)
- server->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 0, 2);
- /* Reprocess this packet after received reply from router */
- silc_server_command_pending(server, SILC_COMMAND_IDENTIFY,
- silc_command_get_ident(cmd->payload),
- silc_server_command_destructor,
- silc_server_command_identify,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
+ /* Get server name */
+ dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+ if (dest_server) {
+ /* Check server name. */
+ dest_server = silc_identifier_check(dest_server, strlen(dest_server),
+ SILC_STRING_UTF8, 256, &tmp_len);
+ if (!dest_server) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+ SILC_STATUS_ERR_BAD_SERVER, 0);
+ goto out;
+ }
+ }
- silc_command_set_ident(cmd->payload, old_ident);
-
- silc_buffer_free(tmpbuf);
- ret = -1;
- goto out;
+ /* Get Server ID */
+ if (silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+ /* Check whether we have this server cached */
+ entry = silc_idlist_find_server_by_id(server->local_list,
+ SILC_ID_GET_ID(id), TRUE, NULL);
+ if (!entry) {
+ entry = silc_idlist_find_server_by_id(server->global_list,
+ SILC_ID_GET_ID(id), TRUE, NULL);
+ if (!entry && server->server_type != SILC_SERVER) {
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_INFO,
+ SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
+ }
}
- /* We are ready to process the command request. Let's search for the
- requested client and send reply to the requesting client. */
+ if ((!dest_server && !entry) || (entry && entry == server->id_entry) ||
+ (dest_server && !cmd->pending &&
+ !memcmp(dest_server, server->server_name, strlen(dest_server)))) {
+ /* Send our reply */
+ char info_string[256];
- /* Parse the IDENTIFY request */
- if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
- &nick, &server_name, &count,
- SILC_COMMAND_IDENTIFY))
- return 0;
+ memset(info_string, 0, sizeof(info_string));
+ snprintf(info_string, sizeof(info_string),
+ "location: %s server: %s admin: %s <%s>",
+ server->config->server_info->location,
+ server->config->server_info->server_type,
+ server->config->server_info->admin,
+ server->config->server_info->email);
- /* Get all clients matching that ID or nickname from local list */
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->local_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
- }
+ server_info = info_string;
+ entry = server->id_entry;
} else {
- clients = silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients_count);
- }
-
- /* Check global list as well */
- if (!clients) {
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->global_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
+ /* Check whether we have this server cached */
+ if (!entry && dest_server) {
+ entry = silc_idlist_find_server_by_name(server->global_list,
+ dest_server, TRUE, NULL);
+ if (!entry) {
+ entry = silc_idlist_find_server_by_name(server->local_list,
+ dest_server, TRUE, NULL);
}
- } else {
- clients = silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients_count);
- }
- }
-
- if (!clients) {
- /* Such a client really does not exist in the SILC network. */
- if (!client_id_count) {
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
- } else {
- SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
- 2, idp->data, idp->len);
- silc_buffer_free(idp);
}
- goto out;
- }
- /* Check that all mandatory fields are present and request those data
- from the server who owns the client if necessary. */
- if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
- ret = -1;
- goto out;
- }
+ if (!cmd->pending &&
+ server->server_type != SILC_SERVER && entry && !entry->server_info) {
+ /* Send to the server */
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
- /* Send the command reply to the client */
- silc_server_command_identify_send_reply(cmd, clients, clients_count);
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
- out:
- if (client_id_count) {
- for (i = 0; i < client_id_count; i++)
- silc_free(client_id[i]);
- silc_free(client_id);
- }
- if (clients)
- silc_free(clients);
- if (nick)
- silc_free(nick);
- if (server_name)
- silc_free(server_name);
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
- return ret;
-}
+ silc_server_packet_send(server, entry->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
-static int
-silc_server_command_identify_from_server(SilcServerCommandContext cmd)
-{
- SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0, clients_count = 0;
- SilcClientEntry *clients = NULL, entry;
- SilcClientID **client_id = NULL;
- unsigned int client_id_count = 0;
- int i, ret = 0;
-
- /* Parse the IDENTIFY request */
- if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
- &nick, &server_name, &count,
- SILC_COMMAND_IDENTIFY))
- return 0;
-
- /* Process the command request. Let's search for the requested client and
- send reply to the requesting server. */
-
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->local_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_INFO,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_info,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
}
- } else {
- clients = silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients_count);
- }
-
- /* If we are router we will check our global list as well. */
- if (!clients && server->server_type == SILC_ROUTER) {
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->global_list,
- client_id[i], NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- }
- }
- } else {
- clients = silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients_count);
- if (!clients)
- clients = silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients_count);
- }
- }
-
- if (!clients) {
- /* Such a client really does not exist in the SILC network. */
- if (!client_id_count) {
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
- } else {
- SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
- 2, idp->data, idp->len);
- silc_buffer_free(idp);
+
+ if (!entry && !cmd->pending && !server->standalone) {
+ /* Send to the primary router */
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
+
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_INFO,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_info,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
}
- goto out;
}
- /* Check that all mandatory fields are present and request those data
- from the server who owns the client if necessary. */
- if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
- ret = -1;
+ if (!entry) {
+ if (dest_server) {
+ silc_free(dest_server);
+ dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_INFO,
+ SILC_STATUS_ERR_NO_SUCH_SERVER, 0,
+ 2, dest_server,
+ strlen(dest_server));
+ dest_server = NULL;
+ }
goto out;
}
- /* Send the command reply */
- silc_server_command_identify_send_reply(cmd, clients, clients_count);
+ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+ if (!server_info)
+ server_info = entry->server_info;
+ server_name = entry->server_name;
+
+ /* Send the reply */
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_INFO,
+ SILC_STATUS_OK, 0, ident, 3,
+ 2, idp->data, silc_buffer_len(idp),
+ 3, server_name,
+ strlen(server_name),
+ 4, server_info,
+ server_info ?
+ strlen(server_info) : 0);
+ silc_buffer_free(idp);
out:
- if (client_id_count) {
- for (i = 0; i < client_id_count; i++)
- silc_free(client_id[i]);
- silc_free(client_id);
- }
- if (clients)
- silc_free(clients);
- if (nick)
- silc_free(nick);
- if (server_name)
- silc_free(server_name);
-
- return ret;
+ silc_free(dest_server);
+ silc_server_command_free(cmd);
}
-SILC_SERVER_CMD_FUNC(identify)
+/* Server side of command PING. This just replies to the ping. */
+
+SILC_SERVER_CMD_FUNC(ping)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- int ret = 0;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
-
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
- ret = silc_server_command_identify_from_client(cmd);
- else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) |
- (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
- ret = silc_server_command_identify_from_server(cmd);
+ SilcServer server = cmd->server;
+ SilcUInt32 tmp_len;
+ unsigned char *tmp;
+ SilcID id;
- if (!ret)
- silc_server_command_free(cmd);
-}
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PING, cmd, 1, 1);
-/* Checks string for bad characters and returns TRUE if they are found. */
+ /* Get Server ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
-static int silc_server_command_bad_chars(char *nick)
-{
- if (strchr(nick, '\\')) return TRUE;
- if (strchr(nick, '\"')) return TRUE;
- if (strchr(nick, '´')) return TRUE;
- if (strchr(nick, '`')) return TRUE;
- if (strchr(nick, '\'')) return TRUE;
- if (strchr(nick, '*')) return TRUE;
- if (strchr(nick, '/')) return TRUE;
- if (strchr(nick, '@')) return TRUE;
+ if (SILC_ID_SERVER_COMPARE(SILC_ID_GET_ID(id), server->id)) {
+ /* Send our reply */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+ SILC_STATUS_OK, 0);
+ } else {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_PING,
+ SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
+ 2, tmp, tmp_len);
+ goto out;
+ }
- return FALSE;
+ out:
+ silc_server_command_free(cmd);
}
-/* Server side of command NICK. Sets nickname for user. Setting
- nickname causes generation of a new client ID for the client. The
- new client ID is sent to the client after changing the nickname. */
+/* Server side of command STATS. */
-SILC_SERVER_CMD_FUNC(nick)
+SILC_SERVER_CMD_FUNC(stats)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
SilcServer server = cmd->server;
- SilcBuffer packet, nidp, oidp;
- SilcClientID *new_id;
- char *nick;
-
- if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
- goto out;
+ SilcID id;
+ unsigned char *tmp;
+ SilcUInt32 tmp_len;
+ SilcBuffer packet, stats;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ SilcUInt32 uptime;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NICK, cmd, 1, 1);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_STATS, cmd, 1, 1);
- /* Check nickname */
- nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
- if (silc_server_command_bad_chars(nick) == TRUE) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
- SILC_STATUS_ERR_BAD_NICKNAME);
+ /* Get Server ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_STATS,
+ SILC_STATUS_ERR_NO_SERVER_ID, 0);
goto out;
}
- if (strlen(nick) > 128)
- nick[127] = '\0';
-
- /* Create new Client ID */
- silc_id_create_client_id(cmd->server->id, cmd->server->rng,
- cmd->server->md5hash, nick,
- &new_id);
-
- /* Send notify about nickname change to our router. We send the new
- ID and ask to replace it with the old one. If we are router the
- packet is broadcasted. Send NICK_CHANGE notify. */
- if (!server->standalone)
- silc_server_send_notify_nick_change(server, server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE, client->id,
- new_id, SILC_ID_CLIENT_LEN);
-
- /* Remove old cache entry */
- silc_idcache_del_by_id(server->local_list->clients, SILC_ID_CLIENT,
- client->id);
-
- oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ SILC_LOG_DEBUG(("id %s", silc_id_render(SILC_ID_GET_ID(id),
+ id.type)));
- /* Free old ID */
- if (client->id) {
- memset(client->id, 0, SILC_ID_CLIENT_LEN);
- silc_free(client->id);
+ /* The ID must be ours */
+ if (!SILC_ID_SERVER_COMPARE(server->id, SILC_ID_GET_ID(id))) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_STATS,
+ SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
+ 2, tmp, tmp_len);
+ goto out;
}
- /* Save the nickname as this client is our local client */
- if (client->nickname)
- silc_free(client->nickname);
+ /* If we are router then just send everything we got. If we are normal
+ server then we'll send this to our router to get all the latest
+ statistical information. */
+ if (!cmd->pending && server->server_type != SILC_ROUTER &&
+ !server->standalone) {
+ SilcBuffer idp;
- client->nickname = strdup(nick);
- client->id = new_id;
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
- /* Update client cache */
- silc_idcache_add(server->local_list->clients, client->nickname,
- SILC_ID_CLIENT, client->id, (void *)client, TRUE, FALSE);
+ /* Send request to our router */
+ idp = silc_id_payload_encode(server->router->id,
+ SILC_ID_SERVER);
+ packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+ ++server->cmd_ident, 1,
+ 1, idp->data,
+ silc_buffer_len(idp));
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, 0, packet->data,
+ silc_buffer_len(packet));
- nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_STATS,
+ server->cmd_ident,
+ silc_server_command_stats,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+ goto out;
+ }
- /* Send NICK_CHANGE notify to the client's channels */
- silc_server_send_notify_on_channels(server, NULL, client,
- SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
- oidp->data, oidp->len,
- nidp->data, nidp->len);
+ /* Send our reply to sender */
+ uptime = time(NULL) - server->starttime;
+
+ stats = silc_buffer_alloc_size(60);
+ silc_buffer_format(stats,
+ SILC_STR_UI_INT(server->starttime),
+ SILC_STR_UI_INT(uptime),
+ SILC_STR_UI_INT(server->stat.my_clients),
+ SILC_STR_UI_INT(server->stat.my_channels),
+ SILC_STR_UI_INT(server->stat.my_server_ops),
+ SILC_STR_UI_INT(server->stat.my_router_ops),
+ SILC_STR_UI_INT(server->stat.cell_clients),
+ SILC_STR_UI_INT(server->stat.cell_channels),
+ SILC_STR_UI_INT(server->stat.cell_servers),
+ SILC_STR_UI_INT(server->stat.clients),
+ SILC_STR_UI_INT(server->stat.channels),
+ SILC_STR_UI_INT(server->stat.servers),
+ SILC_STR_UI_INT(server->stat.routers),
+ SILC_STR_UI_INT(server->stat.server_ops),
+ SILC_STR_UI_INT(server->stat.router_ops),
+ SILC_STR_END);
- /* Send the new Client ID as reply command back to client */
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK,
- SILC_STATUS_OK, 0, 1,
- 2, nidp->data, nidp->len);
- silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
- 0, packet->data, packet->len, FALSE);
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_STATS,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, tmp, tmp_len,
+ 3, stats->data, silc_buffer_len(stats));
+ silc_buffer_free(stats);
- silc_buffer_free(packet);
- silc_buffer_free(nidp);
- silc_buffer_free(oidp);
-
out:
silc_server_command_free(cmd);
}
-SILC_SERVER_CMD_FUNC(list)
-{
-}
-
-/* Server side of TOPIC command. Sets topic for channel and/or returns
- current topic to client. */
+/* Internal routine to join channel. The channel sent to this function
+ has been either created or resolved from ID lists. This joins the sent
+ client to the channel. */
-SILC_SERVER_CMD_FUNC(topic)
+static void silc_server_command_join_channel(SilcServer server,
+ SilcServerCommandContext cmd,
+ SilcChannelEntry channel,
+ SilcClientID *client_id,
+ SilcBool created,
+ SilcBool create_key,
+ SilcUInt32 umode,
+ const unsigned char *auth,
+ SilcUInt32 auth_len,
+ const unsigned char *cauth,
+ SilcUInt32 cauth_len)
{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
- SilcChannelID *channel_id;
- SilcChannelEntry channel;
- SilcChannelClientEntry chl;
- SilcBuffer packet, idp;
+ SilcPacketStream sock = cmd->sock;
+ SilcIDListData idata = silc_packet_get_context(sock);
unsigned char *tmp;
- unsigned int argc, tmp_len;
+ SilcUInt32 tmp_len, user_count;
+ unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4], ulimit[4];
+ SilcClientEntry client;
+ SilcChannelClientEntry chl;
+ SilcBuffer reply, chidp, clidp, keyp = NULL;
+ SilcBuffer user_list, mode_list, invite_list, ban_list;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ char check[512], check2[512];
+ SilcBool founder = FALSE;
+ SilcBool resolve;
+ SilcBuffer fkey = NULL, chpklist = NULL;
+ const char *cipher, *hostname, *ip;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_TOPIC, cmd, 1, 2);
+ SILC_LOG_DEBUG(("Joining client to channel"));
- argc = silc_argument_get_arg_num(cmd->args);
+ if (!channel)
+ return;
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, &ip, NULL);
- /* Check whether the channel exists */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
- if (!channel) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
- goto out;
- }
- }
+ /* Get the client entry */
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ client = (SilcClientEntry)idata;
+ if (!client)
+ return;
+ } else {
+ client = silc_server_query_client(server, client_id, FALSE,
+ &resolve);
+ if (!client) {
+ if (!resolve || cmd->pending) {
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0,
+ 2, tmp, tmp_len);
+ goto out;
+ }
- if (argc > 1) {
- /* Get the topic */
- tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_join,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
goto out;
}
- if (strlen(tmp) > 256) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ if (!client->data.public_key &&
+ (auth || cauth || channel->ban_list ||
+ (channel->mode & SILC_CHANNEL_MODE_INVITE))) {
+ if (cmd->pending == 2)
+ goto out;
+
+ /* We must retrieve the client's public key by sending
+ GETKEY command. Reprocess this packet after receiving the key */
+ clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ silc_server_send_command(server, cmd->sock,
+ SILC_COMMAND_GETKEY, ++server->cmd_ident,
+ 1, 1, clidp->data, silc_buffer_len(clidp));
+ silc_buffer_free(clidp);
+ silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+ server->cmd_ident,
+ silc_server_command_join,
+ silc_server_command_dup(cmd));
+ cmd->pending = 2;
goto out;
}
- /* See whether has rights to change topic */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
- if (chl->client == client)
- break;
+ cmd->pending = FALSE;
+ }
- if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
- if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
- SILC_STATUS_ERR_NO_CHANNEL_PRIV);
- goto out;
+ /*
+ * Check founder auth payload if provided. If client can gain founder
+ * privileges it can override various conditions on joining the channel,
+ * and can have directly the founder mode set on the channel.
+ */
+ if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ SilcIDListData idata = (SilcIDListData)client;
+ SilcChannelClientEntry chl2;
+ SilcHashTableList htl;
+
+ if (channel->founder_key && idata->public_key &&
+ silc_pkcs_public_key_compare(channel->founder_key,
+ idata->public_key)) {
+ /* Check whether the client is to become founder */
+ if (silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+ channel->founder_key, 0, server->sha1hash,
+ client->id, SILC_ID_CLIENT)) {
+
+ /* There cannot be anyone else as founder on the channel now. This
+ client is definitely the founder due to this authentication */
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+ if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+ silc_server_force_cumode_change(server, NULL, channel, chl2,
+ chl2->mode);
+ break;
+ }
+ silc_hash_table_list_reset(&htl);
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ founder = TRUE;
}
}
-
- /* Set the topic for channel */
- if (channel->topic)
- silc_free(channel->topic);
- channel->topic = strdup(tmp);
-
- /* Send TOPIC_SET notify type to the network */
- if (!server->standalone)
- silc_server_send_notify_topic_set(server, server->router->connection,
- server->server_type == SILC_ROUTER ?
- TRUE : FALSE, channel, client->id,
- SILC_ID_CLIENT_LEN, channel->topic);
-
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
- /* Send notify about topic change to all clients on the channel */
- silc_server_send_notify_to_channel(server, NULL, channel, TRUE,
- SILC_NOTIFY_TYPE_TOPIC_SET, 2,
- idp->data, idp->len,
- channel->topic, strlen(channel->topic));
- silc_buffer_free(idp);
}
- /* Send the topic to client as reply packet */
- idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
- if (channel->topic)
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC,
- SILC_STATUS_OK, 0, 2,
- 2, idp->data, idp->len,
- 3, channel->topic,
- strlen(channel->topic));
- else
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC,
- SILC_STATUS_OK, 0, 1,
- 2, idp->data, idp->len);
- silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
- 0, packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_buffer_free(idp);
- silc_free(channel_id);
+ /*
+ * Check channel modes
+ */
- out:
- silc_server_command_free(cmd);
-}
+ if (!umode) {
+ memset(check, 0, sizeof(check));
+ memset(check2, 0, sizeof(check2));
+ silc_strncat(check, sizeof(check),
+ client->nickname, strlen(client->nickname));
+ silc_strncat(check, sizeof(check), "!", 1);
+ silc_strncat(check, sizeof(check),
+ client->username, strlen(client->username));
+ if (!strchr(client->username, '@')) {
+ silc_strncat(check, sizeof(check), "@", 1);
+ silc_strncat(check, sizeof(check),
+ hostname, strlen(hostname));
+ }
-/* Server side of INVITE command. Invites some client to join some channel. */
+ silc_strncat(check2, sizeof(check2),
+ client->nickname, strlen(client->nickname));
+ if (!strchr(client->nickname, '@')) {
+ silc_strncat(check2, sizeof(check2), "@", 1);
+ silc_strncat(check2, sizeof(check2),
+ SILC_IS_LOCAL(client) ? server->server_name :
+ client->router->server_name,
+ SILC_IS_LOCAL(client) ? strlen(server->server_name) :
+ strlen(client->router->server_name));
+ }
+ silc_strncat(check2, sizeof(check2), "!", 1);
+ silc_strncat(check2, sizeof(check2),
+ client->username, strlen(client->username));
+ if (!strchr(client->username, '@')) {
+ silc_strncat(check2, sizeof(check2), "@", 1);
+ silc_strncat(check2, sizeof(check2),
+ hostname, strlen(hostname));
+ }
-SILC_SERVER_CMD_FUNC(invite)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- SilcSocketConnection sock = cmd->sock, dest_sock;
- SilcClientEntry sender, dest;
- SilcClientID *dest_id;
- SilcChannelEntry channel;
- SilcChannelID *channel_id;
- SilcBuffer sidp;
- unsigned char *tmp;
- unsigned int len;
+ /* Check invite list if channel is invite-only channel */
+ if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
+ if (!channel->invite_list ||
+ !silc_hash_table_count(channel->invite_list) ||
+ (!silc_server_inviteban_match(server, channel->invite_list,
+ 3, client->id) &&
+ !silc_server_inviteban_match(server, channel->invite_list,
+ 2, client->data.public_key) &&
+ !silc_server_inviteban_match(server, channel->invite_list,
+ 1, client->nickname) &&
+ !silc_server_inviteban_match(server, channel->invite_list,
+ 1, check) &&
+ !silc_server_inviteban_match(server, channel->invite_list,
+ 1, check2))) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_INVITED, 0,
+ 2, chidp->data,
+ silc_buffer_len(chidp));
+ silc_buffer_free(chidp);
+ goto out;
+ }
+ }
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INVITE, cmd, 1, 2);
+ /* Check ban list if it exists. If the client's nickname, server,
+ username and/or hostname is in the ban list the access to the
+ channel is denied. */
+ if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+ if (silc_server_inviteban_match(server, channel->ban_list,
+ 3, client->id) ||
+ silc_server_inviteban_match(server, channel->ban_list,
+ 2, client->data.public_key) ||
+ silc_server_inviteban_match(server, channel->ban_list,
+ 1, client->nickname) ||
+ silc_server_inviteban_match(server, channel->ban_list,
+ 1, check) ||
+ silc_server_inviteban_match(server, channel->ban_list,
+ 1, check2)) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_BANNED_FROM_CHANNEL, 0,
+ 2, chidp->data,
+ silc_buffer_len(chidp));
+ silc_buffer_free(chidp);
+ goto out;
+ }
+ }
- /* Get destination ID */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_NO_CLIENT_ID);
- goto out;
- }
- dest_id = silc_id_payload_parse_id(tmp, len);
- if (!dest_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_NO_CLIENT_ID);
- goto out;
+ /* Check user count limit if set. */
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
+ if (silc_hash_table_count(channel->user_list) + 1 >
+ channel->user_limit) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_CHANNEL_IS_FULL,
+ 0, 2, chidp->data,
+ silc_buffer_len(chidp));
+ silc_buffer_free(chidp);
+ goto out;
+ }
+ }
}
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
- channel_id = silc_id_payload_parse_id(tmp, len);
- if (!channel_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
+ /* Check the channel passphrase if set. */
+ if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ /* Get passphrase */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+ if (tmp)
+ passphrase = silc_memdup(tmp, tmp_len);
+
+ if (!passphrase || !channel->passphrase ||
+ strlen(channel->passphrase) != strlen(passphrase) ||
+ memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_BAD_PASSWORD, 0,
+ 2, chidp->data,
+ silc_buffer_len(chidp));
+ silc_buffer_free(chidp);
+ goto out;
+ }
}
- /* Check whether the channel exists */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
- if (!channel) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ /* Verify channel authentication with channel public keys if set. */
+ if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+ if (!silc_server_verify_channel_auth(server, channel, client->id,
+ cauth, cauth_len)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_PERM_DENIED, 0);
goto out;
}
}
- /* Check whether the sender of this command is on the channel. */
- sender = (SilcClientEntry)sock->user_data;
- if (!silc_server_client_on_channel(sender, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_NOT_ON_CHANNEL);
+ /*
+ * Client is allowed to join to the channel. Make it happen.
+ */
+
+ /* Check whether the client already is on the channel */
+ if (silc_server_client_on_channel(client, channel, NULL)) {
+ clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ silc_server_command_send_status_data2(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_USER_ON_CHANNEL, 0,
+ 2, clidp->data,
+ silc_buffer_len(clidp),
+ 3, chidp->data,
+ silc_buffer_len(chidp));
+ silc_buffer_free(clidp);
+ silc_buffer_free(chidp);
goto out;
}
- /* Check whether the channel is invite-only channel. If yes then the
- sender of this command must be at least channel operator. */
- if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
- SilcChannelClientEntry chl;
+ /* Generate new channel key as protocol dictates */
+ if (create_key) {
+ if (!silc_server_create_channel_key(server, channel, 0))
+ goto out;
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
- if (chl->client == sender) {
- if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_NO_CHANNEL_PRIV);
- goto out;
- }
- break;
- }
+ /* Send the channel key. This is broadcasted to the channel but is not
+ sent to the client who is joining to the channel. */
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
}
- /* Find the connection data for the destination. If it is local we will
- send it directly otherwise we will send it to router for routing. */
- dest = silc_idlist_find_client_by_id(server->local_list, dest_id, NULL);
- if (dest)
- dest_sock = (SilcSocketConnection)dest->connection;
- else
- dest_sock = silc_server_route_get(server, dest_id, SILC_ID_CLIENT);
+ /* Join the client to the channel by adding it to channel's user list.
+ Add also the channel to client entry's channels list for fast cross-
+ referencing. */
+ chl = silc_calloc(1, sizeof(*chl));
+ chl->mode = umode;
+ chl->client = client;
+ chl->channel = channel;
+ silc_hash_table_add(channel->user_list, client, chl);
+ silc_hash_table_add(client->channels, channel, chl);
+ channel->user_count++;
+ channel->disabled = FALSE;
- /* Check whether the requested client is already on the channel. */
- /* XXX if we are normal server we don't know about global clients on
- the channel thus we must request it (USERS command), check from
- local cache as well. */
- if (silc_server_client_on_channel(dest, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_ERR_USER_ON_CHANNEL);
- goto out;
- }
+ /* Get users on the channel */
+ silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
+ &user_count);
- sidp = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+ /* Encode Client ID Payload of the original client who wants to join */
+ clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- /* Send notify to the client that is invited to the channel */
- silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id,
- SILC_ID_CLIENT,
- SILC_NOTIFY_TYPE_INVITE, 2,
- sidp->data, sidp->len, tmp, len);
+ /* Encode command reply packet */
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ SILC_PUT32_MSB(channel->mode, mode);
+ SILC_PUT32_MSB(created, tmp2);
+ SILC_PUT32_MSB(user_count, tmp3);
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ SILC_PUT32_MSB(channel->user_limit, ulimit);
+
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ unsigned char cid[32];
+ SilcUInt32 cid_len;
+ silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid), &cid_len);
+ cipher = silc_cipher_get_name(channel->send_key);
+ keyp = silc_channel_key_payload_encode(cid_len, cid,
+ strlen(cipher), cipher,
+ channel->key_len / 8, channel->key);
+ }
+
+ if (channel->founder_key)
+ fkey = silc_public_key_payload_encode(channel->founder_key);
+
+ /* Encode invite list */
+ invite_list = NULL;
+ if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+ SilcHashTableList htl;
+
+ invite_list = silc_buffer_alloc_size(2);
+ silc_buffer_format(invite_list,
+ SILC_STR_UI_SHORT(silc_hash_table_count(
+ channel->invite_list)),
+ SILC_STR_END);
+
+ silc_hash_table_list(channel->invite_list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&tmp_len, (void *)&reply))
+ invite_list = silc_argument_payload_encode_one(invite_list,
+ reply->data,
+ silc_buffer_len(reply), tmp_len);
+ silc_hash_table_list_reset(&htl);
+ }
+
+ /* Encode ban list */
+ ban_list = NULL;
+ if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+ SilcHashTableList htl;
+
+ ban_list = silc_buffer_alloc_size(2);
+ silc_buffer_format(ban_list,
+ SILC_STR_UI_SHORT(silc_hash_table_count(
+ channel->ban_list)),
+ SILC_STR_END);
+
+ silc_hash_table_list(channel->ban_list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&tmp_len, (void *)&reply))
+ ban_list = silc_argument_payload_encode_one(ban_list,
+ reply->data,
+ silc_buffer_len(reply), tmp_len);
+ silc_hash_table_list_reset(&htl);
+ }
+
+ if (channel->channel_pubkeys)
+ chpklist = silc_server_get_channel_pk_list(server, channel, FALSE, FALSE);
+
+ reply =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+ SILC_STATUS_OK, 0, ident, 16,
+ 2, channel->channel_name,
+ strlen(channel->channel_name),
+ 3, chidp->data,
+ silc_buffer_len(chidp),
+ 4, clidp->data,
+ silc_buffer_len(clidp),
+ 5, mode, 4,
+ 6, tmp2, 4,
+ 7, keyp ? keyp->data : NULL,
+ keyp ? silc_buffer_len(keyp) : 0,
+ 8, ban_list ? ban_list->data : NULL,
+ ban_list ?
+ silc_buffer_len(ban_list): 0,
+ 9, invite_list ? invite_list->data :
+ NULL,
+ invite_list ?
+ silc_buffer_len(invite_list) : 0,
+ 10, channel->topic,
+ channel->topic ?
+ strlen(channel->topic) : 0,
+ 11, silc_hmac_get_name(channel->hmac),
+ strlen(silc_hmac_get_name(channel->
+ hmac)),
+ 12, tmp3, 4,
+ 13, user_list->data,
+ silc_buffer_len(user_list),
+ 14, mode_list->data,
+ silc_buffer_len(mode_list),
+ 15, fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0,
+ 16, chpklist ? chpklist->data : NULL,
+ chpklist ? silc_buffer_len(chpklist) : 0,
+ 17, (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL),
+ (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 0));
/* Send command reply */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
- SILC_STATUS_OK);
-
- silc_buffer_free(sidp);
-
- out:
- silc_server_command_free(cmd);
-}
-
-typedef struct {
- SilcServer server;
- SilcSocketConnection sock;
- char *signoff;
-} *QuitInternal;
-
-/* Quits connection to client. This gets called if client won't
- close the connection even when it has issued QUIT command. */
+ silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
+ reply->data, silc_buffer_len(reply));
+
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
+ /* Send JOIN notify to locally connected clients on the channel. If
+ we are normal server then router will send or have sent JOIN notify
+ already. However since we've added the client already to our channel
+ we'll ignore it (in packet_receive.c) so we must send it here. If
+ we are router then this will send it to local clients and local
+ servers. */
+ SILC_LOG_DEBUG(("Send JOIN notify to channel"));
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_JOIN, 2,
+ clidp->data, silc_buffer_len(clidp),
+ chidp->data, silc_buffer_len(chidp));
+
+ /* Update statistics */
+ server->stat.my_chanclients++;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.cell_chanclients++;
+ server->stat.chanclients++;
+ }
-SILC_TASK_CALLBACK(silc_server_command_quit_cb)
-{
- QuitInternal q = (QuitInternal)context;
+ if (!cmd->pending) {
+ /* Send JOIN notify packet to our primary router */
+ silc_server_send_notify_join(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel, client->id);
+
+ if (keyp)
+ /* Distribute the channel key to all backup routers. */
+ silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
+ keyp->data, silc_buffer_len(keyp), FALSE, TRUE);
+
+ /* If client became founder by providing correct founder auth data
+ notify the mode change to the channel. */
+ if (founder) {
+ SILC_PUT32_MSB(chl->mode, mode);
+ SILC_LOG_DEBUG(("Send CUMODE_CHANGE notify to channel"));
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
+ clidp->data,
+ silc_buffer_len(clidp),
+ mode, 4, clidp->data,
+ silc_buffer_len(clidp),
+ fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0);
+ }
+ }
- /* Free all client specific data, such as client entry and entires
- on channels this client may be on. */
- silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
- TRUE, q->signoff);
- q->sock->user_data = NULL;
+ /* Set CUMODE notify type to network */
+ if (founder)
+ silc_server_send_notify_cumode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel,
+ chl->mode, client->id, SILC_ID_CLIENT,
+ client->id, channel->founder_key);
- /* Close the connection on our side */
- silc_server_close_connection(q->server, q->sock);
+ silc_buffer_free(reply);
+ silc_buffer_free(clidp);
+ silc_buffer_free(chidp);
+ silc_buffer_free(keyp);
+ silc_buffer_free(user_list);
+ silc_buffer_free(mode_list);
+ silc_buffer_free(fkey);
+ silc_buffer_free(chpklist);
+ silc_buffer_free(invite_list);
+ silc_buffer_free(ban_list);
- silc_free(q->signoff);
- silc_free(q);
+ out:
+ if (passphrase)
+ memset(passphrase, 0, strlen(passphrase));
+ silc_free(passphrase);
}
-/* Quits SILC session. This is the normal way to disconnect client. */
-
-SILC_SERVER_CMD_FUNC(quit)
+/* Server side of command JOIN. Joins client into requested channel. If
+ the channel does not exist it will be created. */
+
+void silc_server_command_join_connected(SilcServer server,
+ SilcServerEntry server_entry,
+ void *context)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- SilcSocketConnection sock = cmd->sock;
- QuitInternal q;
- unsigned char *tmp = NULL;
- unsigned int len = 0;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_QUIT, cmd, 0, 1);
-
- if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
- goto out;
- /* Get destination ID */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
- if (len > 128)
- tmp = NULL;
+ if (!server_entry) {
+ SilcUInt32 tmp_len;
+ unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ char serv[256 + 1];
- q = silc_calloc(1, sizeof(*q));
- q->server = server;
- q->sock = sock;
- q->signoff = tmp ? strdup(tmp) : NULL;
+ SILC_LOG_DEBUG(("Connecting to router failed"));
+ silc_parse_userfqdn(tmp, NULL, 0, serv, sizeof(serv));
- /* We quit the connection with little timeout */
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_command_quit_cb, (void *)q,
- 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ if (serv[0]) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NO_SUCH_SERVER, 0,
+ 2, serv, strlen(serv));
+ } else {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL, 0,
+ 2, tmp, tmp_len);
+ }
+ silc_server_command_free(cmd);
+ return;
+ }
- out:
- silc_server_command_free(cmd);
+ /* Reprocess command */
+ SILC_LOG_DEBUG(("Reprocess JOIN after connecting to router"));
+ silc_server_command_join(cmd, NULL);
}
-/* Server side of command KILL. This command is used by router operator
- to remove an client from the SILC Network temporarily. */
-
-SILC_SERVER_CMD_FUNC(kill)
+SILC_SERVER_CMD_FUNC(join)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
- SilcClientEntry remote_client;
- SilcClientID *client_id;
- unsigned char *tmp, *comment;
- unsigned int tmp_len, tmp_len2;
+ SilcIDListData idata = silc_packet_get_context(cmd->sock);
+ unsigned char *auth, *cauth;
+ SilcUInt32 tmp_len, auth_len, cauth_len;
+ char *tmp, *channel_name, *channel_namec = NULL, *cipher, *hmac;
+ char parsed[256 + 1], serv[256 + 1];
+ SilcChannelEntry channel;
+ SilcUInt32 umode = 0;
+ SilcBool created = FALSE, create_key = TRUE;
+ SilcID id;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_KILL, cmd, 1, 2);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 7);
- if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ /* Get channel name */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
+ }
- /* KILL command works only on router */
- if (server->server_type != SILC_ROUTER) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
- SILC_STATUS_ERR_NO_ROUTER_PRIV);
- goto out;
+ /* Truncate over long channel names */
+ if (tmp_len > 256) {
+ tmp_len = 256;
+ tmp[tmp_len - 1] = '\0';
}
- /* Check whether client has the permissions. */
- if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
- SILC_STATUS_ERR_NO_ROUTER_PRIV);
- goto out;
+ /* Parse server name from the channel name */
+ silc_parse_userfqdn(tmp, parsed, sizeof(parsed), serv,
+ sizeof(serv));
+ channel_name = parsed;
+
+ if (server->config->dynamic_server) {
+ /* If server name is not specified but local channels is FALSE then the
+ channel will be global, based on our router name. */
+ if (!serv[0] && !server->config->local_channels) {
+ if (!server->standalone) {
+ silc_snprintf(serv, sizeof(serv), server->router->server_name);
+ } else {
+ SilcServerConfigRouter *router;
+ router = silc_server_config_get_primary_router(server);
+ if (router) {
+ /* Create connection to primary router */
+ SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d",
+ router->host, router->port));
+ silc_server_create_connection(server, FALSE, TRUE,
+ router->host, router->port,
+ silc_server_command_join_connected,
+ cmd);
+ return;
+ }
+ }
+ }
+
+ /* If server name is ours, ignore it. */
+ if (serv[0] && silc_utf8_strcasecmp(serv, server->server_name))
+ memset(serv, 0, sizeof(serv));
+
+ /* Create connection */
+ if (serv[0] && server->standalone) {
+ SilcServerConfigRouter *router;
+ router = silc_server_config_get_primary_router(server);
+ if (router) {
+ /* Create connection to primary router */
+ SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d",
+ router->host, router->port));
+ silc_server_create_connection(server, FALSE, TRUE,
+ router->host, router->port,
+ silc_server_command_join_connected, cmd);
+ return;
+ }
+ }
}
- /* Get the client ID */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ /* Check for valid channel name. This is cached, the original is saved
+ in the channel context. */
+ channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
+ SILC_STRING_UTF8, 256, NULL);
+ if (!channel_namec) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_BAD_CHANNEL, 0);
goto out;
}
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
- SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+
+ /* Get Client ID of the client who is joining to the channel */
+ if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NO_CLIENT_ID,
+ 0);
goto out;
}
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
- /* Get the client entry */
- remote_client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
- if (!remote_client) {
- remote_client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- if (!remote_client) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
- SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+ /* Get cipher, hmac name and auth payload */
+ cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
+ hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
+ cauth = silc_argument_get_arg_type(cmd->args, 7, &cauth_len);
+
+ /* See if the channel exists */
+ channel = silc_idlist_find_channel_by_name(server->local_list,
+ channel_namec, NULL);
+
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ SilcClientEntry entry = (SilcClientEntry)idata;
+ if (!entry) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
- }
- /* Get comment */
- comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
- if (tmp_len2 > 128)
- comment = NULL;
+#ifndef SILC_DIST_INPLACE
+ /* Limit how many channels client can join */
+ if (!cmd->pending && entry->channels &&
+ silc_hash_table_count(entry->channels) >=
+ server->config->param.chlimit) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ goto out;
+ }
+#endif /* SILC_DIST_INPLACE */
- /* Send reply to the sender */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
- SILC_STATUS_OK);
-
- /* Send the KILL notify packets. First send it to the channel, then
- to our primary router and then directly to the client who is being
- killed right now. */
-
- /* Send KILLED notify to the channels. It is not sent to the client
- as it will be sent differently destined directly to the client and not
- to the channel. */
- silc_server_send_notify_on_channels(server, remote_client,
- remote_client, SILC_NOTIFY_TYPE_KILLED,
- comment ? 2 : 1,
- tmp, tmp_len,
- comment, comment ? tmp_len2 : 0);
-
- /* Send KILLED notify to primary route */
- if (!server->standalone)
- silc_server_send_notify_killed(server, server->router->connection, TRUE,
- remote_client->id, SILC_ID_CLIENT_LEN,
- comment);
-
- /* Send KILLED notify to the client directly */
- silc_server_send_notify_killed(server, remote_client->connection ?
- remote_client->connection :
- remote_client->router->connection, FALSE,
- remote_client->id, SILC_ID_CLIENT_LEN,
- comment);
-
- /* Remove the client from all channels. This generates new keys to the
- channels as well. */
- silc_server_remove_from_channels(server, NULL, remote_client, FALSE,
- NULL, TRUE);
-
- /* Remove the client entry, If it is locally connected then we will also
- disconnect the client here */
- if (remote_client->data.registered && remote_client->connection) {
- /* Remove locally conneted client */
- SilcSocketConnection sock = remote_client->connection;
- silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
- silc_server_close_connection(server, sock);
- } else {
- /* Remove remote client */
- if (!silc_idlist_del_client(server->global_list, remote_client))
- silc_idlist_del_client(server->local_list, remote_client);
- }
+ if (!channel ||
+ (channel->disabled && server->server_type != SILC_ROUTER)) {
+ /* Channel not found or not valid */
- out:
- silc_server_command_free(cmd);
-}
+ /* If we are standalone server we don't have a router, we just create
+ the channel by ourselves (unless it existed). */
+ if (server->standalone) {
+ if (!channel) {
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ if (cipher) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, cipher, strlen(cipher));
+ } else if (hmac) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, hmac, strlen(hmac));
+ } else {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ }
+ goto out;
+ }
-/* Server side of command INFO. This sends information about us to
- the client. If client requested specific server we will send the
- command to that server. */
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ } else {
-SILC_SERVER_CMD_FUNC(info)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- SilcBuffer packet, idp;
- char info_string[256], *dest_server;
+ /* The channel does not exist on our server. If we are normal server
+ we will send JOIN command to our router which will handle the
+ joining procedure (either creates the channel if it doesn't exist
+ or joins the client to it). */
+ if (server->server_type != SILC_ROUTER) {
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 1);
+ /* If this is pending command callback then we've resolved
+ it and it didn't work, return since we've notified the
+ client already in the command reply callback. */
+ if (cmd->pending)
+ goto out;
- /* Get server name */
- dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
- if (!dest_server) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
- SILC_STATUS_ERR_NO_SUCH_SERVER);
- goto out;
- }
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
- if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
- /* Send our reply */
- memset(info_string, 0, sizeof(info_string));
- snprintf(info_string, sizeof(info_string),
- "location: %s server: %s admin: %s <%s>",
- server->config->admin_info->location,
- server->config->admin_info->server_type,
- server->config->admin_info->admin_name,
- server->config->admin_info->admin_email);
-
- idp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
-
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
- SILC_STATUS_OK, 0, 2,
- 2, idp->data, idp->len,
- 3, info_string,
- strlen(info_string));
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
- packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_buffer_free(idp);
- } else {
- /* Send this command to the requested server */
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ /* Send JOIN command to our router */
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_JOIN,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_join,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
- if (server->server_type == SILC_SERVER && !server->standalone) {
+ /* We are router and the channel does not seem exist so we will check
+ our global list as well for the channel. */
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_namec, NULL);
+ if (!channel) {
+ /* Channel really does not exist, create it */
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ if (cipher) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, cipher, strlen(cipher));
+ } else if (hmac) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, hmac, strlen(hmac));
+ } else {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ }
+ goto out;
+ }
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ }
}
+ } else {
+ if (!channel) {
+ /* Channel not found */
- if (server->server_type == SILC_ROUTER) {
+ /* If the command came from router and we are normal server then
+ something went wrong with the joining as the channel was not found.
+ We can't do anything else but ignore this. */
+ if (idata->conn_type == SILC_CONN_ROUTER ||
+ server->server_type != SILC_ROUTER)
+ goto out;
+
+ /* We are router and the channel does not seem exist so we will check
+ our global list as well for the channel. */
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_namec, NULL);
+ if (!channel) {
+ /* Channel really does not exist, create it */
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ if (cipher) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, cipher, strlen(cipher));
+ } else if (hmac) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+ 0, 2, hmac, strlen(hmac));
+ } else {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ }
+ goto out;
+ }
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ }
+ }
+ /* Check whether the channel was created by our router */
+ if (cmd->pending && context2) {
+ SilcServerCommandReplyContext reply = context2;
+
+ if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
+ tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
+ SILC_GET32_MSB(created, tmp);
+ if (silc_argument_get_arg_type(reply->args, 7, NULL))
+ create_key = FALSE; /* Router returned the key already */
+
+ if (silc_command_get_status(reply->payload, NULL, NULL) &&
+ channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ /* Save channel passphrase, if user provided it successfully */
+ unsigned char *pa;
+ SilcUInt32 pa_len;
+ pa = silc_argument_get_arg_type(cmd->args, 3, &pa_len);
+ if (pa) {
+ silc_free(channel->passphrase);
+ channel->passphrase = silc_memdup(pa, pa_len);
+ }
+ }
}
+
+ if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+ !channel->disabled && !silc_hash_table_count(channel->user_list))
+ created = TRUE;
}
-
+
+ /* If the channel does not have global users and is also empty the client
+ will be the channel founder and operator. */
+ if (!channel->disabled &&
+ !channel->global_users && !silc_hash_table_count(channel->user_list))
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
+ /* Join to the channel */
+ silc_server_command_join_channel(server, cmd, channel, SILC_ID_GET_ID(id),
+ created, create_key, umode,
+ auth, auth_len, cauth, cauth_len);
+
out:
+ silc_free(channel_namec);
silc_server_command_free(cmd);
}
-/* Server side of command PING. This just replies to the ping. */
+/* Server side of command MOTD. Sends server's current "message of the
+ day" to the client. */
-SILC_SERVER_CMD_FUNC(ping)
+SILC_SERVER_CMD_FUNC(motd)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcServerID *id;
- unsigned int len;
- unsigned char *tmp;
+ SilcBuffer idp;
+ char *motd, *dest_server = NULL;
+ SilcUInt32 motd_len;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 2);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_MOTD, cmd, 1, 1);
- /* Get Server ID */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
- SILC_STATUS_ERR_NO_SERVER_ID);
+ /* Get server name */
+ dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+ if (!dest_server) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
- id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
- if (!id)
- goto out;
- if (!SILC_ID_SERVER_COMPARE(id, server->id)) {
- /* Send our reply */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
- SILC_STATUS_OK);
- } else {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
- SILC_STATUS_ERR_NO_SUCH_SERVER);
+ /* Check server name */
+ dest_server = silc_identifier_check(dest_server, strlen(dest_server),
+ SILC_STRING_UTF8, 256, NULL);
+ if (!dest_server) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
+ SILC_STATUS_ERR_BAD_SERVER,
+ 0);
goto out;
}
- silc_free(id);
-
- out:
- silc_server_command_free(cmd);
-}
-
-/* Internal routine to join channel. The channel sent to this function
- has been either created or resolved from ID lists. This joins the sent
- client to the channel. */
+ if (!memcmp(dest_server, server->server_name, strlen(dest_server))) {
+ /* Send our MOTD */
-static void silc_server_command_join_channel(SilcServer server,
- SilcServerCommandContext cmd,
- SilcChannelEntry channel,
- SilcClientID *client_id,
- int created,
- unsigned int umode)
-{
- SilcSocketConnection sock = cmd->sock;
- unsigned char *tmp;
- unsigned int tmp_len, user_count;
- unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
- SilcClientEntry client;
- SilcChannelClientEntry chl;
- SilcBuffer reply, chidp, clidp, keyp, user_list, mode_list;
- unsigned short ident = silc_command_get_ident(cmd->payload);
+ idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
- SILC_LOG_DEBUG(("Start"));
+ if (server->config && server->config->server_info &&
+ server->config->server_info->motd_file) {
+ /* Send motd */
+ motd = silc_file_readfile(server->config->server_info->motd_file,
+ &motd_len);
+ if (!motd) {
+ /* No motd */
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+ SILC_STATUS_OK, 0, ident, 1,
+ 2, idp->data, silc_buffer_len(idp));
+ goto out;
+ }
- if (!channel)
- return;
+ motd[motd_len] = 0;
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, idp->data, silc_buffer_len(idp),
+ 3, motd, motd_len);
+ } else {
+ /* No motd */
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+ SILC_STATUS_OK, 0, ident, 1,
+ 2, idp->data, silc_buffer_len(idp));
+ }
+ silc_buffer_free(idp);
+ } else {
+ SilcServerEntry entry;
+
+ /* Check whether we have this server cached */
+ entry = silc_idlist_find_server_by_name(server->global_list,
+ dest_server, TRUE, NULL);
+ if (!entry) {
+ entry = silc_idlist_find_server_by_name(server->local_list,
+ dest_server, TRUE, NULL);
+ }
- /* Get passphrase */
- tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
- if (tmp) {
- passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
- memcpy(passphrase, tmp, tmp_len);
- }
-
- /*
- * Check channel modes
- */
+ if (server->server_type != SILC_SERVER && !cmd->pending &&
+ entry && !entry->motd) {
+ /* Send to the server */
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
- /* Check invite list if channel is invite-only channel */
- if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
- if (channel->mode & SILC_CHANNEL_MODE_INVITE_LIST) {
- /* Invite list is specified. Check whether client is invited in the
- list. If not, then check whether it has been invited otherwise. */
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
- } else {
- /* XXX client must be invited to be able to join the channel */
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, entry->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_MOTD,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_motd,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
}
- }
- /* Check ban list if set */
- if (channel->mode & SILC_CHANNEL_MODE_BAN) {
+ /* Send to primary router only if we don't know the server
+ * the client requested or if the server is not locally connected */
+ if ((!entry || !(entry->data.status & SILC_IDLIST_STATUS_LOCAL))
+ && !cmd->pending && !server->standalone) {
+ /* Send to the primary router */
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
- }
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
- /* Check the channel passphrase if set. */
- if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
- if (!passphrase || memcmp(channel->mode_data.passphrase, passphrase,
- strlen(channel->mode_data.passphrase))) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_BAD_PASSWORD);
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_MOTD,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_motd,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
goto out;
}
- }
- /* Check user count limit if set. */
- if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
- if (silc_list_count(channel->user_list) + 1 >
- channel->mode_data.user_limit) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_CHANNEL_IS_FULL);
+ if (!entry) {
+ silc_free(dest_server);
+ dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_MOTD,
+ SILC_STATUS_ERR_NO_SUCH_SERVER, 0,
+ 2, dest_server,
+ strlen(dest_server));
+ dest_server = NULL;
goto out;
}
+
+ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, idp->data, silc_buffer_len(idp),
+ 3, entry->motd,
+ entry->motd ?
+ strlen(entry->motd) : 0);
+ silc_buffer_free(idp);
}
- /*
- * Client is allowed to join to the channel. Make it happen.
- */
+ out:
+ silc_free(dest_server);
+ silc_server_command_free(cmd);
+}
- /* Get the client entry */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
- client = (SilcClientEntry)sock->user_data;
- } else {
- client = silc_idlist_find_client_by_id(server->local_list, client_id,
- NULL);
- if (!client) {
- /* XXX actually this is useless since router finds always cell's
- local clients from its local lists. */
- client = silc_idlist_find_client_by_id(server->global_list, client_id,
- NULL);
- if (!client)
- goto out;
- }
- }
+/* Server side of command UMODE. Client can use this command to set/unset
+ user mode. Client actually cannot set itself to be as server/router
+ operator so this can be used only to unset the modes. */
- /* Check whether the client already is on the channel */
- if (silc_server_client_on_channel(client, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_USER_ON_CHANNEL);
+SILC_SERVER_CMD_FUNC(umode)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ unsigned char *tmp_mask, m[4];
+ SilcUInt32 mask = 0;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ SilcBool set_mask = FALSE;
+
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
goto out;
- }
- /* Generate new channel key as protocol dictates */
- if ((!created && silc_list_count(channel->user_list) > 0) ||
- !channel->channel_key)
- silc_server_create_channel_key(server, channel, 0);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 1, 2);
- /* Send the channel key. This is broadcasted to the channel but is not
- sent to the client who is joining to the channel. */
- silc_server_send_channel_key(server, NULL, channel,
- server->server_type == SILC_ROUTER ?
- FALSE : !server->standalone);
+ /* Get the client's mode mask */
+ tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
+ if (tmp_mask) {
+ SILC_GET32_MSB(mask, tmp_mask);
+ set_mask = TRUE;
+ }
- /* Join the client to the channel by adding it to channel's user list.
- Add also the channel to client entry's channels list for fast cross-
- referencing. */
- chl = silc_calloc(1, sizeof(*chl));
- chl->mode = umode;
- chl->client = client;
- chl->channel = channel;
- silc_list_add(channel->user_list, chl);
- silc_list_add(client->channels, chl);
+ if (set_mask) {
+ /* Check that mode changing is allowed. */
+ if (!silc_server_check_umode_rights(server, client, mask)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED, 0);
+ goto out;
+ }
- /* Get users on the channel */
- silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
- &user_count);
+ /* Anonymous mode cannot be set by client */
+ if (mask & SILC_UMODE_ANONYMOUS &&
+ !(client->mode & SILC_UMODE_ANONYMOUS)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED, 0);
+ goto out;
+ }
- /* Encode Client ID Payload of the original client who wants to join */
- clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ /* Update statistics */
+ if (mask & SILC_UMODE_GONE) {
+ if (!(client->mode & SILC_UMODE_GONE))
+ server->stat.my_aways++;
+ } else {
+ if (client->mode & SILC_UMODE_GONE)
+ server->stat.my_aways--;
+ }
- /* Encode command reply packet */
- chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
- SILC_PUT32_MSB(channel->mode, mode);
- SILC_PUT32_MSB(created, tmp2);
- SILC_PUT32_MSB(user_count, tmp3);
- tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
- keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp,
- strlen(channel->channel_key->
- cipher->name),
- channel->channel_key->cipher->name,
- channel->key_len / 8, channel->key);
- silc_free(tmp);
- if (!channel->topic) {
- reply =
- silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
- SILC_STATUS_OK, ident, 9,
- 2, channel->channel_name,
- strlen(channel->channel_name),
- 3, chidp->data, chidp->len,
- 4, clidp->data, clidp->len,
- 5, mode, 4,
- 6, tmp2, 4,
- 7, keyp->data, keyp->len,
- 12, tmp3, 4,
- 13, user_list->data, user_list->len,
- 14, mode_list->data,
- mode_list->len);
- } else {
- reply =
- silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
- SILC_STATUS_OK, ident, 10,
- 2, channel->channel_name,
- strlen(channel->channel_name),
- 3, chidp->data, chidp->len,
- 4, clidp->data, clidp->len,
- 5, mode, 4,
- 6, tmp2, 4,
- 7, keyp->data, keyp->len,
- 10, channel->topic,
- strlen(channel->topic),
- 12, tmp3, 4,
- 13, user_list->data, user_list->len,
- 14, mode_list->data,
- mode_list->len);
- }
+ /* If the client has anonymous mode set, preserve it. */
+ if (client->mode & SILC_UMODE_ANONYMOUS)
+ mask |= SILC_UMODE_ANONYMOUS;
- /* Send command reply */
- silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
- reply->data, reply->len, FALSE);
+ /* Change the mode */
+ client->mode = mask;
- if (!cmd->pending) {
- /* Send JOIN notify to locally connected clients on the channel */
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_JOIN, 2,
- clidp->data, clidp->len,
- chidp->data, chidp->len);
+ /* Send UMODE change to primary router */
+ silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), client->id,
+ client->mode);
- /* Send JOIN notify packet to our primary router */
- if (!server->standalone)
- silc_server_send_notify_join(server, server->router->connection,
- server->server_type == SILC_ROUTER ?
- TRUE : FALSE, channel, client->id,
- SILC_ID_CLIENT_LEN);
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_UMODE_CHANGE);
}
- silc_buffer_free(reply);
- silc_buffer_free(clidp);
- silc_buffer_free(chidp);
- silc_buffer_free(keyp);
- silc_buffer_free(user_list);
- silc_buffer_free(mode_list);
+ /* Send command reply to sender */
+ SILC_PUT32_MSB(client->mode, m);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_UMODE,
+ SILC_STATUS_OK, 0, ident, 1,
+ 2, m, sizeof(m));
out:
- if (passphrase)
- silc_free(passphrase);
+ silc_server_command_free(cmd);
}
-/* Server side of command JOIN. Joins client into requested channel. If
- the channel does not exist it will be created. */
+/* Server side command of CMODE. Changes channel mode */
-SILC_SERVER_CMD_FUNC(join)
+SILC_SERVER_CMD_FUNC(cmode)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- int tmp_len;
- char *tmp, *channel_name = NULL, *cipher, *hmac;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ SilcIDListData idata = (SilcIDListData)client;
+ SilcID id;
SilcChannelEntry channel;
- unsigned int umode = 0;
- int created = FALSE;
- SilcClientID *client_id;
+ SilcChannelClientEntry chl;
+ SilcBuffer cidp;
+ unsigned char *tmp, *tmp_id, *tmp_mask, *chpkdata = NULL;
+ char *cipher = NULL, *hmac = NULL, *passphrase = NULL, ulimit[4];
+ SilcUInt32 mode_mask = 0, old_mask = 0, tmp_len, tmp_len2, chpklen;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ SilcBool set_mask = FALSE, set_chpk = FALSE;
+ SilcPublicKey founder_key = NULL;
+ SilcBuffer fkey = NULL, chpklist = NULL;
+ SilcBufferStruct chpk;
+
+ if (!client) {
+ silc_server_command_free(cmd);
+ return;
+ }
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 4);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 1, 9);
- /* Get channel name */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
+ /* Get Channel ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+ silc_server_command_free(cmd);
+ return;
+ }
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 0, 2, tmp_id, tmp_len2);
+ silc_server_command_free(cmd);
+ return;
+ }
}
- channel_name = tmp;
+ old_mask = channel->mode;
- if (strlen(channel_name) > 256)
- channel_name[255] = '\0';
+ /* Get the channel mode mask */
+ tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (tmp_mask) {
+ SILC_GET32_MSB(mode_mask, tmp_mask);
+ set_mask = TRUE;
+ }
- if (silc_server_command_bad_chars(channel_name) == TRUE) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_BAD_CHANNEL);
- silc_free(channel_name);
+ /* Check whether this client is on the channel */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+ 2, tmp_id, tmp_len2);
goto out;
}
- /* Get Client ID of the client who is joining to the channel */
- tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ /* Check that client has rights to change any requested channel modes */
+ if (set_mask && !silc_server_check_cmode_rights(server, channel, chl,
+ mode_mask)) {
+ SILC_LOG_DEBUG(("Client does not have rights to change mode"));
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) ?
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV :
+ SILC_STATUS_ERR_NO_CHANNEL_FOPRIV), 0,
+ 2, tmp_id, tmp_len2);
goto out;
}
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+
+ /* If mode mask was not sent as argument then merely return the current
+ mode mask, founder key and channel public key list to the sender. */
+ if (!set_mask) {
+ unsigned char m[4];
+ SILC_PUT32_MSB(channel->mode, m);
+ if (channel->founder_key)
+ fkey = silc_public_key_payload_encode(channel->founder_key);
+ if (channel->channel_pubkeys)
+ chpklist = silc_server_get_channel_pk_list(server, channel,
+ FALSE, FALSE);
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_CMODE,
+ SILC_STATUS_OK, 0, ident, 4,
+ 2, tmp_id, tmp_len2,
+ 3, m, sizeof(m),
+ 4, fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0,
+ 5, chpklist ? chpklist->data : NULL,
+ chpklist ? silc_buffer_len(chpklist) : 0);
goto out;
}
- /* Get cipher and hmac name */
- cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
- hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ /*
+ * Check the modes. Modes that requires nothing special operation are
+ * not checked here.
+ */
- /* See if the channel exists */
- channel = silc_idlist_find_channel_by_name(server->local_list,
- channel_name, NULL);
-
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
- /* If this is coming from client the Client ID in the command packet must
- be same as the client's ID. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
- SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
- if (SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
+ /* Channel uses private keys to protect traffic. Client(s) has set the
+ key locally they want to use, server does not know that key. */
+ /* Nothing interesting to do here */
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+ /* The mode is removed and we need to generate and distribute
+ new channel key. Clients are not using private channel keys
+ anymore after this. */
+
+ /* if we don't remove the flag from the mode
+ * silc_server_create_channel_key won't create a new key */
+ channel->mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
+
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0))
goto out;
- }
+
+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+
+ cipher = (char *)silc_cipher_get_name(channel->send_key);
+ hmac = (char *)silc_hmac_get_name(channel->hmac);
}
+ }
- if (!channel) {
- /* Channel not found */
+ if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
+ /* User limit is set on channel */
+ SilcUInt32 user_limit;
- /* If we are standalone server we don't have a router, we just create
- the channel by ourselves. */
- if (server->standalone) {
- channel = silc_server_create_new_channel(server, server->id, cipher,
- hmac, channel_name, TRUE);
- umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
- created = TRUE;
+ /* Get user limit */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+ if (!tmp) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
+ }
+ } else {
+ SILC_GET32_MSB(user_limit, tmp);
+ channel->user_limit = user_limit;
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ /* User limit mode is unset. Remove user limit */
+ channel->user_limit = 0;
+ }
- } else {
+ if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
+ /* Passphrase has been set to channel */
- /* The channel does not exist on our server. If we are normal server
- we will send JOIN command to our router which will handle the
- joining procedure (either creates the channel if it doesn't exist
- or joins the client to it). */
- if (server->server_type == SILC_SERVER) {
- SilcBuffer tmpbuf;
- unsigned short old_ident;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send JOIN command to our router */
- silc_server_packet_send(server, (SilcSocketConnection)
- server->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply from router */
- silc_server_command_pending(server, SILC_COMMAND_JOIN,
- silc_command_get_ident(cmd->payload),
- silc_server_command_destructor,
- silc_server_command_join,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
- return;
- }
-
- /* We are router and the channel does not seem exist so we will check
- our global list as well for the channel. */
- channel = silc_idlist_find_channel_by_name(server->global_list,
- channel_name, NULL);
- if (!channel) {
- /* Channel really does not exist, create it */
- channel = silc_server_create_new_channel(server, server->id, cipher,
- hmac, channel_name, TRUE);
- umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
- created = TRUE;
- }
+ /* Get the passphrase */
+ tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
}
+
+ /* Save the passphrase */
+ passphrase = channel->passphrase = silc_memdup(tmp, strlen(tmp));
}
} else {
- if (!channel) {
- /* Channel not found */
+ if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ /* Passphrase mode is unset. remove the passphrase */
+ silc_free(channel->passphrase);
+ channel->passphrase = NULL;
+ }
+ }
- /* If the command came from router and/or we are normal server then
- something went wrong with the joining as the channel was not found.
- We can't do anything else but ignore this. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
- server->server_type == SILC_SERVER)
+ if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
+ /* Cipher to use protect the traffic */
+ SilcCipher send_key, receive_key, olds, oldr;
+
+ /* Get cipher */
+ cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ if (!cipher) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
+ }
+
+ /* Delete old cipher and allocate the new one */
+ if (!silc_cipher_alloc(cipher, &send_key)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, cipher, strlen(cipher));
+ goto out;
+ }
+ if (!silc_cipher_alloc(cipher, &receive_key)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, cipher, strlen(cipher));
goto out;
-
- /* We are router and the channel does not seem exist so we will check
- our global list as well for the channel. */
- channel = silc_idlist_find_channel_by_name(server->global_list,
- channel_name, NULL);
- if (!channel) {
- /* Channel really does not exist, create it */
- channel = silc_server_create_new_channel(server, server->id, cipher,
- hmac, channel_name, TRUE);
- umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
- created = TRUE;
}
+
+ olds = channel->send_key;
+ oldr = channel->receive_key;
+ channel->send_key = send_key;
+ channel->receive_key = receive_key;
+
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0)) {
+ /* We don't have new key, revert to old one */
+ channel->send_key = olds;
+ channel->receive_key = oldr;
+ goto out;
+ }
+
+ /* Remove old channel key for good */
+ silc_cipher_free(olds);
+ silc_cipher_free(oldr);
+
+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
}
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+ /* Cipher mode is unset. Remove the cipher and revert back to
+ default cipher */
+ SilcCipher send_key, receive_key, olds, oldr;
+ cipher = channel->cipher;
+
+ /* Delete old cipher and allocate default one */
+ if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER,
+ &send_key)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, cipher, strlen(cipher));
+ goto out;
+ }
+ if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER,
+ &receive_key)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, cipher, strlen(cipher));
+ goto out;
+ }
+
+ olds = channel->send_key;
+ oldr = channel->receive_key;
+ channel->send_key = send_key;
+ channel->receive_key = receive_key;
+
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0)) {
+ /* We don't have new key, revert to old one */
+ channel->send_key = olds;
+ channel->receive_key = oldr;
+ goto out;
+ }
+
+ /* Remove old channel key for good */
+ silc_cipher_free(olds);
+ silc_cipher_free(oldr);
+
+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
+ /* HMAC to use protect the traffic */
+ unsigned char hash[SILC_HASH_MAXLEN];
+ SilcHmac newhmac;
+
+ /* Get hmac */
+ hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
+ if (!hmac) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
+ }
+
+ /* Delete old hmac and allocate the new one */
+ if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, hmac, strlen(hmac));
+ goto out;
+ }
+
+ silc_hmac_free(channel->hmac);
+ channel->hmac = newhmac;
+
+ /* Set the HMAC key out of current channel key. The client must do
+ this locally. */
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+ channel->key_len / 8, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+ memset(hash, 0, sizeof(hash));
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
+ /* Hmac mode is unset. Remove the hmac and revert back to
+ default hmac */
+ SilcHmac newhmac;
+ unsigned char hash[SILC_HASH_MAXLEN];
+ hmac = channel->hmac_name;
+
+ /* Delete old hmac and allocate default one */
+ if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+ 2, hmac, strlen(hmac));
+ goto out;
+ }
+
+ silc_hmac_free(channel->hmac);
+ channel->hmac = newhmac;
+
+ /* Set the HMAC key out of current channel key. The client must do
+ this locally. */
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+ channel->key_len / 8,
+ hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+ memset(hash, 0, sizeof(hash));
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ /* Check if the founder public key was received */
+ founder_key = idata->public_key;
+ tmp = silc_argument_get_arg_type(cmd->args, 8, &tmp_len);
+ if (tmp) {
+ if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ goto out;
+ }
+ } else {
+ /* If key was not sent and the channel mode has already founder
+ then the key was not to be changed. */
+ if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
+ goto has_founder;
+ }
+
+ /* Set the founder authentication */
+ tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
+ }
+
+ /* Verify the payload before setting the mode */
+ if (!silc_auth_verify_data(tmp, tmp_len, SILC_AUTH_PUBLIC_KEY,
+ founder_key, 0, server->sha1hash,
+ client->id, SILC_ID_CLIENT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ goto out;
+ }
+
+ /* Save the public key */
+ if (channel->founder_key)
+ silc_pkcs_public_key_free(channel->founder_key);
+ if (silc_argument_get_arg_type(cmd->args, 8, NULL))
+ channel->founder_key = founder_key;
+ else
+ channel->founder_key = silc_pkcs_public_key_copy(founder_key);
+ if (!channel->founder_key) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ goto out;
+ }
+
+ fkey = silc_public_key_payload_encode(channel->founder_key);
+ if (!fkey) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ silc_pkcs_public_key_free(channel->founder_key);
+ channel->founder_key = NULL;
+ goto out;
+ }
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (channel->founder_key)
+ silc_pkcs_public_key_free(channel->founder_key);
+ channel->founder_key = NULL;
+ }
+ }
+ }
+ has_founder:
+
+ if (mode_mask & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ SilcStatus st;
+
+ chpkdata = silc_argument_get_arg_type(cmd->args, 9, &chpklen);
+
+ if (!chpkdata && channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
+ goto has_pk_list;
+
+ set_chpk = TRUE;
+
+ /* Process the channel public key(s) */
+ st = silc_server_set_channel_pk_list(server, NULL, channel,
+ chpkdata, chpklen);
+ if (st != SILC_STATUS_OK) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, st, 0);
+ goto out;
+ }
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+ if (channel->channel_pubkeys)
+ silc_hash_table_free(channel->channel_pubkeys);
+ channel->channel_pubkeys = NULL;
+ set_chpk = TRUE;
+ }
+ }
+ }
+ has_pk_list:
+
+ /* Finally, set the mode */
+ old_mask = channel->mode = mode_mask;
+
+ /* Send CMODE_CHANGE notify. */
+ cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (mode_mask & SILC_CHANNEL_MODE_ULIMIT)
+ SILC_PUT32_MSB(channel->user_limit, ulimit);
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_CMODE_CHANGE, 8,
+ cidp->data, silc_buffer_len(cidp),
+ tmp_mask, 4,
+ cipher, cipher ? strlen(cipher) : 0,
+ hmac, hmac ? strlen(hmac) : 0,
+ passphrase, passphrase ?
+ strlen(passphrase) : 0,
+ fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0,
+ chpkdata ? chpkdata : NULL,
+ chpkdata ? chpklen : 0,
+ mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL,
+ mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 0);
+
+ /* Set CMODE notify type to network */
+ if (chpkdata && chpklen)
+ silc_buffer_set(&chpk, chpkdata, chpklen);
+ silc_server_send_notify_cmode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel,
+ mode_mask, client->id, SILC_ID_CLIENT,
+ cipher, hmac, passphrase, founder_key,
+ chpkdata ? &chpk : NULL);
+
+ if (set_chpk)
+ chpklist = silc_server_get_channel_pk_list(server, channel, FALSE, FALSE);
+
+ /* Send command reply to sender */
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_CMODE,
+ SILC_STATUS_OK, 0, ident, 5,
+ 2, tmp_id, tmp_len2,
+ 3, tmp_mask, 4,
+ 4, fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0,
+ 5, chpklist ? chpklist->data :
+ NULL, chpklist ? silc_buffer_len(chpklist)
+ : 0,
+ 6, (mode_mask &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL),
+ (mode_mask &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 0));
+ silc_buffer_free(cidp);
+
+ out:
+ channel->mode = old_mask;
+ silc_buffer_free(chpklist);
+ silc_buffer_free(fkey);
+ silc_server_command_free(cmd);
+}
+
+/* Server side of CUMODE command. Changes client's mode on a channel. */
+
+SILC_SERVER_CMD_FUNC(cumode)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ SilcID id, id2;
+ SilcChannelEntry channel;
+ SilcClientEntry target_client;
+ SilcChannelClientEntry chl;
+ SilcBuffer idp;
+ unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
+ SilcUInt32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
+ int notify = FALSE;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ SilcPublicKey founder_key = NULL;
+ SilcBuffer fkey = NULL;
+
+ if (!client)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
+
+ /* Get Channel ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+ goto out;
+ }
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 0, 2, tmp_ch_id, tmp_ch_len);
+ goto out;
+ }
+ }
+
+ /* Check whether sender is on the channel */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+ 2, tmp_ch_id, tmp_ch_len);
+ goto out;
+ }
+ sender_mask = chl->mode;
+
+ /* Get the target client's channel mode mask */
+ tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
+ if (!tmp_mask) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
+ SILC_GET32_MSB(target_mask, tmp_mask);
+
+ /* Get target Client ID */
+ if (!silc_argument_get_decoded(cmd->args, 3, SILC_ARGUMENT_ID, &id2, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CLIENT_ID, 0);
+ goto out;
+ }
+
+ /* Get target client's entry */
+ target_client = silc_idlist_find_client_by_id(server->local_list,
+ SILC_ID_GET_ID(id2),
+ TRUE, NULL);
+ if (!target_client)
+ target_client = silc_idlist_find_client_by_id(server->global_list,
+ SILC_ID_GET_ID(id2),
+ TRUE, NULL);
+
+ if (target_client != client &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) {
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0,
+ 2, tmp_ch_id, tmp_ch_len);
+ goto out;
+ }
+
+ /* Check whether target client is on the channel */
+ if (target_client != client) {
+ if (!silc_server_client_on_channel(target_client, channel, &chl)) {
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+ silc_server_command_send_status_data2(
+ cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_USER_NOT_ON_CHANNEL, 0,
+ 2, tmp_id, tmp_len,
+ 3, tmp_ch_id, tmp_ch_len);
+ goto out;
+ }
+ }
+
+ /*
+ * Change the mode
+ */
+
+ /* If the target client is founder, no one else can change their mode
+ but themselves. */
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+ 0, 2, tmp_ch_id, tmp_ch_len);
+ goto out;
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+ 0);
+ goto out;
+ }
+
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+ /* The client tries to claim the founder rights. */
+ unsigned char *tmp_auth;
+ SilcUInt32 tmp_auth_len;
+ SilcChannelClientEntry chl2;
+ SilcHashTableList htl;
+
+ if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
+ !channel->founder_key) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ goto out;
+ }
+
+ tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
+ if (!tmp_auth) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ goto out;
+ }
+
+ /* Verify the authentication payload */
+ if (!silc_auth_verify_data(tmp_auth, tmp_auth_len, SILC_AUTH_PUBLIC_KEY,
+ channel->founder_key, 0, server->sha1hash,
+ client->id, SILC_ID_CLIENT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ goto out;
+ }
+
+ notify = TRUE;
+ founder_key = channel->founder_key;
+ fkey = silc_public_key_payload_encode(founder_key);
+ if (!fkey) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ goto out;
+ }
+
+ /* There cannot be anyone else as founder on the channel now. This
+ client is definitely the founder due to this authentication. This
+ is done only on router, not on server, since server cannot know
+ whether router will accept this mode change or not. XXX This
+ probably shouldn't be done anymore at all, may cause problems in
+ router-router connections too (maybe just AUTH_FAILED error should
+ be returned). -Pekka */
+ if (server->server_type == SILC_ROUTER) {
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+ if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+ silc_server_force_cumode_change(server, NULL, channel, chl2,
+ chl2->mode);
+ break;
+ }
+ silc_hash_table_list_reset(&htl);
+ }
+
+ sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (target_client == client) {
+ /* Remove channel founder rights from itself */
+ chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+ notify = TRUE;
+ } else {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0);
+ goto out;
+ }
+ }
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
+ /* Promote to operator */
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+ if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+ 0, 2, tmp_ch_id, tmp_ch_len);
+ goto out;
+ }
+
+ chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
+ notify = TRUE;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
+ if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+ 0, 2, tmp_ch_id, tmp_ch_len);
+ goto out;
+ }
+
+ /* Demote to normal user */
+ chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+ notify = TRUE;
+ }
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0);
+ goto out;
+ }
+
+ if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)) {
+ chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+ notify = TRUE;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0);
+ goto out;
+ }
+
+ chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+ notify = TRUE;
+ }
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0);
+ goto out;
+ }
+
+ if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)) {
+ chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+ notify = TRUE;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0);
+ goto out;
+ }
+
+ chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+ notify = TRUE;
+ }
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0);
+ goto out;
+ }
+
+ if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)) {
+ chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+ notify = TRUE;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU, 0);
+ goto out;
+ }
+
+ chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+ notify = TRUE;
+ }
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_QUIET) {
+ if (!(chl->mode & SILC_CHANNEL_UMODE_QUIET)) {
+ if (client == target_client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_PERM_DENIED, 0);
+ goto out;
+ }
+ chl->mode |= SILC_CHANNEL_UMODE_QUIET;
+ notify = TRUE;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_QUIET) {
+ if (client == target_client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_PERM_DENIED, 0);
+ goto out;
+ }
+ chl->mode &= ~SILC_CHANNEL_UMODE_QUIET;
+ notify = TRUE;
+ }
+ }
+
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+
+ /* Send notify to channel, notify only if mode was actually changed. */
+ if (notify) {
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
+ idp->data, silc_buffer_len(idp),
+ tmp_mask, 4,
+ tmp_id, tmp_len,
+ fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0);
+
+ /* Set CUMODE notify type to network */
+ silc_server_send_notify_cumode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel,
+ target_mask, client->id, SILC_ID_CLIENT,
+ target_client->id, founder_key);
+ }
+
+ /* Send command reply to sender */
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_CUMODE,
+ SILC_STATUS_OK, 0, ident, 3,
+ 2, tmp_mask, 4,
+ 3, tmp_ch_id, tmp_ch_len,
+ 4, tmp_id, tmp_len);
+ silc_buffer_free(idp);
+
+ out:
+ silc_buffer_free(fkey);
+ silc_server_command_free(cmd);
+}
+
+/* Server side of KICK command. Kicks client out of channel. */
+
+SILC_SERVER_CMD_FUNC(kick)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ SilcClientEntry target_client;
+ SilcID id, id2;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ SilcBuffer idp;
+ SilcUInt32 tmp_len, target_idp_len, clen;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ unsigned char *tmp, *comment, *target_idp;
+
+ if (!client)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
+
+ /* Get Channel ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+ goto out;
+ }
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
+ }
+
+ /* Check whether sender is on the channel */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
+
+ /* Check that the kicker is channel operator or channel founder */
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
+
+ /* Get target Client ID */
+ if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id2, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+ SILC_STATUS_ERR_NO_CLIENT_ID, 0);
+ goto out;
+ }
+
+ /* Get target client's entry */
+ target_client = silc_idlist_find_client_by_id(server->local_list,
+ SILC_ID_GET_ID(id2),
+ TRUE, NULL);
+ if (!target_client)
+ target_client = silc_idlist_find_client_by_id(server->global_list,
+ SILC_ID_GET_ID(id2),
+ TRUE, NULL);
+
+ /* Check whether target client is on the channel */
+ if (!silc_server_client_on_channel(target_client, channel, &chl)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
+ silc_server_command_send_status_data2(cmd, SILC_COMMAND_KICK,
+ SILC_STATUS_ERR_USER_NOT_ON_CHANNEL,
+ 0, 2, target_idp, target_idp_len,
+ 3, tmp, tmp_len);
+ goto out;
+ }
+
+ /* Check that the target client is not channel founder. Channel founder
+ cannot be kicked from the channel. */
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+ SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
+
+ /* Get comment */
+ comment = silc_argument_get_arg_type(cmd->args, 3, &clen);
+ if (clen > 128)
+ comment = NULL;
+
+ /* Send the reply back to the client */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_KICK,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, tmp, tmp_len,
+ 3, target_idp, target_idp_len);
+
+ /* Send KICKED notify to local clients on the channel */
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_KICKED, 3,
+ target_idp, target_idp_len,
+ comment, comment ? strlen(comment) : 0,
+ idp->data, silc_buffer_len(idp));
+ silc_buffer_free(idp);
+
+ /* Send KICKED notify to primary route */
+ silc_server_send_notify_kicked(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel,
+ target_client->id, client->id, comment);
+
+ /* Remove the client from channel's invite list */
+ if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+ SilcBuffer ab =
+ silc_argument_payload_encode_one(NULL, target_idp, target_idp_len, 3);
+ SilcArgumentPayload args =
+ silc_argument_payload_parse(ab->data, silc_buffer_len(ab), 1);
+
+ silc_server_inviteban_process(server, channel->invite_list, 1, args);
+ silc_buffer_free(ab);
+ silc_argument_payload_free(args);
+ }
+
+ /* Remove the client from the channel. If the channel does not exist
+ after removing the client then the client kicked itself off the channel
+ and we don't have to send anything after that. */
+ if (!silc_server_remove_from_one_channel(server, NULL, channel,
+ target_client, FALSE))
+ goto out;
+
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0))
+ goto out;
+
+ /* Send the channel key to the channel. The key of course is not sent
+ to the client who was kicked off the channel. */
+ silc_server_send_channel_key(server, target_client->connection, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of OPER command. Client uses this comand to obtain server
+ operator privileges to this server/router. */
+
+SILC_SERVER_CMD_FUNC(oper)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ unsigned char *username = NULL, *auth;
+ SilcUInt32 tmp_len;
+ SilcServerConfigAdmin *admin;
+ SilcIDListData idata = (SilcIDListData)client;
+ SilcBool result = FALSE;
+ SilcPublicKey cached_key;
+ const char *hostname, *ip;
+
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
+
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
+ NULL, &hostname, &ip, NULL);
+
+ /* Get the username */
+ username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (!username) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
+
+ /* Check username */
+ username = silc_identifier_check(username, strlen(username),
+ SILC_STRING_UTF8, 128, &tmp_len);
+ if (!username) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+ SILC_STATUS_ERR_BAD_USERNAME,
+ 0);
+ goto out;
+ }
+
+ /* Get the admin configuration */
+ admin = silc_server_config_find_admin(server, (char *)ip,
+ username, client->nickname);
+ if (!admin) {
+ admin = silc_server_config_find_admin(server, (char *)hostname,
+ username, client->nickname);
+ if (!admin) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ SILC_LOG_INFO(("OPER authentication failed for username '%s' by "
+ "nickname '%s' from %s", username,
+ client->nickname, hostname));
+ goto out;
+ }
+ }
+
+ /* Get the authentication payload */
+ auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!auth) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
}
- /* If the channel does not have global users and is also empty it means the
- channel was created globally (by our router) and the client will be the
- channel founder and operator. */
- if (!channel->global_users && silc_list_count(channel->user_list) == 0) {
- umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
- created = TRUE; /* Created globally by our router */
+ /* Verify the authentication data. If both passphrase and public key
+ is set then try both of them. */
+ if (admin->passphrase)
+ result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PASSWORD,
+ admin->passphrase, admin->passphrase_len,
+ idata->hash, client->id, SILC_ID_CLIENT);
+ if (!result && admin->publickeys) {
+ cached_key =
+ silc_server_get_public_key(server,
+ SILC_SKR_USAGE_SERVICE_AUTHORIZATION, admin);
+ if (!cached_key)
+ goto out;
+ result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PUBLIC_KEY,
+ cached_key, 0, idata->hash,
+ client->id, SILC_ID_CLIENT);
+ }
+ if (!result) {
+ /* Authentication failed */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+ SILC_STATUS_ERR_AUTH_FAILED,
+ 0);
+ goto out;
}
- /* Join to the channel */
- silc_server_command_join_channel(server, cmd, channel, client_id,
- created, umode);
+ /* Client is now server operator */
+ client->mode |= SILC_UMODE_SERVER_OPERATOR;
- silc_free(client_id);
+ /* Update statistics */
+ if (SILC_IS_LOCAL(client))
+ server->stat.my_server_ops++;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.server_ops++;
+
+ /* Send UMODE change to primary router */
+ silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), client->id,
+ client->mode);
+
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
+ /* Send reply to the sender */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+ SILC_STATUS_OK, 0);
out:
+ silc_free(username);
silc_server_command_free(cmd);
}
-/* Server side of command MOTD. Sends server's current "message of the
- day" to the client. */
-
-SILC_SERVER_CMD_FUNC(motd)
+SILC_TASK_CALLBACK(silc_server_command_detach_cb)
{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- char *motd;
- int motd_len;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_MOTD, cmd, 1, 2);
-
- /* XXX show currently only our motd */
-
- if (server->config && server->config->motd &&
- server->config->motd->motd_file) {
+ SilcServer server = app_context;
+ QuitInternal q = (QuitInternal)context;
+ SilcClientID *client_id = (SilcClientID *)q->sock;
+ SilcClientEntry client;
+ SilcPacketStream sock;
- /* Send motd */
- motd = silc_file_read(server->config->motd->motd_file, &motd_len);
- if (!motd)
- goto out;
+ client = silc_idlist_find_client_by_id(server->local_list, client_id,
+ TRUE, NULL);
+ if (client && client->connection) {
+ sock = client->connection;
- motd[motd_len] = 0;
- silc_server_command_send_status_data(cmd, SILC_COMMAND_MOTD,
- SILC_STATUS_OK,
- 2, motd, motd_len);
- goto out;
- } else {
- /* No motd */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
- SILC_STATUS_OK);
+ /* Close the connection on our side */
+ client->router = NULL;
+ client->connection = NULL;
+ silc_server_close_connection(server, sock);
}
- out:
- silc_server_command_free(cmd);
+ silc_free(client_id);
+ silc_free(q);
}
-SILC_SERVER_CMD_FUNC(umode)
+SILC_TASK_CALLBACK(silc_server_command_detach_timeout)
{
+ SilcServer server = app_context;
+ QuitInternal q = (QuitInternal)context;
+ SilcClientID *client_id = (SilcClientID *)q->sock;
+ SilcClientEntry client;
+
+ client = silc_idlist_find_client_by_id(server->local_list, client_id,
+ TRUE, NULL);
+ if (client && client->mode & SILC_UMODE_DETACHED) {
+ SILC_LOG_DEBUG(("Detach timeout"));
+ silc_server_free_client_data(server, NULL, client, TRUE,
+ "Detach timeout");
+ }
+
+ silc_free(client_id);
+ silc_free(q);
}
-/* Checks that client has rights to add or remove channel modes. If any
- of the checks fails FALSE is returned. */
+/* Server side of DETACH command. Detached the client from the network
+ by closing the connection but preserving the session. */
-int silc_server_check_cmode_rights(SilcChannelEntry channel,
- SilcChannelClientEntry client,
- unsigned int mode)
+SILC_SERVER_CMD_FUNC(detach)
{
- int is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
- int is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ QuitInternal q;
- /* Check whether has rights to change anything */
- if (!is_op && !is_fo)
- return FALSE;
+ if (server->config->detach_disabled) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+ SILC_STATUS_ERR_OPERATION_ALLOWED,
+ 0);
+ goto out;
+ }
- /* Check whether has rights to change everything */
- if (is_op && is_fo)
- return TRUE;
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+ goto out;
- /* We know that client is channel operator, check that they are not
- changing anything that requires channel founder rights. Rest of the
- modes are available automatically for channel operator. */
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_DETACH, cmd, 0, 0);
+
+ /* Remove operator privileges, since the client may resume in some
+ other server which to it does not have operator privileges. */
+ SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+ /* Send the user mode notify to notify that client is detached */
+ client->mode |= SILC_UMODE_DETACHED;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+ client->data.status &= ~SILC_IDLIST_STATUS_NOATTR;
+ client->last_command = 0;
+ client->fast_command = 0;
+ silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), client->id,
+ client->mode);
+ server->stat.my_detached++;
+
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_UMODE_CHANGE);
- if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
- if (is_op && !is_fo)
- return FALSE;
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
- if (is_op && !is_fo)
- return FALSE;
- }
- }
-
- if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
- if (is_op && !is_fo)
- return FALSE;
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
- if (is_op && !is_fo)
- return FALSE;
- }
- }
+ q = silc_calloc(1, sizeof(*q));
+ q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_command_detach_cb,
+ q, 0, 200000);
- if (mode & SILC_CHANNEL_MODE_CIPHER) {
- if (is_op && !is_fo)
- return FALSE;
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
- if (is_op && !is_fo)
- return FALSE;
- }
+ if (server->config->detach_timeout) {
+ q = silc_calloc(1, sizeof(*q));
+ q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_command_detach_timeout,
+ q, server->config->detach_timeout * 60, 0);
}
-
- return TRUE;
+
+ /* Send reply to the sender */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+ SILC_STATUS_OK, 0);
+
+ out:
+ silc_server_command_free(cmd);
}
-/* Server side command of CMODE. Changes channel mode */
+/* Server side of WATCH command. */
-SILC_SERVER_CMD_FUNC(cmode)
+SILC_SERVER_CMD_FUNC(watch)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
- SilcChannelID *channel_id;
- SilcChannelEntry channel;
- SilcChannelClientEntry chl;
- SilcBuffer packet, cidp;
- unsigned char *tmp, *tmp_id, *tmp_mask;
- unsigned int argc, mode_mask, tmp_len, tmp_len2;
+ char *add_nick, *del_nick;
+ SilcUInt32 add_nick_len, del_nick_len, tmp_len, pk_len;
+ unsigned char hash[SILC_HASH_MAXLEN], *tmp, *pk, *nick;
+ SilcClientEntry client;
+ SilcID id;
+ SilcUInt16 old_ident;
- SILC_LOG_DEBUG(("Start"));
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WATCH, cmd, 1, 3);
- argc = silc_argument_get_arg_num(cmd->args);
- if (argc < 2) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
- if (argc > 8) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_TOO_MANY_PARAMS);
- goto out;
- }
+ if (server->server_type != SILC_ROUTER && !server->standalone) {
+ if (!cmd->pending) {
+ /* Send the command to router */
+ SilcBuffer tmpbuf;
- /* Get Channel ID */
- tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
- if (!tmp_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
- channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
- if (!channel_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
+ /* If backup receives this from primary, handle it locally */
+ if (server->server_type == SILC_BACKUP_ROUTER &&
+ cmd->sock == SILC_PRIMARY_ROUTE(server))
+ goto process_watch;
+
+ SILC_LOG_DEBUG(("Forwarding WATCH to router"));
+
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_WATCH,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_watch,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ } else {
+ SilcServerCommandReplyContext reply = context2;
+ SilcStatus status;
+
+ if (!reply)
+ goto out;
+
+ silc_command_get_status(reply->payload, &status, NULL);
+
+ /* Backup router handles the WATCH command also. */
+ if (server->server_type != SILC_BACKUP_ROUTER ||
+ SILC_STATUS_IS_ERROR(status)) {
+ /* Received reply from router, just send same data to the client. */
+ SILC_LOG_DEBUG(("Received reply to WATCH from router"));
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status,
+ 0);
+ goto out;
+ }
+ }
}
- /* Get the channel mode mask */
- tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
- if (!tmp_mask) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ /* We are router and keep the watch list for local cell */
+ process_watch:
+
+ /* Get the client ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
- SILC_GET32_MSB(mode_mask, tmp_mask);
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
- if (!channel) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ /* Get the client entry which must be in local list */
+ client = silc_idlist_find_client_by_id(server->local_list,
+ SILC_ID_GET_ID(id), TRUE, NULL);
+ if (!client) {
+ /* Backup checks global list also */
+ if (server->server_type == SILC_BACKUP_ROUTER)
+ client = silc_idlist_find_client_by_id(server->global_list,
+ SILC_ID_GET_ID(id), TRUE, NULL);
+ if (!client) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 0, 2, tmp, tmp_len);
goto out;
}
}
- /* Check whether this client is on the channel */
- if (!silc_server_client_on_channel(client, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ON_CHANNEL);
+ /* Take public key for watching by public key */
+ pk = silc_argument_get_arg_type(cmd->args, 4, &pk_len);
+
+ /* Take nickname */
+ add_nick = silc_argument_get_arg_type(cmd->args, 2, &add_nick_len);
+ del_nick = silc_argument_get_arg_type(cmd->args, 3, &del_nick_len);
+ if (!add_nick && !del_nick && !pk) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
- /* Get entry to the channel user list */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
- if (chl->client == client)
- break;
-
- /* Check that client has rights to change any requested channel modes */
- if (!silc_server_check_cmode_rights(channel, chl, mode_mask)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NO_CHANNEL_PRIV);
- goto out;
+ if (add_nick && add_nick_len > 128) {
+ add_nick_len = 128;
+ add_nick[add_nick_len - 1] = '\0';
+ }
+ if (del_nick && del_nick_len > 128) {
+ del_nick_len = 128;
+ del_nick[del_nick_len - 1] = '\0';
}
- /*
- * Check the modes. Modes that requires nothing special operation are
- * not checked here.
- */
+ /* Add new nickname to be watched in our cell */
+ if (add_nick) {
+ nick = silc_identifier_check(add_nick, add_nick_len, SILC_STRING_UTF8, 128,
+ &add_nick_len);
+ if (!nick) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_BAD_NICKNAME, 0);
+ goto out;
+ }
- if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
- /* Channel uses private keys to protect traffic. Client(s) has set the
- key locally they want to use, server does not know that key. */
- /* Nothing interesting to do here now */
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
- /* The mode is removed and we need to generate and distribute
- new channel key. Clients are not using private channel keys
- anymore after this. */
+ /* Hash the nick, we have the hash saved, not nicks because we can
+ do one to one mapping to the nick from Client ID hash this way. */
+ silc_hash_make(server->md5hash, nick, add_nick_len, hash);
+
+ /* Check whether this client is already watching this nickname */
+ if (silc_hash_table_find_by_context(server->watcher_list, hash,
+ client, NULL)) {
+ /* Nickname is alredy being watched for this client */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NICKNAME_IN_USE,
+ 0);
+ silc_free(nick);
+ goto out;
+ }
- /* XXX Duplicated code, make own function for this!! LEAVE uses this
- as well */
+ /* Get the nickname from the watcher list and use the same key in
+ new entries as well. If key doesn't exist then create it. */
+ if (!silc_hash_table_find(server->watcher_list, hash, (void *)&tmp, NULL))
+ tmp = silc_memdup(hash, CLIENTID_HASH_LEN);
- /* Re-generate channel key */
- silc_server_create_channel_key(server, channel, 0);
-
- /* Encode channel key payload to be distributed on the channel */
- packet =
- silc_channel_key_payload_encode(tmp_len2, tmp_id,
- strlen(channel->channel_key->
- cipher->name),
- channel->channel_key->cipher->name,
- channel->key_len / 8, channel->key);
-
- /* If we are normal server then we will send it to our router. If we
- are router we will send it to all local servers that has clients on
- the channel */
- if (server->server_type == SILC_SERVER) {
- if (!server->standalone)
- silc_server_packet_send(server,
- cmd->server->router->connection,
- SILC_PACKET_CHANNEL_KEY, 0, packet->data,
- packet->len, TRUE);
- } else {
-
- }
-
- /* Send to locally connected clients on the channel */
- silc_server_packet_send_local_channel(server, channel,
- SILC_PACKET_CHANNEL_KEY, 0,
- packet->data, packet->len, FALSE);
- silc_buffer_free(packet);
- }
+ /* Add the client to the watcher list with the specified nickname hash. */
+ silc_hash_table_add(server->watcher_list, tmp, client);
+ silc_free(nick);
}
-
- if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
- /* User limit is set on channel */
- unsigned int user_limit;
-
- /* Get user limit */
- tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
- if (!tmp) {
- if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
- } else {
- SILC_GET32_MSB(user_limit, tmp);
- channel->mode_data.user_limit = user_limit;
+
+ /* Delete nickname from watch list */
+ if (del_nick) {
+ nick = silc_identifier_check(del_nick, del_nick_len, SILC_STRING_UTF8, 128,
+ &del_nick_len);
+ if (!nick) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_BAD_NICKNAME, 0);
+ goto out;
}
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
- /* User limit mode is unset. Remove user limit */
- channel->mode_data.user_limit = 0;
+
+ /* Hash the nick, we have the hash saved, not nicks because we can
+ do one to one mapping to the nick from Client ID hash this way. */
+ silc_hash_make(server->md5hash, nick, del_nick_len, hash);
+
+ /* Check that this client is watching for this nickname */
+ if (!silc_hash_table_find_by_context(server->watcher_list, hash,
+ client, (void *)&tmp)) {
+ /* Nickname is alredy being watched for this client */
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NO_SUCH_NICK, 0,
+ 2, nick, del_nick_len);
+ silc_free(nick);
+ goto out;
+ }
+
+ /* Delete the nickname from the watcher list. */
+ silc_hash_table_del_by_context(server->watcher_list, hash, client);
+
+ /* Now check whether there still exists entries with this key, if not
+ then free the key to not leak memory. */
+ if (!silc_hash_table_find(server->watcher_list, hash, NULL, NULL))
+ silc_free(tmp);
+ silc_free(nick);
}
- if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
- if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
- /* Passphrase has been set to channel */
-
- /* Get the passphrase */
- tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
+ /* Add/del public key */
+ if (pk) {
+ SilcUInt16 pkargc;
+ SilcArgumentPayload pkargs;
+ SilcUInt32 type;
+ SilcPublicKey public_key, pkkey;
- /* Save the passphrase */
- channel->mode_data.passphrase = strdup(tmp);
+ if (pk_len < 2) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
}
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
- /* Passphrase mode is unset. remove the passphrase */
- if (channel->mode_data.passphrase) {
- silc_free(channel->mode_data.passphrase);
- channel->mode_data.passphrase = NULL;
- }
+
+ /* Get the argument from the Argument List Payload */
+ SILC_GET16_MSB(pkargc, pk);
+ pkargs = silc_argument_payload_parse(pk + 2, pk_len - 2, pkargc);
+ if (!pkargs) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
}
- }
- if (mode_mask & SILC_CHANNEL_MODE_BAN) {
- if (!(channel->mode & SILC_CHANNEL_MODE_BAN)) {
- /* Ban list is specified for channel */
+ pk = silc_argument_get_next_arg(pkargs, &type, &pk_len);
+ while (pk) {
+ if (!silc_public_key_payload_decode(pk, pk_len, &public_key))
+ continue;
+ if (type == 0x03)
+ type = 0x00;
+
+ if (type == 0x00) {
+ /* Add public key to watch list */
+
+ /* Check whether this client is already watching this public key */
+ if (silc_hash_table_find_by_context(server->watcher_list_pk,
+ public_key, client, NULL)) {
+ silc_pkcs_public_key_free(public_key);
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NICKNAME_IN_USE, 0);
+ goto out;
+ }
- /* Get ban list */
- tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
+ /* Get the public key from the watcher list and use the same key in
+ new entries as well. If key doesn't exist then create it. */
+ pkkey = NULL;
+ if (!silc_hash_table_find(server->watcher_list_pk, public_key,
+ (void *)&pkkey, NULL))
+ pkkey = public_key;
+ else
+ silc_pkcs_public_key_free(public_key);
+
+ /* Add the client to the watcher list with the specified public
+ key. */
+ silc_hash_table_add(server->watcher_list_pk, pkkey, client);
+
+ } else if (type == 0x01) {
+ /* Delete public key from watch list */
+
+ /* Check that this client is watching this public key */
+ if (silc_hash_table_find_by_context(server->watcher_list_pk,
+ public_key, client,
+ (void *)&pkkey)) {
+ silc_pkcs_public_key_free(public_key);
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+ goto out;
+ }
- /* XXX check that channel founder is not banned */
+ /* Delete the public key from the watcher list. */
+ silc_hash_table_del_by_context(server->watcher_list_pk,
+ public_key, client);
- /* Save the ban list */
- channel->mode_data.ban_list = strdup(tmp);
- }
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_BAN) {
- /* Ban mode is unset. Remove the entire ban list */
- if (channel->mode_data.ban_list) {
- silc_free(channel->mode_data.ban_list);
- channel->mode_data.ban_list = NULL;
+ /* Now check whether there still exists entries with this key, if
+ not then free the key to not leak memory. */
+ if (!silc_hash_table_find(server->watcher_list_pk, hash, NULL, NULL))
+ silc_pkcs_public_key_free(pkkey);
+ silc_pkcs_public_key_free(public_key);
}
+
+ pk = silc_argument_get_next_arg(pkargs, &type, &pk_len);
}
}
- if (mode_mask & SILC_CHANNEL_MODE_INVITE_LIST) {
- if (!(channel->mode & SILC_CHANNEL_MODE_INVITE_LIST)) {
- /* Invite list is specified for channel */
+ /* Send reply */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_OK, 0);
- /* Get invite list */
- tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
+ /* Distribute the watch list to backup routers too */
+ if (server->backup) {
+ SilcBuffer tmpbuf;
- /* Save the invite linst */
- channel->mode_data.invite_list = strdup(tmp);
- }
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_INVITE_LIST) {
- /* Invite list mode is unset. Remove the entire invite list */
- if (channel->mode_data.invite_list) {
- silc_free(channel->mode_data.invite_list);
- channel->mode_data.invite_list = NULL;
- }
- }
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+ silc_server_backup_send(server, silc_packet_get_context(cmd->sock),
+ SILC_PACKET_COMMAND,
+ cmd->packet->flags, tmpbuf->data,
+ silc_buffer_len(tmpbuf),
+ FALSE, TRUE);
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
}
- if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
- if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
- /* Cipher to use protect the traffic */
- unsigned int key_len;
+ out:
+ silc_server_command_free(cmd);
+}
- /* Get cipher */
- tmp = silc_argument_get_arg_type(cmd->args, 8, NULL);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
+/* Server side of SILCOPER command. Client uses this comand to obtain router
+ operator privileges to this router. */
- /* XXX Duplicated code, make own function for this!! */
-
- /* Delete old cipher and allocate the new one */
- silc_cipher_free(channel->channel_key);
- if (!silc_cipher_alloc(tmp, &channel->channel_key)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
- goto out;
- }
- key_len = silc_cipher_get_key_len(channel->channel_key) / 8;
+SILC_SERVER_CMD_FUNC(silcoper)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ unsigned char *username = NULL, *auth;
+ SilcUInt32 tmp_len;
+ SilcServerConfigAdmin *admin;
+ SilcIDListData idata = (SilcIDListData)client;
+ SilcBool result = FALSE;
+ SilcPublicKey cached_key;
+ const char *hostname, *ip;
- /* Re-generate channel key */
- silc_server_create_channel_key(server, channel, key_len);
-
- /* Encode channel key payload to be distributed on the channel */
- packet =
- silc_channel_key_payload_encode(tmp_len2, tmp_id,
- strlen(channel->channel_key->
- cipher->name),
- channel->channel_key->cipher->name,
- channel->key_len / 8, channel->key);
-
- /* If we are normal server then we will send it to our router. If we
- are router we will send it to all local servers that has clients on
- the channel */
- if (server->server_type == SILC_SERVER) {
- if (!server->standalone)
- silc_server_packet_send(server,
- cmd->server->router->connection,
- SILC_PACKET_CHANNEL_KEY, 0, packet->data,
- packet->len, TRUE);
- } else {
-
- }
-
- /* Send to locally connected clients on the channel */
- silc_server_packet_send_local_channel(server, channel,
- SILC_PACKET_CHANNEL_KEY, 0,
- packet->data, packet->len, FALSE);
- silc_buffer_free(packet);
- }
- } else {
- if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
- /* Cipher mode is unset. Remove the cipher and revert back to
- default cipher */
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
+ NULL, &hostname, &ip, NULL);
+
+ if (server->server_type != SILC_ROUTER) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ goto out;
+ }
- if (channel->mode_data.cipher) {
- silc_free(channel->mode_data.cipher);
- channel->mode_data.cipher = NULL;
- channel->mode_data.key_len = 0;
- }
+ /* Get the username */
+ username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (!username) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
- /* Generate new cipher and key for the channel */
+ /* Check username */
+ username = silc_identifier_check(username, tmp_len, SILC_STRING_UTF8, 128,
+ &tmp_len);
+ if (!username) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+ SILC_STATUS_ERR_BAD_USERNAME,
+ 0);
+ goto out;
+ }
- /* XXX Duplicated code, make own function for this!! */
+ /* Get the admin configuration */
+ admin = silc_server_config_find_admin(server, (char *)ip,
+ username, client->nickname);
+ if (!admin) {
+ admin = silc_server_config_find_admin(server, (char *)hostname,
+ username, client->nickname);
+ if (!admin) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ SILC_LOG_INFO(("SILCOPER authentication failed for username '%s' by "
+ "nickname '%s' from %s", username,
+ client->nickname, hostname));
+ goto out;
+ }
+ }
- /* Delete old cipher and allocate default one */
- silc_cipher_free(channel->channel_key);
- if (!channel->cipher)
- silc_cipher_alloc("aes-256-cbc", &channel->channel_key);
- else {
- if (!silc_cipher_alloc(channel->cipher, &channel->channel_key)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
- goto out;
- }
- }
+ /* Get the authentication payload */
+ auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!auth) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
- /* Re-generate channel key */
- silc_server_create_channel_key(server, channel, 0);
-
- /* Encode channel key payload to be distributed on the channel */
- packet =
- silc_channel_key_payload_encode(tmp_len2, tmp_id,
- strlen(channel->channel_key->
- cipher->name),
- channel->channel_key->cipher->name,
- channel->key_len / 8, channel->key);
-
- /* If we are normal server then we will send it to our router. If we
- are router we will send it to all local servers that has clients on
- the channel */
- if (server->server_type == SILC_SERVER) {
- if (!server->standalone)
- silc_server_packet_send(server,
- cmd->server->router->connection,
- SILC_PACKET_CHANNEL_KEY, 0, packet->data,
- packet->len, TRUE);
- } else {
-
- }
-
- /* Send to locally connected clients on the channel */
- silc_server_packet_send_local_channel(server, channel,
- SILC_PACKET_CHANNEL_KEY, 0,
- packet->data, packet->len, FALSE);
- silc_buffer_free(packet);
- }
+ /* Verify the authentication data. If both passphrase and public key
+ is set then try both of them. */
+ if (admin->passphrase)
+ result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PASSWORD,
+ admin->passphrase, admin->passphrase_len,
+ idata->hash, client->id, SILC_ID_CLIENT);
+ if (!result && admin->publickeys) {
+ cached_key =
+ silc_server_get_public_key(server,
+ SILC_SKR_USAGE_SERVICE_AUTHORIZATION, admin);
+ if (!cached_key)
+ goto out;
+ result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PUBLIC_KEY,
+ cached_key, 0, idata->hash,
+ client->id, SILC_ID_CLIENT);
+ }
+ if (!result) {
+ /* Authentication failed */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+ SILC_STATUS_ERR_AUTH_FAILED, 0);
+ goto out;
}
- /* Finally, set the mode */
- channel->mode = mode_mask;
+ /* Client is now router operator */
+ client->mode |= SILC_UMODE_ROUTER_OPERATOR;
- /* Send CMODE_CHANGE notify */
- cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_CMODE_CHANGE, 2,
- cidp->data, cidp->len,
- tmp_mask, tmp_len);
+ /* Update statistics */
+ if (SILC_IS_LOCAL(client))
+ server->stat.my_router_ops++;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.router_ops++;
- /* Set CMODE notify type to network */
- if (!server->standalone)
- silc_server_send_notify_cmode(server, server->router->connection,
- server->server_type == SILC_ROUTER ?
- TRUE : FALSE, channel,
- mode_mask, client->id, SILC_ID_CLIENT_LEN);
+ /* Send UMODE change to primary router */
+ silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), client->id,
+ client->mode);
- /* Send command reply to sender */
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
- SILC_STATUS_OK, 0, 1,
- 2, tmp_mask, 4);
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
- packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_free(channel_id);
- silc_free(cidp);
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
+ /* Send reply to the sender */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+ SILC_STATUS_OK, 0);
out:
+ silc_free(username);
silc_server_command_free(cmd);
}
-/* Server side of CUMODE command. Changes client's mode on a channel. */
+/* Server side of command BAN. This is used to manage the ban list of the
+ channel. To add clients and remove clients from the ban list. */
-SILC_SERVER_CMD_FUNC(cumode)
+SILC_SERVER_CMD_FUNC(ban)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
- SilcChannelID *channel_id;
- SilcClientID *client_id;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
+ SilcBuffer list, tmp2;
SilcChannelEntry channel;
- SilcClientEntry target_client;
SilcChannelClientEntry chl;
- SilcBuffer packet, idp;
- unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
- unsigned int target_mask, sender_mask, tmp_len, tmp_ch_len;
- int notify = FALSE;
+ SilcID id;
+ unsigned char *tmp_id, *tmp, *atype = NULL;
+ SilcUInt32 id_len, len, len2;
+ SilcArgumentPayload args;
+ SilcHashTableList htl;
+ SilcUInt32 type;
+ SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
+ SilcBufferStruct blist;
+
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+ goto out;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CUMODE, cmd, 3, 3);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
/* Get Channel ID */
- tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
- if (!tmp_ch_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
- channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
- if (!channel_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
goto out;
}
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
+ /* Get channel entry. The server must know about the channel since the
+ client is expected to be on the channel. */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ SILC_ID_GET_ID(id), NULL);
if (!channel) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_BAN,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 0, 2, tmp_id, id_len);
goto out;
}
}
- /* Check whether sender is on the channel */
- if (!silc_server_client_on_channel(client, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NOT_ON_CHANNEL);
+ /* Check whether this client is on the channel */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_BAN,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+ 2, tmp_id, id_len);
goto out;
}
- /* Check that client has rights to change other's rights */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (chl->client == client) {
- if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO) &&
- !(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NO_CHANNEL_PRIV);
- goto out;
+ /* The client must be at least channel operator. */
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_BAN,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0,
+ 2, tmp_id, id_len);
+ goto out;
+ }
+
+ /* Get the ban information */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, &len2);
+ if (tmp && len2 > 2) {
+ /* Parse the arguments to see they are constructed correctly */
+ SILC_GET16_MSB(argc, tmp);
+ args = silc_argument_payload_parse(tmp + 2, len2 - 2, argc);
+ if (!args) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
+
+ /* Get the type of action */
+ atype = silc_argument_get_arg_type(cmd->args, 2, &len);
+ if (atype && len == 1) {
+ if (atype[0] == 0x00) {
+ /* Allocate hash table for ban list if it doesn't exist yet */
+ if (!channel->ban_list)
+ channel->ban_list =
+ silc_hash_table_alloc(0, silc_hash_ptr,
+ NULL, NULL, NULL,
+ silc_server_inviteban_destruct, channel,
+ TRUE);
+
+ /* Check for resource limit */
+ if (silc_hash_table_count(channel->ban_list) > 64) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ 0);
+ goto out;
+ }
}
- sender_mask = chl->mode;
- break;
+ /* Now add or delete the information. */
+ if (!silc_server_inviteban_process(server, channel->ban_list,
+ (SilcUInt8)atype[0], args)) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_BAN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
+ goto out;
+ }
}
- }
-
- /* Get the target client's channel mode mask */
- tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (!tmp_mask) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
- SILC_GET32_MSB(target_mask, tmp_mask);
+ silc_argument_payload_free(args);
+ }
+
+ /* Encode ban list */
+ list = NULL;
+ if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+ list = silc_buffer_alloc_size(2);
+ silc_buffer_format(list,
+ SILC_STR_UI_SHORT(silc_hash_table_count(
+ channel->ban_list)),
+ SILC_STR_END);
+ silc_hash_table_list(channel->ban_list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
+ list = silc_argument_payload_encode_one(list, tmp2->data,
+ silc_buffer_len(tmp2), type);
+ silc_hash_table_list_reset(&htl);
+ }
+
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+
+ /* Send BAN notify type to local servers (but not clients) and to
+ network. */
+ if (atype && tmp && len2) {
+ silc_buffer_set(&blist, tmp, len2);
+
+ /* Send to local servers if we are router */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, FALSE,
+ SILC_NOTIFY_TYPE_BAN, 3,
+ tmp_id, id_len,
+ atype, 1,
+ tmp ? blist.data : NULL,
+ tmp ? silc_buffer_len(&blist) : 0);
+
+ /* Send to network. */
+ silc_server_send_notify_ban(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel, atype,
+ &blist);
+ }
+
+ /* Send the reply back to the client */
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_BAN,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, tmp_id, id_len,
+ 3, list ? list->data : NULL,
+ list ? silc_buffer_len(list) : 0);
+ silc_buffer_free(list);
- /* Get target Client ID */
- tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
- if (!tmp_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NO_CLIENT_ID);
- goto out;
- }
- client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
- if (!client_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NO_CLIENT_ID);
- goto out;
- }
+ out:
+ silc_server_command_free(cmd);
+}
- /* Get target client's entry */
- target_client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
- if (!target_client) {
- target_client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- }
+/* Server side command of LEAVE. Removes client from a channel. */
- /* Check whether target client is on the channel */
- if (!silc_server_client_on_channel(target_client, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
- goto out;
- }
+SILC_SERVER_CMD_FUNC(leave)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcPacketStream sock = cmd->sock;
+ SilcClientEntry id_entry = silc_packet_get_context(cmd->sock);
+ SilcID id;
+ SilcChannelEntry channel;
+ SilcUInt32 len;
+ unsigned char *tmp;
- /* Get entry to the channel user list */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
- if (chl->client == target_client)
- break;
+ if (id_entry->data.conn_type != SILC_CONN_CLIENT || !id_entry)
+ goto out;
- /*
- * Change the mode
- */
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 2);
- /* If the target client is founder, no one else can change their mode
- but themselves. */
- if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && chl->client != target_client) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NOT_YOU);
+ /* Get Channel ID */
+ if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
goto out;
}
- if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
- /* Cannot promote anyone to channel founder */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NOT_YOU);
- goto out;
- } else {
- if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
- if (target_client == client) {
- /* Remove channel founder rights from itself */
- chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
- notify = TRUE;
- } else {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
- SILC_STATUS_ERR_NOT_YOU);
- goto out;
- }
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ SILC_ID_GET_ID(id), NULL);
+ if (!channel) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 0, 2, tmp, len);
+ goto out;
}
}
- if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
- /* Promote to operator */
- if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
- chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
- notify = TRUE;
- }
- } else {
- if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
- /* Demote to normal user */
- chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
- notify = TRUE;
- }
+ /* Check whether this client is on the channel */
+ if (!silc_server_client_on_channel(id_entry, channel, NULL)) {
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+ 2, tmp, len);
+ goto out;
}
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ /* Notify routers that they should remove this client from their list
+ of clients on the channel. Send LEAVE notify type. */
+ silc_server_send_notify_leave(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), channel, id_entry->id);
- /* Send notify to channel, notify only if mode was actually changed. */
- if (notify) {
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
- idp->data, idp->len,
- tmp_mask, 4,
- tmp_id, tmp_len);
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+ SILC_STATUS_OK, 0, 2, tmp, len);
- /* Set CUMODE notify type to network */
- if (!server->standalone)
- silc_server_send_notify_cumode(server, server->router->connection,
- server->server_type == SILC_ROUTER ?
- TRUE : FALSE, channel,
- target_mask, client->id,
- SILC_ID_CLIENT_LEN,
- target_client->id,
- SILC_ID_CLIENT_LEN);
- }
+ /* Remove client from channel */
+ if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
+ TRUE))
+ /* If the channel does not exist anymore we won't send anything */
+ goto out;
- /* Send command reply to sender */
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
- SILC_STATUS_OK, 0, 2,
- 2, tmp_mask, 4,
- 3, tmp_id, tmp_len);
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
- packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_free(channel_id);
- silc_free(client_id);
- silc_buffer_free(idp);
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0))
+ goto out;
+
+ /* Send the channel key */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
out:
silc_server_command_free(cmd);
}
-/* Server side of KICK command. Kicks client out of channel. */
+/* Server side of command USERS. Resolves clients and their USERS currently
+ joined on the requested channel. The list of Client ID's and their modes
+ on the channel is sent back. */
-SILC_SERVER_CMD_FUNC(kick)
+SILC_SERVER_CMD_FUNC(users)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
- SilcClientEntry target_client;
- SilcChannelID *channel_id;
- SilcClientID *client_id;
+ SilcIDListData idata = silc_packet_get_context(cmd->sock);
SilcChannelEntry channel;
- SilcChannelClientEntry chl;
+ SilcID id;
SilcBuffer idp;
- unsigned int tmp_len;
- unsigned char *tmp, *comment;
+ unsigned char *channel_id;
+ SilcUInt32 channel_id_len;
+ SilcBuffer client_id_list;
+ SilcBuffer client_mode_list;
+ unsigned char lc[4];
+ SilcUInt32 list_count = 0;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ char *channel_name, *channel_namec = NULL;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_LEAVE, cmd, 1, 3);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
/* Get Channel ID */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
+ channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
+
+ /* Get channel name */
+ channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+
+ if (!channel_id && !channel_name) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+ SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
goto out;
}
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
+
+ /* Check channel name */
+ if (channel_name) {
+ channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
+ SILC_STRING_UTF8, 256, NULL);
+ if (!channel_namec) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+ SILC_STATUS_ERR_BAD_CHANNEL, 0);
+ goto out;
+ }
}
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ /* Check Channel ID */
+ if (channel_id) {
+ if (!silc_id_payload_parse_id(channel_id, channel_id_len, &id)) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_USERS,
+ SILC_STATUS_ERR_BAD_CHANNEL_ID, 0,
+ 2, channel_id, channel_id_len);
goto out;
}
}
- /* Check whether sender is on the channel */
- if (!silc_server_client_on_channel(client, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NOT_ON_CHANNEL);
- goto out;
+ /* If we are server and we don't know about this channel we will send
+ the command to our router. If we know about the channel then we also
+ have the list of users already. */
+ if (channel_id)
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ SILC_ID_GET_ID(id), NULL);
+ else
+ channel = silc_idlist_find_channel_by_name(server->local_list,
+ channel_namec, NULL);
+
+ if (!channel || (!server->standalone && (channel->disabled ||
+ !channel->users_resolved))) {
+ if (server->server_type != SILC_ROUTER && !server->standalone &&
+ !cmd->pending) {
+ SilcBuffer tmpbuf;
+
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
+
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ /* Send USERS command */
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
+
+ /* Reprocess this packet after received reply */
+ silc_server_command_pending(server, SILC_COMMAND_USERS,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_users,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+
+ /* Check the global list as well. */
+ if (channel_id)
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ SILC_ID_GET_ID(id), NULL);
+ else
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_namec, NULL);
+ if (!channel) {
+ /* Channel really does not exist */
+ if (channel_id)
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_USERS,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID, 0,
+ 2, channel_id, channel_id_len);
+ else
+ silc_server_command_send_status_data(
+ cmd, SILC_COMMAND_USERS,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL, 0,
+ 2, channel_name, strlen(channel_name));
+ goto out;
+ }
}
- /* Check that the kicker is channel operator or channel founder */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (chl->client == client) {
- if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NO_CHANNEL_PRIV);
- goto out;
- }
- break;
+ /* If the channel is private or secret do not send anything, unless the
+ user requesting this command is on the channel or is server */
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
+ && !silc_server_client_on_channel((SilcClientEntry)idata, channel,
+ NULL)) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_USERS,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL, 0,
+ 2, channel->channel_name,
+ strlen(channel->channel_name));
+ goto out;
}
}
-
- /* Get target Client ID */
- tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+
+ /* Get the users list */
+ if (!silc_server_get_users_on_channel(server, channel, &client_id_list,
+ &client_mode_list, &list_count)) {
+ list_count = 0;
+ client_id_list = NULL;
+ client_mode_list = NULL;
+ }
+
+ /* List count */
+ SILC_PUT32_MSB(list_count, lc);
+
+ /* Send reply */
+ idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_USERS,
+ SILC_STATUS_OK, 0, ident, 4,
+ 2, idp->data, silc_buffer_len(idp),
+ 3, lc, 4,
+ 4, client_id_list ?
+ client_id_list->data : NULL,
+ client_id_list ?
+ silc_buffer_len(client_id_list) : 0,
+ 5, client_mode_list ?
+ client_mode_list->data : NULL,
+ client_mode_list ?
+ silc_buffer_len(client_mode_list) : 0);
+ silc_buffer_free(idp);
+ if (client_id_list)
+ silc_buffer_free(client_id_list);
+ if (client_mode_list)
+ silc_buffer_free(client_mode_list);
+
+ out:
+ silc_free(channel_namec);
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command GETKEY. This fetches the client's public key
+ from the server where to the client is connected. */
+
+SILC_SERVER_CMD_FUNC(getkey)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client;
+ SilcServerEntry server_entry;
+ SilcClientID client_id;
+ SilcServerID server_id;
+ SilcIDPayload idp = NULL;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ unsigned char *tmp;
+ SilcUInt32 tmp_len;
+ SilcBuffer pk = NULL;
+ SilcIdType id_type;
+ SilcPublicKey public_key;
+
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NO_CLIENT_ID);
- goto out;
- }
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NO_CLIENT_ID);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
-
- /* Get target client's entry */
- target_client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
- if (!target_client) {
- target_client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- }
-
- /* Check that the target client is not channel founder. Channel founder
- cannot be kicked from the channel. */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (chl->client == target_client) {
- if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
- goto out;
- }
- break;
- }
- }
-
- /* Check whether target client is on the channel */
- if (!silc_server_client_on_channel(target_client, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+ idp = silc_id_payload_parse(tmp, tmp_len);
+ if (!idp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
- /* Get comment */
- tmp_len = 0;
- comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
- if (tmp_len > 128)
- comment = NULL;
+ id_type = silc_id_payload_get_type(idp);
+ if (id_type == SILC_ID_CLIENT) {
+ silc_id_payload_get_id(idp, &client_id, sizeof(client_id));
- /* Send command reply to sender */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
- SILC_STATUS_OK);
+ /* If the client is not found from local list there is no chance it
+ would be locally connected client so send the command further. */
+ client = silc_idlist_find_client_by_id(server->local_list,
+ &client_id, TRUE, NULL);
+ if (!client)
+ client = silc_idlist_find_client_by_id(server->global_list,
+ &client_id, TRUE, NULL);
- /* Send KICKED notify to local clients on the channel */
- idp = silc_id_payload_encode(target_client->id, SILC_ID_CLIENT);
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_KICKED,
- comment ? 2 : 1,
- idp->data, idp->len,
- comment, comment ? strlen(comment) : 0);
- silc_buffer_free(idp);
+ if ((!client && !cmd->pending && !server->standalone) ||
+ (client && !client->connection && !cmd->pending &&
+ !(client->mode & SILC_UMODE_DETACHED)) ||
+ (client && !client->data.public_key && !cmd->pending)) {
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
+ SilcPacketStream dest_sock;
- /* Remove the client from the channel. If the channel does not exist
- after removing the client then the client kicked itself off the channel
- and we don't have to send anything after that. */
- if (!silc_server_remove_from_one_channel(server, NULL, channel,
- target_client, FALSE))
- goto out;
+ dest_sock = silc_server_get_client_route(server, NULL, 0,
+ &client_id, NULL, NULL);
+ if (!dest_sock)
+ goto out;
- /* Send KICKED notify to primary route */
- if (!server->standalone)
- silc_server_send_notify_kicked(server, server->router->connection,
- server->server_type == SILC_ROUTER ?
- TRUE : FALSE, channel,
- target_client->id, SILC_ID_CLIENT_LEN,
- comment);
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
- /* Re-generate channel key */
- silc_server_create_channel_key(server, channel, 0);
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
- /* Send the channel key to the channel. The key of course is not sent
- to the client who was kicked off the channel. */
- silc_server_send_channel_key(server, target_client->connection, channel,
- server->server_type == SILC_ROUTER ?
- FALSE : !server->standalone);
+ silc_server_packet_send(server, dest_sock,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
- out:
- silc_server_command_free(cmd);
-}
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_getkey,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
-/* Server side of OPER command. Client uses this comand to obtain server
- operator privileges to this server/router. */
+ if (!client) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_GETKEY,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
-SILC_SERVER_CMD_FUNC(oper)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
- unsigned char *username, *auth;
- unsigned int tmp_len;
- SilcServerConfigSectionAdminConnection *admin;
- SilcIDListData idata = (SilcIDListData)client;
+ /* The client is locally connected, just get the public key and
+ send it back. If they key does not exist then do not send it,
+ send just OK reply */
+ public_key = client->data.public_key;
+ if (public_key)
+ pk = silc_public_key_payload_encode(public_key);
+ } else if (id_type == SILC_ID_SERVER) {
+ silc_id_payload_get_id(idp, &server_id, sizeof(server_id));
+
+ /* If the server is not found from local list there is no chance it
+ would be locally connected server so send the command further. */
+ server_entry = silc_idlist_find_server_by_id(server->local_list,
+ &server_id, TRUE, NULL);
+ if (!server_entry)
+ server_entry = silc_idlist_find_server_by_id(server->global_list,
+ &server_id, TRUE, NULL);
+
+ if (server_entry != server->id_entry &&
+ ((!server_entry && !cmd->pending && !server->standalone) ||
+ (server_entry && !server_entry->connection && !cmd->pending &&
+ !server->standalone) ||
+ (server_entry && !server_entry->data.public_key && !cmd->pending &&
+ !server->standalone))) {
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_OPER, cmd, 1, 2);
+ /* Statistics */
+ cmd->server->stat.commands_sent++;
- if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
- goto out;
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
- /* Get the username */
- username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
- if (!username) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, silc_buffer_len(tmpbuf));
- /* Get the admin configuration */
- admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
- username, client->nickname);
- if (!admin) {
- admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
- username, client->nickname);
- if (!admin) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
- SILC_STATUS_ERR_AUTH_FAILED);
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_getkey,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
goto out;
}
- }
- /* Get the authentication payload */
- auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
- if (!auth) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
+ if (!server_entry) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_GETKEY,
+ SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+ 0, 2, tmp, tmp_len);
+ goto out;
+ }
- /* Verify the authentication data */
- if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth,
- admin->auth_data, admin->auth_data_len,
- idata->hash, client->id, SILC_ID_CLIENT)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
- SILC_STATUS_ERR_AUTH_FAILED);
+ /* If they key does not exist then do not send it, send just OK reply */
+ public_key = (!server_entry->data.public_key ?
+ (server_entry == server->id_entry ? server->public_key :
+ NULL) : server_entry->data.public_key);
+ if (public_key)
+ pk = silc_public_key_payload_encode(public_key);
+ } else {
goto out;
}
- /* Client is now server operator */
- client->mode |= SILC_UMODE_SERVER_OPERATOR;
-
- /* Send reply to the sender */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
- SILC_STATUS_OK);
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_GETKEY,
+ SILC_STATUS_OK, 0, ident, 2,
+ 2, tmp, tmp_len,
+ 3, pk ? pk->data : NULL,
+ pk ? silc_buffer_len(pk) : 0);
out:
+ if (idp)
+ silc_id_payload_free(idp);
+ silc_buffer_free(pk);
silc_server_command_free(cmd);
}
-/* Server side of SILCOPER command. Client uses this comand to obtain router
- operator privileges to this router. */
+/* Server side of command SERVICE. */
+/* XXX currently this just sends empty reply back */
-SILC_SERVER_CMD_FUNC(silcoper)
+SILC_SERVER_CMD_FUNC(service)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
- unsigned char *username, *auth;
- unsigned int tmp_len;
- SilcServerConfigSectionAdminConnection *admin;
- SilcIDListData idata = (SilcIDListData)client;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_SILCOPER, cmd, 1, 2);
-
- if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
- goto out;
-
- /* Get the username */
- username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
- if (!username) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
-
- /* Get the admin configuration */
- admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
- username, client->nickname);
- if (!admin) {
- admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
- username, client->nickname);
- if (!admin) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
- SILC_STATUS_ERR_AUTH_FAILED);
+ SilcUInt32 tmp_len, auth_len;
+ unsigned char *service_name, *auth;
+ SilcBool send_list = FALSE;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SERVICE, cmd, 0, 256);
+
+ /* Get requested service */
+ service_name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (service_name && tmp_len) {
+ /* Verify service name */
+ if (!silc_identifier_verify(service_name, tmp_len,
+ SILC_STRING_UTF8, 256)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_SERVICE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
}
- /* Get the authentication payload */
- auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
- if (!auth) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
+ /* Get authentication payload if present */
+ auth = silc_argument_get_arg_type(cmd->args, 2, &auth_len);
+ if (auth) {
+ /* XXX */
}
- /* Verify the authentication data */
- if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth,
- admin->auth_data, admin->auth_data_len,
- idata->hash, client->id, SILC_ID_CLIENT)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
- SILC_STATUS_ERR_AUTH_FAILED);
- goto out;
- }
- /* Client is now router operator */
- client->mode |= SILC_UMODE_ROUTER_OPERATOR;
+ send_list = TRUE;
- /* Send reply to the sender */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
- SILC_STATUS_OK);
+ /* Send our service list back */
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_SERVICE,
+ SILC_STATUS_OK, 0, ident, 0);
out:
silc_server_command_free(cmd);
}
+
+/* Private range commands, specific to this implementation */
+
/* Server side command of CONNECT. Connects us to the specified remote
server or router. */
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
unsigned char *tmp, *host;
- unsigned int tmp_len;
- unsigned int port = SILC_PORT;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CONNECT, cmd, 1, 2);
+ SilcUInt32 tmp_len;
+ SilcUInt32 port = SILC_PORT;
- if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
goto out;
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CONNECT, cmd, 1, 2);
+
/* Check whether client has the permissions. */
- if (client->mode == SILC_UMODE_NONE) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
- SILC_STATUS_ERR_NO_SERVER_PRIV);
+ if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+ !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+ SILC_STATUS_ERR_NO_SERVER_PRIV, 0);
goto out;
}
- if (server->server_type == SILC_ROUTER &&
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
client->mode & SILC_UMODE_SERVER_OPERATOR) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
- SILC_STATUS_ERR_NO_ROUTER_PRIV);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+ SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
goto out;
}
/* Get the remote server */
host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
if (!host) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
SILC_GET32_MSB(port, tmp);
/* Create the connection. It is done with timeout and is async. */
- silc_server_create_connection(server, host, port);
+ silc_server_create_connection(server, FALSE, FALSE, host, port, NULL, NULL);
/* Send reply to the sender */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
- SILC_STATUS_OK);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+ SILC_STATUS_OK, 0);
out:
silc_server_command_free(cmd);
}
-SILC_SERVER_CMD_FUNC(restart)
-{
-}
-
/* Server side command of CLOSE. Closes connection to a specified server. */
-
+
SILC_SERVER_CMD_FUNC(close)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
SilcServerEntry server_entry;
- SilcSocketConnection sock;
+ SilcPacketStream sock;
unsigned char *tmp;
- unsigned int tmp_len;
+ SilcUInt32 tmp_len;
unsigned char *name;
- unsigned int port = SILC_PORT;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CLOSE, cmd, 1, 2);
+ SilcUInt32 port = SILC_PORT;
- if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
goto out;
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CLOSE, cmd, 1, 2);
+
/* Check whether client has the permissions. */
- if (client->mode == SILC_UMODE_NONE) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
- SILC_STATUS_ERR_NO_SERVER_PRIV);
+ if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+ !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+ SILC_STATUS_ERR_NO_SERVER_PRIV,
+ 0);
goto out;
}
/* Get the remote server */
name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
if (!name) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+ 0);
goto out;
}
SILC_GET32_MSB(port, tmp);
server_entry = silc_idlist_find_server_by_conn(server->local_list,
- name, port, NULL);
+ name, port, TRUE, NULL);
+ if (!server_entry)
+ server_entry = silc_idlist_find_server_by_conn(server->global_list,
+ name, port, TRUE, NULL);
if (!server_entry) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
- SILC_STATUS_ERR_NO_SERVER_ID);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+ SILC_STATUS_ERR_NO_SERVER_ID, 0);
goto out;
}
- /* Send reply to the sender */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
- SILC_STATUS_OK);
-
- /* Close the connection to the server */
- sock = (SilcSocketConnection)server_entry->connection;
- silc_server_free_sock_user_data(server, sock);
- silc_server_close_connection(server, sock);
-
- out:
- silc_server_command_free(cmd);
-}
-
-/* Server side command of SHUTDOWN. Shutdowns the server and closes all
- active connections. */
-
-SILC_SERVER_CMD_FUNC(shutdown)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
-
- if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
- goto out;
-
- /* Check whether client has the permission. */
- if (client->mode == SILC_UMODE_NONE) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
- SILC_STATUS_ERR_NO_SERVER_PRIV);
+ if (server_entry == server->id_entry) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+ SILC_STATUS_ERR_NO_SERVER_ID, 0);
goto out;
}
/* Send reply to the sender */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
- SILC_STATUS_OK);
-
- /* Then, gracefully, or not, bring the server down. */
- silc_server_stop(server);
- exit(0);
-
- out:
- silc_server_command_free(cmd);
-}
-
-/* Server side command of LEAVE. Removes client from a channel. */
-
-SILC_SERVER_CMD_FUNC(leave)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- SilcServer server = cmd->server;
- SilcSocketConnection sock = cmd->sock;
- SilcClientEntry id_entry = (SilcClientEntry)cmd->sock->user_data;
- SilcChannelID *id;
- SilcChannelEntry channel;
- SilcBuffer packet;
- unsigned int i, len;
- unsigned char *tmp;
-
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_LEAVE, cmd, 1, 2);
-
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
- id = silc_id_payload_parse_id(tmp, len);
- if (!id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+ SILC_STATUS_OK, 0);
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
- if (!channel) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
- goto out;
- }
- }
+ /* Close the connection to the server */
+ sock = server_entry->connection;
- /* Check whether this client is on the channel */
- if (!silc_server_client_on_channel(id_entry, channel)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
- SILC_STATUS_ERR_NOT_ON_CHANNEL);
- goto out;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER) {
+ server->backup_closed = TRUE;
+ silc_server_backup_del(server, server_entry);
}
- /* Notify routers that they should remove this client from their list
- of clients on the channel. Send LEAVE notify type. */
- if (!server->standalone)
- silc_server_send_notify_leave(server, server->router->connection,
- server->server_type == SILC_ROUTER ?
- TRUE : FALSE, channel, id_entry->id,
- SILC_ID_CLIENT_LEN);
-
- /* Remove client from channel */
- i = silc_server_remove_from_one_channel(server, sock, channel, id_entry,
- TRUE);
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
- SILC_STATUS_OK);
-
- /* If the channel does not exist anymore we won't send anything */
- if (!i)
- goto out;
-
- /* Re-generate channel key */
- silc_server_create_channel_key(server, channel, 0);
-
- /* Encode channel key payload to be distributed on the channel */
- packet =
- silc_channel_key_payload_encode(len, tmp,
- strlen(channel->channel_key->cipher->name),
- channel->channel_key->cipher->name,
- channel->key_len / 8, channel->key);
-
- /* If we are normal server then we will send it to our router. If we
- are router we will send it to all local servers that has clients on
- the channel */
- if (server->server_type == SILC_SERVER) {
- if (!server->standalone)
- silc_server_packet_send(server,
- cmd->server->router->connection,
- SILC_PACKET_CHANNEL_KEY, 0, packet->data,
- packet->len, FALSE);
- } else {
-
+ server->backup_noswitch = TRUE;
+ if (server->router == server_entry) {
+ server->id_entry->router = NULL;
+ server->router = NULL;
+ server->standalone = TRUE;
}
-
- /* Send to locally connected clients on the channel */
- silc_server_packet_send_local_channel(server, channel,
- SILC_PACKET_CHANNEL_KEY, 0,
- packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_free(id);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER,
+ "Closed by administrator");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->backup_noswitch = FALSE;
out:
silc_server_command_free(cmd);
}
-/* Server side of command USERS. Resolves clients and their USERS currently
- joined on the requested channel. The list of Client ID's and their modes
- on the channel is sent back. */
+/* Server side command of SHUTDOWN. Shutdowns the server and closes all
+ active connections. */
-SILC_SERVER_CMD_FUNC(users)
+SILC_SERVER_CMD_FUNC(shutdown)
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- SilcChannelEntry channel;
- SilcChannelID *id;
- SilcBuffer packet;
- unsigned char *channel_id;
- unsigned int channel_id_len;
- SilcBuffer client_id_list;
- SilcBuffer client_mode_list;
- unsigned char lc[4];
- unsigned int list_count = 0;
- unsigned short ident = silc_command_get_ident(cmd->payload);
+ SilcClientEntry client = silc_packet_get_context(cmd->sock);
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_USERS, cmd, 1, 1);
-
- /* Get Channel ID */
- channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
- if (!channel_id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
- goto out;
- }
- id = silc_id_payload_parse_id(channel_id, channel_id_len);
- if (!id) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
- SILC_STATUS_ERR_NO_CHANNEL_ID);
+ if (client->data.conn_type != SILC_CONN_CLIENT || !client)
goto out;
- }
- /* If we are server and we don't know about this channel we will send
- the command to our router. If we know about the channel then we also
- have the list of users already. */
- channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
- if (!channel) {
- if (server->server_type == SILC_SERVER && !server->standalone &&
- !cmd->pending) {
- SilcBuffer tmpbuf;
-
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send USERS command */
- silc_server_packet_send(server, server->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_USERS,
- silc_command_get_ident(cmd->payload),
- silc_server_command_destructor,
- silc_server_command_users,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
- silc_command_set_ident(cmd->payload, ident);
-
- silc_buffer_free(tmpbuf);
- silc_free(id);
- return;
- }
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_SHUTDOWN, cmd, 0, 0);
- /* We are router and we will check the global list as well. */
- channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
- if (!channel) {
- /* Channel really does not exist */
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
- goto out;
- }
+ /* Check whether client has the permission. */
+ if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+ !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+ SILC_STATUS_ERR_NO_SERVER_PRIV,
+ 0);
+ goto out;
}
- /* Get the users list */
- silc_server_get_users_on_channel(server, channel, &client_id_list,
- &client_mode_list, &list_count);
-
- /* List count */
- SILC_PUT32_MSB(list_count, lc);
+ /* Send reply to the sender */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+ SILC_STATUS_OK, 0);
- /* Send reply */
- packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
- SILC_STATUS_OK, ident, 4,
- 2, channel_id, channel_id_len,
- 3, lc, 4,
- 4, client_id_list->data,
- client_id_list->len,
- 5, client_mode_list->data,
- client_mode_list->len);
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
- packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_buffer_free(client_id_list);
- silc_buffer_free(client_mode_list);
- silc_free(id);
+ /* Then, gracefully, or not, bring the server down. */
+ silc_server_stop(server);
+ exit(0);
out:
silc_server_command_free(cmd);