+Wed Oct 9 17:22:57 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
+
+ * Added silc_attribute_payload_encode_data to directly encode
+ the data into the attributes buffer. Renamed function
+ silc_attribute_payload_parse_list to silc_attribute_payload_parse.
+ Affected files are lib/silccore/silcattrs.[ch].
+
+ * silc_hash_table_find_foreach calls the foreach function now
+ once even if the nothing was found with context set to NULL.
+ Affected file lib/silcutil/silchashtable.[ch].
+
+ * Remove the RESOLVING flag from client entry after received
+ the Requested Attributes from the client. Fixed memory leak
+ in client entry freeing. Affected files silcd/server_query.c
+ and idlist.c.
+
+ * Added Requested Attributes support into SILC Client Library.
+ It is not able to send requested attributes in WHOIS command,
+ and also receive and process requested attributes. Added
+ silc_client_attribute_[add|del] and silc_client_attributes_get
+ functions. Added also `ignore_requested_attributes' to the
+ SilcClientParams to not use attributes in client. Affected
+ files are lib/silcclient/command.c, client_attrs.[ch],
+ silcclient.h, client_internal and client.[ch].
+
+ * Changed the silc_client_get_client_by_id to support Requested
+ Attributes, it takes them as argument now. Affected file
+ is lib/silcclient/silcclient.h, and idlist.c.
+
+ * Added -details option to WHOIS command in Irssi SILC Client
+ to support the requested attributes. By default it requests
+ all attributes. Affected file lib/silcclient/command.c and
+ irssi/doc/help/in/whois.in.
+
Tue Oct 8 17:58:28 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
* Add also SERVICE attribute for server-constructed attribute
TODO/bugs in Irssi SILC client
==============================
+ o Add support for the Requested Attributes in WHOIS (setting information
+ and displaying information). Do /SET MY_* settings to set them.
+
o Server password is not used at all. It is not possible to automize
the password authentication currently. The silc_get_auth_method
in irssi/src/silc/core/client_ops.c should find the connection's
TODO/bugs In SILC Client Library
================================
- o Add support for the <Requested Attributes> in WHOIS. Sending and
- reception should be added.
-
o The PRIVATE_MESSAGE_KEY packet is not handled (it is implemented
though). This should be added and perhaps new client operation
should be added to notify application that it was received and
TODO/bugs In SILC Server
========================
+ o If client's public key is saved in the server (and doing public key
+ authentication) then the hostname and the username information could
+ be taken from the public key. Should be a configuration option!
+
o Backup router related issues:
o Add special handling in router and server for "connection
each JOIN command will create and distribute the new channel key
to everybody on the channel (Fix this to 0.9.x).
- o If client's public key is saved in the server (and doing public key
- authentication) then the hostname and the username information could
- be taken from the public key. Should be a configuration option!
-
o Testing
@SYNTAX:whois@
-Shows whois information of the specified client.
-By default, this is aliased to /WI.
+Shows WHOIS information of the specified client. By default,
+this is aliased to /WI. If the -details option is used WHOIS
+will retrieve as detailed information about the user as possible.
+Using this option WHOIS MAY return following information about
+the user:
-See also: WHOWAS, CHANNEL
+ o User's public key
+ o Business card of the user (VCard)
+ o List of network services user is currently using
+ o Text and/or multimedia message (MIME, image, etc)
+ o User's personal mood
+ o User's preferred language
+ o User's preferred contact method (chat, email, etc)
+ o User's timezone
+ o User's geographical location
+ o Information about the device user is using (computer, PDA, etc)
+
+It is also possible to receive other information. Note that all
+users do not want to send these informations or may send only
+some of the information. It also possible that none of these
+informations is received.
+If you want to send your information in WHOIS you can set the
+information with SET command. See /SET MY.
+
+See also: WHOWAS, CHANNEL
/* SYNTAX: SILCOPER <username> [-pubkey] */
/* SYNTAX: TOPIC <channel> [<topic>] */
/* SYNTAX: UMODE +|-<modes> */
-/* SYNTAX: WHOIS <nickname>[@<hostname>] [<count>] */
+/* SYNTAX: WHOIS <nickname>[@<hostname>] [-details] [<count>] */
/* SYNTAX: WHOWAS <nickname>[@<hostname>] [<count>] */
/* SYNTAX: CLOSE <server> [<port>] */
/* SYNTAX: SHUTDOWN */
client_channel.c \
client_ftp.c \
client_resume.c \
+ client_attrs.c \
command.c \
command_reply.c \
idlist.c \
SilcBuffer buffer = packet->buffer;
SilcPacketType type = packet->type;
- SILC_LOG_DEBUG(("Parsing packet type %d", type));
+ SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(type)));
/* Parse the packet type */
switch(type) {
+
case SILC_PACKET_DISCONNECT:
silc_client_disconnected_by_server(client, sock, buffer);
break;
+
case SILC_PACKET_SUCCESS:
/*
* Success received for something. For now we can have only
if (sock->protocol)
silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
break;
+
case SILC_PACKET_FAILURE:
/*
* Failure received for some protocol. Set the protocol state to
*/
silc_client_process_failure(client, sock, packet);
break;
+
case SILC_PACKET_REJECT:
break;
*/
silc_client_channel_message(client, sock, packet);
break;
+
case SILC_PACKET_CHANNEL_KEY:
/*
* Received key for a channel. By receiving this key the client will be
*/
silc_client_private_message(client, sock, packet);
break;
+
case SILC_PACKET_PRIVATE_MESSAGE_KEY:
/*
* Received private message key
*/
break;
+ case SILC_PACKET_COMMAND:
+ /*
+ * Received command packet, a special case since normally client
+ * does not receive commands.
+ */
+ silc_client_command_process(client, sock, packet);
+ break;
+
case SILC_PACKET_COMMAND_REPLY:
/*
* Recived reply for a command
"protocol active, packet dropped."));
}
break;
+
case SILC_PACKET_KEY_EXCHANGE_2:
if (sock->protocol && sock->protocol->protocol &&
(sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
SilcUInt32 next_session_id;
SilcClientFtpSession active_session;
+ /* Requested Attributes */
+ SilcHashTable attrs;
+
/* Pointer back to the SilcClient. This object is passed to the application
and the actual client object is accesible through this pointer. */
SilcClient client;
--- /dev/null
+/*
+
+ client_attrs.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2002 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "client_internal.h"
+
+/* Add one attribute that was found from hash table */
+
+static void silc_client_attributes_process_foreach(void *key, void *context,
+ void *user_context)
+{
+ SilcAttribute attribute = (SilcAttribute)(SilcUInt32)key;
+ SilcAttributePayload attr = context;
+ SilcBuffer buffer = user_context;
+ const unsigned char *data;
+ SilcUInt32 data_len;
+
+ if (!context) {
+ SILC_LOG_DEBUG(("Attribute %d was not set", attribute));
+
+ /* USER_PUBLIC_KEY we have set earlier */
+ if (attribute == SILC_ATTRIBUTE_USER_PUBLIC_KEY)
+ return;
+
+ /* The requested attribute was not found */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ NULL, 0);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Attribute %d found", attribute));
+ data = silc_attribute_get_data(attr, &data_len);
+ buffer = silc_attribute_payload_encode_data(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ data, data_len);
+}
+
+/* Process list of attributes. Returns reply to the requested attributes. */
+
+SilcBuffer silc_client_attributes_process(SilcClient client,
+ SilcSocketConnection sock,
+ SilcDList attrs)
+{
+ SilcClientConnection conn = sock->user_data;
+ SilcBuffer buffer = NULL;
+ SilcAttribute attribute;
+ SilcAttributePayload attr;
+ SilcAttributeObjPk pk;
+ unsigned char sign[2048];
+ SilcUInt32 sign_len;
+
+ SILC_LOG_DEBUG(("Process Requested Attributes"));
+
+ /* If nothing is set by application assume that we don't want to use
+ attributes, ignore the request. */
+ if (!conn->attrs)
+ return NULL;
+
+ /* Always put our public key. */
+ pk.type = "silc-rsa";
+ pk.data = silc_pkcs_public_key_encode(client->public_key, &pk.data_len);
+ buffer = silc_attribute_payload_encode(buffer,
+ SILC_ATTRIBUTE_USER_PUBLIC_KEY,
+ pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ &pk, sizeof(pk));
+ silc_free(pk.data);
+
+ /* Go through all requested attributes */
+ silc_dlist_start(attrs);
+ while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
+ /* Put all attributes of this type */
+ attribute = silc_attribute_get_attribute(attr);
+
+ /* Ignore signature since we will compute it later */
+ if (attribute == SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE)
+ continue;
+
+ silc_hash_table_find_foreach(conn->attrs, (void *)(SilcUInt32)attribute,
+ silc_client_attributes_process_foreach,
+ buffer);
+ }
+
+ /* Finally compute the digital signature of all the data we provided. */
+ if (silc_pkcs_sign_with_hash(client->pkcs, client->internal->sha1hash,
+ buffer->data, buffer->len,
+ sign, &sign_len)) {
+ pk.type = NULL;
+ pk.data = sign;
+ pk.data_len = sign_len;
+ buffer =
+ silc_attribute_payload_encode(buffer,
+ SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ &pk, sizeof(pk));
+ }
+
+ return buffer;
+}
+
+static void silc_client_attribute_destruct(void *key, void *context,
+ void *user_context)
+{
+ silc_attribute_payload_free(context);
+}
+
+/* Add new attribute */
+
+SilcAttributePayload silc_client_attribute_add(SilcClient client,
+ SilcClientConnection conn,
+ SilcAttribute attribute,
+ void *object,
+ SilcUInt32 object_size)
+{
+ SilcAttributePayload attr;
+
+ attr = silc_attribute_payload_alloc(attribute, SILC_ATTRIBUTE_FLAG_VALID,
+ object, object_size);
+ if (!attr)
+ return NULL;
+
+ if (!conn->attrs)
+ conn->attrs = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
+ NULL, silc_client_attribute_destruct,
+ NULL, TRUE);
+ silc_hash_table_add(conn->attrs, (void *)(SilcUInt32)attribute, attr);
+ return attr;
+}
+
+/* Delete one attribute */
+
+bool silc_client_attribute_del(SilcClient client,
+ SilcClientConnection conn,
+ SilcAttributePayload attr)
+{
+ SilcAttribute attribute = silc_attribute_get_attribute(attr);
+ bool ret;
+
+ ret = silc_hash_table_del_by_context(conn->attrs,
+ (void *)(SilcUInt32)attribute, attr);
+
+ if (ret)
+ if (!silc_hash_table_count(conn->attrs)) {
+ silc_hash_table_free(conn->attrs);
+ conn->attrs = NULL;
+ }
+
+ return ret;
+}
+
+/* Return all attributes */
+
+const SilcHashTable silc_client_attributes_get(SilcClient client,
+ SilcClientConnection conn)
+{
+ return (const SilcHashTable)conn->attrs;
+}
+
+/* Construct a Requested Attributes buffer. If the `attribute' is zero (0)
+ then all attributes are requested. Additionally `attribute' and
+ all variable arguments can be one requested attribute. Always set
+ the last requested attribute to zero (0) to complete list of
+ requested attribute. */
+
+SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...)
+{
+ va_list va;
+ SilcBuffer buffer = NULL;
+
+ if (!attribute)
+ return silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
+ SILC_ATTRIBUTE_SERVICE,
+ SILC_ATTRIBUTE_STATUS_MOOD,
+ SILC_ATTRIBUTE_STATUS_FREETEXT,
+ SILC_ATTRIBUTE_STATUS_MESSAGE,
+ SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
+ SILC_ATTRIBUTE_PREFERRED_CONTACT,
+ SILC_ATTRIBUTE_TIMEZONE,
+ SILC_ATTRIBUTE_GEOLOCATION,
+ SILC_ATTRIBUTE_DEVICE_INFO,
+ SILC_ATTRIBUTE_USER_PUBLIC_KEY, 0);
+
+ va_start(va, attribute);
+ while (attribute) {
+ buffer = silc_attribute_payload_encode(buffer, attribute, 0, NULL, 0);
+ attribute = (SilcAttribute)va_arg(va, SilcUInt32);
+ }
+ va_end(va);
+
+ return buffer;
+}
SilcChannelClientResolve res = silc_calloc(1, sizeof(*res));
res->payload = payload;
res->channel_id = id;
- silc_client_get_client_by_id_resolve(client, conn, client_id,
+ silc_client_get_client_by_id_resolve(client, conn, client_id, NULL,
silc_client_channel_message_cb,
res);
payload = NULL;
/* Resolve the client */
silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
- silc_client_ftp_resolve_cb,
+ NULL, silc_client_ftp_resolve_cb,
silc_packet_context_dup(packet));
silc_free(remote_id);
}
SilcClientConnection conn,
SilcClientResumeSessionCallback callback,
void *context);
+SilcBuffer silc_client_attributes_process(SilcClient client,
+ SilcSocketConnection sock,
+ SilcDList attrs);
#endif
return;
silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+ NULL,
silc_client_key_agreement_resolve_cb,
silc_packet_context_dup(packet));
silc_free(remote_id);
SilcClient client = res->context;
SilcClientConnection conn = res->sock->user_data;
SilcClientID *client_id = res->packet;
- silc_client_get_client_by_id_resolve(client, conn, client_id, NULL, NULL);
+ silc_client_get_client_by_id_resolve(client, conn, client_id,
+ NULL, NULL, NULL);
silc_free(client_id);
silc_socket_free(res->sock);
silc_free(res);
}
/* Resolve the client info */
- silc_client_get_client_by_id_resolve(client, conn, remote_id,
+ silc_client_get_client_by_id_resolve(client, conn, remote_id, NULL,
silc_client_private_message_cb,
silc_packet_context_dup(packet));
return;
return;
silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+ NULL,
silc_client_private_message_key_cb,
silc_packet_context_dup(packet));
silc_free(remote_id);
{
SilcClientCommandContext cmd = (SilcClientCommandContext)context;
SilcClientConnection conn = cmd->conn;
- SilcBuffer buffer;
- unsigned char count[4];
+ SilcBuffer buffer, attrs = NULL;
+ unsigned char count[4], *tmp = NULL;
if (!cmd->conn) {
SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1, cmd->argv[1],
cmd->argv_lens[1]);
} else {
- int c = atoi(cmd->argv[2]);
- memset(count, 0, sizeof(count));
- SILC_PUT32_MSB(c, count);
+ if (!strcasecmp(cmd->argv[2], "-details"))
+ attrs = silc_client_attributes_request(0);
+
+ if (!attrs || cmd->argc > 3) {
+ int c = atoi(cmd->argc > 3 ? cmd->argv[3] : cmd->argv[2]);
+ SILC_PUT32_MSB(c, count);
+ tmp = count;
+ }
+
buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
- ++conn->cmd_ident, 2,
+ ++conn->cmd_ident, 3,
1, cmd->argv[1], cmd->argv_lens[1],
- 2, count, sizeof(count));
+ 2, tmp ? tmp : NULL, tmp ? 4 : 0,
+ 3, attrs ? attrs->data : NULL,
+ attrs ? attrs->len : 0);
}
silc_client_packet_send(cmd->client, cmd->conn->sock,
SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
}
+
+/**** Client side incoming command handling **********************************/
+
+void silc_client_command_process_whois(SilcClient client,
+ SilcSocketConnection sock,
+ SilcCommandPayload payload,
+ SilcArgumentPayload args);
+
+/* Client is able to receive some command packets even though they are
+ special case. Server may send WHOIS command to the client to retrieve
+ Requested Attributes information for WHOIS query the server is
+ processing. This function currently handles only the WHOIS command,
+ but if in the future for commands may arrive then this can be made
+ to support other commands too. */
+
+void silc_client_command_process(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcCommandPayload payload;
+ SilcCommand command;
+ SilcArgumentPayload args;
+
+ /* Get command payload from packet */
+ payload = silc_command_payload_parse(packet->buffer->data,
+ packet->buffer->len);
+ if (!payload) {
+ /* Silently ignore bad reply packet */
+ SILC_LOG_DEBUG(("Bad command packet"));
+ return;
+ }
+
+ /* Get arguments */
+ args = silc_command_get_args(payload);
+
+ /* Get the command */
+ command = silc_command_get(payload);
+ switch (command) {
+
+ case SILC_COMMAND_WHOIS:
+ /* Ignore everything if requested by application */
+ if (client->internal->params->ignore_requested_attributes)
+ break;
+
+ silc_client_command_process_whois(client, sock, payload, args);
+ break;
+
+ default:
+ break;
+ }
+
+ silc_command_payload_free(payload);
+}
+
+void silc_client_command_process_whois(SilcClient client,
+ SilcSocketConnection sock,
+ SilcCommandPayload payload,
+ SilcArgumentPayload args)
+{
+ SilcDList attrs;
+ unsigned char *tmp;
+ SilcUInt32 tmp_len;
+ SilcBuffer buffer, packet;
+
+ SILC_LOG_DEBUG(("Received WHOIS command"));
+
+ /* Try to take the Requested Attributes */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (!tmp)
+ return;
+
+ attrs = silc_attribute_payload_parse(tmp, tmp_len);
+ if (!attrs)
+ return;
+
+ /* Process requested attributes */
+ buffer = silc_client_attributes_process(client, sock, attrs);
+ if (!buffer) {
+ silc_attribute_payload_list_free(attrs);
+ return;
+ }
+
+ /* Send the attributes back */
+ packet =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+ SILC_STATUS_OK, 0,
+ silc_command_get_ident(payload),
+ 1, 11, buffer->data, buffer->len);
+ silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
+ NULL, 0, NULL, NULL, packet->data,
+ packet->len, TRUE);
+ silc_buffer_free(packet);
+ silc_buffer_free(buffer);
+}
SilcCommand command,
SilcUInt16 ident,
SilcUInt32 *callbacks_count);
+void silc_client_command_process(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
SILC_CLIENT_CMD_FUNC(whois);
SILC_CLIENT_CMD_FUNC(whowas);
SILC_CLIENT_CMD_FUNC(identify);
client_entry->fingerprint_len = fingerprint_len;
}
+ /* Take Requested Attributes if set. */
+ tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
+ if (tmp)
+ client_entry->attrs = silc_attribute_payload_parse(tmp, len);
+
client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
/* Notify application */
if (!cmd->callbacks_count && notify)
COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
has_channels ? &channels : NULL, mode, idle,
- fingerprint, has_user_modes ? &ch_user_modes : NULL));
+ fingerprint, has_user_modes ? &ch_user_modes : NULL,
+ client_entry->attrs));
}
/* Received reply for WHOIS command. This maybe called several times
void silc_client_get_client_by_id_resolve(SilcClient client,
SilcClientConnection conn,
SilcClientID *client_id,
+ SilcBuffer attributes,
SilcGetClientCallback completion,
void *context)
{
/* Send the command */
idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
- 1, 4, idp->data, idp->len);
+ 2, 3, attributes ? attributes->data : NULL,
+ attributes ? attributes->len : 0,
+ 4, idp->data, idp->len);
silc_buffer_free(idp);
/* Add pending callback */
SilcCipher send_key; /* Private message key for sending */
SilcCipher receive_key; /* Private message key for receiving */
SilcClientKeyAgreement ke; /* Current key agreement context or NULL */
+ SilcDList attrs; /* Requested Attributes (maybe NULL) */
SilcEntryStatus status; /* Status mask */
SilcHashTable channels; /* All channels client has joined */
unsigned char *key; /* Set only if appliation provided the
nickname string whenever it needs the true nickname. */
SilcNicknameFormatParse nickname_parse;
+ /* If this is set to TRUE then the client will ignore all incoming
+ Requested Attributes queries and does not reply anything back. This
+ usually leads into situation where server does not anymore send
+ the queries after seeing that client does not reply anything back.
+ If your application does not support Requested Attributes or you do
+ not want to use them set this to TRUE. See SilcAttribute and
+ silc_client_attribute_add for more information on attributes. */
+ bool ignore_requested_attributes;
+
} SilcClientParams;
/***/
* silc_client_get_client_by_id_resolve(SilcClient client,
* SilcClientConnection conn,
* SilcClientID *client_id,
+ * SilcBuffer attributes,
* SilcGetClientCallback completion,
* void *context);
*
* is its ID. When server returns the client information it will be
* cache and can be accessed locally at a later time.
*
+ * If the `attributes' is non-NULL then the buffer includes Requested
+ * Attributes which can be used to fetch very detailed information
+ * about the user. If it is NULL then only normal WHOIS query is
+ * made (for more information about attributes see SilcAttribute).
+ * Caller may create the `attributes' with silc_client_attributes_request
+ * function.
+ *
***/
void silc_client_get_client_by_id_resolve(SilcClient client,
SilcClientConnection conn,
SilcClientID *client_id,
+ SilcBuffer attributes,
SilcGetClientCallback completion,
void *context);
SilcClientConnection conn,
SilcUInt32 session_id);
+/****f* silcclient/SilcClientAPI/silc_client_attribute_add
+ *
+ * SYNOPSIS
+ *
+ * SilcAttributePayload
+ * silc_client_attribute_add(SilcClient client,
+ * SilcClientConnection conn,
+ * SilcAttribute attribute,
+ * void *object,
+ * SilcUInt32 object_size);
+ *
+ * DESCRIPTION
+ *
+ * Add new Requsted Attribute for WHOIS command to the client library.
+ * The `attribute' object indicated by `object' is added and allocated
+ * SilcAttributePayload is returned. The `object' must be of correct
+ * type and of correct size. See the SilcAttribute for object types
+ * for different attributes. You may also get all added attributes
+ * from the client with silc_client_attributes_get function.
+ *
+ * Requested Attributes are different personal information about the
+ * user, status information and other information which other users
+ * may query with WHOIS command. Application may set these so that
+ * if someone sends WHOIS query these attributes will be replied back
+ * to the sender. The library always puts the public key to the
+ * Requested Attributes, but if application wishes to add additional
+ * public keys (or certificates) it can be done with this interface.
+ * Library also always computes digital signature of the attributes
+ * automatically, so application does not need to do that.
+ *
+ ***/
+SilcAttributePayload silc_client_attribute_add(SilcClient client,
+ SilcClientConnection conn,
+ SilcAttribute attribute,
+ void *object,
+ SilcUInt32 object_size);
+
+/****f* silcclient/SilcClientAPI/silc_client_attribute_del
+ *
+ * SYNOPSIS
+ *
+ * bool silc_client_attribute_del(SilcClient client,
+ * SilcClientConnection conn,
+ * SilcAttributePayload attr);
+ *
+ * DESCRIPTION
+ *
+ * Delete the Requested Attribute indicated by `attribute' from the
+ * client. You may get all added attributes with the function
+ * silc_client_attributes_get. Returns TRUE if the attribute was
+ * found and deleted.
+ *
+ ***/
+bool silc_client_attribute_del(SilcClient client,
+ SilcClientConnection conn,
+ SilcAttributePayload attr);
+
+/****f* silcclient/SilcClientAPI/silc_client_attributes_get
+ *
+ * SYNOPSIS
+ *
+ * const SilcHashTable
+ * silc_client_attributes_get(SilcClient client,
+ * SilcClientConnection conn);
+ *
+ * DESCRIPTION
+ *
+ * Returns pointer to the SilcHashTable which includes all the added
+ * Requested Attributes. The caller must not free the hash table.
+ * The caller may use SilcHashTableList and silc_hash_table_list to
+ * traverse the table. Each entry in the hash table is one added
+ * SilcAttributePayload. It is possible to delete a attribute
+ * payload while traversing the table.
+ *
+ ***/
+const SilcHashTable silc_client_attributes_get(SilcClient client,
+ SilcClientConnection conn);
+
+/****f* silcclient/SilcClientAPI/silc_client_attributes_request
+ *
+ * SYNOPSIS
+ *
+ * SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...);
+ *
+ * DESCRIPTION
+ *
+ * Constructs a Requested Attributes buffer. If the `attribute' is zero (0)
+ * then all attributes are requested. Alternatively, `attribute' and
+ * all variable arguments can each be requested attribute. In this case
+ * the last must be set to zero (0) to complete the variable list of
+ * requested attributes. See SilcAttribute for all attributes.
+ * You can give the returned buffer as argument to for example
+ * silc_client_get_client_by_id_resolve function.
+ *
+ ***/
+SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...);
+
#include "client.h"
#include "command.h"
#include "command_reply.h"