+ /* Try to get server name */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+ if (tmp) {
+ entry = silc_idlist_find_server_by_name(server->local_list,
+ tmp, TRUE, NULL);
+ if (!entry && check_global)
+ entry = silc_idlist_find_server_by_name(server->local_list,
+ tmp, TRUE, NULL);
+ if (entry) {
+ *servers = silc_realloc(*servers, sizeof(**servers) *
+ (*servers_count + 1));
+ (*servers)[(*servers_count)++] = entry;
+ }
+
+ if (!(*servers)) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_SERVER,
+ 3, tmp, strlen(tmp));
+ return FALSE;
+ }
+ }
+
+ /* Try to get channel name */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+ if (tmp) {
+ entry = silc_idlist_find_channel_by_name(server->local_list,
+ tmp, NULL);
+ if (!entry && check_global)
+ entry = silc_idlist_find_channel_by_name(server->local_list,
+ tmp, NULL);
+ if (entry) {
+ *channels = silc_realloc(*channels, sizeof(**channels) *
+ (*channels_count + 1));
+ (*channels)[(*channels_count)++] = entry;
+ }
+
+ if (!(*channels)) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+ 3, tmp, strlen(tmp));
+ return FALSE;
+ }
+ }
+
+ if (!(*clients) && !(*servers) && !(*channels)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ return FALSE;
+ }
+ } else {
+ /* Command includes ID, we must use that. Also check whether the command
+ has more than one ID set - take them all. */
+
+ /* Take all ID's from the command packet */
+ for (i = 0; i < argc; i++) {
+ void *id;
+
+ tmp = silc_argument_get_arg_type(cmd->args, i + 5, &len);
+ if (!tmp)
+ continue;
+
+ idp = silc_id_payload_parse_data(tmp, len);
+ if (!idp) {
+ silc_free(*clients);
+ silc_free(*servers);
+ silc_free(*channels);
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ return FALSE;
+ }
+
+ id = silc_id_payload_get_id(idp);
+
+ switch (silc_id_payload_get_type(idp)) {
+
+ case SILC_ID_CLIENT:
+ entry = (void *)silc_idlist_find_client_by_id(server->local_list,
+ id, TRUE, NULL);
+ if (!entry && check_global)
+ entry = (void *)silc_idlist_find_client_by_id(server->global_list,
+ id, TRUE, NULL);
+ if (entry) {
+ *clients = silc_realloc(*clients, sizeof(**clients) *
+ (*clients_count + 1));
+ (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
+ } else {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 2, tmp, len);
+ error = TRUE;
+ }
+
+ break;
+
+ case SILC_ID_SERVER:
+ entry = (void *)silc_idlist_find_server_by_id(server->local_list,
+ id, TRUE, NULL);
+ if (!entry && check_global)
+ entry = (void *)silc_idlist_find_server_by_id(server->global_list,
+ id, TRUE, NULL);
+ if (entry) {
+ *servers = silc_realloc(*servers, sizeof(**servers) *
+ (*servers_count + 1));
+ (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
+ } else {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+ 2, tmp, len);
+ error = TRUE;
+ }
+ break;
+
+ case SILC_ID_CHANNEL:
+ entry = (void *)silc_idlist_find_channel_by_id(server->local_list,
+ id, NULL);
+ if (!entry && check_global)
+ entry = (void *)silc_idlist_find_channel_by_id(server->global_list,
+ id, NULL);
+ if (entry) {
+ *channels = silc_realloc(*channels, sizeof(**channels) *
+ (*channels_count + 1));
+ (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
+ } else {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+ 2, tmp, len);
+ error = TRUE;
+ }
+ break;
+ }
+
+ silc_free(id);
+ }
+ }
+
+ if (error) {
+ silc_free(*clients);
+ silc_free(*servers);
+ silc_free(*channels);
+ return FALSE;
+ }
+
+ /* Get the max count of reply messages allowed */
+ tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+ if (tmp)
+ *count = atoi(tmp);
+ else
+ *count = 0;
+
+ return TRUE;
+}
+
+/* Checks that all mandatory fields in client entry are present. If not
+ then send WHOIS request to the server who owns the client. We use
+ WHOIS because we want to get as much information as possible at once. */
+
+static bool
+silc_server_command_identify_check_client(SilcServerCommandContext cmd,
+ SilcClientEntry *clients,
+ uint32 clients_count)
+{
+ SilcServer server = cmd->server;
+ SilcClientEntry entry;
+ SilcServerResolveContext resolve = NULL, r = NULL;
+ uint32 resolve_count = 0;
+ int i, k;
+ bool no_res = TRUE;
+
+ for (i = 0; i < clients_count; i++) {
+ entry = clients[i];
+
+ if (!entry || entry->nickname ||
+ !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
+ !entry->router)
+ continue;
+
+ /* We need to resolve this entry since it is not complete */
+
+ if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+ /* The entry is being resolved (and we are not the resolver) so attach
+ to the command reply and we're done with this one. */
+ silc_server_command_pending(server, SILC_COMMAND_NONE,
+ entry->resolve_cmd_ident,
+ silc_server_command_destructor,
+ silc_server_command_identify,
+ silc_server_command_dup(cmd));
+ no_res = FALSE;
+ } else {
+ if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+ /* We've resolved this and it still is not ready. We'll return
+ and are that this will be handled again after it is resolved. */
+ for (i = 0; i < resolve_count; i++) {
+ for (k = 0; k < r->res_argc; k++)
+ silc_free(r->res_argv[k]);
+ silc_free(r->res_argv);
+ silc_free(r->res_argv_lens);
+ silc_free(r->res_argv_types);
+ }
+ silc_free(resolve);
+ return FALSE;
+ } else {
+ /* We'll resolve this client */
+ SilcBuffer idp;
+
+ r = NULL;
+ for (k = 0; k < resolve_count; k++) {
+ if (resolve[k].router == entry->router) {
+ r = &resolve[k];
+ break;
+ }
+ }
+
+ if (!r) {
+ resolve = silc_realloc(resolve, sizeof(*resolve) *
+ (resolve_count + 1));
+ r = &resolve[resolve_count];
+ memset(r, 0, sizeof(*r));
+ r->router = entry->router;
+ r->ident = ++server->cmd_ident;
+ resolve_count++;
+ }
+
+ r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
+ (r->res_argc + 1));
+ r->res_argv_lens = silc_realloc(r->res_argv_lens,
+ sizeof(*r->res_argv_lens) *
+ (r->res_argc + 1));
+ r->res_argv_types = silc_realloc(r->res_argv_types,
+ sizeof(*r->res_argv_types) *
+ (r->res_argc + 1));
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+ r->res_argv[r->res_argc] = silc_calloc(idp->len,
+ sizeof(**r->res_argv));
+ memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
+ r->res_argv_lens[r->res_argc] = idp->len;
+ r->res_argv_types[r->res_argc] = r->res_argc + 3;
+ r->res_argc++;
+ silc_buffer_free(idp);
+
+ entry->resolve_cmd_ident = r->ident;
+ entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+ entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+ }
+ }
+ }
+
+ /* Do the resolving */
+ for (i = 0; i < resolve_count; i++) {
+ SilcBuffer res_cmd;
+
+ r = &resolve[i];
+
+ /* Send WHOIS request. We send WHOIS since we're doing the requesting
+ now anyway so make it a good one. */
+ res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
+ r->res_argc, r->res_argv,
+ r->res_argv_lens,
+ r->res_argv_types,
+ r->ident);
+ silc_server_packet_send(server, r->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ res_cmd->data, res_cmd->len, FALSE);
+
+ /* Reprocess this packet after received reply */
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ r->ident,
+ silc_server_command_destructor,
+ silc_server_command_identify,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+
+ silc_buffer_free(res_cmd);
+ for (k = 0; k < r->res_argc; k++)
+ silc_free(r->res_argv[k]);
+ silc_free(r->res_argv);
+ silc_free(r->res_argv_lens);
+ silc_free(r->res_argv_types);
+ no_res = FALSE;
+ }
+ silc_free(resolve);
+
+ return no_res;
+}
+
+static void
+silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
+ SilcClientEntry *clients,
+ uint32 clients_count,
+ SilcServerEntry *servers,
+ uint32 servers_count,
+ SilcChannelEntry *channels,
+ uint32 channels_count,
+ int count)
+{
+ SilcServer server = cmd->server;
+ int i, k, len;
+ SilcBuffer packet, idp;
+ SilcCommandStatus status;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+ char nh[256], uh[256];
+ SilcSocketConnection hsock;
+
+ status = SILC_STATUS_OK;
+
+ if (clients) {
+ SilcClientEntry entry;
+
+ len = 0;
+ for (i = 0; i < clients_count; i++)
+ if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+ len++;
+
+ if (len == 0 && clients_count) {
+ entry = clients[0];
+ if (entry->nickname) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_NICK,
+ 3, entry->nickname,
+ strlen(entry->nickname));
+ } else {
+ SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 2, idp->data, idp->len);
+ silc_buffer_free(idp);
+ }
+
+ return;
+ }
+
+ if (len > 1)
+ status = SILC_STATUS_LIST_START;
+
+ for (i = 0, k = 0; i < clients_count; i++) {
+ entry = clients[i];
+
+ if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+ if (clients_count == 1) {
+ SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 2, idp->data, idp->len);
+ silc_buffer_free(idp);
+ }
+ continue;
+ }
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (clients_count > 1 && k == clients_count - 1
+ && !servers_count && !channels_count)
+ status = SILC_STATUS_LIST_END;
+ if (count && k - 1 == count)
+ status = SILC_STATUS_LIST_END;
+ if (count && k - 1 > count)
+ break;
+
+ /* Send IDENTIFY reply */
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+
+ memset(uh, 0, sizeof(uh));
+ memset(nh, 0, sizeof(nh));
+
+ strncat(nh, entry->nickname, strlen(entry->nickname));
+ if (!strchr(entry->nickname, '@')) {
+ strncat(nh, "@", 1);
+ if (entry->servername) {
+ strncat(nh, entry->servername, strlen(entry->servername));
+ } else {
+ len = entry->router ? strlen(entry->router->server_name) :
+ strlen(server->server_name);
+ strncat(nh, entry->router ? entry->router->server_name :
+ server->server_name, len);
+ }
+ }
+
+ if (!entry->username) {
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+ status, ident, 2,
+ 2, idp->data, idp->len,
+ 3, nh, strlen(nh));
+ } else {
+ strncat(uh, entry->username, strlen(entry->username));
+ if (!strchr(entry->username, '@')) {
+ strncat(uh, "@", 1);
+ hsock = (SilcSocketConnection)entry->connection;
+ len = strlen(hsock->hostname);
+ strncat(uh, hsock->hostname, len);
+ }
+
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+ status, ident, 3,
+ 2, idp->data, idp->len,
+ 3, nh, strlen(nh),
+ 4, uh, strlen(uh));
+ }
+
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+ 0, packet->data, packet->len, FALSE);
+
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+
+ k++;
+ }
+ }
+
+ status = (status == SILC_STATUS_LIST_ITEM ?
+ SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
+
+ if (servers) {
+ SilcServerEntry entry;
+
+ if (status == SILC_STATUS_OK && servers_count > 1)
+ status = SILC_STATUS_LIST_START;
+
+ for (i = 0, k = 0; i < servers_count; i++) {
+ entry = servers[i];
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (servers_count > 1 && k == servers_count - 1 && !channels_count)
+ status = SILC_STATUS_LIST_END;
+ if (count && k - 1 == count)
+ status = SILC_STATUS_LIST_END;
+ if (count && k - 1 > count)
+ break;
+
+ /* Send IDENTIFY reply */
+ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+ if (entry->server_name) {
+ packet =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+ status, ident, 2,
+ 2, idp->data, idp->len,
+ 3, entry->server_name,
+ strlen(entry->server_name));
+ } else {
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+ status, ident, 1,
+ 2, idp->data, idp->len);
+ }
+
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+ 0, packet->data, packet->len, FALSE);
+
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+
+ k++;
+ }
+ }
+
+ status = (status == SILC_STATUS_LIST_ITEM ?
+ SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
+
+ if (channels) {
+ SilcChannelEntry entry;
+
+ if (status == SILC_STATUS_OK && channels_count > 1)
+ status = SILC_STATUS_LIST_START;
+
+ for (i = 0, k = 0; i < channels_count; i++) {
+ entry = channels[i];
+
+ if (k >= 1)
+ status = SILC_STATUS_LIST_ITEM;
+ if (channels_count > 1 && k == channels_count - 1)
+ status = SILC_STATUS_LIST_END;
+ if (count && k - 1 == count)
+ status = SILC_STATUS_LIST_END;
+ if (count && k - 1 > count)
+ break;
+
+ /* Send IDENTIFY reply */
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+ if (entry->channel_name) {
+ packet =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+ status, ident, 2,
+ 2, idp->data, idp->len,
+ 3, entry->channel_name,
+ strlen(entry->channel_name));
+ } else {
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+ status, ident, 1,
+ 2, idp->data, idp->len);
+ }
+
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+ 0, packet->data, packet->len, FALSE);
+
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+
+ k++;
+ }
+ }
+}
+
+static int
+silc_server_command_identify_process(SilcServerCommandContext cmd)
+{
+ SilcServer server = cmd->server;
+ uint32 count = 0;
+ int ret = 0;
+ SilcClientEntry *clients = NULL;
+ SilcServerEntry *servers = NULL;
+ SilcChannelEntry *channels = NULL;
+ uint32 clients_count = 0, servers_count = 0, channels_count = 0;
+
+ /* Protocol dictates that we must always send the received IDENTIFY request
+ to our router if we are normal server, so let's do it now unless we
+ are standalone. We will not send any replies to the client until we
+ have received reply from the router. */
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+ server->server_type == SILC_SERVER && !cmd->pending &&
+ !server->standalone) {
+ SilcBuffer tmpbuf;
+ uint16 old_ident;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ /* Send IDENTIFY command to our router */
+ silc_server_packet_send(server, (SilcSocketConnection)
+ server->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_IDENTIFY,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_destructor,
+ silc_server_command_identify,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+
+ silc_command_set_ident(cmd->payload, old_ident);
+
+ silc_buffer_free(tmpbuf);
+ ret = -1;
+ goto out;
+ }
+
+ /* We are ready to process the command request. Let's search for the
+ requested client and send reply to the requesting client. */
+
+ /* Parse the IDENTIFY request */
+ if (!silc_server_command_identify_parse(cmd,
+ &clients, &clients_count,
+ &servers, &servers_count,
+ &channels, &channels_count,
+ &count))
+ return 0;
+
+ /* Check that all mandatory fields are present and request those data
+ from the server who owns the client if necessary. */
+ if (clients && !silc_server_command_identify_check_client(cmd, clients,
+ clients_count)) {
+ ret = -1;
+ goto out;
+ }
+
+ /* Send the command reply to the client */
+ silc_server_command_identify_send_reply(cmd,
+ clients, clients_count,
+ servers, servers_count,
+ channels, channels_count,
+ count);
+
+ out:
+ silc_free(clients);
+ silc_free(servers);
+ silc_free(channels);
+
+ return ret;
+}
+
+SILC_SERVER_CMD_FUNC(identify)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ int ret = 0;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
+
+ ret = silc_server_command_identify_process(cmd);
+
+ if (!ret)
+ silc_server_command_free(cmd);
+}
+
+/* Checks string for bad characters and returns TRUE if they are found. */
+
+static int silc_server_command_bad_chars(char *nick)
+{
+ int i;
+
+ for (i = 0; i < strlen(nick); i++) {
+ if (!isascii(nick[i]))
+ return TRUE;
+ if (nick[i] == ' ') return TRUE;
+ if (nick[i] == '\\') return TRUE;
+ if (nick[i] == '\"') return TRUE;
+ if (nick[i] == '*') return TRUE;
+ if (nick[i] == '?') return TRUE;
+ if (nick[i] == ',') return TRUE;
+ if (nick[i] == '@') return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Server side of command NICK. Sets nickname for user. Setting
+ nickname causes generation of a new client ID for the client. The
+ new client ID is sent to the client after changing the nickname. */
+
+SILC_SERVER_CMD_FUNC(nick)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcServer server = cmd->server;
+ SilcBuffer packet, nidp, oidp;
+ SilcClientID *new_id;
+ char *nick;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+ int nickfail = 0;
+
+ if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
+
+ /* Check nickname */
+ nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
+ if (silc_server_command_bad_chars(nick) == TRUE) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+ SILC_STATUS_ERR_BAD_NICKNAME);
+ goto out;
+ }
+
+ if (strlen(nick) > 128)
+ nick[128] = '\0';
+
+ /* Create new Client ID */
+ while (!silc_id_create_client_id(cmd->server, cmd->server->id,
+ cmd->server->rng,
+ cmd->server->md5hash, nick,
+ &new_id)) {
+ nickfail++;
+ snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
+ }
+
+ /* Send notify about nickname change to our router. We send the new
+ ID and ask to replace it with the old one. If we are router the
+ packet is broadcasted. Send NICK_CHANGE notify. */
+ if (!server->standalone)
+ silc_server_send_notify_nick_change(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, client->id,
+ new_id);
+
+ oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+ /* Remove old cache entry */
+ silc_idcache_del_by_context(server->local_list->clients, client);
+
+ /* Free old ID */
+ silc_free(client->id);