+Sun Jan 28 16:19:49 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Fixed JOIN command on client library. Wrong number of arguments
+ used to crash the client.
+
+ * Added silc_server_channel_has_global function to check whether
+ channel has global users or not.
+
+ * Added silc_server_channel_has_local function to check whether channel
+ has locally connected clients on the channel.
+
+ * The silc_server_remove_from_one_channel now checks whether the
+ channel has global users or not after given client was removed from
+ the channel. It also checks whether the channel has local clients
+ on the channel anymore. If it does not have then the channel entry
+ is removed as it is not needed anymore.
+
+ * The silc_server_notify now checks on JOIN notify whether the joining
+ client is one of locally connected or global. If it is global then
+ the channel has now global users on the channel and that is marked
+ to the channel entry. Also, it now saves the global client to
+ global list who is joining and JOINs it to the channel. This is
+ for normal server, that is.
+
+ Changed silc_server_send_notify_on_channel,
+ silc_server_packet_relay_to_channel and
+ silc_server_packet_send_to_channel check if we are normal server
+ and client has router set (ie. global client) do not send the
+ message to that client, as it is already routed to our router.
+
+ * Implemented LEAVE notify type handling in silc_server_notify
+ function.
+
+ * Tested LEAVE command in router environment successfully. Tested
+ with two routers, two servers and two clients.
+
+ * Updated TODO.
+
+ * idlist_find_xxx_by_id routines now dumps the ID on the debug mode.
+
+ * Implemented SIGNOFF notify type handling in silc_server_notify
+ function.
+
+ * silc_server_remove_id now removes the client entry from all channels
+ it has joined and thusly sends SIGNOFF notify type.
+
+ * Rewrote the NAMES list generation in server by removing two excess
+ loops. The lists are created now inside one loop.
+
Sat Jan 27 22:34:56 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
* silc_server_remove_channel_user checks now also global list
New features TODO
=================
+ o Optimization in general. I have not focused to program optimized code
+ in many circumstances. It has been more important to get this up and
+ working. A lot must be optimized especially the ID cache. Instead of
+ using lists some hash tables should be used. The searching by ID and
+ such must be made a lot faster. If someone would like dedicate their
+ efforts purely to generate optimized code I'd be appreaciated.
+
o We should replace all short, int, long, unsigned short, unsigned int,
unsigned long with some pre-defined datatypes that really are what
we want on all platforms. int16, uint16, int32, uint32 etc. are
char *name_list = NULL, *n;
SilcBuffer client_id_list;
SilcBuffer client_mode_list;
+ SilcBuffer idp;
SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NAMES, cmd, 1, 2);
goto out;
}
- /* Assemble the name list now */
+ /* Assemble the lists now */
+
name_list = NULL;
- len = 0;
+ len = i = 0;
+ client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) *
+ silc_list_count(channel->user_list));
+ silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
+ client_mode_list =
+ silc_buffer_alloc(4 * silc_list_count(channel->user_list));
+ silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
+
silc_list_start(channel->user_list);
- i = 0;
while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ /* Nickname */
n = chl->client->nickname;
if (n) {
len2 = strlen(n);
len++;
i++;
}
- }
- if (!name_list)
- name_list = "";
-
- /* Assemble the Client ID list now */
- client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) *
- silc_list_count(channel->user_list));
- silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- SilcBuffer idp;
+ /* Client ID */
idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
- silc_buffer_format(client_id_list,
- SILC_STR_UI_XNSTRING(idp->data, idp->len),
- SILC_STR_END);
+ silc_buffer_put(client_id_list, idp->data, idp->len);
silc_buffer_pull(client_id_list, idp->len);
silc_buffer_free(idp);
- }
- silc_buffer_push(client_id_list,
- client_id_list->data - client_id_list->head);
- /* Assemble mode list */
- client_mode_list = silc_buffer_alloc(4 *
- silc_list_count(channel->user_list));
- silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ /* Client's mode on channel */
SILC_PUT32_MSB(chl->mode, client_mode_list->data);
silc_buffer_pull(client_mode_list, 4);
}
+ silc_buffer_push(client_id_list,
+ client_id_list->data - client_id_list->head);
silc_buffer_push(client_mode_list,
client_mode_list->data - client_mode_list->head);
+ if (!name_list)
+ name_list = "";
/* Send reply */
packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NAMES,
SILC_SERVER_CMD_REPLY(join, JOIN),
SILC_SERVER_CMD_REPLY(whois, WHOIS),
SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
+ SILC_SERVER_CMD_REPLY(names, NAMES),
{ NULL, 0 },
};
/* Add new channel */
SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
- (created == 0 ? "created" : "existing"), channel_name,
+ (created == 0 ? "existing" : "created"), channel_name,
silc_id_render(id, SILC_ID_CHANNEL)));
/* Add the channel to our local list. */
out:
silc_server_command_reply_free(cmd);
}
+
+SILC_SERVER_CMD_REPLY_FUNC(names)
+{
+ SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+ SilcServer server = cmd->server;
+ SilcCommandStatus status;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ COMMAND_CHECK_STATUS;
+
+}
SILC_SERVER_CMD_REPLY_FUNC(join);
SILC_SERVER_CMD_REPLY_FUNC(whois);
SILC_SERVER_CMD_REPLY_FUNC(identify);
+SILC_SERVER_CMD_REPLY_FUNC(names);
#endif
if (!id)
return NULL;
- SILC_LOG_DEBUG(("Finding server by ID"));
+ SILC_LOG_DEBUG(("Server ID (%s)",
+ silc_id_render(id, SILC_ID_SERVER)));
if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
SILC_ID_SERVER, &id_cache))
SilcIDCacheEntry id_cache = NULL;
SilcClientEntry client = NULL;
- SILC_LOG_DEBUG(("Finding client by nickname"));
+ SILC_LOG_DEBUG(("Client by nickname"));
if (server) {
if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
SilcClientEntry client = NULL;
unsigned char hash[32];
- SILC_LOG_DEBUG(("Finding client by hash"));
+ SILC_LOG_DEBUG(("Client by hash"));
silc_hash_make(md5hash, nickname, strlen(nickname), hash);
if (!id)
return NULL;
- SILC_LOG_DEBUG(("Finding client by ID"));
+ SILC_LOG_DEBUG(("Client ID (%s)",
+ silc_id_render(id, SILC_ID_CLIENT)));
if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id,
SILC_ID_CLIENT, &id_cache))
SilcIDCacheEntry id_cache = NULL;
SilcChannelEntry channel;
- SILC_LOG_DEBUG(("Finding channel by name"));
+ SILC_LOG_DEBUG(("Channel by name"));
if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
return NULL;
if (!id)
return NULL;
- SILC_LOG_DEBUG(("Finding channel by ID"));
+ SILC_LOG_DEBUG(("Channel ID (%s)",
+ silc_id_render(id, SILC_ID_CHANNEL)));
if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id,
SILC_ID_CHANNEL, &id_cache))
/* Received Remove Channel User packet to remove a user from a channel.
Routers notify other routers that user has left a channel. Client must
- not send this packet.. Normal server may send this packet but must not
+ not send this packet. Normal server may send this packet but must not
receive it. */
void silc_server_remove_channel_user(SilcServer server,
goto out;
}
- /* Remove from channel */
- silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+ /* Remove user from channel */
+ silc_server_remove_from_one_channel(server, sock, channel, client, TRUE);
out:
if (tmp1)
SilcNotifyType type;
SilcArgumentPayload args;
SilcChannelID *channel_id;
+ SilcClientID *client_id;
SilcChannelEntry channel;
+ SilcClientEntry client;
+ unsigned char *tmp;
+ unsigned int tmp_len;
SILC_LOG_DEBUG(("Start"));
/*
* Distribute the notify to local clients on the channel
*/
+ SILC_LOG_DEBUG(("JOIN notify"));
channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
if (!channel_id)
goto out;
}
- channel->global_users = TRUE;
+ /* Get client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp) {
+ silc_free(channel_id);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+ /* If the the client is not in local list we check global list (ie. the
+ channel will be global channel) and if it does not exist then create
+ entry for the client. */
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, NULL);
+ if (!client) {
+ SilcChannelClientEntry chl;
+
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client)
+ client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+ client_id, sock->user_data, sock);
+
+ /* The channel is global now */
+ channel->global_users = TRUE;
+
+ /* Now actually JOIN the global client to the channel */
+ chl = silc_calloc(1, sizeof(*chl));
+ chl->client = client;
+ chl->channel = channel;
+ silc_list_add(channel->user_list, chl);
+ silc_list_add(client->channels, chl);
+ } else {
+ silc_free(client_id);
+ }
/* Send to channel */
silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
break;
case SILC_NOTIFY_TYPE_LEAVE:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+ SILC_LOG_DEBUG(("LEAVE notify"));
+
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
+ if (!channel_id)
+ goto out;
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+
+ /* Get client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp) {
+ silc_free(channel_id);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+ /* Send to channel */
+ silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
+ packet->buffer->data,
+ packet->buffer->len, FALSE);
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, NULL);
+ if (!client) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
+
+ /* Remove the user from channel */
+ silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
break;
case SILC_NOTIFY_TYPE_SIGNOFF:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+ SILC_LOG_DEBUG(("SIGNOFF notify"));
+
+ /* Get client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, NULL);
+ if (!client)
+ goto out;
+ }
+ silc_free(client_id);
+
+ /* Remove the client from all channels */
+ silc_server_remove_from_channels(server, NULL, client);
+
+ /* Remove the client entry */
+ silc_idlist_del_client(server->global_list, client);
break;
/* Ignore rest notify types for now */
SilcIdType id_type;
void *id, *id_entry;
+ SILC_LOG_DEBUG(("Start"));
+
if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
server->server_type == SILC_SERVER ||
packet->src_id_type != SILC_ID_SERVER)
id_list = server->global_list;
/* Remove the ID */
- switch(id_type) {
+ switch (id_type) {
case SILC_ID_CLIENT:
id_entry = silc_idlist_find_client_by_id(id_list, (SilcClientID *)id,
NULL);
if (id_entry) {
+ /* Remove from channels */
+ silc_server_remove_from_channels(server, NULL, id_entry);
+
+ /* Remove the client entry */
silc_idlist_del_client(id_list, (SilcClientEntry)id_entry);
SILC_LOG_DEBUG(("Removed client id(%s) from [%s] %s",
if (server->server_type == SILC_ROUTER && !route)
continue;
+ if (server->server_type == SILC_SERVER && client->router)
+ continue;
+
/* Send to locally connected client */
if (client) {
continue;
}
- /* XXX Check client's mode on the channel. */
+ if (server->server_type == SILC_SERVER && client->router)
+ continue;
/* Get data used in packet header encryption, keys and stuff. */
sock = (SilcSocketConnection)client->connection;
continue;
}
+ if (server->server_type == SILC_SERVER && client->router)
+ continue;
+
/* Send to locally connected client */
if (c) {
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;
SilcSocketConnection sock);
void silc_server_free_sock_user_data(SilcServer server,
SilcSocketConnection sock);
+int silc_server_channel_has_global(SilcChannelEntry channel);
+int silc_server_channel_has_local(SilcChannelEntry channel);
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
SilcClientEntry client);
this packet. This packet maybe sent to entity that is
indirectly connected to the sender.
- When received and the replaced ID is Client ID the server or
+ When received and the removed ID is Client ID the server or
router must distribute SILC_NOTIFY_TYPE_SIGNOFF to the
local clients on the channels (if any) of the client whose
- ID was changed. However, the notify type must be sent only
+ ID was removed. However, the notify type must be sent only
once per client.
Payload of the packet: See section 2.3.25 Remove ID Payload
silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
1, cmd->argv[1], cmd->argv_lens[1],
2, idp->data, idp->len);
- else if (cmd->argc == 4)
+ else if (cmd->argc == 3)
/* XXX Buggy */
buffer =
silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
{
SilcIDCacheEntry id_cache;
+ SILC_LOG_DEBUG(("Finding client by ID (%s)",
+ silc_id_render(client_id, SILC_ID_CLIENT)));
+
/* Find ID from cache */
if (!silc_idcache_find_by_id_one(conn->client_cache, client_id,
SILC_ID_CLIENT, &id_cache)) {