Created SILC GIT repository.
[silc.git] / apps / irssi / src / silc / core / silc-queries.c
index ef0090e3215f8707207eb516444bde34ab97cc7e..18590f767e5d15defcb07a8726b9157fc07e7bf4 100644 (file)
@@ -1,41 +1,51 @@
 /*
- silc-queries.c : irssi
 
-    Copyright (C) 2000 Timo Sirainen
+  silc-queries.c
 
-    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.
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-    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.
+  Copyright (C) 2002 - 2008 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.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #include "module.h"
 #include "signals.h"
 #include "misc.h"
-
 #include "silc-queries.h"
+#include "settings.h"
+#include "levels.h"
+#include "modules.h"
+#include "commands.h"
+#include "misc.h"
 
-QUERY_REC *silc_query_create(SILC_SERVER_REC *server,
+#include "fe-common/core/printtext.h"
+#include "fe-common/core/fe-channels.h"
+#include "fe-common/core/keyboard.h"
+#include "fe-common/silc/module-formats.h"
+
+static void silc_query_attributes_print_final(bool success, void *context);
+static void silc_query_attributes_accept(const char *line, void *context);
+
+QUERY_REC *silc_query_create(const char *server_tag,
                             const char *nick, int automatic)
 {
   QUERY_REC *rec;
 
-  g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
   g_return_val_if_fail(nick != NULL, NULL);
 
   rec = g_new0(QUERY_REC, 1);
   rec->chat_type = SILC_PROTOCOL;
   rec->name = g_strdup(nick);
-  rec->server = (SERVER_REC *) server;
+  rec->server_tag = g_strdup(server_tag);
   query_init(rec, automatic);
   return rec;
 }
@@ -47,3 +57,1029 @@ void silc_queries_init(void)
 void silc_queries_deinit(void)
 {
 }
+
+/* ATTR command */
+
+void command_attr(const char *data, SILC_SERVER_REC *server,
+                 WI_ITEM_REC *item)
+{
+  char *tmp;
+  unsigned char **argv;
+  SilcUInt32 argc;
+  SilcUInt32 *argv_lens, *argv_types;
+  const char *sv;
+  bool allowed;
+
+  /* Now parse all arguments */
+  tmp = g_strconcat("ATTR", " ", data, NULL);
+  silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 3);
+  g_free(tmp);
+
+  if (argc == 1) {
+    /* Show all attributes */
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_ATTR_HEADER);
+
+    allowed = settings_get_bool("attr_allow");
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_ATTR_ALLOW,
+                      allowed ? "Yes" : "No");
+
+    sv = settings_get_str("attr_vcard");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_VCARD_FILE, sv);
+
+    sv = settings_get_str("attr_services");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_SERVICES, sv);
+
+    sv = settings_get_str("attr_status_mood");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MOOD, sv);
+
+    sv = settings_get_str("attr_status_text");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_TEXT, sv);
+
+    sv = settings_get_str("attr_status_message");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MESSAGE_FILE,
+                        sv);
+
+    sv = settings_get_str("attr_preferred_language");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_LANGUAGE,
+                        sv);
+
+    sv = settings_get_str("attr_preferred_contact");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_CONTACT,
+                        sv);
+
+    sv = settings_get_str("attr_geolocation");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_GEOLOCATION,
+                        sv);
+
+    sv = settings_get_str("attr_device_info");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_DEVICE_INFO,
+                        sv);
+
+    sv = settings_get_str("attr_public_keys");
+    if (sv && *sv)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_PUBLIC_KEYS,
+                        sv);
+
+    allowed = settings_get_bool("attr_timezone");
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_ATTR_TIMEZONE_ALLOW,
+                      allowed ? "Yes" : "No");
+
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_ATTR_FOOTER);
+    return;
+  }
+
+  if (argc < 3)
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  if (!strcasecmp(argv[1], "-del")) {
+    /* Delete attribute */
+    if (!strcasecmp(argv[2], "vcard")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_USER_INFO, NULL);
+      settings_set_str("attr_vcard", "");
+    } else if (!strcasecmp(argv[2], "services")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_SERVICE, NULL);
+      settings_set_str("attr_services", argv[2]);
+    } else if (!strcasecmp(argv[2], "status_mood")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_STATUS_MOOD, NULL);
+      settings_set_str("attr_status_mood", "");
+    } else if (!strcasecmp(argv[2], "status_text")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
+      settings_set_str("attr_status_text", "");
+    } else if (!strcasecmp(argv[2], "status_message")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
+      settings_set_str("attr_status_message", "");
+    } else if (!strcasecmp(argv[2], "preferred_language")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
+      settings_set_str("attr_preferred_language", "");
+    } else if (!strcasecmp(argv[2], "preferred_contact")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
+      settings_set_str("attr_preferred_contact", "");
+    } else if (!strcasecmp(argv[2], "timezone")) {
+      return;
+    } else if (!strcasecmp(argv[2], "geolocation")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_GEOLOCATION, NULL);
+      settings_set_str("attr_geolocation", "");
+    } else if (!strcasecmp(argv[2], "device_info")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_DEVICE_INFO, NULL);
+      settings_set_str("attr_device_info", "");
+    } else if (!strcasecmp(argv[2], "public_keys")) {
+      silc_client_attribute_del(silc_client, server->conn,
+                               SILC_ATTRIBUTE_USER_PUBLIC_KEY, NULL);
+      settings_set_str("attr_public_keys", "");
+    } else {
+      cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+    }
+    return;
+  }
+
+  /* Add new attribute */
+  if (!strcasecmp(argv[1], "allow")) {
+    allowed = !strcasecmp(argv[2], "ON") || !strcasecmp(argv[2], "YES");
+    settings_set_bool("attr_allow", allowed);
+  } else if (!strcasecmp(argv[1], "vcard")) {
+    settings_set_str("attr_vcard", argv[2]);
+  } else if (!strcasecmp(argv[1], "services")) {
+    settings_set_str("attr_services", argv[2]);
+  } else if (!strcasecmp(argv[1], "status_mood")) {
+    settings_set_str("attr_status_mood", argv[2]);
+  } else if (!strcasecmp(argv[1], "status_text")) {
+    settings_set_str("attr_status_text", argv[2]);
+  } else if (!strcasecmp(argv[1], "status_message")) {
+    settings_set_str("attr_status_message", argv[2]);
+  } else if (!strcasecmp(argv[1], "preferred_language")) {
+    settings_set_str("attr_preferred_language", argv[2]);
+  } else if (!strcasecmp(argv[1], "preferred_contact")) {
+    settings_set_str("attr_preferred_contact", argv[2]);
+  } else if (!strcasecmp(argv[1], "timezone")) {
+    allowed = !strcasecmp(argv[2], "ON") || !strcasecmp(argv[2], "YES");
+    settings_set_bool("attr_timezone", allowed);
+  } else if (!strcasecmp(argv[1], "geolocation")) {
+    settings_set_str("attr_geolocation", argv[2]);
+  } else if (!strcasecmp(argv[1], "device_info")) {
+    settings_set_str("attr_device_info", argv[2]);
+  } else if (!strcasecmp(argv[1], "public_keys")) {
+    settings_set_str("attr_public_keys", argv[2]);
+  } else {
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+  }
+
+  silc_query_attributes_default(silc_client, server->conn);
+}
+
+/* Put default attributes to client library */
+
+void silc_query_attributes_default(SilcClient client,
+                                  SilcClientConnection conn)
+{
+  char *tmp, **list, **entry;
+  const char *sv;
+  SilcUInt32 tmp_len, mask;
+  SilcAttributeObjService service;
+  SilcMime mime;
+  SilcAttributeObjGeo geo;
+  SilcAttributeObjDevice dev;
+  SilcAttributeObjPk pk;
+  SilcVCardStruct vcard;
+  bool allowed;
+
+  memset(&service, 0, sizeof(service));
+  memset(&geo, 0, sizeof(geo));
+  memset(&dev, 0, sizeof(dev));
+  memset(&pk, 0, sizeof(pk));
+  memset(&vcard, 0, sizeof(vcard));
+
+  allowed = settings_get_bool("attr_allow");
+  if (!allowed) {
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_USER_INFO, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_SERVICE, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_MOOD, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_TIMEZONE, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_GEOLOCATION, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_DEVICE_INFO, NULL);
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_USER_PUBLIC_KEY, NULL);
+    return;
+  }
+
+  sv = settings_get_str("attr_vcard");
+  if (sv && *sv) {
+    /* Put USER_INFO */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_USER_INFO, NULL);
+    tmp = silc_file_readfile(sv, &tmp_len, NULL);
+    if (tmp) {
+      tmp[tmp_len] = 0;
+      if (silc_vcard_decode(tmp, tmp_len, &vcard))
+       silc_client_attribute_add(silc_client, conn,
+                                 SILC_ATTRIBUTE_USER_INFO, (void *)&vcard,
+                                 sizeof(vcard));
+    }
+    silc_vcard_free(&vcard);
+    silc_free(tmp);
+  }
+
+  sv = settings_get_str("attr_services");
+  if (sv && *sv) {
+    /* Put SERVICE */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_SERVICE, NULL);
+    list = g_strsplit(sv, " ", -1);
+    for (entry = list; *entry != NULL; entry++) {
+      if (!strchr(*entry, ':'))
+       continue;
+      tmp = strchr(*entry, ':') + 1;
+      if (!tmp || !(*tmp))
+       continue;
+      memset(&service, 0, sizeof(service));
+      service.port = atoi(tmp);
+      *strchr(*entry, ':') = '\0';
+      silc_strncat(service.address, sizeof(service.address), *entry,
+                  strlen(*entry));
+      service.status = TRUE;
+      service.idle = 0;
+      silc_client_attribute_add(silc_client, conn,
+                               SILC_ATTRIBUTE_SERVICE, &service,
+                               sizeof(service));
+    }
+    g_strfreev(list);
+  }
+
+  sv = settings_get_str("attr_status_mood");
+  if (sv && *sv) {
+    /* Put STATUS_MOOD */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_MOOD, NULL);
+    mask = 0;
+    list = g_strsplit(sv, " ", -1);
+    for (entry = list; *entry != NULL; entry++) {
+      if (!strcasecmp(*entry, "NORMAL"))
+       mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
+      if (!strcasecmp(*entry, "HAPPY"))
+       mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
+      if (!strcasecmp(*entry, "SAD"))
+       mask |= SILC_ATTRIBUTE_MOOD_SAD;
+      if (!strcasecmp(*entry, "ANGRY"))
+       mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
+      if (!strcasecmp(*entry, "JEALOUS"))
+       mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
+      if (!strcasecmp(*entry, "ASHAMED"))
+       mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
+      if (!strcasecmp(*entry, "INVINCIBLE"))
+       mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
+      if (!strcasecmp(*entry, "INLOVE"))
+       mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
+      if (!strcasecmp(*entry, "SLEEPY"))
+       mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
+      if (!strcasecmp(*entry, "BORED"))
+       mask |= SILC_ATTRIBUTE_MOOD_BORED;
+      if (!strcasecmp(*entry, "EXCITED"))
+       mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
+      if (!strcasecmp(*entry, "ANXIOUS"))
+       mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
+    }
+    silc_client_attribute_add(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_MOOD,
+                             SILC_32_TO_PTR(mask),
+                             sizeof(SilcUInt32));
+    g_strfreev(list);
+  }
+
+  sv = settings_get_str("attr_status_text");
+  if (sv && *sv) {
+    /* Put STATUS_TEXT */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
+    silc_client_attribute_add(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_FREETEXT, (void *)sv,
+                             strlen(sv));
+  }
+
+  sv = settings_get_str("attr_status_message");
+  if (sv && *sv) {
+    /* Put STATUS_MESSAGE */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
+    tmp = silc_file_readfile(sv, &tmp_len, NULL);
+    if (tmp) {
+      mime = silc_mime_decode(NULL, tmp, tmp_len);
+      if (mime)
+       silc_client_attribute_add(silc_client, conn,
+                                 SILC_ATTRIBUTE_STATUS_MESSAGE, mime,
+                                 sizeof(*mime));
+    }
+    silc_free(tmp);
+  }
+
+  sv = settings_get_str("attr_preferred_language");
+  if (sv && *sv) {
+    /* Put PREFERRED_LANGUAGE */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
+    list = g_strsplit(sv, " ", -1);
+    for (entry = list; *entry != NULL; entry++) {
+      silc_client_attribute_add(silc_client, conn,
+                               SILC_ATTRIBUTE_PREFERRED_LANGUAGE, *entry,
+                               strlen(*entry));
+    }
+    g_strfreev(list);
+  }
+
+  sv = settings_get_str("attr_preferred_contact");
+  if (sv && *sv) {
+    /* Put PREFERRED_CONTACT */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
+    mask = 0;
+    list = g_strsplit(sv, " ", -1);
+    for (entry = list; *entry != NULL; entry++) {
+      if (!strcasecmp(*entry, "NONE"))
+       mask = 0;
+      if (!strcasecmp(*entry, "EMAIL"))
+       mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
+      if (!strcasecmp(*entry, "CALL"))
+       mask |= SILC_ATTRIBUTE_CONTACT_CALL;
+      if (!strcasecmp(*entry, "PAGE"))
+       mask |= SILC_ATTRIBUTE_CONTACT_PAGE;
+      if (!strcasecmp(*entry, "SMS"))
+       mask |= SILC_ATTRIBUTE_CONTACT_SMS;
+      if (!strcasecmp(*entry, "MMS"))
+       mask |= SILC_ATTRIBUTE_CONTACT_MMS;
+      if (!strcasecmp(*entry, "CHAT"))
+       mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
+      if (!strcasecmp(*entry, "VIDEO"))
+       mask |= SILC_ATTRIBUTE_CONTACT_VIDEO;
+    }
+    silc_client_attribute_add(silc_client, conn,
+                             SILC_ATTRIBUTE_PREFERRED_CONTACT,
+                             SILC_32_TO_PTR(mask),
+                             sizeof(SilcUInt32));
+    g_strfreev(list);
+  }
+
+  /* Put TIMEZONE */
+  allowed = settings_get_bool("attr_timezone");
+  silc_client_attribute_del(silc_client, conn,
+                           SILC_ATTRIBUTE_TIMEZONE, NULL);
+  if (allowed)
+    silc_client_attribute_add(silc_client, conn,
+                             SILC_ATTRIBUTE_TIMEZONE, "foo", 3);
+
+  sv = settings_get_str("attr_geolocation");
+  if (sv && *sv) {
+    /* Put GEOLOCATION */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_GEOLOCATION, NULL);
+    list = g_strsplit(sv, ":", -1);
+    for (entry = list; *entry != NULL; entry++) {
+      if (!geo.longitude) {
+       geo.longitude = *entry;
+       continue;
+      }
+      if (!geo.latitude) {
+       geo.latitude = *entry;
+       continue;
+      }
+      if (!geo.altitude) {
+       geo.altitude = *entry;
+       continue;
+      }
+      if (!geo.accuracy) {
+       geo.accuracy = *entry;
+       continue;
+      }
+    }
+    silc_client_attribute_add(silc_client, conn,
+                             SILC_ATTRIBUTE_GEOLOCATION, &geo,
+                             sizeof(geo));
+    g_strfreev(list);
+  }
+
+  sv = settings_get_str("attr_device_info");
+  if (sv && *sv) {
+    /* Put DEVICE_INFO */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_DEVICE_INFO, NULL);
+    allowed = FALSE;
+    list = g_strsplit(sv, ":", -1);
+    for (entry = list; *entry != NULL; entry++) {
+      if (!allowed) {
+       allowed = TRUE;
+       if (!strcasecmp(*entry, "COMPUTER"))
+         dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
+       if (!strcasecmp(*entry, "MOBILE_PHONE"))
+         dev.type = SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE;
+       if (!strcasecmp(sv, "PDA"))
+         dev.type = SILC_ATTRIBUTE_DEVICE_PDA;
+       if (!strcasecmp(sv, "TERMINAL"))
+         dev.type = SILC_ATTRIBUTE_DEVICE_TERMINAL;
+       continue;
+      }
+      if (!dev.manufacturer) {
+       dev.manufacturer = *entry;
+       continue;
+      }
+      if (!dev.version) {
+       dev.version = *entry;
+       continue;
+      }
+      if (!dev.model) {
+       dev.model = *entry;
+       continue;
+      }
+      if (!dev.language) {
+       dev.language = *entry;
+       continue;
+      }
+    }
+    silc_client_attribute_add(silc_client, conn,
+                             SILC_ATTRIBUTE_DEVICE_INFO, &dev,
+                             sizeof(dev));
+    g_strfreev(list);
+  }
+
+  sv = settings_get_str("attr_public_keys");
+  if (sv && *sv) {
+    /* Put USER_PUBLIC_KEY */
+    silc_client_attribute_del(silc_client, conn,
+                             SILC_ATTRIBUTE_USER_PUBLIC_KEY, NULL);
+    list = g_strsplit(sv, " ", -1);
+    for (entry = list; *entry != NULL; entry++) {
+      if (!strncasecmp(*entry, "silc-rsa:", 8)) {
+       tmp = silc_file_readfile((*entry) + 8, &tmp_len, NULL);
+       if (tmp) {
+         tmp[tmp_len] = 0;
+         pk.type = "silc-rsa";
+         pk.data = tmp;
+         pk.data_len = tmp_len;
+         silc_client_attribute_add(silc_client, conn,
+                                   SILC_ATTRIBUTE_USER_PUBLIC_KEY, &pk,
+                                   sizeof(pk));
+       }
+       silc_free(tmp);
+      } else {
+       silc_say_error("Unsupported public key type '%s'", *entry);
+      }
+    }
+    g_strfreev(list);
+  }
+}
+
+typedef struct {
+  SilcClient client;
+  SILC_SERVER_REC *server;
+  char *name;
+  SilcAttributeObjPk userpk;
+  SilcPublicKey public_key;
+  SilcVCardStruct vcard;
+  SilcMime message;
+  SilcMime extension;
+  bool nopk;
+} *AttrVerify;
+
+static void silc_query_attributes_verify(SilcBool success, void *context)
+{
+  *(SilcBool *)context = success;
+}
+
+void silc_query_attributes_print(SILC_SERVER_REC *server,
+                                SilcClient client,
+                                SilcClientConnection conn,
+                                SilcDList attrs,
+                                SilcClientEntry client_entry)
+{
+  SilcAttributePayload attr;
+  SilcAttribute attribute;
+  char tmp[512];
+  SilcAttributeObjPk serverpk, usersign, serversign;
+  AttrVerify verify;
+
+  printformat_module("fe-common/silc", server, NULL,
+                    MSGLEVEL_CRAP, SILCTXT_ATTR_HEADER);
+
+  memset(&serverpk, 0, sizeof(serverpk));
+  memset(&usersign, 0, sizeof(usersign));
+  memset(&serversign, 0, sizeof(serversign));
+
+  verify = silc_calloc(1, sizeof(*verify));
+  if (!verify)
+    return;
+  verify->client = client;
+  verify->server = server;
+  verify->name = strdup(client_entry->nickname);
+
+  silc_dlist_start(attrs);
+  while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
+    attribute = silc_attribute_get_attribute(attr);
+    memset(tmp, 0, sizeof(tmp));
+
+    switch (attribute) {
+
+    case SILC_ATTRIBUTE_USER_INFO:
+      {
+       if (!silc_attribute_get_object(attr, (void *)&verify->vcard,
+                                      sizeof(verify->vcard)))
+         continue;
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_VCARD_FILE,
+                          "present");
+      }
+      break;
+
+    case SILC_ATTRIBUTE_SERVICE:
+      {
+       SilcAttributeObjService service;
+       memset(&service, 0, sizeof(service));
+       if (!silc_attribute_get_object(attr, (void *)&service,
+                                      sizeof(service)))
+         continue;
+       snprintf(tmp, sizeof(tmp) - 1, "%s:%d (logged %s) idle %d seconds",
+                service.address, (unsigned int)service.port,
+                service.status ? "in" : "out",
+                (unsigned int)service.idle);
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_SERVICES, tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_STATUS_MOOD:
+      {
+       SilcUInt32 mask;
+       if (!silc_attribute_get_object(attr, (void *)&mask, sizeof(mask)))
+         continue;
+       if (!mask)
+         silc_strncat(tmp, sizeof(tmp), "NORMAL ", strlen(" NORMAL"));
+       if (mask & SILC_ATTRIBUTE_MOOD_HAPPY)
+         silc_strncat(tmp, sizeof(tmp), "HAPPY ", strlen(" HAPPY"));
+       if (mask & SILC_ATTRIBUTE_MOOD_SAD)
+         silc_strncat(tmp, sizeof(tmp), "SAD ", strlen(" SAD"));
+       if (mask & SILC_ATTRIBUTE_MOOD_ANGRY)
+         silc_strncat(tmp, sizeof(tmp), "ANGRY ", strlen(" ANGRY"));
+       if (mask & SILC_ATTRIBUTE_MOOD_JEALOUS)
+         silc_strncat(tmp, sizeof(tmp), "JEALOUS ", strlen(" JEALOUS"));
+       if (mask & SILC_ATTRIBUTE_MOOD_ASHAMED)
+         silc_strncat(tmp, sizeof(tmp), "ASHAMED ", strlen(" ASHAMED"));
+       if (mask & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
+         silc_strncat(tmp, sizeof(tmp), "INVINCIBLE ", strlen(" INVINCIBLE"));
+       if (mask & SILC_ATTRIBUTE_MOOD_INLOVE)
+         silc_strncat(tmp, sizeof(tmp), "INLOVE ", strlen(" INLOVE"));
+       if (mask & SILC_ATTRIBUTE_MOOD_SLEEPY)
+         silc_strncat(tmp, sizeof(tmp), "SLEEPY ", strlen(" SLEEPY"));
+       if (mask & SILC_ATTRIBUTE_MOOD_BORED)
+         silc_strncat(tmp, sizeof(tmp), "BORED ", strlen(" BORED"));
+       if (mask & SILC_ATTRIBUTE_MOOD_EXCITED)
+         silc_strncat(tmp, sizeof(tmp), "EXCITED ", strlen(" EXCITED"));
+       if (mask & SILC_ATTRIBUTE_MOOD_ANXIOUS)
+         silc_strncat(tmp, sizeof(tmp), "ANXIOUS ", strlen(" ANXIOUS"));
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MOOD, tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_STATUS_FREETEXT:
+      {
+       if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
+         continue;
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_TEXT, tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_STATUS_MESSAGE:
+      {
+       verify->message = silc_mime_alloc();
+       if (!verify->message)
+         continue;
+       if (!silc_attribute_get_object(attr, (void *)verify->message,
+                                      sizeof(*verify->message)))
+         continue;
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_STATUS_MESSAGE,
+                          "present");
+      }
+      break;
+
+    case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
+      {
+       if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
+         continue;
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_LANGUAGE,
+                          tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_PREFERRED_CONTACT:
+      {
+       SilcUInt32 mask;
+       if (!silc_attribute_get_object(attr, (void *)&mask, sizeof(mask)))
+         continue;
+       if (!mask)
+         silc_strncat(tmp, sizeof(tmp), "NONE ", strlen(" NONE"));
+       if (mask & SILC_ATTRIBUTE_CONTACT_CHAT)
+         silc_strncat(tmp, sizeof(tmp), "CHAT ", strlen(" CHAT"));
+       if (mask & SILC_ATTRIBUTE_CONTACT_EMAIL)
+         silc_strncat(tmp, sizeof(tmp), "EMAIL ", strlen(" EMAIL"));
+       if (mask & SILC_ATTRIBUTE_CONTACT_CALL)
+         silc_strncat(tmp, sizeof(tmp), "CALL ", strlen(" CALL"));
+       if (mask & SILC_ATTRIBUTE_CONTACT_PAGE)
+         silc_strncat(tmp, sizeof(tmp), "PAGE ", strlen(" PAGE"));
+       if (mask & SILC_ATTRIBUTE_CONTACT_SMS)
+         silc_strncat(tmp, sizeof(tmp), "SMS ", strlen(" SMS"));
+       if (mask & SILC_ATTRIBUTE_CONTACT_MMS)
+         silc_strncat(tmp, sizeof(tmp), "MMS ", strlen(" MMS"));
+       if (mask & SILC_ATTRIBUTE_CONTACT_VIDEO)
+         silc_strncat(tmp, sizeof(tmp), "VIDEO ", strlen(" VIDEO"));
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_PREFERRED_CONTACT, tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_TIMEZONE:
+      {
+       if (!silc_attribute_get_object(attr, (void *)&tmp, sizeof(tmp) - 1))
+         continue;
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_TIMEZONE, tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_EXTENSION:
+      {
+       verify->extension = silc_mime_alloc();
+       if (!verify->extension)
+         continue;
+       if (!silc_attribute_get_object(attr, (void *)verify->extension,
+                                      sizeof(*verify->extension)))
+         continue;
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_EXTENSION,
+                          "present");
+      }
+      break;
+
+    case SILC_ATTRIBUTE_GEOLOCATION:
+      {
+       SilcAttributeObjGeo geo;
+       memset(&geo, 0, sizeof(geo));
+       if (!silc_attribute_get_object(attr, (void *)&geo, sizeof(geo)))
+         continue;
+       snprintf(tmp, sizeof(tmp) - 1, "%s:%s:%s:%s",
+                geo.longitude ? geo.longitude : "",
+                geo.latitude ? geo.latitude : "",
+                geo.altitude ? geo.altitude : "",
+                geo.accuracy ? geo.accuracy : "");
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_GEOLOCATION, tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_DEVICE_INFO:
+      {
+       SilcAttributeObjDevice dev;
+       memset(&dev, 0, sizeof(dev));
+       if (!silc_attribute_get_object(attr, (void *)&dev, sizeof(dev)))
+         continue;
+       snprintf(tmp, sizeof(tmp) - 1, "%s:%s:%s:%s:%s",
+                (dev.type == SILC_ATTRIBUTE_DEVICE_COMPUTER ? "COMPUTER" :
+                 dev.type == SILC_ATTRIBUTE_DEVICE_PDA ? "PDA" :
+                 dev.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE ?
+                 "MOBILE PHONE" :
+                 dev.type == SILC_ATTRIBUTE_DEVICE_TERMINAL ? "TERMINAL" :
+                 ""),
+                dev.manufacturer ? dev.manufacturer : "",
+                dev.version ? dev.version : "",
+                dev.model ? dev.model: "",
+                dev.language ? dev.language : "");
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_DEVICE_INFO, tmp);
+      }
+      break;
+
+    case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+      {
+       if (verify->userpk.type)
+         continue;
+       if (!silc_attribute_get_object(attr, (void *)&verify->userpk,
+                                      sizeof(verify->userpk)))
+         continue;
+      }
+      break;
+
+    case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
+      {
+       if (serverpk.type)
+         continue;
+       if (!silc_attribute_get_object(attr, (void *)&serverpk,
+                                      sizeof(serverpk)))
+         continue;
+      }
+      break;
+
+    case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
+      {
+       if (usersign.data)
+         continue;
+       if (!silc_attribute_get_object(attr, (void *)&usersign,
+                                      sizeof(usersign)))
+         continue;
+      }
+      break;
+
+    case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
+      {
+       if (serversign.data)
+         continue;
+       if (!silc_attribute_get_object(attr, (void *)&serversign,
+                                      sizeof(serversign)))
+         continue;
+      }
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  /* Handle the signature verifications and public key verifying here */
+
+  if (verify->userpk.data) {
+    SilcPKCSType type = 0;
+
+    if (!strcmp(verify->userpk.type, "silc-rsa"))
+      type = SILC_PKCS_SILC;
+    else if (!strcmp(verify->userpk.type, "ssh-rsa"))
+      type = SILC_PKCS_SSH2;
+    else if (!strcmp(verify->userpk.type, "x509v3-sign-rsa"))
+      type = SILC_PKCS_X509V3;
+    else if (!strcmp(verify->userpk.type, "pgp-sign-rsa"))
+      type = SILC_PKCS_OPENPGP;
+
+    silc_pkcs_public_key_alloc(type, verify->userpk.data,
+                              verify->userpk.data_len,
+                              &verify->public_key);
+  }
+
+  if (usersign.data) {
+    /* Verify the signature now */
+    unsigned char *verifyd;
+    SilcUInt32 verify_len;
+    SilcBool verified = FALSE;
+
+    if (verify->public_key) {
+      verifyd = silc_attribute_get_verify_data(attrs, FALSE, &verify_len);
+      if (verifyd)
+       silc_pkcs_verify_async(verify->public_key, usersign.data,
+                              usersign.data_len, verifyd, verify_len,
+                              TRUE, sha1hash,
+                              silc_query_attributes_verify, &verified);
+
+      if (verified) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_VERIFIED);
+      } else {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_FAILED);
+      }
+
+      silc_free(verifyd);
+    } else {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_FAILED);
+    }
+  } else {
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_NOT_PRESENT);
+  }
+
+  if (serversign.data) {
+    /* Verify the signature now */
+    SilcPublicKey public_key;
+    SilcPKCSType type = 0;
+    unsigned char *verifyd;
+    SilcUInt32 verify_len;
+    SilcBool verified = FALSE;
+
+    if (!strcmp(serverpk.type, "silc-rsa"))
+      type = SILC_PKCS_SILC;
+    else if (!strcmp(serverpk.type, "ssh-rsa"))
+      type = SILC_PKCS_SSH2;
+    else if (!strcmp(serverpk.type, "x509v3-sign-rsa"))
+      type = SILC_PKCS_X509V3;
+    else if (!strcmp(serverpk.type, "pgp-sign-rsa"))
+      type = SILC_PKCS_OPENPGP;
+
+    if (silc_pkcs_public_key_alloc(type, serverpk.data,
+                                  serverpk.data_len,
+                                  &public_key)) {
+      verifyd = silc_attribute_get_verify_data(attrs, TRUE, &verify_len);
+      if (verifyd)
+       silc_pkcs_verify_async(public_key, serversign.data,
+                              serversign.data_len, verifyd,
+                              verify_len, TRUE, sha1hash,
+                              silc_query_attributes_verify, &verified);
+      if (verified) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_SERVER_SIGN_VERIFIED);
+      } else {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ATTR_SERVER_SIGN_FAILED);
+      }
+
+      silc_pkcs_public_key_free(public_key);
+      silc_free(verifyd);
+    } else {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_ATTR_SERVER_SIGN_FAILED);
+    }
+  }
+
+  if (verify->public_key) {
+    silc_verify_public_key(client, conn, SILC_CONN_CLIENT,
+                          verify->public_key,
+                          silc_query_attributes_print_final, verify);
+  } else {
+    verify->nopk = TRUE;
+    silc_query_attributes_print_final(FALSE, verify);
+  }
+}
+
+static void silc_query_attributes_print_final(bool success, void *context)
+{
+  AttrVerify verify = context;
+  SILC_SERVER_REC *server = verify->server;
+  char *format = NULL;
+  unsigned char filename[256], *fingerprint = NULL, *tmp;
+  struct stat st;
+  int i;
+
+  if (!verify->nopk) {
+    if (success) {
+      printformat_module("fe-common/silc", NULL, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, "user",
+                        verify->name);
+    } else {
+      printformat_module("fe-common/silc", NULL, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED, "user",
+                        verify->name);
+    }
+  }
+
+  printformat_module("fe-common/silc", server, NULL,
+                    MSGLEVEL_CRAP, SILCTXT_ATTR_FOOTER);
+
+  /* Replace all whitespaces with `_'. */
+  fingerprint = silc_hash_fingerprint(sha1hash,
+                                     verify->userpk.data,
+                                     verify->userpk.data_len);
+  for (i = 0; i < strlen(fingerprint); i++)
+    if (fingerprint[i] == ' ')
+      fingerprint[i] = '_';
+
+  /* Filename for dir */
+  tmp = fingerprint + strlen(fingerprint) - 9;
+  snprintf(filename, sizeof(filename) - 1, "%s/friends/%s",
+          get_irssi_dir(), tmp);
+  silc_free(fingerprint);
+
+  if ((stat(filename, &st)) == -1) {
+    /* Ask to accept save requested attributes */
+    format = format_get_text("fe-common/silc", NULL, NULL, NULL,
+                            SILCTXT_ATTR_SAVE);
+    keyboard_entry_redirect((SIGNAL_FUNC)silc_query_attributes_accept,
+                           format, 0, verify);
+  } else {
+    /* Save new data to existing directory */
+    silc_query_attributes_accept("Y", verify);
+  }
+
+  g_free(format);
+}
+
+static void silc_query_attributes_accept(const char *line, void *context)
+{
+  AttrVerify verify = context;
+  SILC_SERVER_REC *server = verify->server;
+  struct stat st;
+  struct passwd *pw;
+  unsigned char filename[256], filename2[256], *fingerprint = NULL, *tmp;
+  SilcUInt32 len;
+  int i;
+
+  if (line[0] == 'Y' || line[0] == 'y') {
+    /* Save the attributes */
+    memset(filename, 0, sizeof(filename));
+    memset(filename2, 0, sizeof(filename2));
+
+    pw = getpwuid(getuid());
+    if (!pw)
+      goto out;
+
+    /* Replace all whitespaces with `_'. */
+    fingerprint = silc_hash_fingerprint(sha1hash,
+                                       verify->userpk.data,
+                                       verify->userpk.data_len);
+    for (i = 0; i < strlen(fingerprint); i++)
+      if (fingerprint[i] == ' ')
+       fingerprint[i] = '_';
+
+    /* Filename for dir */
+    tmp = fingerprint + strlen(fingerprint) - 9;
+    snprintf(filename, sizeof(filename) - 1, "%s/friends/%s",
+            get_irssi_dir(), tmp);
+
+    /* Create dir if it doesn't exist */
+    if ((stat(filename, &st)) == -1) {
+      /* If dir doesn't exist */
+      if (errno == ENOENT) {
+       if (pw->pw_uid == geteuid()) {
+         if ((mkdir(filename, 0755)) == -1) {
+           silc_say_error("Couldn't create `%s' directory",
+                          filename);
+           goto out;
+         }
+       } else {
+         silc_say_error("Couldn't create `%s' directory due to a "
+                        "wrong uid!", filename);
+         goto out;
+       }
+      } else {
+       silc_say_error("%s", strerror(errno));
+       goto out;
+      }
+    }
+
+    /* Save the stuff to the directory */
+
+    /* Save VCard */
+    snprintf(filename2, sizeof(filename2) - 1, "%s/vcard", filename);
+    if (verify->vcard.full_name) {
+      tmp = silc_vcard_encode(&verify->vcard, &len);
+      silc_file_writefile(filename2, tmp, len);
+      silc_free(tmp);
+    }
+
+    /* Save public key */
+    if (verify->public_key) {
+      memset(filename2, 0, sizeof(filename2));
+      snprintf(filename2, sizeof(filename2) - 1, "%s/clientkey_%s.pub",
+              filename, fingerprint);
+      silc_pkcs_save_public_key(filename2, verify->public_key,
+                               SILC_PKCS_FILE_BASE64);
+    }
+
+    /* Save extension data */
+    if (verify->extension) {
+      memset(filename2, 0, sizeof(filename2));
+      snprintf(filename2, sizeof(filename2) - 1, "%s/extension.mime",
+              filename);
+      tmp = silc_mime_encode(verify->extension, &len);
+      if (tmp)
+       silc_file_writefile(filename2, tmp, len);
+    }
+
+    /* Save MIME message data */
+    if (verify->message) {
+      memset(filename2, 0, sizeof(filename2));
+      snprintf(filename2, sizeof(filename2) - 1, "%s/status_message.mime",
+              filename);
+      tmp = silc_mime_encode(verify->message, &len);
+      if (tmp)
+       silc_file_writefile(filename2, tmp, len);
+    }
+
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_ATTR_SAVED, filename);
+  }
+
+ out:
+  silc_free(fingerprint);
+  silc_free(verify->name);
+  silc_vcard_free(&verify->vcard);
+  silc_free(verify);
+}