updates.
[silc.git] / lib / silcclient / idlist.c
index e03b89df7015313146b9cbe43fbd2b7667067e4d..ffbcf29bda3b2b989750853ca57a2b0afc7d0559 100644 (file)
@@ -2,9 +2,9 @@
 
   idlist.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2001 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
@@ -74,8 +74,8 @@ static void silc_client_get_client_destructor(void *context)
 
 void silc_client_get_clients(SilcClient client,
                             SilcClientConnection conn,
-                            char *nickname,
-                            char *server,
+                            const char *nickname,
+                            const char *server,
                             SilcGetClientCallback completion,
                             void *context)
 {
@@ -93,38 +93,47 @@ void silc_client_get_clients(SilcClient client,
   snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
   silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
                          &ctx->argv_types, &ctx->argc, 2);
-  ctx->command->cb(ctx);
-      
-  i->cmd = ctx;
+
+  i->cmd = silc_client_command_dup(ctx);
   i->nickname = nickname ? strdup(nickname) : NULL;
   i->server = server ? strdup(server) : NULL;
   i->completion = completion;
   i->context = context;
 
+  /* Call the command */
+  ctx->command->cb(ctx, NULL);
+
   /* Add pending callback */
   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
-                             ++conn->cmd_ident, 
+                             conn->cmd_ident, 
                              silc_client_get_client_destructor,
                              silc_client_command_get_client_callback, 
                              (void *)i);
 }
 
-/* Same as above function but does not resolve anything from the server.
-   This checks local cache and returns all clients from the cache. */
+/* Same as silc_client_get_clients function but does not resolve anything
+   from the server. This checks local cache and returns all matching
+   clients from the local cache. If none was found this returns NULL.
+   The `nickname' is the real nickname of the client, and the `format'
+   is the formatted nickname to find exact match from multiple found
+   entries. The format must be same as given in the SilcClientParams
+   structure to the client library. If the `format' is NULL all found
+   clients by `nickname' are returned. */
 
 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
                                               SilcClientConnection conn,
