void silc_server_free(SilcServer server)
{
if (server) {
+#ifdef SILC_SIM
SilcSimContext *sim;
+#endif
if (server->local_list)
silc_free(server->local_list);
if (server->server_type == SILC_ROUTER) {
/* Route the packet if it is not destined to us. Other ID types but
server are handled separately after processing them. */
- if (packet->dst_id_type == SILC_ID_SERVER &&
- !(packet->flags & SILC_PACKET_FLAG_FORWARDED) &&
+ if (packet->dst_id_type == SILC_ID_SERVER &&
sock->type != SILC_SOCKET_TYPE_CLIENT &&
SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
/*
* Received command reply packet. Received command reply to command. It
* may be reply to command sent by us or reply to command sent by client
- * that we've forwarded.
+ * that we've routed further.
*/
SILC_LOG_DEBUG(("Command Reply packet"));
silc_server_command_reply(server, sock, packet);
sock->user_data = NULL;
}
-/* Removes client from all channels it has joined. This is used when
- client connection is disconnected. If the client on a channel
- is last, the channel is removed as well. */
+/* Checks whether given channel has global users. If it does this returns
+ TRUE and FALSE if there is only locally connected clients on the channel. */
+
+int silc_server_channel_has_global(SilcChannelEntry channel)
+{
+ SilcChannelClientEntry chl;
+
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ if (chl->client->router)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Checks whether given channel has locally connected users. If it does this
+ returns TRUE and FALSE if there is not one locally connected client. */
+
+int silc_server_channel_has_local(SilcChannelEntry channel)
+{
+ SilcChannelClientEntry chl;
+
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ if (!chl->client->router)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Removes client from all channels it has joined. This is used when client
+ connection is disconnected. If the client on a channel is last, the
+ channel is removed as well. This sends the SIGNOFF notify types. */
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
channel globally from SILC network, in this case we will
notify that this client has left the channel. */
if (channel->global_users)
- silc_server_send_notify_to_channel(server, channel, TRUE,
+ silc_server_send_notify_to_channel(server, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF, 1,
clidp->data, clidp->len);
/* Send notify to channel about client leaving SILC and thus
the entire channel. */
- silc_server_send_notify_to_channel(server, channel, TRUE,
+ silc_server_send_notify_to_channel(server, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF, 1,
clidp->data, clidp->len);
silc_buffer_free(chidp);
/* If this client is last one on the channel the channel
is removed all together. */
if (silc_list_count(channel->user_list) < 2) {
- /* Notify about leaving client if this channel has global users,
- ie. the channel is not created locally. */
+ /* Notify about leaving client if this channel has global users. */
if (notify && channel->global_users)
- silc_server_send_notify_to_channel(server, channel, TRUE,
+ silc_server_send_notify_to_channel(server, channel, FALSE,
SILC_NOTIFY_TYPE_LEAVE, 1,
clidp->data, clidp->len);
silc_list_del(channel->user_list, chl);
silc_free(chl);
+ /* If there is no global users on the channel anymore mark the channel
+ as local channel. */
+ if (server->server_type == SILC_SERVER &&
+ !silc_server_channel_has_global(channel))
+ channel->global_users = FALSE;
+
+ /* If tehre is not at least one local user on the channel then we don't
+ need the channel entry anymore, we can remove it safely. */
+ if (server->server_type == SILC_SERVER &&
+ !silc_server_channel_has_local(channel)) {
+ silc_idlist_del_channel(server->local_list, channel);
+ silc_buffer_free(clidp);
+ return FALSE;
+ }
+
/* Send notify to channel about client leaving the channel */
if (notify)
- silc_server_send_notify_to_channel(server, channel, TRUE,
+ silc_server_send_notify_to_channel(server, channel, FALSE,
SILC_NOTIFY_TYPE_LEAVE, 1,
clidp->data, clidp->len);
break;
unsigned int len;
if (!channel->channel_key)
- return;
+ silc_cipher_alloc("twofish", &channel->channel_key);
if (key_len)
len = key_len;
memcpy(channel->key, channel_key, len);
memset(channel_key, 0, sizeof(channel_key));
}
+
+/* Saves the channel key found in the encoded `key_payload' buffer. This
+ function is used when we receive Channel Key Payload and also when we're
+ processing JOIN command reply. Returns entry to the channel. */
+
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+ SilcBuffer key_payload,
+ SilcChannelEntry channel)
+{
+ SilcChannelKeyPayload payload = NULL;
+ SilcChannelID *id = NULL;
+ unsigned char *tmp;
+ unsigned int tmp_len;
+ char *cipher;
+
+ /* Decode channel key payload */
+ payload = silc_channel_key_payload_parse(key_payload);
+ if (!payload) {
+ SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+ channel = NULL;
+ goto out;
+ }
+
+ /* Get the channel entry */
+ if (!channel) {
+
+ /* Get channel ID */
+ tmp = silc_channel_key_get_id(payload, &tmp_len);
+ id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
+ if (!id) {
+ channel = NULL;
+ goto out;
+ }
+
+ channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+ if (!channel) {
+ SILC_LOG_ERROR(("Received key for non-existent channel"));
+ goto out;
+ }
+ }
+
+ tmp = silc_channel_key_get_key(payload, &tmp_len);
+ if (!tmp) {
+ channel = NULL;
+ goto out;
+ }
+
+ cipher = silc_channel_key_get_cipher(payload, NULL);;
+ if (!cipher) {
+ channel = NULL;
+ goto out;
+ }
+
+ /* Remove old key if exists */
+ if (channel->key) {
+ memset(channel->key, 0, channel->key_len / 8);
+ silc_free(channel->key);
+ silc_cipher_free(channel->channel_key);
+ }
+
+ /* Create new cipher */
+ if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+ channel = NULL;
+ goto out;
+ }
+
+ /* Save the key */
+ channel->key_len = tmp_len * 8;
+ channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
+ memcpy(channel->key, tmp, tmp_len);
+ channel->channel_key->cipher->set_key(channel->channel_key->context,
+ tmp, tmp_len);
+
+ out:
+ if (id)
+ silc_free(id);
+ if (payload)
+ silc_channel_key_payload_free(payload);
+
+ return channel;
+}