+ SilcServerCommandContext cmd = query->cmd;
+ SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ SilcStatus status;
+ unsigned char *tmp;
+ SilcUInt32 len;
+ SilcBuffer idp;
+ int i, k, valid_count;
+ char nh[384], uh[384];
+ bool sent_reply = FALSE;
+
+ SILC_LOG_DEBUG(("Sending reply to query"));
+ SILC_LOG_DEBUG(("Sending %d clients", clients_count));
+ SILC_LOG_DEBUG(("Sending %d servers", servers_count));
+ SILC_LOG_DEBUG(("Sending %d channels", channels_count));
+ SILC_LOG_DEBUG(("Sending %d errors", query->errors_count));
+
+ status = SILC_STATUS_OK;
+
+ /* Send clients */
+ if (clients_count) {
+ SilcClientEntry entry;
+ SilcSocketConnection hsock;
+
+ /* Mark all invalid entries */
+ for (i = 0, valid_count = 0; i < clients_count; i++) {
+ entry = clients[i];
+ if (!entry)
+ continue;
+
+ switch (query->querycmd) {
+ case SILC_COMMAND_WHOIS:
+ if (!entry->nickname || !entry->username || !entry->userinfo ||
+ !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+ /* When querying by ID, every "unfound" entry must cause error */
+ if (query->ids)
+ silc_server_query_add_error_id(server, query,
+ SILC_STATUS_ERR_TIMEDOUT,
+ entry->id, SILC_ID_CLIENT);
+ clients[i] = NULL;
+ continue;
+ }
+ break;
+
+ case SILC_COMMAND_IDENTIFY:
+ if (!entry->nickname ||
+ !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+ /* When querying by ID, every "unfound" entry must cause error */
+ if (query->ids)
+ silc_server_query_add_error_id(server, query,
+ SILC_STATUS_ERR_TIMEDOUT,
+ entry->id, SILC_ID_CLIENT);
+ clients[i] = NULL;
+ continue;
+ }
+ break;
+
+ case SILC_COMMAND_WHOWAS:
+ if (!entry->nickname || !entry->username ||
+ entry->data.status & SILC_IDLIST_STATUS_REGISTERED) {
+ clients[i] = NULL;
+ continue;
+ }
+ break;
+ }
+ valid_count++;
+ }
+
+ /* Start processing found clients */
+ status = SILC_STATUS_OK;
+ if (valid_count > 1)
+ status = SILC_STATUS_LIST_START;
+
+ /* Now do the sending of valid entries */
+ k = 0;
+ for (i = 0; i < clients_count && valid_count; i++) {
+ entry = clients[i];
+ if (!entry)
+ continue;
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (valid_count > 1 && k == valid_count - 1
+ && !servers_count && !channels_count && !query->errors_count)
+ status = SILC_STATUS_LIST_END;
+ if (query->reply_count && k - 1 == query->reply_count)
+ status = SILC_STATUS_LIST_END;
+
+ SILC_LOG_DEBUG(("%s: client %s",
+ (status == SILC_STATUS_OK ? " OK" :
+ status == SILC_STATUS_LIST_START ? "START" :
+ status == SILC_STATUS_LIST_ITEM ? " ITEM" :
+ status == SILC_STATUS_LIST_END ? " END" :
+ " : "), entry->nickname));
+
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+ memset(nh, 0, sizeof(nh));
+
+ silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname));
+ if (!strchr(entry->nickname, '@')) {
+ silc_strncat(nh, sizeof(nh), "@", 1);
+ if (entry->servername) {
+ silc_strncat(nh, sizeof(nh), entry->servername,
+ strlen(entry->servername));
+ } else {
+ len = entry->router ? strlen(entry->router->server_name) :
+ strlen(server->server_name);
+ silc_strncat(nh, sizeof(nh), entry->router ?
+ entry->router->server_name :
+ server->server_name, len);
+ }
+ }
+
+ switch (query->querycmd) {
+
+ case SILC_COMMAND_WHOIS:
+ {
+ unsigned char idle[4], mode[4];
+ unsigned char *fingerprint, fempty[20], *attrs = NULL;
+ SilcBuffer channels, umode_list = NULL, tmpattrs = NULL;
+
+ memset(fempty, 0, sizeof(fempty));
+ memset(idle, 0, sizeof(idle));
+ memset(uh, 0, sizeof(uh));
+
+ silc_strncat(uh, sizeof(uh), entry->username,
+ strlen(entry->username));
+ if (!strchr(entry->username, '@') && entry->connection) {
+ hsock = entry->connection;
+ silc_strncat(uh, sizeof(uh), "@", 1);
+ len = strlen(hsock->hostname);
+ silc_strncat(uh, sizeof(uh), hsock->hostname, len);
+ }
+
+ 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 (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
+ fingerprint = entry->data.fingerprint;
+ else
+ fingerprint = NULL;
+
+ SILC_PUT32_MSB(entry->mode, mode);
+ if (entry->connection)
+ SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
+
+ /* If Requested Attribute were present, and we do not have the
+ attributes we will reply to them on behalf of the client. */
+ len = 0;
+ if (query->attrs) {
+ if (!entry->attrs && SILC_IS_LOCAL(entry)) {
+ tmpattrs = silc_server_query_reply_attrs(server, query, entry);
+ entry->attrs = silc_buffer_steal(tmpattrs, &len);
+ entry->attrs_len = len;
+ silc_buffer_free(tmpattrs);
+ }
+ attrs = entry->attrs;
+ len = entry->attrs_len;
+ }
+
+ /* Send command reply */
+ silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+ status, 0, ident, 10,
+ 2, idp->data, idp->len,
+ 3, nh, strlen(nh),
+ 4, uh, strlen(uh),
+ 5, entry->userinfo,
+ strlen(entry->userinfo),
+ 6, channels ? channels->data : NULL,
+ channels ? channels->len : 0,
+ 7, mode, 4,
+ 8, idle, 4,
+ 9, fingerprint,
+ fingerprint ? 20 : 0,
+ 10, umode_list ? umode_list->data :
+ NULL, umode_list ? umode_list->len :
+ 0, 11, attrs, len);
+
+ sent_reply = TRUE;
+
+ /* For now we always delete Requested Attributes, unless the client
+ is detached, in which case we don't want to reconstruct the
+ same data everytime */
+ if (!(entry->mode & SILC_UMODE_DETACHED) &&
+ !(entry->data.status & SILC_IDLIST_STATUS_NOATTR)) {
+ silc_free(entry->attrs);
+ entry->attrs = NULL;
+ }
+
+ if (channels)
+ silc_buffer_free(channels);
+ if (umode_list) {
+ silc_buffer_free(umode_list);
+ umode_list = NULL;
+ }
+ }
+ break;
+
+ case SILC_COMMAND_IDENTIFY:
+ if (!entry->username) {
+ silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+ status, 0, ident, 2,
+ 2, idp->data, idp->len,
+ 3, nh, strlen(nh));
+ sent_reply = TRUE;
+ } else {
+ silc_strncat(uh, sizeof(uh), entry->username,
+ strlen(entry->username));
+ if (!strchr(entry->username, '@') && entry->connection) {
+ hsock = entry->connection;
+ silc_strncat(uh, sizeof(uh), "@", 1);
+ len = strlen(hsock->hostname);
+ silc_strncat(uh, sizeof(uh), hsock->hostname, len);
+ }
+
+ silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+ status, 0, ident, 3,
+ 2, idp->data, idp->len,
+ 3, nh, strlen(nh),
+ 4, uh, strlen(uh));
+ sent_reply = TRUE;
+ }
+ break;
+
+ case SILC_COMMAND_WHOWAS:
+ silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
+ if (!strchr(entry->username, '@'))
+ silc_strncat(uh, sizeof(uh), "@-private-", 10);
+
+ /* Send command reply */
+ silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+ status, 0, ident, 4,
+ 2, idp->data, idp->len,
+ 3, nh, strlen(nh),
+ 4, uh, strlen(uh),
+ 5, entry->userinfo,
+ entry->userinfo ?
+ strlen(entry->userinfo) : 0);
+ sent_reply = TRUE;
+ break;
+ }
+
+ silc_buffer_free(idp);
+
+ if (status == SILC_STATUS_LIST_END)
+ break;
+ k++;
+ }
+
+ if (k == 0) {
+ /* Not one valid entry was found, send error. If nickname was used
+ in query send error based on that, otherwise the query->errors
+ already includes proper errors. */
+ if (query->nickname || (!query->ids && query->attrs))
+ silc_server_query_add_error(server, query, 1, 1,
+ SILC_STATUS_ERR_NO_SUCH_NICK);
+
+ /* Make sure some error is sent */
+ if (!query->errors_count && !servers_count && !channels_count)
+ silc_server_query_add_error(server, query, 2, 0,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ }
+ }
+
+ /* Send servers */
+ if (query->querycmd == SILC_COMMAND_IDENTIFY && servers_count) {
+ SilcServerEntry entry;
+
+ if (status == SILC_STATUS_OK && servers_count > 1)
+ status = SILC_STATUS_LIST_START;
+
+ k = 0;
+ for (i = 0; i < servers_count; i++) {
+ entry = servers[i];
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (servers_count == 1 && status != SILC_STATUS_OK && !channels_count &&
+ !query->errors_count)
+ status = SILC_STATUS_LIST_END;
+ if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
+ !query->errors_count)
+ status = SILC_STATUS_LIST_END;
+ if (query->reply_count && k - 1 == query->reply_count)
+ status = SILC_STATUS_LIST_END;
+
+ SILC_LOG_DEBUG(("%s: server %s",
+ (status == SILC_STATUS_OK ? " OK" :
+ status == SILC_STATUS_LIST_START ? "START" :
+ status == SILC_STATUS_LIST_ITEM ? " ITEM" :
+ status == SILC_STATUS_LIST_END ? " END" :
+ " : "),
+ entry->server_name ? entry->server_name : ""));
+
+ /* Send command reply */
+ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
+ status, 0, ident, 2,
+ 2, idp->data, idp->len,
+ 3, entry->server_name,
+ entry->server_name ?
+ strlen(entry->server_name) : 0);
+ silc_buffer_free(idp);
+ sent_reply = TRUE;
+
+ if (status == SILC_STATUS_LIST_END)
+ break;
+ k++;
+ }
+ }
+
+ /* Send channels */
+ if (query->querycmd == SILC_COMMAND_IDENTIFY && channels_count) {
+ SilcChannelEntry entry;
+
+ if (status == SILC_STATUS_OK && channels_count > 1)
+ status = SILC_STATUS_LIST_START;
+
+ k = 0;
+ for (i = 0; i < channels_count; i++) {
+ entry = channels[i];
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (channels_count == 1 && status != SILC_STATUS_OK &&
+ !query->errors_count)
+ status = SILC_STATUS_LIST_END;
+ if (channels_count > 1 && k == channels_count - 1 &&
+ !query->errors_count)
+ status = SILC_STATUS_LIST_END;
+ if (query->reply_count && k - 1 == query->reply_count)
+ status = SILC_STATUS_LIST_END;
+
+ SILC_LOG_DEBUG(("%s: channel %s",
+ (status == SILC_STATUS_OK ? " OK" :
+ status == SILC_STATUS_LIST_START ? "START" :
+ status == SILC_STATUS_LIST_ITEM ? " ITEM" :
+ status == SILC_STATUS_LIST_END ? " END" :
+ " : "),
+ entry->channel_name ? entry->channel_name : ""));
+
+ /* Send command reply */
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+ silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
+ status, 0, ident, 2,
+ 2, idp->data, idp->len,
+ 3, entry->channel_name,
+ entry->channel_name ?
+ strlen(entry->channel_name) : 0);
+ silc_buffer_free(idp);
+ sent_reply = TRUE;
+
+ if (status == SILC_STATUS_LIST_END)
+ break;
+ k++;
+ }
+ }
+
+ /* Send errors */
+ if (query->errors_count) {
+ int type;
+
+ if (status == SILC_STATUS_OK && query->errors_count > 1)
+ status = SILC_STATUS_LIST_START;
+
+ k = 0;
+ for (i = 0; i < query->errors_count; i++) {
+ idp = NULL;
+
+ /* Take error argument */
+ if (query->errors[i].type == 1) {
+ /* Take from sent arguments */
+ len = 0;
+ tmp = silc_argument_get_arg_type(cmd->args,
+ query->errors[i].index, &len);
+ type = 2;
+ } else if (query->errors[i].type == 2) {
+ /* No argument */
+ len = 0;
+ tmp = NULL;
+ type = 0;
+ } else if (!query->errors[i].id) {
+ /* Take from query->ids */
+ idp =
+ silc_id_payload_encode(query->ids[query->errors[i].index].id,
+ query->ids[query->errors[k].index].id_type);
+ tmp = idp->data;
+ len = idp->len;
+ type = 2;
+ } else {
+ /* Take added ID. */
+ idp = silc_id_payload_encode(query->errors[i].id,
+ query->errors[k].id_type);
+ tmp = idp->data;
+ len = idp->len;
+ type = 2;
+ }
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (query->errors_count == 1 && status != SILC_STATUS_OK)
+ status = SILC_STATUS_LIST_END;
+ if (query->errors_count > 1 && k == query->errors_count - 1)
+ status = SILC_STATUS_LIST_END;
+ if (query->reply_count && k - 1 == query->reply_count)
+ status = SILC_STATUS_LIST_END;
+
+ SILC_LOG_DEBUG(("%s: ERROR: %s (%d)",
+ (status == SILC_STATUS_OK ? " OK" :
+ status == SILC_STATUS_LIST_START ? "START" :
+ status == SILC_STATUS_LIST_ITEM ? " ITEM" :
+ status == SILC_STATUS_LIST_END ? " END" :
+ " : "),
+ silc_get_status_message(query->errors[i].error),
+ query->errors[i].error));
+
+#if 1 /* XXX Backwards compatibility. Remove in 1.0. */
+ if (query->errors[i].error == SILC_STATUS_ERR_NO_SUCH_NICK)
+ /* Send error */
+ silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+ (status == SILC_STATUS_OK ?
+ query->errors[i].error : status),
+ (status == SILC_STATUS_OK ?
+ 0 : query->errors[i].error), ident, 2,
+ type, tmp, len,
+ 3, tmp, len);
+ else
+#endif
+ /* Send error */
+ silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+ (status == SILC_STATUS_OK ?
+ query->errors[i].error : status),
+ (status == SILC_STATUS_OK ?
+ 0 : query->errors[i].error), ident, 1,
+ type, tmp, len);
+
+ silc_buffer_free(idp);
+ sent_reply = TRUE;
+
+ if (status == SILC_STATUS_LIST_END)
+ break;
+ k++;
+ }
+ }
+
+ if (!sent_reply)
+ SILC_LOG_ERROR(("BUG: Query did not send anything"));
+
+ /* Cleanup */
+ silc_server_query_free(query);
+}
+
+/* This routine is used to reply to Requested Attributes in WHOIS on behalf
+ of the client since we were unable to resolve them from the client.
+ Either client does not support Requested Attributes or isn't replying
+ to them like it should. */
+
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+ SilcServerQuery query,
+ SilcClientEntry client_entry)
+{
+ SilcBuffer buffer = NULL;
+ SilcAttribute attribute;
+ SilcAttributePayload attr;
+ SilcAttributeObjPk pk;
+ SilcAttributeObjService service;
+ unsigned char *tmp;
+ unsigned char sign[2048 + 1];
+ SilcUInt32 sign_len;
+
+ SILC_LOG_DEBUG(("Constructing Requested Attributes"));
+
+ /* Go through all requested attributes */
+ silc_dlist_start(query->attrs);
+ while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
+ attribute = silc_attribute_get_attribute(attr);
+ switch (attribute) {
+
+ case SILC_ATTRIBUTE_SERVICE:
+ /* Put SERVICE. Put only SILC service. */
+ memset(&service, 0, sizeof(service));
+ service.port = (server->config->server_info->primary ?
+ server->config->server_info->primary->port : SILC_PORT);
+ silc_strncat(service.address, sizeof(service.address),
+ server->server_name, strlen(server->server_name));
+ service.status = !(client_entry->mode & SILC_UMODE_DETACHED);
+ if (client_entry->connection)
+ service.idle = time(NULL) - client_entry->data.last_receive;
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ &service, sizeof(service));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_STATUS_MOOD:
+ /* Put STATUS_MOOD */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ (void *)
+ SILC_ATTRIBUTE_MOOD_NORMAL,
+ sizeof(SilcUInt32));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_STATUS_FREETEXT:
+ /* Put STATUS_FREETEXT. We just tell in the message that we are
+ replying on behalf of the client. */
+ tmp =
+ "This information was provided by the server on behalf of the user";
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ tmp, strlen(tmp));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_PREFERRED_CONTACT:
+ /* Put PREFERRED_CONTACT */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ (void *)
+ SILC_ATTRIBUTE_CONTACT_CHAT,
+ sizeof(SilcUInt32));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+ /* Put USER_PUBLIC_KEY */
+ if (client_entry->data.public_key) {
+ pk.type = "silc-rsa";
+ pk.data = silc_pkcs_public_key_encode(client_entry->data.public_key,
+ &pk.data_len);
+ buffer = silc_attribute_payload_encode(buffer, attribute, pk.data ?
+ SILC_ATTRIBUTE_FLAG_VALID :
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ &pk, sizeof(pk));
+ silc_free(pk.data);
+ if (!buffer)
+ return NULL;
+ break;
+ }
+
+ /* No public key available */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ NULL, 0);
+ if (!buffer)
+ return NULL;
+ break;
+
+ default:
+ /* Ignore SERVER_PUBLIC_KEY since we are going to put it anyway later */
+ if (attribute == SILC_ATTRIBUTE_SERVER_PUBLIC_KEY ||
+ attribute == SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE)
+ break;
+
+ /* For other attributes we cannot reply so mark it invalid */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ NULL, 0);
+ if (!buffer)
+ return NULL;
+ break;
+ }
+ }
+
+ /* Always put our public key. This assures that we send at least
+ something valid back always. */
+ pk.type = "silc-rsa";
+ pk.data = silc_pkcs_public_key_encode(server->public_key, &pk.data_len);
+ buffer = silc_attribute_payload_encode(buffer,
+ SILC_ATTRIBUTE_SERVER_PUBLIC_KEY,
+ pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ &pk, sizeof(pk));
+ silc_free(pk.data);
+ if (!buffer)
+ return NULL;