Do not save locally resolved hostname for local entry but take
[silc.git] / lib / silcclient / idlist.c
index 2c520d148031f9cc3a4611145d4dc9fcad805ec0..92b1dc37c5a354e07811583e941d76d6b8c79002 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  idlist.c
+  idlist.c 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 Pekka Riikonen
+  Copyright (C) 2001 - 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; either version 2 of the License, or
-  (at your option) any later version.
-  
+  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
@@ -19,7 +18,8 @@
 */
 /* $Id$ */
 
-#include "clientlibincludes.h"
+#include "silcincludes.h"
+#include "silcclient.h"
 #include "client_internal.h"
 
 /******************************************************************************
@@ -41,7 +41,7 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
                                               SilcClientConnection conn,
                                               const char *nickname,
                                               const char *format,
-                                              uint32 *clients_count)
+                                              SilcUInt32 *clients_count)
 {
   SilcIDCacheEntry id_cache;
   SilcIDCacheList list = NULL;
@@ -117,33 +117,24 @@ typedef struct {
   void *context;
   char *nickname;
   char *server;
-  bool found;
 } *GetClientInternal;
 
 SILC_CLIENT_CMD_FUNC(get_client_callback)
 {
   GetClientInternal i = (GetClientInternal)context;
   SilcClientEntry *clients;
-  uint32 clients_count;
+  SilcUInt32 clients_count;
 
   /* Get the clients */
   clients = silc_client_get_clients_local(i->client, i->conn,
                                          i->nickname, i->server,
                                          &clients_count);
   if (clients) {
-    i->completion(i->client, i->conn, clients, 
-                 clients_count, i->context);
-    i->found = TRUE;
+    i->completion(i->client, i->conn, clients, clients_count, i->context);
     silc_free(clients);
-  }
-}
-
-static void silc_client_get_client_destructor(void *context)
-{
-  GetClientInternal i = (GetClientInternal)context;
-
-  if (i->found == FALSE)
+  } else {
     i->completion(i->client, i->conn, NULL, 0, i->context);
+  }
 
   silc_free(i->nickname);
   silc_free(i->server);
@@ -202,7 +193,6 @@ void silc_client_get_clients(SilcClient client,
 
   /* Add pending callback */
   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
-                             silc_client_get_client_destructor,
                              silc_client_command_get_client_callback, 
                              (void *)i);
 
@@ -293,11 +283,11 @@ SilcClientEntry silc_idlist_get_client(SilcClient client,
 typedef struct {
   SilcClient client;
   SilcClientConnection conn;
-  uint32 list_count;
+  SilcUInt32 list_count;
   SilcBuffer client_id_list;
   SilcGetClientCallback completion;
   void *context;
-  int found;
+  int res_count;
 } *GetClientsByListInternal;
 
 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
@@ -306,19 +296,28 @@ SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
   SilcIDCacheEntry id_cache = NULL;
   SilcBuffer client_id_list = i->client_id_list;
   SilcClientEntry *clients = NULL;
-  uint32 clients_count = 0;
+  SilcUInt32 clients_count = 0;
+  bool found = FALSE;
   int c;
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (i->res_count) {
+    i->res_count--;
+    if (i->res_count)
+      return;
+  }
+
+  SILC_LOG_DEBUG(("Resolved all clients"));
+
   for (c = 0; c < i->list_count; c++) {
-    uint16 idp_len;
+    SilcUInt16 idp_len;
     SilcClientID *client_id;
 
     /* Get Client ID */
     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
     if (!client_id) {
       silc_buffer_pull(client_id_list, idp_len);
       continue;
@@ -334,27 +333,19 @@ SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
                             (clients_count + 1));
       clients[clients_count] = (SilcClientEntry)id_cache->context;
       clients_count++;
-      i->found = TRUE;
+      found = TRUE;
     }
 
     silc_free(client_id);
     silc_buffer_pull(client_id_list, idp_len);
   }
 
-  if (i->found) {
+  if (found) {
     i->completion(i->client, i->conn, clients, clients_count, i->context);
     silc_free(clients);
-  }
-}
-
-static void silc_client_get_clients_list_destructor(void *context)
-{
-  GetClientsByListInternal i = (GetClientsByListInternal)context;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (i->found == FALSE)
+  } else {
     i->completion(i->client, i->conn, NULL, 0, i->context);
+  }
 
   if (i->client_id_list)
     silc_buffer_free(i->client_id_list);
