From 57fe1d5d88e8687ac876aa3725028bd3343f5067 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Fri, 12 Apr 2002 12:34:36 +0000 Subject: [PATCH] updates. --- CHANGES | 19 +++ apps/irssi/src/silc/core/client_ops.c | 16 ++- apps/irssi/src/silc/core/silc-servers.c | 5 +- apps/silcd/command.c | 25 +++- apps/silcd/command_reply.c | 39 ++++-- apps/silcd/idlist.h | 1 + apps/silcd/packet_receive.c | 62 +++++++-- apps/silcd/server.c | 154 ++++++++++++++++++++-- apps/silcd/server.h | 10 +- doc/draft-riikonen-silc-commands-03.nroff | 24 ++-- lib/silcclient/client.c | 20 +-- lib/silcclient/client_notify.c | 4 +- lib/silcclient/client_resume.c | 47 ++++--- lib/silcclient/command_reply.c | 16 ++- lib/silcutil/silcutil.c | 22 ++++ lib/silcutil/silcutil.h | 18 +++ 16 files changed, 398 insertions(+), 84 deletions(-) diff --git a/CHANGES b/CHANGES index 99dde0ab..dcb48950 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,22 @@ +Fri Apr 12 10:17:51 EEST 2002 Pekka Riikonen + + * Defined that server receives WHOIS command reply for private + and secret channels too. Updated protocol specs and the + code in server. Affected file silcd/command.c. + + * Defined argument to WHOIS command + reply for returning user modes on the channels. The + channel list now doesn't include the user mode anymore but the + actual channel mode. Updated protocol specs and the code in + client and server. Affected files are silcd/command_reply.c, + silcd/command.c, silcd/server.c, irssi/src/silc/core/client_ops.c, + and lib/silcclient/command_reply.c. + + * Save the channels list in WHOIS command reply in normal server + so that WHOIS always shows joined channels also in normal + server and not just on router. Affected file is + silcd/command_reply.c. + Thu Apr 11 22:29:33 EEST 2002 Pekka Riikonen * Defined that server receives USERS command reply for private diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c index 664d625a..6bbf81a5 100644 --- a/apps/irssi/src/silc/core/client_ops.c +++ b/apps/irssi/src/silc/core/client_ops.c @@ -633,7 +633,7 @@ void silc_connect(SilcClient client, SilcClientConnection conn, { SILC_SERVER_REC *server = conn->context; - if (!server && status == SILC_CLIENT_CONN_ERROR) { + if (!server || status == SILC_CLIENT_CONN_ERROR) { silc_client_close_connection(client, conn); return; } @@ -856,7 +856,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, char buf[1024], *nickname, *username, *realname, *nick; unsigned char *fingerprint; SilcUInt32 idle, mode; - SilcBuffer channels; + SilcBuffer channels, user_modes; SilcClientEntry client_entry; if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { @@ -901,6 +901,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, mode = va_arg(vp, SilcUInt32); idle = va_arg(vp, SilcUInt32); fingerprint = va_arg(vp, unsigned char *); + user_modes = va_arg(vp, SilcBuffer); silc_parse_userfqdn(nickname, &nick, NULL); printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, @@ -911,16 +912,20 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, SILCTXT_WHOIS_REALNAME, realname); silc_free(nick); - if (channels) { + if (channels && user_modes) { + SilcUInt32 *umodes; SilcDList list = silc_channel_payload_parse_list(channels->data, channels->len); - if (list) { + if (list && silc_get_mode_list(user_modes, silc_dlist_count(list), + &umodes)) { SilcChannelPayload entry; + int i = 0; + memset(buf, 0, sizeof(buf)); silc_dlist_start(list); while ((entry = silc_dlist_get(list)) != SILC_LIST_END) { - char *m = silc_client_chumode_char(silc_channel_get_mode(entry)); SilcUInt32 name_len; + char *m = silc_client_chumode_char(umodes[i++]); char *name = silc_channel_get_name(entry, &name_len); if (m) @@ -933,6 +938,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, SILCTXT_WHOIS_CHANNELS, buf); silc_channel_payload_list_free(list); + silc_free(umodes); } } diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index 9ae0003b..841b8411 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -204,8 +204,7 @@ static void sig_connected(SILC_SERVER_REC *server) /* Try to read detached session data and use it if found. */ memset(¶ms, 0, sizeof(params)); memset(file, 0, sizeof(file)); - snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir(), - server->connrec->address, server->connrec->port); + snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir()); params.detach_data = silc_file_readfile(file, ¶ms.detach_data_len); /* Add connection to the client library */ @@ -239,7 +238,7 @@ static void sig_disconnected(SILC_SERVER_REC *server) if (server->conn && server->conn->sock != NULL) { silc_client_close_connection(silc_client, server->conn); - + /* SILC closes the handle */ g_io_channel_unref(net_sendbuffer_handle(server->handle)); net_sendbuffer_destroy(server->handle, FALSE); diff --git a/apps/silcd/command.c b/apps/silcd/command.c index dd8a43fe..184382ea 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -701,7 +701,7 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, 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; SilcUInt16 ident = silc_command_get_ident(cmd->payload); @@ -782,7 +782,12 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, 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; @@ -791,12 +796,13 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, SILC_PUT32_MSB(entry->mode, mode); - if (entry->connection) + if (entry->connection) { SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle); + } 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), @@ -807,7 +813,10 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, 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); @@ -816,6 +825,10 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd, silc_buffer_free(idp); if (channels) silc_buffer_free(channels); + if (umode_list) { + silc_buffer_free(umode_list); + umode_list = NULL; + } k++; } @@ -5062,7 +5075,7 @@ SILC_SERVER_CMD_FUNC(users) 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; diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index c524111b..13e6c8db 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -121,15 +121,15 @@ static char silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - unsigned char *tmp, *id_data; + unsigned char *tmp, *id_data, *umodes; char *nickname, *username, *realname, *servername = NULL; unsigned char *fingerprint; SilcClientID *client_id; SilcClientEntry client; + SilcIDCacheEntry cache = NULL; char global = FALSE; char *nick; - SilcUInt32 mode = 0, len, id_len, flen; - int expire = 0; + SilcUInt32 mode = 0, len, len2, id_len, flen; id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); nickname = silc_argument_get_arg_type(cmd->args, 3, &len); @@ -210,19 +210,34 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) client->data.status |= SILC_IDLIST_STATUS_RESOLVED; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; - /* If client is global and is not on any channel then add that we'll - expire the entry after a while. */ - if (global && !silc_hash_table_count(client->channels) && - server->server_type == SILC_SERVER) - expire = time(NULL) + 300; - /* Create new cache entry */ silc_idcache_add(global ? server->global_list->clients : server->local_list->clients, nick, client->id, - client, expire, NULL); + client, 0, &cache); silc_free(client_id); } + /* Save channel list if it was sent to us */ + if (server->server_type == SILC_SERVER) { + tmp = silc_argument_get_arg_type(cmd->args, 6, &len); + umodes = silc_argument_get_arg_type(cmd->args, 10, &len2); + if (tmp && umodes) { + SilcBufferStruct channels_buf, umodes_buf; + silc_buffer_set(&channels_buf, tmp, len); + silc_buffer_set(&umodes_buf, umodes, len2); + silc_server_save_user_channels(server, cmd->sock, client, &channels_buf, + &umodes_buf); + } else { + silc_server_save_user_channels(server, cmd->sock, client, NULL, NULL); + } + + if (cache) + /* If client is global and is not on any channel then add that we'll + expire the entry after a while. */ + if (global && !silc_hash_table_count(client->channels)) + cache->expire = time(NULL) + 300; + } + if (fingerprint && flen == sizeof(client->data.fingerprint)) memcpy(client->data.fingerprint, fingerprint, flen); @@ -939,6 +954,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join) silc_server_save_users_on_channel(server, cmd->sock, entry, client_id, client_id_list, client_mode_list, list_count); + entry->users_resolved = TRUE; out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN); @@ -1071,6 +1087,9 @@ SILC_SERVER_CMD_REPLY_FUNC(users) client_id_list, client_mode_list, list_count); + channel->global_users = silc_server_channel_has_global(channel); + channel->users_resolved = TRUE; + silc_buffer_free(client_id_list); silc_buffer_free(client_mode_list); diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index 888e509b..1d92d4c3 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -496,6 +496,7 @@ struct SilcChannelEntryStruct { unsigned long created; bool disabled; + bool users_resolved; }; /* diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index eddd848a..6bfe3089 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -2427,9 +2427,15 @@ void silc_server_new_channel(SilcServer server, silc_id_render(channel_id, SILC_ID_CHANNEL), sock->hostname)); - silc_idlist_add_channel(server->global_list, strdup(channel_name), - 0, channel_id, sock->user_data, NULL, NULL, 0); + channel = + silc_idlist_add_channel(server->global_list, strdup(channel_name), + 0, channel_id, sock->user_data, NULL, NULL, 0); + if (!channel) + return; + server->stat.channels++; + if (server->server_type == SILC_ROUTER) + channel->users_resolved = TRUE; } } else { /* The channel is coming from our server, thus it is in our cell @@ -2946,12 +2952,14 @@ void silc_server_resume_client(SilcServer server, return; } - /* Check that the client is detached */ - if (!(detached_client->mode & SILC_UMODE_DETACHED)) { + /* Check that the client is detached, and that we have other info too */ + if (!(detached_client->mode & SILC_UMODE_DETACHED) || + !silc_hash_table_count(detached_client->channels) || + !detached_client->nickname) { if (server->server_type == SILC_SERVER && !server->standalone) { /* The client info is being resolved. Reprocess this packet after receiving the reply to the query. */ - SILC_LOG_DEBUG(("Resolving client mode")); + SILC_LOG_DEBUG(("Resolving client info")); silc_server_get_client_resolve(server, client_id, TRUE, NULL); r = silc_calloc(1, sizeof(*r)); if (!r) @@ -3093,6 +3101,24 @@ void silc_server_resume_client(SilcServer server, client->nickname); } + /* Resolve users on those channels that client has joined but we + haven't resolved user list yet. */ + if (server->server_type == SILC_SERVER && !server->standalone) { + silc_hash_table_list(client->channels, &htl); + while (silc_hash_table_get(&htl, NULL, (void **)&chl)) { + channel = chl->channel; + SILC_LOG_DEBUG(("Resolving users for %s channel", + channel->channel_name)); + if (channel->disabled || !channel->users_resolved) { + silc_server_send_command(server, server->router->connection, + SILC_COMMAND_USERS, ++server->cmd_ident, + 1, 2, channel->channel_name, + strlen(channel->channel_name)); + } + } + silc_hash_table_list_reset(&htl); + } + /* Send the new client ID to the client. After this client may start receiving other packets, and may start sending packets too. */ silc_server_send_new_id(server, sock, FALSE, client_id, SILC_ID_CLIENT, @@ -3125,15 +3151,22 @@ void silc_server_resume_client(SilcServer server, /* Send some nice info to the client */ silc_server_send_connect_notifys(server, sock, client); - /* XXX normal server may not know about any joined channels!!! - Do this by saving the joined list in the resume_resolve callback. - Resolve it here with USERS per channel. */ - - /* Send all channel keys of channels the client has joined */ silc_hash_table_list(client->channels, &htl); while (silc_hash_table_get(&htl, NULL, (void **)&chl)) { + bool created = FALSE; channel = chl->channel; + + if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) + continue; + + /* If we don't have channel key, then create one */ + if (!channel->channel_key) { + if (!silc_server_create_channel_key(server, channel, 0)) + continue; + created = TRUE; + } + id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL); keyp = silc_channel_key_payload_encode(silc_id_get_len(channel->id, @@ -3148,7 +3181,14 @@ void silc_server_resume_client(SilcServer server, /* Send the key packet to client */ silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, keyp->data, keyp->len, FALSE); - channel->global_users = silc_server_channel_has_global(channel); + + if (created && server->server_type == SILC_SERVER && + !server->standalone) + silc_server_packet_send(server, server->router->connection, + SILC_PACKET_CHANNEL_KEY, 0, + keyp->data, keyp->len, FALSE); + + silc_buffer_free(keyp); } silc_hash_table_list_reset(&htl); diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 064ff386..87de942e 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -3015,6 +3015,9 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, server->stat.my_channels++; + if (server->server_type == SILC_ROUTER) + entry->users_resolved = TRUE; + return entry; } @@ -3079,6 +3082,9 @@ silc_server_create_new_channel_with_id(SilcServer server, server->stat.my_channels++; + if (server->server_type == SILC_ROUTER) + entry->users_resolved = TRUE; + return entry; } @@ -3968,6 +3974,113 @@ void silc_server_save_users_on_channel(SilcServer server, } } +/* 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); + } +} + /* Lookups route to the client indicated by the `id_data'. The connection object and internal data object is returned. Returns NULL if route could not be found to the client. If the `client_id' is specified then @@ -4060,7 +4173,10 @@ silc_server_get_client_route(SilcServer server, 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; @@ -4071,12 +4187,16 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server, 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); @@ -4085,23 +4205,37 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server, 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; } @@ -4135,9 +4269,11 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server, 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, diff --git a/apps/silcd/server.h b/apps/silcd/server.h index 74326d19..93ec1f49 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -208,6 +208,11 @@ void silc_server_save_users_on_channel(SilcServer server, SilcBuffer user_list, SilcBuffer mode_list, SilcUInt32 user_count); +void silc_server_save_user_channels(SilcServer server, + SilcSocketConnection sock, + SilcClientEntry client, + SilcBuffer channels, + SilcBuffer channels_user_modes); SilcSocketConnection silc_server_get_client_route(SilcServer server, unsigned char *id_data, @@ -216,7 +221,10 @@ silc_server_get_client_route(SilcServer server, SilcIDListData *idata, SilcClientEntry *client_entry); SilcBuffer silc_server_get_client_channel_list(SilcServer server, - SilcClientEntry client); + SilcClientEntry client, + bool get_private, + bool get_secret, + SilcBuffer *user_mode_list); SilcClientEntry silc_server_get_client_resolve(SilcServer server, SilcClientID *client_id, bool always_resolve, diff --git a/doc/draft-riikonen-silc-commands-03.nroff b/doc/draft-riikonen-silc-commands-03.nroff index 421013fe..5f76f8e4 100644 --- a/doc/draft-riikonen-silc-commands-03.nroff +++ b/doc/draft-riikonen-silc-commands-03.nroff @@ -261,13 +261,14 @@ List of all defined commands in SILC follows. Reply messages to the command: - Max Arguments: 9 + Max Arguments: 10 Arguments: (1) (2) (3) [@] (4) (5) (6) [] (7) [] (8) [] - (9) [] + (9) [] (10) This command may reply with several command reply messages to @@ -284,14 +285,19 @@ List of all defined commands in SILC follows. option were defined in the query there will be only many replies from the server. - The server may return the list of channels if the client has + The server returns the list of channels if the client has joined channels. In this case the list is list of Channel - Payloads. The Mode Mask in the Channel Payload (see [SILC2] and - section 2.3.2.3 for the Channel Payload) is the client's mode - on the channel. The list is encoded by adding the Channel - Payloads one after the other. - - The server may also send client's user mode, idle time, and the + Payloads. The Mode Mask in the Channel Payload is the channel's + mode. The list is encoded by adding the Channel Payloads one + after the other. Private and secret channels MUST NOT be sent, + except if the sender of this command is on those channels, or + the sender is server. The MUST also + be sent if client is joined channels. This list includes 32 bit + MSB first order values one after the other and each indicate + the user's mode on a channel. The order of these values MUST + be same as the channel order in the . + + The server also returns client's user mode, idle time, and the fingerprint of the client's public key. The is the binary hash digest of the public key. The fingerprint MUST NOT be sent if the server has not verified the proof of posession of diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 2619df26..becb0829 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -1543,15 +1543,17 @@ static void silc_client_resume_session_cb(SilcClient client, SILC_CLIENT_CONN_SUCCESS_RESUME : SILC_CLIENT_CONN_ERROR); - /* Issue INFO command to fetch the real server name and server - information and other stuff. */ - silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL, - silc_client_command_reply_info_i, 0, - ++conn->cmd_ident); - sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); - silc_client_command_send(client, conn, SILC_COMMAND_INFO, - conn->cmd_ident, 1, 2, sidp->data, sidp->len); - silc_buffer_free(sidp); + if (success) { + /* Issue INFO command to fetch the real server name and server + information and other stuff. */ + silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL, + silc_client_command_reply_info_i, 0, + ++conn->cmd_ident); + sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); + silc_client_command_send(client, conn, SILC_COMMAND_INFO, + conn->cmd_ident, 1, 2, sidp->data, sidp->len); + silc_buffer_free(sidp); + } } /* Processes the received new Client ID from server. Old Client ID is diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index 50aebb3c..7a48419f 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -482,7 +482,7 @@ void silc_client_notify_by_server(SilcClient client, tmp = silc_argument_get_arg_type(args, 3, NULL); if (tmp) { /* Protocol version 1.1 */ - char *tmp_nick; + char *tmp_nick = NULL; /* Check whether nickname changed at all. It is possible that nick change notify is received but nickname didn't changed, only the @@ -493,7 +493,7 @@ void silc_client_notify_by_server(SilcClient client, else tmp_nick = strdup(tmp); - if (!strcmp(tmp, tmp_nick)) { + if (tmp_nick && !strcmp(tmp, tmp_nick)) { /* Nickname didn't change. Update only the ID */ silc_idcache_del_by_context(conn->client_cache, client_entry); silc_free(client_entry->id); diff --git a/lib/silcclient/client_resume.c b/lib/silcclient/client_resume.c index 0d0d24ba..b0e08ed6 100644 --- a/lib/silcclient/client_resume.c +++ b/lib/silcclient/client_resume.c @@ -27,6 +27,14 @@ SILC_CLIENT_CMD_FUNC(resume_identify); SILC_CLIENT_CMD_FUNC(resume_cmode); SILC_CLIENT_CMD_FUNC(resume_users); +#define RESUME_CALL_COMPLETION(client, session, s) \ +do { \ + session->success = s; \ + silc_schedule_task_add(client->schedule, 0, \ + silc_client_resume_call_completion, session, \ + 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); \ +} while(0) + /* Generates the session detachment data. This data can be used later to resume back to the server. */ @@ -157,6 +165,17 @@ bool silc_client_process_detach_data(SilcClient client, return TRUE; } + +/* Resume session context */ +typedef struct { + SilcClient client; + SilcClientConnection conn; + SilcClientResumeSessionCallback callback; + void *context; + SilcUInt32 channel_count; + bool success; +} *SilcClientResumeSession; + /* Generic command reply callback */ SILC_CLIENT_CMD_REPLY_FUNC(resume) @@ -169,14 +188,14 @@ SILC_CLIENT_CMD_REPLY_FUNC(resume) (*cmd->callback)(cmd->context, cmd); } -/* Resume session context */ -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientResumeSessionCallback callback; - void *context; - SilcUInt32 channel_count; -} *SilcClientResumeSession; +/* Completion calling callback */ + +SILC_TASK_CALLBACK(silc_client_resume_call_completion) +{ + SilcClientResumeSession session = context; + session->callback(session->client, session->conn, session->success, + session->context); +} /* This function is used to perform the resuming procedure after the client has connected to the server properly and has received the @@ -355,8 +374,7 @@ SILC_CLIENT_CMD_FUNC(resume_identify) err: session->channel_count--; if (!session->channel_count) - session->callback(session->client, session->conn, FALSE, - session->context); + RESUME_CALL_COMPLETION(client, session, FALSE); } /* Received cmode to channel entry */ @@ -418,8 +436,7 @@ SILC_CLIENT_CMD_FUNC(resume_cmode) err: session->channel_count--; if (!session->channel_count) - session->callback(session->client, session->conn, FALSE, - session->context); + RESUME_CALL_COMPLETION(client, session, FALSE); } /* Received users reply to a channel entry */ @@ -506,8 +523,7 @@ SILC_CLIENT_CMD_FUNC(resume_users) our channels */ session->channel_count--; if (!session->channel_count) - session->callback(session->client, session->conn, TRUE, - session->context); + RESUME_CALL_COMPLETION(client, session, TRUE); silc_free(channel_id); return; @@ -516,6 +532,5 @@ SILC_CLIENT_CMD_FUNC(resume_users) silc_free(channel_id); session->channel_count--; if (!session->channel_count) - session->callback(session->client, session->conn, FALSE, - session->context); + RESUME_CALL_COMPLETION(client, session, FALSE); } diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index 85609d33..415f6d49 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -195,7 +195,8 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, char *nickname = NULL, *username = NULL; char *realname = NULL; SilcUInt32 idle = 0, mode = 0; - SilcBufferStruct channels; + SilcBufferStruct channels, ch_user_modes; + bool has_channels = FALSE, has_user_modes = FALSE; unsigned char *fingerprint; SilcUInt32 fingerprint_len; @@ -223,8 +224,10 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, } tmp = silc_argument_get_arg_type(cmd->args, 6, &len); - if (tmp) + if (tmp) { silc_buffer_set(&channels, tmp, len); + has_channels = TRUE; + } tmp = silc_argument_get_arg_type(cmd->args, 7, &len); if (tmp) @@ -236,6 +239,12 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len); + tmp = silc_argument_get_arg_type(cmd->args, 10, &len); + if (tmp) { + silc_buffer_set(&ch_user_modes, tmp, len); + has_user_modes = TRUE; + } + /* Check if we have this client cached already. */ client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); if (!client_entry) { @@ -260,7 +269,8 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, /* Notify application */ if (!cmd->callback && notify) COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, - &channels, mode, idle, fingerprint)); + has_channels ? &channels : NULL, mode, idle, + fingerprint, has_user_modes ? &ch_user_modes : NULL)); } /* Received reply for WHOIS command. This maybe called several times diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c index 4ac00f38..e3778bb7 100644 --- a/lib/silcutil/silcutil.c +++ b/lib/silcutil/silcutil.c @@ -900,3 +900,25 @@ char *silc_get_input(const char *prompt, bool echo_off) return NULL; #endif /* SILC_UNIX */ } + +/* Return mode list */ + +bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count, + SilcUInt32 **list) +{ + int i; + + if (mode_list->len / 4 != mode_list_count) + return FALSE; + + *list = silc_calloc(mode_list_count, sizeof(**list)); + + for (i = 0; i < mode_list_count; i++) { + SILC_GET32_MSB((*list)[i], mode_list->data); + silc_buffer_pull(mode_list, 4); + } + + silc_buffer_push(mode_list, mode_list->data - mode_list->head); + + return TRUE; +} diff --git a/lib/silcutil/silcutil.h b/lib/silcutil/silcutil.h index d1374d90..ecaf5796 100644 --- a/lib/silcutil/silcutil.h +++ b/lib/silcutil/silcutil.h @@ -529,4 +529,22 @@ char *silc_get_username(); ***/ char *silc_get_real_name(); +/****f* silcutil/SilcUtilAPI/silc_get_mode_list + * + * SYNOPSIS + * + * bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count, + * SilcUInt32 **list); + * + * DESCRIPTION + * + * Returns modes from list of 32 bit MSB first order values that are + * encoded one after the other in the `mode_list' into the `list' + * array. The caller must free the returned list. Return FALSE if + * there is error parsing the list. + * + ***/ +bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count, + SilcUInt32 **list); + #endif /* !SILCUTIL_H */ -- 2.24.0