server->stat.packets_sent++;
if (sock->outbuf->data - sock->outbuf->head)
- silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+ silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
ret = silc_server_packet_send_real(server, sock, TRUE);
void silc_server_free_client_data(SilcServer server,
SilcSocketConnection sock,
- SilcClientEntry client, char *signoff)
+ SilcClientEntry client,
+ int notify,
+ char *signoff)
{
FreeClientInternal i = silc_calloc(1, sizeof(*i));
- /* Send REMOVE_ID packet to routers. */
- if (!server->standalone && server->router)
+ /* If there is pending outgoing data for the client then purge it
+ to the network before removing the client entry. */
+ if (SILC_IS_OUTBUF_PENDING(sock) && (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+ server->stat.packets_sent++;
+
+ if (sock->outbuf->data - sock->outbuf->head)
+ silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+ silc_server_packet_send_real(server, sock, TRUE);
+
+ SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
+ }
+
+ /* 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,
SILC_ID_CLIENT_LEN, signoff);
/* Remove client from all channels */
- silc_server_remove_from_channels(server, sock, client, signoff);
+ if (notify)
+ silc_server_remove_from_channels(server, NULL, client,
+ TRUE, signoff, TRUE);
+ else
+ silc_server_remove_from_channels(server, NULL, client,
+ FALSE, NULL, FALSE);
/* We will not delete the client entry right away. We will take it
into history (for WHOWAS command) for 5 minutes */
case SILC_SOCKET_TYPE_CLIENT:
{
SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
- silc_server_free_client_data(server, sock, user_data, NULL);
+ silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
break;
}
case SILC_SOCKET_TYPE_SERVER:
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, NULL);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->local_list, client);
if (!silc_idcache_list_next(list, &id_cache))
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, NULL);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->global_list, client);
if (!silc_idcache_list_next(list, &id_cache))
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
SilcClientEntry client,
- char *signoff_message)
+ int notify,
+ char *signoff_message,
+ int keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
if (server->server_type == SILC_SERVER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
- if (channel->global_users)
+ if (notify && channel->global_users)
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message ? 2 : 1,
/* Send notify to channel about client leaving SILC and thus
the entire channel. */
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_SIGNOFF,
- signoff_message ? 2 : 1,
- clidp->data, clidp->len,
- signoff_message, signoff_message ?
- strlen(signoff_message) : 0);
+ if (notify)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_SIGNOFF,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
+
+ if (keygen) {
+ /* Re-generate channel key */
+ silc_server_create_channel_key(server, channel, 0);
+
+ /* Send the channel key to the channel. The key of course is not sent
+ to the client who was removed f rom the channel. */
+ silc_server_send_channel_key(server, client->connection, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
}
silc_buffer_free(clidp);
/* Notify other routers about the new channel. We send the packet
to our primary route. */
- if (broadcast && server->standalone == FALSE) {
+ if (broadcast && server->standalone == FALSE)
silc_server_send_new_channel(server, server->router->connection, TRUE,
- channel_name, entry->id, SILC_ID_CHANNEL_LEN);
- }
+ channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+ entry->mode);
server->stat.my_channels++;
/* Notify other routers about the new channel. We send the packet
to our primary route. */
- if (broadcast && server->standalone == FALSE) {
+ if (broadcast && server->standalone == FALSE)
silc_server_send_new_channel(server, server->router->connection, TRUE,
- channel_name, entry->id, SILC_ID_CHANNEL_LEN);
- }
+ channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+ entry->mode);
server->stat.my_channels++;
unsigned char channel_key[32], hash[32];
unsigned int len;
+ SILC_LOG_DEBUG(("Generating channel key"));
+
if (!channel->channel_key)
if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
return;
cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
name_len = strlen(channel->channel_name);
- len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+ len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
*channels =
silc_buffer_realloc(*channels,
(*channels ? (*channels)->truelen + len : len));
name_len),
SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_INT(0),
SILC_STR_END);
silc_buffer_pull(*channels, len);
}
}
}
+
+/* Lookups route to the client indicated by `id' client ID. The connection
+ object and internal data object is returned. Returns NULL if route
+ could not be found to the client. */
+
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+ unsigned char *id_data,
+ unsigned int id_len,
+ SilcIDListData *idata)
+{
+ SilcClientID *id;
+ SilcClientEntry client;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Decode destination Client ID */
+ id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
+ if (!id) {
+ SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+ return NULL;
+ }
+
+ /* If the destination belongs to our server we don't have to route
+ the packet anywhere but to send it to the local destination. */
+ client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ if (client) {
+ silc_free(id);
+
+ if (client && client->data.registered == FALSE)
+ return NULL;
+
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ /* We are of course in this case the client's router thus the real
+ "router" of the client is the server who owns the client. Thus
+ we will send the packet to that server. */
+ *idata = (SilcIDListData)client->router;
+ return client->router->connection;
+ }
+
+ /* Seems that client really is directly connected to us */
+ *idata = (SilcIDListData)client;
+ return client->connection;
+ }
+
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ silc_free(id);
+ *idata = (SilcIDListData)server->router;
+ return server->router->connection;
+ }
+
+ /* We are router and we will perform route lookup for the destination
+ and send the packet to fastest route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+ /* Check first that the ID is valid */
+ client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ if (client) {
+ SilcSocketConnection dst_sock;
+
+ dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+
+ silc_free(id);
+ *idata = (SilcIDListData)dst_sock->user_data;
+ return dst_sock;
+ }
+ }
+
+ silc_free(id);
+ return NULL;
+}
+
+/* Encodes and returns channel list of channels the `client' has joined.
+ Secret channels are not put to the list. */
+
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+ SilcClientEntry client)
+{
+ SilcBuffer buffer = NULL;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ unsigned char *cid;
+ unsigned short name_len;
+ int len;
+
+ silc_list_start(client->channels);
+ while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+ channel = chl->channel;
+
+ if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+ continue;
+
+ cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ name_len = strlen(channel->channel_name);
+
+ len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+ buffer = silc_buffer_realloc(buffer,
+ (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(SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_INT(chl->mode), /* Client's mode */
+ SILC_STR_END);
+ silc_buffer_pull(buffer, len);
+ silc_free(cid);
+ }
+
+ silc_buffer_push(buffer, buffer->data - buffer->head);
+ return buffer;
+}