@@ -370,7 +361,7 @@ static void silc_client_get_clients_list_destructor(void *context)
 
 void silc_client_get_clients_by_list(SilcClient client,
                                     SilcClientConnection conn,
-                                    uint32 list_count,
+                                    SilcUInt32 list_count,
                                     SilcBuffer client_id_list,
                                     SilcGetClientCallback completion,
                                     void *context)
@@ -378,8 +369,9 @@ void silc_client_get_clients_by_list(SilcClient client,
   SilcIDCacheEntry id_cache = NULL;
   int i;
   unsigned char **res_argv = NULL;
-  uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
+  SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
   GetClientsByListInternal in;
+  bool wait_res = FALSE;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -392,44 +384,54 @@ void silc_client_get_clients_by_list(SilcClient client,
   in->context = context;
 
   for (i = 0; i < list_count; i++) {
-    uint16 idp_len;
+    SilcUInt16 idp_len;
     SilcClientID *client_id;
     SilcClientEntry entry;
+    bool ret;
 
     /* Get Client ID */
     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
     idp_len += 4;
-    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
     if (!client_id) {
       silc_buffer_pull(client_id_list, idp_len);
       continue;
     }
 
     /* Check if we have this client cached already. */
-    id_cache = NULL;
-    silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
-                                   NULL, NULL, 
-                                   silc_hash_client_id_compare, NULL,
-                                   &id_cache);
+    ret =
+      silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
+                                     NULL, NULL, 
+                                     silc_hash_client_id_compare, NULL,
+                                     &id_cache);
 
     /* If we don't have the entry or it has incomplete info, then resolve
        it from the server. */
-    entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
-    if (!id_cache || !entry->nickname) {
+    if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
+      entry = ret ? (SilcClientEntry)id_cache->context : NULL;
 
       if (entry) {
        if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-         entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+         /* Attach to this resolving and wait until it finishes */
+         silc_client_command_pending(
+                           conn, SILC_COMMAND_NONE, 
+                           entry->resolve_cmd_ident,
+                           silc_client_command_get_clients_list_callback, 
+                           (void *)in);
+         wait_res = TRUE;
+         in->res_count++;
+
          silc_free(client_id);
          silc_buffer_pull(client_id_list, idp_len);
          continue;
        }
 
        entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+       entry->resolve_cmd_ident = conn->cmd_ident + 1;
       }
 
       /* No we don't have it, query it from the server. Assemble argument
-        table that will be sent fr the IDENTIFY command later. */
+        table that will be sent for the IDENTIFY command later. */
       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
                              (res_argc + 1));
       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
@@ -446,6 +448,9 @@ void silc_client_get_clients_by_list(SilcClient client,
     silc_buffer_pull(client_id_list, idp_len);
   }
 
+  silc_buffer_push(client_id_list, client_id_list->data - 
+                  client_id_list->head);
+
   /* Query the client information from server if the list included clients
      that we don't know about. */
   if (res_argc) {
@@ -466,12 +471,10 @@ void silc_client_get_clients_by_list(SilcClient client,
 
     /* Process the applications request after reply has been received  */
     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
-                               silc_client_get_clients_list_destructor,
                                silc_client_command_get_clients_list_callback, 
                                (void *)in);
+    in->res_count++;
 
-    silc_buffer_push(client_id_list, client_id_list->data - 
-                    client_id_list->head);
     silc_buffer_free(res_cmd);
     silc_free(res_argv);
     silc_free(res_argv_lens);
@@ -479,8 +482,8 @@ void silc_client_get_clients_by_list(SilcClient client,
     return;
   }
 
-  silc_buffer_push(client_id_list, client_id_list->data - 
-                  client_id_list->head);
+  if (wait_res)
+    return;
 
   /* We have the clients in cache, get them and call the completion */
   silc_client_command_get_clients_list_callback((void *)in, NULL);
@@ -516,7 +519,6 @@ typedef struct {
   SilcClientID *client_id;
   SilcGetClientCallback completion;
   void *context;
-  int found;
 } *GetClientByIDInternal;
 
 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
@@ -525,23 +527,16 @@ SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
   SilcClientEntry entry;
 
   /* Get the client */
-  entry = silc_client_get_client_by_id(i->client, i->conn,
-                                      i->client_id);
+  entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
   if (entry) {
-    i->completion(i->client, i->conn, &entry, 1, i->context);
-    i->found = TRUE;
+    if (i->completion)
+      i->completion(i->client, i->conn, &entry, 1, i->context);
+  } else {
+    if (i->completion)
+      i->completion(i->client, i->conn, NULL, 0, i->context);
   }
-}
-
-static void silc_client_get_client_by_id_destructor(void *context)
-{
-  GetClientByIDInternal i = (GetClientByIDInternal)context;
 
-  if (i->found == FALSE)
-    i->completion(i->client, i->conn, NULL, 0, i->context);
-
-  if (i->client_id)
-    silc_free(i->client_id);
+  silc_free(i->client_id);
   silc_free(i);
 }
 