-                                              char *nickname,
-                                              char *server,
+                                              const char *nickname,
+                                              const char *format,
                                               uint32 *clients_count)
 {
   SilcIDCacheEntry id_cache;
   SilcIDCacheList list = NULL;
   SilcClientEntry entry, *clients;
   int i = 0;
+  bool found = FALSE;
 
   /* Find ID from cache */
-  if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list))
+  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, &list))
     return NULL;
 
   if (!silc_idcache_list_count(list)) {
@@ -135,11 +144,12 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
   *clients_count = silc_idcache_list_count(list);
 
-  if (!server) {
+  if (!format) {
     /* Take all without any further checking */
     silc_idcache_list_first(list, &id_cache);
     while (id_cache) {
       clients[i++] = id_cache->context;
+      found = TRUE;
       if (!silc_idcache_list_next(list, &id_cache))
        break;
     }
@@ -148,9 +158,7 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
     silc_idcache_list_first(list, &id_cache);
     while (id_cache) {
       entry = (SilcClientEntry)id_cache->context;
-      
-      if (entry->server && 
-         strncasecmp(server, entry->server, strlen(server))) {
+      if (strcasecmp(entry->nickname, format)) {
        if (!silc_idcache_list_next(list, &id_cache)) {
          break;
        } else {
@@ -159,6 +167,7 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
       }
       
       clients[i++] = id_cache->context;
+      found = TRUE;
       if (!silc_idcache_list_next(list, &id_cache))
        break;
     }
@@ -167,6 +176,13 @@ SilcClientEntry *silc_client_get_clients_local(SilcClient client,
   if (list)
     silc_idcache_list_free(list);
 
+  if (!found) {
+    *clients_count = 0;
+    if (clients)
+      silc_free(clients);
+    return NULL;
+  }
+
   return clients;
 }
 
@@ -296,7 +312,7 @@ void silc_client_get_clients_by_list(SilcClient client,
                                    (res_argc + 1));
       res_argv[res_argc] = client_id_list->data;
       res_argv_lens[res_argc] = idp_len;
-      res_argv_types[res_argc] = res_argc + 3;
+      res_argv_types[res_argc] = res_argc + 5;
       res_argc++;
     }
 
@@ -336,7 +352,7 @@ void silc_client_get_clients_by_list(SilcClient client,
                   client_id_list->head);
 
   /* We have the clients in cache, get them and call the completion */
-  silc_client_command_get_clients_list_callback((void *)in);
+  silc_client_command_get_clients_list_callback((void *)in, NULL);
 }
 
 /* The old style function to find client entry. This is used by the
@@ -346,17 +362,17 @@ void silc_client_get_clients_by_list(SilcClient client,
 
 SilcClientEntry silc_idlist_get_client(SilcClient client,
                                       SilcClientConnection conn,
-                                      char *nickname,
-                                      char *server,
-                                      uint32 num,
-                                      int query)
+                                      const char *nickname,
+                                      const char *format,
+                                      bool query)
 {
   SilcIDCacheEntry id_cache;
   SilcIDCacheList list = NULL;
   SilcClientEntry entry = NULL;
 
   /* Find ID from cache */
-  if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list)) {
+  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, 
+                                &list)) {
   identify:
 
     if (query) {
@@ -375,7 +391,7 @@ SilcClientEntry silc_idlist_get_client(SilcClient client,
       snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
       silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
                              &ctx->argv_types, &ctx->argc, 2);
-      ctx->command->cb(ctx);
+      ctx->command->cb(ctx, NULL);
       
       if (list)
        silc_idcache_list_free(list);
@@ -385,7 +401,7 @@ SilcClientEntry silc_idlist_get_client(SilcClient client,
     return NULL;
   }
 
-  if (!server && !num) {
+  if (!format) {
     /* Take first found cache entry */
     if (!silc_idcache_list_first(list, &id_cache))
       goto identify;
@@ -394,22 +410,20 @@ SilcClientEntry silc_idlist_get_client(SilcClient client,
   } else {
     /* Check multiple cache entries for match */
     silc_idcache_list_first(list, &id_cache);
-    entry = (SilcClientEntry)id_cache->context;
-    
-    while (entry) {
-      if (server && entry->server && 
-         !strncasecmp(server, entry->server, strlen(server)))
-       break;
-      
-      if (num && entry->num == num)
-       break;
+    while (id_cache) {
+      entry = (SilcClientEntry)id_cache->context;
 
-      if (!silc_idcache_list_next(list, &id_cache)) {
-       entry = NULL;
-       break;
+      if (strcasecmp(entry->nickname, format)) {
+       if (!silc_idcache_list_next(list, &id_cache)) {
+         entry = NULL;
+         break;
+       } else {
+         entry = NULL;
+         continue;
+       }
       }
 
-      entry = (SilcClientEntry)id_cache->context;
+      break;
     }
 
     /* If match weren't found, request it */
@@ -515,6 +529,124 @@ void silc_client_get_client_by_id_resolve(SilcClient client,
                              (void *)i);
 }
 
+/* Creates new client entry and adds it to the ID cache. Returns pointer
+   to the new entry. */
+
+SilcClientEntry
+silc_client_add_client(SilcClient client, SilcClientConnection conn,
+                      char *nickname, char *username, 
+                      char *userinfo, SilcClientID *id, uint32 mode)
+{
+  SilcClientEntry client_entry;
+  char *nick = NULL;
+
+  /* Save the client infos */
+  client_entry = silc_calloc(1, sizeof(*client_entry));
+  client_entry->id = id;
+  silc_parse_userfqdn(nickname, &nick, &client_entry->server);
+  silc_parse_userfqdn(username, &client_entry->username, 
+                     &client_entry->hostname);
+  if (userinfo)
+    client_entry->realname = strdup(userinfo);
+  client_entry->mode = mode;
+  if (nick)
+    client_entry->nickname = strdup(nick);
+
+  /* Format the nickname */
+  silc_client_nickname_format(client, conn, client_entry);
+  
+  /* Add client to cache, the non-formatted nickname is saved to cache */
+  if (!silc_idcache_add(conn->client_cache, nick, client_entry->id, 
+                       (void *)client_entry, FALSE)) {
+    silc_free(client_entry->nickname);
+    silc_free(client_entry->username);
+    silc_free(client_entry->hostname);
+    silc_free(client_entry->server);
+    silc_free(client_entry);
+    return NULL;
+  }
+
+  return client_entry;
+}
+
+/* Updates the `client_entry' with the new information sent as argument. */
+
+void silc_client_update_client(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcClientEntry client_entry,
+                              const char *nickname,
+                              const char *username,
+                              const char *userinfo,
+                              uint32 mode)
+{
+  char *nick = NULL;
+
+  if (!client_entry->username && username)
+    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) {
+    silc_parse_userfqdn(nickname, &nick, &client_entry->server);
+    client_entry->nickname = strdup(nick);
+    silc_client_nickname_format(client, conn, client_entry);
+  }
+  client_entry->mode = mode;
+
+  if (nick) {
+    /* Remove the old cache entry and create a new one */
+    silc_idcache_del_by_context(conn->client_cache, client_entry);
+    silc_idcache_add(conn->client_cache, nick, client_entry->id, 
+                    client_entry, FALSE);
+  }
+}
+
+/* Deletes the client entry and frees all memory. */
+
+void silc_client_del_client_entry(SilcClient client, 
+                                 SilcClientEntry client_entry)
+{
+  silc_free(client_entry->nickname);
+  silc_free(client_entry->username);
+  silc_free(client_entry->realname);
+  silc_free(client_entry->server);
+  silc_free(client_entry->id);
+  if (client_entry->send_key)
+    silc_cipher_free(client_entry->send_key);
+  if (client_entry->receive_key)
+    silc_cipher_free(client_entry->receive_key);
+  silc_free(client_entry->key);
+  silc_free(client_entry);
+}
+
+/* Removes client from the cache by the client entry. */
+
+bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+                           SilcClientEntry client_entry)
+{
+  bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
+  silc_client_del_client_entry(client, client_entry);
+  return ret;
+}
+
+/* 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_free(channel->channel_name);
+  silc_free(channel->id);
+  silc_free(channel->key);
+  if (channel->channel_key)
+    silc_cipher_free(channel->channel_key);
+  if (channel->hmac)
+    silc_hmac_free(channel->hmac);
+  silc_client_del_channel_private_keys(client, conn, channel);
+  silc_free(channel);
+  return ret;
+}
+
 /* 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. */
@@ -534,3 +666,291 @@ SilcChannelEntry silc_client_get_channel(SilcClient client,
 
   return entry;
 }
+
+/* Finds entry for channel by the channel ID. Returns the entry or NULL
+   if the entry was not found. It is found only if the client is joined
+   to the channel. */
+
+SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id)
+{
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry entry;
+
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
+                                  &id_cache))
+    return NULL;
+
+  entry = (SilcChannelEntry)id_cache->context;
+
+  return entry;
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcChannelID *channel_id;
+  SilcGetChannelCallback completion;
+  void *context;
+  int found;
+} *GetChannelByIDInternal;
+
+SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
+{
+  GetChannelByIDInternal i = (GetChannelByIDInternal)context;
+  SilcChannelEntry entry;
+
+  /* Get the channel */
+  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)
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+
+  silc_free(i->channel_id);
+  silc_free(i);
+}
+
+/* Resolves channel information from the server by the channel ID. */
+
+void silc_client_get_channel_by_id_resolve(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcChannelID *channel_id,
+                                          SilcGetChannelCallback completion,
+                                          void *context)
+{
+  SilcBuffer idp;
+  GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
+
+  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+  silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
+                          ++conn->cmd_ident,
+                          1, 5, idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  i->client = client;
+  i->conn = conn;
+  i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
+  i->completion = completion;
+  i->context = context;
+      
+  /* 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;
+
+  channel = silc_client_get_channel_by_id(client, conn, channel_id);
+  if (channel)
+    return channel;
+
+  if (query) {
+    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+    silc_client_send_command(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,
+                                      SilcClientConnection conn,
+                                      char *server_name)
+{
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+
+  if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
+                                    &id_cache))
+    return NULL;
+
+  entry = (SilcServerEntry)id_cache->context;
+
+  return entry;
+}
+
+/* Finds entry for server by the server ID. */
+
+SilcServerEntry silc_client_get_server_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcServerID *server_id)
+{
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+
+  if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
+                                  &id_cache))
+    return NULL;
+
+  entry = (SilcServerEntry)id_cache->context;
+
+  return entry;
+}
+
+/* Removes server from the cache by the server entry. */
+
+bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
+                           SilcServerEntry server)
+{
+  bool ret = silc_idcache_del_by_context(conn->server_cache, server);
+  silc_free(server->server_name);
+  silc_free(server->server_info);
+  silc_free(server->server_id);
+  silc_free(server);
+  return ret;
+}
+
+/* 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
+   format string is not specified then this function has no effect. */
+
+void silc_client_nickname_format(SilcClient client, 
+                                SilcClientConnection conn,
+                                SilcClientEntry client_entry)
+{
+  char *cp;
+  char *newnick = NULL;
+  int i, off = 0, len;
+  SilcClientEntry *clients;
+  uint32 clients_count;
+
+  if (!client->params->nickname_format[0])
+    return;
+
+  if (!client_entry->nickname)
+    return;
+
+  /* Get all clients with same nickname. Do not perform the formatting
+     if there aren't any clients with same nickname unless the application
+     is forcing us to do so. */
+  clients = silc_client_get_clients_local(client, conn,
+                                         client_entry->nickname, NULL,
+                                         &clients_count);
+  if (!clients && !client->params->nickname_force_format)
+    return;
+
+  cp = client->params->nickname_format;
+  while (*cp) {
+    if (*cp == '%') {
+      cp++;
+      continue;
+    }
+
+    switch(*cp) {
+    case 'n':
+      /* Nickname */
+      if (!client_entry->nickname)
+       break;
+      len = strlen(client_entry->nickname);
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->nickname, len);
+      off += len;
+      break;
+    case 'h':
+      /* Stripped hostname */
+      if (!client_entry->hostname)
+       break;
+      len = strcspn(client_entry->hostname, ".");
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->hostname, len);
+      off += len;
+      break;
+    case 'H':
+      /* Full hostname */
+      if (!client_entry->hostname)
+       break;
+      len = strlen(client_entry->hostname);
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->hostname, len);
+      off += len;
+      break;
+    case 's':
+      /* Stripped server name */
+      if (!client_entry->server)
+       break;
+      len = strcspn(client_entry->server, ".");
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->server, len);
+      off += len;
+      break;
+    case 'S':
+      /* Full server name */
+      if (!client_entry->server)
+       break;
+      len = strlen(client_entry->server);
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->server, len);
+      off += len;
+      break;
+    case 'a':
+      /* Ascending number */
+      {
+       char tmp[6];
+       int num, max = 1;
+
+       if (clients_count == 1)
+         break;
+
+       for (i = 0; i < clients_count; i++) {
+         if (strncmp(clients[i]->nickname, newnick, off))
+           continue;
+         if (strlen(clients[i]->nickname) <= off)
+           continue;
+         num = atoi(&clients[i]->nickname[off]);
+         if (num > max)
+           max = num;
+       }
+       
+       memset(tmp, 0, sizeof(tmp));
+       snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
+       len = strlen(tmp);
+       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+       memcpy(&newnick[off], tmp, len);
+       off += len;
+      }
+      break;
+    default:
+      /* Some other character in the string */
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
+      memcpy(&newnick[off], cp, 1);
+      off++;
+      break;
+    }
+
+    cp++;
+  }
+
+  newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
+  newnick[off] = 0;
+
+  silc_free(client_entry->nickname);
+  client_entry->nickname = newnick;
+  silc_free(clients);
+}