/*

  silc-queries.c

  Author: Pekka Riikonen <priikone@silcnet.org>

  Copyright (C) 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; 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.

*/

#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"

#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(nick != NULL, NULL);

  rec = g_new0(QUERY_REC, 1);
  rec->chat_type = SILC_PROTOCOL;
  rec->name = g_strdup(nick);
  rec->server_tag = g_strdup(server_tag);
  query_init(rec, automatic);
  return rec;
}

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;
  SilcAttributeObjMime mime;
  SilcAttributeObjGeo geo;
  SilcAttributeObjDevice dev;
  SilcAttributeObjPk pk;
  SilcVCardStruct vcard;
  bool allowed;

  memset(&service, 0, sizeof(service));
  memset(&mime, 0, sizeof(mime));
  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);
    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, (void *)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);
    if (tmp) {
      tmp[tmp_len] = 0;
      mime.mime = (const unsigned char *)tmp;
      mime.mime_len = tmp_len;
      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, (void *)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);
	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;
  SilcVCardStruct vcard;
  SilcAttributeObjMime message;
  SilcAttributeObjMime extension;
  bool nopk;
} *AttrVerify;

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:
      {
	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:
      {
	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 (usersign.data && !strcmp(verify->userpk.type, "silc-rsa")) {
    /* Verify the signature now */
    SilcPublicKey public_key;
    SilcPKCS pkcs;
    unsigned char *verifyd;
    SilcUInt32 verify_len;

    if (silc_pkcs_public_key_decode(verify->userpk.data,
				    verify->userpk.data_len,
				    &public_key)) {
      silc_pkcs_alloc("rsa", &pkcs);
      verifyd = silc_attribute_get_verify_data(attrs, FALSE, &verify_len);
      if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)){
	if (silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
				       usersign.data,
				       usersign.data_len,
				       verifyd, verify_len)) {
	  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_pkcs_public_key_free(public_key);
      silc_free(verifyd);
    }
  } else {
    printformat_module("fe-common/silc", server, NULL,
		       MSGLEVEL_CRAP, SILCTXT_ATTR_USER_SIGN_NOT_PRESENT);
  }

  if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
    /* Verify the signature now */
    SilcPublicKey public_key;
    SilcPKCS pkcs;
    unsigned char *verifyd;
    SilcUInt32 verify_len;

    if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
				    &public_key)) {
      silc_pkcs_alloc("rsa", &pkcs);
      verifyd = silc_attribute_get_verify_data(attrs, TRUE, &verify_len);
      if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
	if (silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
				       serversign.data,
				       serversign.data_len,
				       verifyd, verify_len)) {
	  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);
    }
  }

  if (verify->userpk.data) {
    silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
			   verify->userpk.data, verify->userpk.data_len,
			   SILC_SKE_PK_TYPE_SILC,
			   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;
  SilcClient client = verify->client;
  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(client->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;
  SilcClient client = verify->client;
  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(client->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 */
    memset(filename2, 0, sizeof(filename2));
    snprintf(filename2, sizeof(filename2) - 1, "%s/clientkey_%s.pub",
	     filename, fingerprint);
    silc_pkcs_save_public_key_data(filename2, verify->userpk.data,
				   verify->userpk.data_len,
				   SILC_PKCS_FILE_PEM);

    /* Save extension data */
    if (verify->extension.mime) {
      memset(filename2, 0, sizeof(filename2));
      snprintf(filename2, sizeof(filename2) - 1, "%s/extension.mime",
	       filename);
      silc_file_writefile(filename2, verify->extension.mime,
			  verify->extension.mime_len);
    }

    /* Save MIME message data */
    if (verify->message.mime) {
      memset(filename2, 0, sizeof(filename2));
      snprintf(filename2, sizeof(filename2) - 1, "%s/status_message.mime",
	       filename);
      silc_file_writefile(filename2, verify->message.mime,
			  verify->message.mime_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);
}