@@ -574,12 +569,11 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
   /* 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, 3, idp->data, idp->len);
+                          1, 4, idp->data, idp->len);
   silc_buffer_free(idp);
 
   /* Add pending callback */
   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
-                             silc_client_get_client_by_id_destructor,
                              silc_client_command_get_client_by_id_callback, 
                              (void *)i);
 }
@@ -598,7 +592,7 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
 SilcClientEntry
 silc_client_add_client(SilcClient client, SilcClientConnection conn,
                       char *nickname, char *username, 
-                      char *userinfo, SilcClientID *id, uint32 mode)
+                      char *userinfo, SilcClientID *id, SilcUInt32 mode)
 {
   SilcClientEntry client_entry;
   char *nick = NULL;
@@ -617,6 +611,8 @@ silc_client_add_client(SilcClient client, SilcClientConnection conn,
   client_entry->mode = mode;
   if (nick)
     client_entry->nickname = strdup(nick);
+  client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
+                                                NULL, NULL, NULL, TRUE);
 
   /* Format the nickname */
   silc_client_nickname_format(client, conn, client_entry);
@@ -628,6 +624,7 @@ silc_client_add_client(SilcClient client, SilcClientConnection conn,
     silc_free(client_entry->username);
     silc_free(client_entry->hostname);
     silc_free(client_entry->server);
+    silc_hash_table_free(client_entry->channels);
     silc_free(client_entry);
     return NULL;
   }
@@ -643,15 +640,20 @@ void silc_client_update_client(SilcClient client,
                               const char *nickname,
                               const char *username,
                               const char *userinfo,
-                              uint32 mode)
+                              SilcUInt32 mode)
 {
   char *nick = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!client_entry->username && username)
-    silc_parse_userfqdn(username, &client_entry->username, 
+  if ((!client_entry->username || !client_entry->hostname) && username) {
+    silc_free(client_entry->username);
+    silc_free(client_entry->hostname);
+    client_entry->username = NULL;
+    client_entry->hostname = NULL;
+    silc_parse_userfqdn(username, &client_entry->username,
                        &client_entry->hostname);
+  }
   if (!client_entry->realname && userinfo)
     client_entry->realname = strdup(userinfo);
   if (!client_entry->nickname && nickname) {
@@ -680,9 +682,11 @@ void silc_client_del_client_entry(SilcClient client,
   silc_free(client_entry->nickname);
   silc_free(client_entry->username);
   silc_free(client_entry->realname);
+  silc_free(client_entry->hostname);
   silc_free(client_entry->server);
   silc_free(client_entry->id);
   silc_free(client_entry->fingerprint);
+  silc_hash_table_free(client_entry->channels);
   if (client_entry->send_key)
     silc_cipher_free(client_entry->send_key);
   if (client_entry->receive_key)
@@ -700,16 +704,79 @@ bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
                            SilcClientEntry client_entry)
 {
   bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
+
+  /* Remove from channels */
+  silc_client_remove_from_channels(client, conn, client_entry);
+
+  /* Free the client entry data */
   silc_client_del_client_entry(client, conn, client_entry);
+
   return ret;
 }
 
+/* Add new channel entry to the ID Cache */
+
+SilcChannelEntry silc_client_add_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        const char *channel_name,
+                                        SilcUInt32 mode, 
+                                        SilcChannelID *channel_id)
+{
+  SilcChannelEntry channel;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  channel = silc_calloc(1, sizeof(*channel));
+  channel->channel_name = strdup(channel_name);
+  channel->id = channel_id;
+  channel->mode = mode;
+  channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
+                                            NULL, NULL, NULL, TRUE);
+
+  /* Put it to the ID cache */
+  if (!silc_idcache_add(conn->channel_cache, channel->channel_name, 
+                       (void *)channel->id, (void *)channel, 0, NULL)) {
+    silc_free(channel->channel_name);
+    silc_hash_table_free(channel->user_list);
+    silc_free(channel);
+    return NULL;
+  }
+
+  return channel;
+}
+
+/* Foreach callbcak to free all users from the channel when deleting a
+   channel entry. */
+
+static void silc_client_del_channel_foreach(void *key, void *context,
+                                           void *user_context)
+{
+  SilcChannelUser chu = (SilcChannelUser)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Remove the context from the client's channel hash table as that
+     table and channel's user_list hash table share this same context. */
+  silc_hash_table_del(chu->client->channels, chu->channel);
+  silc_free(chu);
+}
+
 /* Removes channel from the cache by the channel entry. */
 
 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
                             SilcChannelEntry channel)
 {
   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Free all client entrys from the users list. The silc_hash_table_free
+     will free all the entries so they are not freed at the foreach 
+     callback. */
+  silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
+                         NULL);
+  silc_hash_table_free(channel->user_list);
+
   silc_free(channel->channel_name);
   silc_free(channel->id);
   silc_free(channel->key);
