/*
 * protocol.c - IdleProtocol
 * Copyright © 2007-2010 Collabora Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "config.h"

#include "protocol.h"

#include <dbus/dbus-glib.h>
#include <dbus/dbus-protocol.h>

#include "idle-connection.h"
#include "idle-handles.h"
#include "idle-im-manager.h"
#include "idle-muc-manager.h"

#define PROTOCOL_NAME "irc"
#define ICON_NAME "im-" PROTOCOL_NAME
#define ENGLISH_NAME "IRC"
#define VCARD_FIELD_NAME "x-" PROTOCOL_NAME
#define DEFAULT_PORT 6667
#define DEFAULT_KEEPALIVE_INTERVAL 30 /* sec */

G_DEFINE_TYPE (IdleProtocol, idle_protocol, TP_TYPE_BASE_PROTOCOL)

static gboolean
filter_nick (const TpCMParamSpec *paramspec,
    GValue *value,
    GError **error)
{
  const gchar *nick = g_value_get_string (value);

  g_assert (value);
  g_assert (G_VALUE_HOLDS_STRING(value));

  if (!idle_nickname_is_valid (nick, TRUE))
    {
      g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE,
          "Invalid account name '%s'", nick);
      return FALSE;
    }

  return TRUE;
}

static gboolean
filter_username (const TpCMParamSpec *paramspec,
    GValue *value,
    GError **error)
{
  const gchar *username;
  size_t i;

  g_assert (value);
  g_assert (G_VALUE_HOLDS_STRING (value));

  username = g_value_get_string (value);

  for (i = 0; username[i] != '\0'; i++)
    {
      const char ch = username[i];

      if (ch == '\0' || ch == '\n' || ch == '\r' || ch == ' ' || ch == '@')
        {
          g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
              "Invalid user name '%s'", username);
          return FALSE;
        }
    }

  return TRUE;
}

static const TpCMParamSpec idle_params[] = {
    {"account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
      TP_CONN_MGR_PARAM_FLAG_REQUIRED, NULL, 0, filter_nick},
    { "server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
      TP_CONN_MGR_PARAM_FLAG_REQUIRED },
    { "port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT,
      TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER (DEFAULT_PORT) },
    { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
      TP_CONN_MGR_PARAM_FLAG_SECRET },
    { "fullname", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0 },
    { "username", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, 0,
      filter_username },
    { "charset", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
      TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "UTF-8" },
    { "keepalive-interval", DBUS_TYPE_UINT32_AS_STRING, G_TYPE_UINT,
      TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT,
      GUINT_TO_POINTER (DEFAULT_KEEPALIVE_INTERVAL) },
    { "quit-message", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0 },
    { "use-ssl", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN,
      TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER (FALSE) },
    { "password-prompt", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN,
      TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER (FALSE) },
    { NULL, NULL, 0, 0, NULL, 0 }
};

static void
idle_protocol_init (IdleProtocol *self)
{
}

static const TpCMParamSpec *
get_parameters (TpBaseProtocol *self G_GNUC_UNUSED)
{
  return idle_params;
}

static TpBaseConnection *
new_connection (TpBaseProtocol *protocol G_GNUC_UNUSED,
                GHashTable *params,
                GError **error G_GNUC_UNUSED)
{
  guint port = tp_asv_get_uint32 (params, "port", NULL);

  if (port == 0)
    port = DEFAULT_PORT;

  return g_object_new (IDLE_TYPE_CONNECTION,
      "protocol", PROTOCOL_NAME,
      "nickname", tp_asv_get_string (params, "account"),
      "server", tp_asv_get_string (params, "server"),
      "port", port,
      "password", tp_asv_get_string (params, "password"),
      "realname", tp_asv_get_string (params, "fullname"),
      "username", tp_asv_get_string (params, "username"),
      "charset", tp_asv_get_string (params, "charset"),
      "keepalive-interval", tp_asv_get_uint32 (params, "keepalive-interval", NULL),
      "quit-message", tp_asv_get_string (params, "quit-message"),
      "use-ssl", tp_asv_get_boolean (params, "use-ssl", NULL),
      "password-prompt", tp_asv_get_boolean (params, "password-prompt",
          NULL),
      NULL);
}

static gchar *
normalize_contact (TpBaseProtocol *self G_GNUC_UNUSED,
                   const gchar *contact,
                   GError **error)
{
  return idle_normalize_nickname (contact, error);
}

static gchar *
identify_account (TpBaseProtocol *self G_GNUC_UNUSED,
    GHashTable *asv,
    GError **error)
{
  gchar *nick = idle_normalize_nickname (tp_asv_get_string (asv, "account"),
      error);
  gchar *server;
  gchar *nick_at_server;

  if (nick == NULL)
    return NULL;

  server = g_ascii_strdown (tp_asv_get_string (asv, "server"), -1);

  nick_at_server = g_strdup_printf ("%s@%s", nick, server);
  g_free (server);
  g_free (nick);
  return nick_at_server;
}

static GPtrArray *
get_interfaces_array (TpBaseProtocol *self)
{
  GPtrArray *interfaces;

  interfaces = TP_BASE_PROTOCOL_CLASS (
      idle_protocol_parent_class)->get_interfaces_array (self);

  return interfaces;
}

static void
get_connection_details (TpBaseProtocol *self,
    GStrv *connection_interfaces,
    GType **channel_managers,
    gchar **icon_name,
    gchar **english_name,
    gchar **vcard_field)
{
  if (connection_interfaces != NULL)
    {
      *connection_interfaces = g_strdupv (
          (GStrv) idle_connection_get_implemented_interfaces ());
    }

  if (channel_managers != NULL)
    {
      GType types[] = {
          IDLE_TYPE_IM_MANAGER,
          IDLE_TYPE_MUC_MANAGER,
          G_TYPE_INVALID };

      *channel_managers = g_memdup (types, sizeof(types));
    }

  if (icon_name != NULL)
    {
      *icon_name = g_strdup (ICON_NAME);
    }

  if (vcard_field != NULL)
    {
      *vcard_field = g_strdup (VCARD_FIELD_NAME);
    }

  if (english_name != NULL)
    {
      *english_name = g_strdup (ENGLISH_NAME);
    }
}

static GStrv
dup_authentication_types (TpBaseProtocol *base)
{
  const gchar * const types[] = {
    TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
    NULL,
  };

  return g_strdupv ((GStrv) types);
}

static void
idle_protocol_class_init (IdleProtocolClass *klass)
{
  TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) klass;

  base_class->get_parameters = get_parameters;
  base_class->new_connection = new_connection;
  base_class->normalize_contact = normalize_contact;
  base_class->identify_account = identify_account;
  base_class->get_interfaces_array = get_interfaces_array;
  base_class->get_connection_details = get_connection_details;
  base_class->dup_authentication_types = dup_authentication_types;
}

TpBaseProtocol *
idle_protocol_new (void)
{
  return g_object_new (IDLE_TYPE_PROTOCOL,
      "name", PROTOCOL_NAME,
      NULL);
}
