static void
silc_server_command_send_status_reply(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status);
+ SilcStatus status);
static void
silc_server_command_send_status_data(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status,
+ SilcStatus status,
SilcUInt32 arg_type,
const unsigned char *arg,
SilcUInt32 arg_len);
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(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),
of arguments. */
#define SILC_SERVER_COMMAND_CHECK(command, context, min, max) \
do { \
- SilcUInt32 _argc; \
+ SilcUInt32 _argc; \
\
SILC_LOG_DEBUG(("Start")); \
\
static void
silc_server_command_send_status_reply(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status)
+ SilcStatus status)
{
SilcBuffer buffer;
static void
silc_server_command_send_status_data(SilcServerCommandContext cmd,
SilcCommand command,
- SilcCommandStatus status,
+ SilcStatus status,
SilcUInt32 arg_type,
const unsigned char *arg,
SilcUInt32 arg_len)
/* Get the max count of reply messages allowed */
tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (tmp)
- *count = atoi(tmp);
- else
+ if (tmp) {
+ SILC_GET32_MSB(*count, tmp);
+ } else {
*count = 0;
+ }
return TRUE;
}
SilcServer server = cmd->server;
char *tmp;
int i, k, len, valid_count;
- SilcBuffer packet, idp, channels;
+ SilcBuffer packet, idp, channels, umode_list = NULL;
SilcClientEntry entry;
- SilcCommandStatus status;
+ SilcStatus status;
SilcUInt16 ident = silc_command_get_ident(cmd->payload);
char nh[256], uh[256];
unsigned char idle[4], mode[4];
strncat(uh, hsock->hostname, len);
}
- channels = silc_server_get_client_channel_list(server, entry);
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+ channels = silc_server_get_client_channel_list(server, entry, FALSE,
+ FALSE, &umode_list);
+ else
+ channels = silc_server_get_client_channel_list(server, entry, TRUE,
+ TRUE, &umode_list);
if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
fingerprint = entry->data.fingerprint;
packet =
silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
- status, 0, ident, 8,
+ status, 0, ident, 9,
2, idp->data, idp->len,
3, nh, strlen(nh),
4, uh, strlen(uh),
7, mode, 4,
8, idle, 4,
9, fingerprint,
- fingerprint ? 20 : 0);
+ fingerprint ? 20 : 0,
+ 10, umode_list ? umode_list->data :
+ NULL, umode_list ? umode_list->len :
+ 0);
silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
0, packet->data, packet->len, FALSE);
silc_buffer_free(idp);
if (channels)
silc_buffer_free(channels);
+ if (umode_list) {
+ silc_buffer_free(umode_list);
+ umode_list = NULL;
+ }
k++;
}
/* Get the max count of reply messages allowed */
tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (tmp)
- *count = atoi(tmp);
- else
+ if (tmp) {
+ SILC_GET32_MSB(*count, tmp);
+ } else {
*count = 0;
+ }
return TRUE;
}
int i, k, count = 0, len;
SilcBuffer packet, idp;
SilcClientEntry entry = NULL;
- SilcCommandStatus status;
+ SilcStatus status;
SilcUInt16 ident = silc_command_get_ident(cmd->payload);
char nh[256], uh[256];
int valid_count;
/* Get the max count of reply messages allowed */
tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
- if (tmp)
- *count = atoi(tmp);
- else
+ if (tmp) {
+ SILC_GET32_MSB(*count, tmp);
+ } else {
*count = 0;
+ }
return 1;
}
SilcServer server = cmd->server;
int i, k, len, valid_count;
SilcBuffer packet, idp;
- SilcCommandStatus status;
+ SilcStatus status;
SilcUInt16 ident = silc_command_get_ident(cmd->payload);
char nh[256], uh[256];
SilcSocketConnection hsock;
cmd->server->md5hash, nick,
&new_id)) {
nickfail++;
+ if (nickfail > 9) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+ SILC_STATUS_ERR_BAD_NICKNAME);
+ goto out;
+ }
snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
}
FALSE : TRUE, 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);
/* Remove old cache entry */
silc_idcache_del_by_context(server->local_list->clients, client);
- /* Free old ID */
silc_free(client->id);
+ client->id = new_id;
- /* Save the nickname as this client is our local client */
silc_free(client->nickname);
-
client->nickname = strdup(nick);
- client->id = new_id;
/* Update client cache */
silc_idcache_add(server->local_list->clients, client->nickname,
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 */
packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK,
int i, k;
SilcBuffer packet, idp;
SilcChannelEntry entry;
- SilcCommandStatus status;
+ SilcStatus status;
SilcUInt16 ident = silc_command_get_ident(cmd->payload);
char *topic;
unsigned char usercount[4];
goto out;
}
- if (chl->mode == SILC_CHANNEL_UMODE_NONE &&
- channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+ if (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
SILC_STATUS_ERR_NO_CHANNEL_PRIV);
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 (chl->mode == SILC_CHANNEL_UMODE_NONE &&
- channel->mode & SILC_CHANNEL_MODE_INVITE) {
+ if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
SILC_STATUS_ERR_NO_CHANNEL_PRIV);
goto out;
}
/* Get the client entry */
- dest = silc_server_get_client_resolve(server, dest_id, &resolve);
+ dest = silc_server_get_client_resolve(server, dest_id, FALSE, &resolve);
if (!dest) {
if (server->server_type != SILC_SERVER || !resolve) {
silc_server_command_send_status_reply(
if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
goto out;
- /* Get destination ID */
+ /* Get message */
tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
if (len > 128)
tmp = NULL;
/* We quit the connection with little timeout */
silc_schedule_task_add(server->schedule, sock->sock,
- silc_server_command_quit_cb, (void *)q,
- 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ silc_server_command_quit_cb, (void *)q,
+ 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
out:
silc_server_command_free(cmd);
SilcClientID *client_id;
unsigned char *tmp, *comment;
SilcUInt32 tmp_len, tmp_len2;
- SilcBuffer killer;
bool local;
SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
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. */
- killer = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- silc_server_send_notify_on_channels(server, remote_client,
- remote_client, SILC_NOTIFY_TYPE_KILLED,
- 3, tmp, tmp_len,
- comment, comment ? tmp_len2 : 0,
- killer->data, killer->len);
- silc_buffer_free(killer);
-
- /* Send KILLED notify to primary route */
- if (!server->standalone)
- silc_server_send_notify_killed(server, server->router->connection, TRUE,
- remote_client->id, comment, client->id);
-
- /* 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, comment, client->id);
-
- /* 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->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 {
- /* Update statistics */
- if (remote_client->connection)
- server->stat.my_clients--;
- if (server->stat.cell_clients)
- server->stat.cell_clients--;
- SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
- SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+ /* 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);
- /* Remove remote client */
- silc_idlist_del_client(local ? server->local_list :
- server->global_list, remote_client);
- }
+ /* Now do the killing */
+ silc_server_kill_client(server, remote_client, comment, client->id,
+ SILC_ID_CLIENT);
out:
silc_server_command_free(cmd);
if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
client = (SilcClientEntry)sock->user_data;
} else {
- client = silc_server_get_client_resolve(server, client_id, &resolve);
+ client = silc_server_get_client_resolve(server, client_id, FALSE,
+ &resolve);
if (!client) {
if (cmd->pending)
goto out;
if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
- keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp,
+ keyp = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+ SILC_ID_CHANNEL),
+ tmp,
strlen(channel->channel_key->
cipher->name),
channel->channel_key->cipher->name,
SilcServer server = cmd->server;
SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
SilcBuffer packet;
- unsigned char *tmp_mask;
- SilcUInt32 mask;
+ unsigned char *tmp_mask, m[4];
+ SilcUInt32 mask = 0;
SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ bool set_mask = FALSE;
if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
goto out;
- SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 1, 2);
/* Get the client's 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_UMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
- SILC_GET32_MSB(mask, tmp_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);
- goto out;
+ if (tmp_mask) {
+ SILC_GET32_MSB(mask, tmp_mask);
+ set_mask = TRUE;
}
- /* Anonymous mode cannot be set by client */
- if (mask & SILC_UMODE_ANONYMOUS) {
- if (!(client->mode & SILC_UMODE_ANONYMOUS)) {
+ 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);
goto out;
}
- } else {
- if (client->mode & SILC_UMODE_ANONYMOUS) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
- SILC_STATUS_ERR_PERM_DENIED);
- goto out;
+
+ /* Anonymous mode cannot be set by client */
+ if (mask & SILC_UMODE_ANONYMOUS) {
+ if (!(client->mode & SILC_UMODE_ANONYMOUS)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED);
+ goto out;
+ }
+ } else {
+ if (client->mode & SILC_UMODE_ANONYMOUS) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED);
+ goto out;
+ }
}
- }
- /* Change the mode */
- client->mode = mask;
+ /* Change the mode */
+ client->mode = mask;
- /* Send UMODE change to primary router */
- if (!server->standalone)
- silc_server_send_notify_umode(server, server->router->connection, TRUE,
- client->id, client->mode);
+ /* Send UMODE change to primary router */
+ if (!server->standalone)
+ silc_server_send_notify_umode(server, server->router->connection, TRUE,
+ 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 command reply to sender */
+ SILC_PUT32_MSB(client->mode, m);
packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
SILC_STATUS_OK, 0, ident, 1,
- 2, tmp_mask, 4);
+ 2, m, sizeof(m));
silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
packet->data, packet->len, FALSE);
silc_buffer_free(packet);
/* 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_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- (chl->mode == 0 ?
- SILC_STATUS_ERR_NO_CHANNEL_PRIV :
- SILC_STATUS_ERR_NO_CHANNEL_FOPRIV));
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_CMODE,
+ (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) ?
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV :
+ SILC_STATUS_ERR_NO_CHANNEL_FOPRIV));
goto out;
}
}
}
+ 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);
+ 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);
+ 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);
+ 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);
+ goto out;
+ }
+
+ chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+ notify = TRUE;
+ }
+ }
idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
}
/* Check that the kicker is channel operator or channel founder */
- if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
SILC_STATUS_ERR_NO_CHANNEL_PRIV);
goto out;
silc_server_send_notify_umode(server, server->router->connection, TRUE,
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);
silc_server_command_free(cmd);
}
+SILC_TASK_CALLBACK(silc_server_command_detach_cb)
+{
+ QuitInternal q = (QuitInternal)context;
+ SilcClientEntry client = (SilcClientEntry)q->sock->user_data;
+
+ /* If there is pending outgoing data for the client then purge it
+ to the network before closing connection. */
+ silc_server_packet_queue_purge(q->server, q->sock);
+
+ /* Close the connection on our side */
+ client->router = NULL;
+ client->connection = NULL;
+ q->sock->user_data = NULL;
+ silc_server_close_connection(q->server, q->sock);
+
+ silc_free(q);
+}
+
+SILC_TASK_CALLBACK(silc_server_command_detach_timeout)
+{
+ QuitInternal q = (QuitInternal)context;
+ SilcClientEntry client = (SilcClientEntry)q->sock;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (client->mode & SILC_UMODE_DETACHED)
+ silc_server_free_client_data(q->server, NULL, client, TRUE,
+ "Detach timeout");
+ silc_free(q);
+}
+
+/* Server side of DETACH command. Detached the client from the network
+ by closing the connection but preserving the session. */
+
+SILC_SERVER_CMD_FUNC(detach)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ QuitInternal q;
+
+ if (server->config->detach_disabled) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+ SILC_STATUS_ERR_UNKNOWN_COMMAND);
+ goto out;
+ }
+
+ if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_DETACH, cmd, 0, 0);
+
+ /* Send the user mode notify to notify that client is detached */
+ client->mode |= SILC_UMODE_DETACHED;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+ if (!server->standalone)
+ silc_server_send_notify_umode(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, 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);
+
+ q = silc_calloc(1, sizeof(*q));
+ q->server = server;
+ q->sock = cmd->sock;
+ silc_schedule_task_add(server->schedule, 0, silc_server_command_detach_cb,
+ q, 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+ if (server->config->detach_timeout) {
+ q = silc_calloc(1, sizeof(*q));
+ q->server = server;
+ q->sock = (void *)client;
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_command_detach_timeout,
+ q, server->config->detach_timeout * 60,
+ 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ }
+
+ /* Send reply to the sender */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+ SILC_STATUS_OK);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of WATCH command. */
+
+SILC_SERVER_CMD_FUNC(watch)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ char *add_nick, *del_nick;
+ SilcUInt32 add_nick_len, del_nick_len, tmp_len;
+ char nick[128 + 1];
+ unsigned char hash[16], *tmp;
+ SilcClientEntry client;
+ SilcClientID *client_id = NULL;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WATCH, cmd, 1, 3);
+
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ if (!cmd->pending) {
+ /* Send the command to router */
+ SilcBuffer tmpbuf;
+ SilcUInt16 old_ident;
+
+ 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, 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_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);
+ } else if (context2) {
+ /* Received reply from router, just send same data to the client. */
+ SilcServerCommandReplyContext reply = context2;
+ SilcStatus status;
+ silc_command_get_status(reply->payload, &status, NULL);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status);
+ }
+
+ goto out;
+ }
+
+ /* We are router and keep the watch list for local cell */
+
+ /* 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_WATCH,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ if (!client_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+ goto out;
+ }
+
+ /* Get the client entry which must be in local list */
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+ goto out;
+ }
+
+ /* 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) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ if (add_nick && add_nick_len > 128)
+ add_nick[128] = '\0';
+ if (del_nick && del_nick_len > 128)
+ del_nick[128] = '\0';
+
+ memset(nick, 0, sizeof(nick));
+
+ /* Add new nickname to be watched in our cell */
+ if (add_nick) {
+ if (silc_server_name_bad_chars(add_nick, strlen(add_nick)) == TRUE) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_BAD_NICKNAME);
+ goto out;
+ }
+
+ /* 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_to_lower(add_nick, nick, sizeof(nick) - 1);
+ silc_hash_make(server->md5hash, nick, strlen(nick), 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);
+ goto out;
+ }
+
+ /* 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);
+
+ /* Add the client to the watcher list with the specified nickname hash. */
+ silc_hash_table_add(server->watcher_list, tmp, client);
+ }
+
+ /* Delete nickname from watch list */
+ if (del_nick) {
+ if (silc_server_name_bad_chars(del_nick, strlen(del_nick)) == TRUE) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_BAD_NICKNAME);
+ goto out;
+ }
+
+ /* 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_to_lower(del_nick, nick, sizeof(nick) - 1);
+ silc_hash_make(server->md5hash, nick, strlen(nick), 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_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_ERR_NO_SUCH_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_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+ SILC_STATUS_OK);
+
+ out:
+ silc_free(client_id);
+ silc_server_command_free(cmd);
+}
+
/* Server side of SILCOPER command. Client uses this comand to obtain router
operator privileges to this router. */
silc_server_send_notify_umode(server, server->router->connection, TRUE,
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_SILCOPER,
SILC_STATUS_OK);
channel = silc_idlist_find_channel_by_name(server->local_list,
channel_name, NULL);
- if (!channel || channel->disabled) {
+ if (!channel || channel->disabled || !channel->users_resolved) {
if (server->server_type != SILC_ROUTER && !server->standalone &&
!cmd->pending) {
SilcBuffer tmpbuf;
}
/* If the channel is private or secret do not send anything, unless the
- user requesting this command is on the channel. */
+ user requesting this command is on the channel or is server */
if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
&& !silc_server_client_on_channel(cmd->sock->user_data, channel,
NULL)) {
silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
- goto out;
- }
- } else {
- if (channel->mode &
- (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ SILC_STATUS_ERR_NOT_ON_CHANNEL);
goto out;
}
}
client_id, TRUE, NULL);
if ((!client && !cmd->pending && !server->standalone) ||
- (client && !client->connection && !cmd->pending) ||
+ (client && !client->connection && !cmd->pending &&
+ !(client->mode & SILC_UMODE_DETACHED)) ||
(client && !client->data.public_key && !cmd->pending)) {
SilcBuffer tmpbuf;
SilcUInt16 old_ident;
goto out;
/* Check whether client has the permissions. */
- if (client->mode == SILC_UMODE_NONE) {
+ 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);
goto out;
goto out;
/* Check whether client has the permissions. */
- if (client->mode == SILC_UMODE_NONE) {
+ 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);
goto out;
goto out;
/* Check whether client has the permission. */
- if (client->mode == SILC_UMODE_NONE) {
+ 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);
goto out;