@@ -728,6 +795,30 @@ bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
   return ret;
 }
 
+/* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
+   if the ID could not be changed. */
+
+bool silc_client_replace_channel_id(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcChannelEntry channel,
+                                   SilcChannelID *new_id)
+{
+  if (!new_id)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
+                 silc_id_render(channel->id, SILC_ID_CHANNEL)));
+  SILC_LOG_DEBUG(("New Channel ID id(%s)", 
+                 silc_id_render(new_id, SILC_ID_CHANNEL)));
+
+  silc_idcache_del_by_id(conn->channel_cache, channel->id);
+  silc_free(channel->id);
+  channel->id = new_id;
+  return silc_idcache_add(conn->channel_cache, channel->channel_name, 
+                         (void *)channel->id, (void *)channel, 0, NULL);
+
+}
+
 /* Finds entry for channel by the channel name. Returns the entry or NULL
    if the entry was not found. It is found only if the client is joined
    to the channel. */
@@ -747,6 +838,8 @@ SilcChannelEntry silc_client_get_channel(SilcClient client,
 
   entry = (SilcChannelEntry)id_cache->context;
 
+  SILC_LOG_DEBUG(("Found"));
+
   return entry;
 }
 
@@ -769,6 +862,8 @@ SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
 
   entry = (SilcChannelEntry)id_cache->context;
 
+  SILC_LOG_DEBUG(("Found"));
+
   return entry;
 }
 
@@ -778,7 +873,6 @@ typedef struct {
   SilcChannelID *channel_id;
   SilcGetChannelCallback completion;
   void *context;
-  int found;
 } *GetChannelByIDInternal;
 
 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
@@ -789,20 +883,12 @@ SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
   SILC_LOG_DEBUG(("Start"));
 
   /* Get the channel */
-  entry = silc_client_get_channel_by_id(i->client, i->conn,
-                                       i->channel_id);
+  entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
   if (entry) {
     i->completion(i->client, i->conn, &entry, 1, i->context);
-    i->found = TRUE;
-  }
-}
-
-static void silc_client_get_channel_by_id_destructor(void *context)
-{
-  GetChannelByIDInternal i = (GetChannelByIDInternal)context;
-
-  if (i->found == FALSE)
+  } else {
     i->completion(i->client, i->conn, NULL, 0, i->context);
+  }
 
   silc_free(i->channel_id);
   silc_free(i);
@@ -841,44 +927,10 @@ void silc_client_get_channel_by_id_resolve(SilcClient client,
 
   /* Add pending callback */
   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
-                             silc_client_get_channel_by_id_destructor,
                              silc_client_command_get_channel_by_id_callback, 
                              (void *)i);
 }
 
-/* Find channel entry by ID. This routine is used internally by the library. */
-
-SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
-                                              SilcClientConnection conn,
-                                              SilcChannelID *channel_id,
-                                              int query)
-{
-  SilcBuffer idp;
-  SilcChannelEntry channel;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  channel = silc_client_get_channel_by_id(client, conn, channel_id);
-  if (channel)
-    return channel;
-
-  if (query) {
-    /* Register our own command reply for this command */
-    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
-                                silc_client_command_reply_identify_i, 0,
-                                ++conn->cmd_ident);
-
-    /* Send the command */
-    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-    silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
-                            conn->cmd_ident,
-                            1, 5, idp->data, idp->len);
-    silc_buffer_free(idp);
-  }
-
-  return NULL;
-}
-
 /* Finds entry for server by the server name. */
 
 SilcServerEntry silc_client_get_server(SilcClient client,
@@ -919,6 +971,41 @@ SilcServerEntry silc_client_get_server_by_id(SilcClient client,
   return entry;
 }
 
+/* Add new server entry */
+
+SilcServerEntry silc_client_add_server(SilcClient client,
+                                      SilcClientConnection conn,
+                                      const char *server_name,
+                                      const char *server_info,
+                                      SilcServerID *server_id)
+{
+  SilcServerEntry server_entry;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  server_entry = silc_calloc(1, sizeof(*server_entry));
+  if (!server_entry || !server_id)
+    return NULL;
+
+  server_entry->server_id = server_id;
+  if (server_name)
+    server_entry->server_name = strdup(server_name);
+  if (server_info)
+    server_entry->server_info = strdup(server_info);
+
+  /* Add server to cache */
+  if (!silc_idcache_add(conn->server_cache, server_entry->server_name,
+                       server_entry->server_id, server_entry, 0, NULL)) {
+    silc_free(server_entry->server_id);
+    silc_free(server_entry->server_name);
+    silc_free(server_entry->server_info);
+    silc_free(server_entry);
+    return NULL;
+  }
+
+  return server_entry;
+}
+
 /* Removes server from the cache by the server entry. */
 
 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
@@ -932,6 +1019,34 @@ bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
   return ret;
 }
 
