silc_idcache_free(server->global_list->clients);
silc_idcache_free(server->global_list->servers);
silc_idcache_free(server->global_list->channels);
+ silc_hash_table_free(server->watcher_list);
silc_free(server->sockets);
silc_free(server);
server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+ /* Init watcher list */
+ server->watcher_list =
+ silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
+ silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
+ NULL, NULL, TRUE);
+ if (!server->watcher_list)
+ goto err;
+
/* Create a listening server */
if (!silc_server_listen(server, &sock))
goto err;
silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
silc_ske_free_key_material(ctx->keymat);
silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_free(ctx->dest_id);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
+ NULL);
goto out;
}
if (!id_entry) {
silc_free(ctx->dest_id);
SILC_LOG_ERROR(("Cannot add new server entry to cache"));
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
+ NULL);
goto out;
}
sock->ip ? sock->ip : ""));
server->stat.conn_failures++;
silc_server_disconnect_remote(server, sock,
- "Server closed connection: Unknown host");
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Unknown host or IP");
return;
}
/* The connection is denied */
SILC_LOG_INFO(("Connection %s (%s) is denied",
sock->hostname, sock->ip));
- silc_server_disconnect_remote(server, sock, deny->reason ?
- deny->reason :
- "Server closed connection: "
- "Connection refused");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER,
+ deny->reason);
server->stat.conn_failures++;
return;
}
SILC_LOG_INFO(("Connection %s (%s) is not allowed", sock->hostname,
sock->ip));
silc_server_disconnect_remote(server, sock,
- "Server closed connection: "
- "Connection refused");
+ SILC_STATUS_ERR_BANNED_FROM_SERVER);
server->stat.conn_failures++;
return;
}
silc_free(ctx);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
+ NULL);
server->stat.auth_failures++;
return;
}
silc_free(ctx);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
server->stat.auth_failures++;
return;
}
silc_free(ctx);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
+ NULL);
server->stat.auth_failures++;
return;
}
if (!client) {
SILC_LOG_ERROR(("Could not add new client to cache"));
silc_free(sock->user_data);
- silc_server_disconnect_remote(server, sock,
- "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
server->stat.auth_failures++;
goto out;
}
if (!new_server) {
SILC_LOG_ERROR(("Could not add new server to cache"));
silc_free(sock->user_data);
- silc_server_disconnect_remote(server, sock,
- "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
server->stat.auth_failures++;
goto out;
}
/* Parse the packet type */
switch (type) {
case SILC_PACKET_DISCONNECT:
- SILC_LOG_DEBUG(("Disconnect packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- if (silc_string_is_ascii(packet->buffer->data, packet->buffer->len)) {
- /* Duplicate to null terminate the string. */
- char *message = silc_memdup(packet->buffer->data, packet->buffer->len);
- SILC_LOG_ERROR(("%s", message));
+ {
+ SilcStatus status;
+ char *message = NULL;
+
+ SILC_LOG_DEBUG(("Disconnect packet"));
+
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ if (packet->buffer->len < 1)
+ break;
+
+ status = (SilcStatus)packet->buffer->data[0];
+ if (packet->buffer->len > 1 &&
+ silc_utf8_valid(packet->buffer->data + 1, packet->buffer->len - 1))
+ message = silc_memdup(packet->buffer->data, packet->buffer->len);
+
+ SILC_LOG_ERROR(("Disconnected by %s (%s): %s (%d) %s",
+ sock->ip, sock->hostname,
+ silc_get_status_message(status), status,
+ message ? message : ""));
silc_free(message);
}
break;
void silc_server_disconnect_remote(SilcServer server,
SilcSocketConnection sock,
- const char *fmt, ...)
+ SilcStatus status, ...)
{
va_list ap;
- unsigned char buf[4096];
+ unsigned char buf[512];
+ SilcBuffer buffer;
+ char *cp;
+ int len;
if (!sock)
return;
memset(buf, 0, sizeof(buf));
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
+ va_start(ap, status);
+ cp = va_arg(ap, char *);
+ if (cp) {
+ vsnprintf(buf, sizeof(buf) - 1, cp, ap);
+ cp = buf;
+ }
va_end(ap);
SILC_LOG_DEBUG(("Disconnecting remote host"));
/* Notify remote end that the conversation is over. The notify message
is tried to be sent immediately. */
+
+ len = 1;
+ if (cp)
+ len += silc_utf8_encoded_len(buf, strlen(buf), SILC_STRING_ASCII);
+
+ buffer = silc_buffer_alloc_size(len);
+ if (!buffer)
+ goto out;
+
+ buffer->data[0] = status;
+ if (cp)
+ silc_utf8_encode(buf, strlen(buf), SILC_STRING_ASCII, buffer->data + 1,
+ buffer->len - 1);
silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,
- buf, strlen(buf), TRUE);
+ buffer->data, buffer->len, TRUE);
+ silc_buffer_free(buffer);
+
+ out:
silc_server_packet_queue_purge(server, sock);
/* Mark the connection to be disconnected */
to the network before removing the client entry. */
silc_server_packet_queue_purge(server, sock);
- if (!client->id)
- return;
+ if (client->id) {
+ /* Send SIGNOFF notify to routers. */
+ if (notify && !server->standalone && server->router)
+ silc_server_send_notify_signoff(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, client->id, signoff);
- /* Send SIGNOFF notify to routers. */
- if (notify && !server->standalone && server->router)
- silc_server_send_notify_signoff(server, server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE, client->id, signoff);
+ /* Remove client from all channels */
+ if (notify)
+ silc_server_remove_from_channels(server, NULL, client,
+ TRUE, (char *)signoff, TRUE);
+ else
+ silc_server_remove_from_channels(server, NULL, client,
+ FALSE, NULL, FALSE);
- /* Remove client from all channels */
- if (notify)
- silc_server_remove_from_channels(server, NULL, client,
- TRUE, (char *)signoff, TRUE);
- else
- silc_server_remove_from_channels(server, NULL, client,
- FALSE, NULL, FALSE);
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_SIGNOFF);
+
+ /* Remove this client from watcher list if it is */
+ silc_server_del_from_watcher_list(server, client);
+ }
/* Update statistics */
server->stat.my_clients--;
server->stat.cell_clients--;
SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+ silc_schedule_task_del_by_context(server->schedule, client);
/* We will not delete the client entry right away. We will take it
into history (for WHOWAS command) for 5 minutes */
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
SilcClientEntry client,
- int notify,
- char *signoff_message,
- int keygen)
+ bool notify,
+ const char *signoff_message,
+ bool keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
return;
clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (!clidp)
+ notify = FALSE;
/* Remove the client from all channels. The client is removed from
the channels' user list. */
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
- /* Remove channel from client's channel list */
- silc_hash_table_del(client->channels, channel);
-
- /* Remove channel if there is no users anymore */
+ /* Remove channel if this is last client leaving the channel, unless
+ the channel is permanent. */
if (server->server_type == SILC_ROUTER &&
silc_hash_table_count(channel->user_list) < 2) {
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
+ silc_server_channel_delete(server, channel);
continue;
}
- /* Remove client from channel's client list */
+ silc_hash_table_del(client->channels, channel);
silc_hash_table_del(channel->user_list, chl->client);
channel->user_count--;
server->stat.my_chanclients--;
/* If there is not at least one local user on the channel then we don't
- need the channel entry anymore, we can remove it safely. */
+ need the channel entry anymore, we can remove it safely, unless the
+ channel is permanent channel */
if (server->server_type != SILC_ROUTER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
signoff_message, signoff_message ?
strlen(signoff_message) : 0);
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
- if (channel->founder_key) {
- /* The founder auth data exists, do not remove the channel entry */
- SilcChannelClientEntry chl2;
- SilcHashTableList htl2;
-
- channel->disabled = TRUE;
-
- silc_hash_table_list(channel->user_list, &htl2);
- while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
- silc_hash_table_del(chl2->client->channels, channel);
- silc_hash_table_del(channel->user_list, chl2->client);
- channel->user_count--;
- silc_free(chl2);
- }
- silc_hash_table_list_reset(&htl2);
- continue;
- }
-
- /* Remove the channel entry */
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ silc_server_channel_delete(server, channel);
continue;
}
- /* Send notify to channel about client leaving SILC and thus
- the entire channel. */
+ /* Send notify to channel about client leaving SILC and channel too */
if (notify)
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message, signoff_message ?
strlen(signoff_message) : 0);
+ /* Re-generate channel key if needed */
if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
- /* Re-generate channel key */
if (!silc_server_create_channel_key(server, channel, 0))
- goto out;
+ continue;
/* Send the channel key to the channel. The key of course is not sent
to the client who was removed from the channel. */
}
}
- out:
silc_hash_table_list_reset(&htl);
silc_buffer_free(clidp);
}
last client leaves the channel. If `notify' is FALSE notify messages
are not sent. */
-int silc_server_remove_from_one_channel(SilcServer server,
- SilcSocketConnection sock,
- SilcChannelEntry channel,
- SilcClientEntry client,
- int notify)
+bool silc_server_remove_from_one_channel(SilcServer server,
+ SilcSocketConnection sock,
+ SilcChannelEntry channel,
+ SilcClientEntry client,
+ bool notify)
{
SilcChannelClientEntry chl;
SilcBuffer clidp;
- SILC_LOG_DEBUG(("Start"));
+ SILC_LOG_DEBUG(("Removing %s from channel %s",
+ silc_id_render(client->id, SILC_ID_CLIENT),
+ channel->channel_name));
/* Get the entry to the channel, if this client is not on the channel
then return Ok. */
if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl))
return TRUE;
- /* Remove the client from the channel. The client is removed from
- the channel's user list. */
-
- clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
- /* Remove channel from client's channel list */
- silc_hash_table_del(client->channels, chl->channel);
-
- /* Remove channel if there is no users anymore */
+ /* Remove channel if this is last client leaving the channel, unless
+ the channel is permanent. */
if (server->server_type == SILC_ROUTER &&
silc_hash_table_count(channel->user_list) < 2) {
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
- silc_buffer_free(clidp);
+ silc_server_channel_delete(server, channel);
return FALSE;
}
- /* Remove client from channel's client list */
+ silc_hash_table_del(client->channels, chl->channel);
silc_hash_table_del(channel->user_list, chl->client);
channel->user_count--;
silc_free(chl);
server->stat.my_chanclients--;
+ clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (!clidp)
+ notify = FALSE;
+
/* If there is not at least one local user on the channel then we don't
- need the channel entry anymore, we can remove it safely. */
+ need the channel entry anymore, we can remove it safely, unless the
+ channel is permanent channel */
if (server->server_type != SILC_ROUTER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
SILC_NOTIFY_TYPE_LEAVE, 1,
clidp->data, clidp->len);
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ silc_server_channel_delete(server, channel);
silc_buffer_free(clidp);
-
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
- if (channel->founder_key) {
- /* The founder auth data exists, do not remove the channel entry */
- SilcChannelClientEntry chl2;
- SilcHashTableList htl2;
-
- channel->disabled = TRUE;
-
- silc_hash_table_list(channel->user_list, &htl2);
- while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
- silc_hash_table_del(chl2->client->channels, channel);
- silc_hash_table_del(channel->user_list, chl2->client);
- channel->user_count--;
- silc_free(chl2);
- }
- silc_hash_table_list_reset(&htl2);
- return FALSE;
- }
-
- /* Remove the channel entry */
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
return FALSE;
}
{
SilcServer server = (SilcServer)context;
SilcSocketConnection sock = server->sockets[fd];
+ SilcProtocolType protocol = 0;
SILC_LOG_DEBUG(("Start"));
/* If we have protocol active we must assure that we call the protocol's
final callback so that all the memory is freed. */
if (sock->protocol) {
+ protocol = sock->protocol->protocol->type;
silc_protocol_cancel(sock->protocol, server->schedule);
sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
silc_protocol_execute_final(sock->protocol, server->schedule);
if (sock->user_data)
silc_server_free_sock_user_data(server, sock, NULL);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ silc_server_disconnect_remote(server, sock,
+ protocol ==
+ SILC_PROTOCOL_SERVER_CONNECTION_AUTH ?
+ SILC_STATUS_ERR_AUTH_FAILED :
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
"Connection timeout");
}
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER)
+ entry->users_resolved = TRUE;
+
return entry;
}
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER)
+ entry->users_resolved = TRUE;
+
return entry;
}
void silc_server_announce_get_channel_users(SilcServer server,
SilcChannelEntry channel,
+ SilcBuffer *channel_modes,
SilcBuffer *channel_users,
SilcBuffer *channel_users_modes)
{
SilcBuffer chidp, clidp;
SilcBuffer tmp;
int len;
- unsigned char mode[4];
+ unsigned char mode[4], *fkey = NULL;
+ SilcUInt32 fkey_len = 0;
+ char *hmac;
SILC_LOG_DEBUG(("Start"));
- /* Now find all users on the channel */
chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+
+ /* CMODE notify */
+ clidp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+ SILC_PUT32_MSB(channel->mode, mode);
+ hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
+ if (channel->founder_key)
+ fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
+ tmp =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
+ 6, clidp->data, clidp->len,
+ mode, sizeof(mode),
+ NULL, 0,
+ hmac, hmac ? strlen(hmac) : 0,
+ channel->passphrase,
+ channel->passphrase ?
+ strlen(channel->passphrase) : 0,
+ fkey, fkey_len);
+ len = tmp->len;
+ *channel_modes =
+ silc_buffer_realloc(*channel_modes,
+ (*channel_modes ?
+ (*channel_modes)->truelen + len : len));
+ silc_buffer_pull_tail(*channel_modes,
+ ((*channel_modes)->end -
+ (*channel_modes)->data));
+ silc_buffer_put(*channel_modes, tmp->data, tmp->len);
+ silc_buffer_pull(*channel_modes, len);
+ silc_buffer_free(tmp);
+ silc_buffer_free(clidp);
+ silc_free(fkey);
+
+ /* Now find all users on the channel */
silc_hash_table_list(channel->user_list, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
/* CUMODE notify for mode change on the channel */
SILC_PUT32_MSB(chl->mode, mode);
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && channel->founder_key)
+ fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
- 3, clidp->data, clidp->len,
+ 4, clidp->data, clidp->len,
mode, 4,
- clidp->data, clidp->len);
+ clidp->data, clidp->len,
+ fkey, fkey_len);
len = tmp->len;
*channel_users_modes =
silc_buffer_realloc(*channel_users_modes,
silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
silc_buffer_pull(*channel_users_modes, len);
silc_buffer_free(tmp);
-
+ silc_free(fkey);
silc_buffer_free(clidp);
}
silc_hash_table_list_reset(&htl);
void silc_server_announce_get_channels(SilcServer server,
SilcIDList id_list,
SilcBuffer *channels,
+ SilcBuffer **channel_modes,
SilcBuffer *channel_users,
SilcBuffer **channel_users_modes,
SilcUInt32 *channel_users_modes_c,
sizeof(**channel_users_modes) *
(i + 1));
(*channel_users_modes)[i] = NULL;
+ *channel_modes = silc_realloc(*channel_modes,
+ sizeof(**channel_modes) * (i + 1));
+ (*channel_modes)[i] = NULL;
*channel_ids = silc_realloc(*channel_ids,
sizeof(**channel_ids) * (i + 1));
(*channel_ids)[i] = NULL;
silc_server_announce_get_channel_users(server, channel,
+ &(*channel_modes)[i],
channel_users,
&(*channel_users_modes)[i]);
(*channel_ids)[i] = channel->id;
unsigned long creation_time,
SilcSocketConnection remote)
{
- SilcBuffer channels = NULL, channel_users = NULL;
+ SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
SilcBuffer *channel_users_modes = NULL;
SilcBuffer *channel_topics = NULL;
SilcUInt32 channel_users_modes_c = 0;
/* Get channels and channel users in local list */
silc_server_announce_get_channels(server, server->local_list,
- &channels, &channel_users,
+ &channels, &channel_modes,
+ &channel_users,
&channel_users_modes,
&channel_users_modes_c,
&channel_topics,
/* Get channels and channel users in global list */
if (server->server_type != SILC_SERVER)
silc_server_announce_get_channels(server, server->global_list,
- &channels, &channel_users,
+ &channels, &channel_modes,
+ &channel_users,
&channel_users_modes,
&channel_users_modes_c,
&channel_topics,
silc_buffer_free(channels);
}
+ if (channel_modes) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_modes[i])
+ continue;
+ silc_buffer_push(channel_modes[i],
+ channel_modes[i]->data -
+ channel_modes[i]->head);
+ SILC_LOG_HEXDUMP(("channel modes"), channel_modes[i]->data,
+ channel_modes[i]->len);
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_modes[i]->data,
+ channel_modes[i]->len,
+ FALSE);
+ silc_buffer_free(channel_modes[i]);
+ }
+ silc_free(channel_modes);
+ }
+
if (channel_users) {
silc_buffer_push(channel_users, channel_users->data - channel_users->head);
SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
SilcClientID *client_id;
SilcClientEntry client;
SilcIDCacheEntry cache;
+ SilcChannelClientEntry chl;
bool global;
SILC_LOG_DEBUG(("Start"));
silc_free(client_id);
- if (!silc_server_client_on_channel(client, channel, NULL)) {
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
/* Client was not on the channel, add it. */
- SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+ chl = silc_calloc(1, sizeof(*chl));
chl->client = client;
chl->mode = mode;
chl->channel = channel;
silc_hash_table_add(channel->user_list, chl->client, chl);
silc_hash_table_add(client->channels, chl->channel, chl);
channel->user_count++;
+ } else {
+ /* Update mode */
+ chl->mode = mode;
+ }
+ }
+}
+
+/* Saves channels and channels user modes to the `client'. Removes
+ the client from those channels that are not sent in the list but
+ it has joined. */
+
+void silc_server_save_user_channels(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientEntry client,
+ SilcBuffer channels,
+ SilcBuffer channels_user_modes)
+{
+ SilcDList ch;
+ SilcUInt32 *chumodes;
+ SilcChannelPayload entry;
+ SilcChannelEntry channel;
+ SilcChannelID *channel_id;
+ SilcChannelClientEntry chl;
+ SilcHashTable ht = NULL;
+ SilcHashTableList htl;
+ char *name;
+ int i = 0;
+
+ if (!channels ||!channels_user_modes)
+ goto out;
+
+ ch = silc_channel_payload_parse_list(channels->data, channels->len);
+ if (ch && silc_get_mode_list(channels_user_modes, silc_dlist_count(ch),
+ &chumodes)) {
+ ht = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
+ NULL, NULL, NULL, TRUE);
+ silc_dlist_start(ch);
+ while ((entry = silc_dlist_get(ch)) != SILC_LIST_END) {
+ /* Check if we have this channel, and add it if we don't have it.
+ Also add the client on the channel unless it is there already. */
+ channel_id = silc_channel_get_id_parse(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) {
+ if (server->server_type != SILC_SERVER) {
+ silc_free(channel_id);
+ i++;
+ continue;
+ }
+
+ /* We don't have that channel anywhere, add it. */
+ name = silc_channel_get_name(entry, NULL);
+ channel = silc_idlist_add_channel(server->global_list, strdup(name), 0,
+ channel_id, server->router,
+ NULL, NULL, 0);
+ if (!channel) {
+ silc_free(channel_id);
+ i++;
+ continue;
+ }
+ channel_id = NULL;
+ }
+
+ channel->mode = silc_channel_get_mode(entry);
+
+ /* Add the client on the channel */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ chl = silc_calloc(1, sizeof(*chl));
+ chl->client = client;
+ chl->mode = chumodes[i++];
+ chl->channel = channel;
+ silc_hash_table_add(channel->user_list, chl->client, chl);
+ silc_hash_table_add(client->channels, chl->channel, chl);
+ channel->user_count++;
+ } else {
+ /* Update mode */
+ chl->mode = chumodes[i++];
+ }
+
+ silc_hash_table_add(ht, channel, channel);
+ silc_free(channel_id);
}
+ silc_channel_payload_list_free(ch);
+ silc_free(chumodes);
+ }
+
+ out:
+ /* Go through the list again and remove client from channels that
+ are no part of the list. */
+ if (ht) {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ if (!silc_hash_table_find(ht, chl->channel, NULL, NULL)) {
+ silc_hash_table_del(chl->channel->user_list, chl->client);
+ silc_hash_table_del(chl->client->channels, chl->channel);
+ silc_free(chl);
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ silc_hash_table_free(ht);
+ } else {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ silc_hash_table_del(chl->channel->user_list, chl->client);
+ silc_hash_table_del(chl->client->channels, chl->channel);
+ silc_free(chl);
+ }
+ silc_hash_table_list_reset(&htl);
}
}
Secret channels are not put to the list. */
SilcBuffer silc_server_get_client_channel_list(SilcServer server,
- SilcClientEntry client)
+ SilcClientEntry client,
+ bool get_private,
+ bool get_secret,
+ SilcBuffer *user_mode_list)
{
SilcBuffer buffer = NULL;
SilcChannelEntry channel;
SilcUInt16 name_len;
int len;
+ if (user_mode_list)
+ *user_mode_list = NULL;
+
silc_hash_table_list(client->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
- if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
- channel->mode & SILC_CHANNEL_MODE_PRIVATE)
+ if (channel->mode & SILC_CHANNEL_MODE_SECRET && !get_secret)
+ continue;
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVATE && !get_private)
continue;
cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
len = 4 + name_len + id_len + 4;
buffer = silc_buffer_realloc(buffer,
- (buffer ? (buffer)->truelen + len : len));
- silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+ (buffer ? buffer->truelen + len : len));
+ silc_buffer_pull_tail(buffer, (buffer->end - buffer->data));
silc_buffer_format(buffer,
SILC_STR_UI_SHORT(name_len),
SILC_STR_UI_XNSTRING(channel->channel_name,
name_len),
SILC_STR_UI_SHORT(id_len),
SILC_STR_UI_XNSTRING(cid, id_len),
- SILC_STR_UI_INT(chl->mode), /* Client's mode */
+ SILC_STR_UI_INT(chl->channel->mode),
SILC_STR_END);
silc_buffer_pull(buffer, len);
silc_free(cid);
+
+ if (user_mode_list) {
+ *user_mode_list = silc_buffer_realloc(*user_mode_list,
+ (*user_mode_list ?
+ (*user_mode_list)->truelen + 4 :
+ 4));
+ silc_buffer_pull_tail(*user_mode_list, ((*user_mode_list)->end -
+ (*user_mode_list)->data));
+ SILC_PUT32_MSB(chl->mode, (*user_mode_list)->data);
+ silc_buffer_pull(*user_mode_list, 4);
+ }
}
silc_hash_table_list_reset(&htl);
if (buffer)
silc_buffer_push(buffer, buffer->data - buffer->head);
+ if (user_mode_list && *user_mode_list)
+ silc_buffer_push(*user_mode_list, ((*user_mode_list)->data -
+ (*user_mode_list)->head));
return buffer;
}
SilcClientEntry silc_server_get_client_resolve(SilcServer server,
SilcClientID *client_id,
+ bool always_resolve,
bool *resolved)
{
SilcClientEntry client;
if (!client && server->standalone)
return NULL;
- if (!client || !client->nickname || !client->username) {
+ if (!client || !client->nickname || !client->username ||
+ always_resolve) {
SilcBuffer buffer, idp;
- client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
- client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
- client->resolve_cmd_ident = ++server->cmd_ident;
+ if (client) {
+ client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+ client->resolve_cmd_ident = ++server->cmd_ident;
+ }
idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
server->cmd_ident, 1,
- 3, idp->data, idp->len);
+ 4, idp->data, idp->len);
silc_server_packet_send(server, client ? client->router->connection :
server->router->connection,
SILC_PACKET_COMMAND, 0,