+/* Updates the `server_entry' with the new information sent as argument. */
+
+void silc_client_update_server(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcServerEntry server_entry,
+                              const char *server_name,
+                              const char *server_info)
+{
+  SILC_LOG_DEBUG(("Start"));
+
+  if (server_name && (!server_entry->server_name ||
+                     strcmp(server_entry->server_name, server_name))) {
+
+    silc_idcache_del_by_context(conn->server_cache, server_entry);
+    silc_free(server_entry->server_name);
+    server_entry->server_name = strdup(server_name);
+    silc_idcache_add(conn->server_cache, server_entry->server_name,
+                    server_entry->server_id,
+                    server_entry, 0, NULL);
+  }
+
+  if (server_info && (!server_entry->server_info ||
+                     strcmp(server_entry->server_info, server_info))) {
+    silc_free(server_entry->server_info);
+    server_entry->server_info = strdup(server_info);
+  }
+}
+
 /* Formats the nickname of the client specified by the `client_entry'.
    If the format is specified by the application this will format the
    nickname and replace the old nickname in the client entry. If the
@@ -944,8 +1059,10 @@ void silc_client_nickname_format(SilcClient client,
   char *cp;
   char *newnick = NULL;
   int i, off = 0, len;
+  bool freebase;
   SilcClientEntry *clients;
-  uint32 clients_count = 0;
+  SilcUInt32 clients_count = 0;
+  SilcClientEntry unformatted = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -965,10 +1082,15 @@ void silc_client_nickname_format(SilcClient client,
     return;
 
   len = 0;
-  for (i = 0; i < clients_count; i++)
+  freebase = TRUE;
+  for (i = 0; i < clients_count; i++) {
     if (clients[i]->valid && clients[i] != client_entry)
       len++;
-  if (!len)
+    if (clients[i]->valid && clients[i] != client_entry &&
+       !strcmp(clients[i]->nickname, client_entry->nickname))
+      freebase = FALSE;
+  }
+  if (!len || freebase)
     return;
 
   cp = client->internal->params->nickname_format;
@@ -993,6 +1115,9 @@ void silc_client_nickname_format(SilcClient client,
       if (!client_entry->hostname)
        break;
       len = strcspn(client_entry->hostname, ".");
+      i = strcspn(client_entry->hostname, "-");
+      if (i < len)
+        len = i;
       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
       memcpy(&newnick[off], client_entry->hostname, len);
       off += len;
@@ -1030,10 +1155,15 @@ void silc_client_nickname_format(SilcClient client,
        char tmp[6];
        int num, max = 1;
 
-       if (clients_count == 1)
+       if (clients_count == 1) {
+         unformatted = clients[0];
          break;
+       }
 
        for (i = 0; i < clients_count; i++) {
+         if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
+                          strlen(clients[i]->nickname)))
+           unformatted = clients[i];
          if (strncasecmp(clients[i]->nickname, newnick, off))
            continue;
          if (strlen(clients[i]->nickname) <= off)
@@ -1042,7 +1172,7 @@ void silc_client_nickname_format(SilcClient client,
          if (num > max)
            max = num;
        }
-       
+
        memset(tmp, 0, sizeof(tmp));
        snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
        len = strlen(tmp);
@@ -1065,6 +1195,12 @@ void silc_client_nickname_format(SilcClient client,
   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
   newnick[off] = 0;
 
+  /* If we are changing nickname of our local entry we'll enforce
+     that we will always get the unformatted nickname.  Give our
+     format number to the one that is not formatted now. */
+  if (unformatted && client_entry == conn->local_entry)
+    client_entry = unformatted;
+
   silc_free(client_entry->nickname);
   client_entry->nickname = newnick;
   silc_free(clients);