telepathygabble/src/gabble-connection.c
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:10:06 +0200
changeset 0 d0f3a028347a
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
 * gabble-connection.c - Source for GabbleConnection
 * Copyright (C) 2005 Collabora Ltd.
 *  and/or its subsidiary/subsidiaries. All rights reserved.
 *
 * 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 <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <glib-object.h>
#include "loudmouth/loudmouth.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


#include "handles.h"
#include "handle-set.h"
#include "telepathy-constants.h"
#include "telepathy-errors.h"
#include "telepathy-helpers.h"
#include "telepathy-interfaces.h"
#include "loudmouth/lm-connection.h"

#include "tp-channel-iface.h"
#include "tp-channel-factory-iface.h"

#include "gabble-connection.h"
#include "gabble-connection-glue.h"
#include "gabble-connection-signals-marshal.h"


#include "capabilities.h"
#include "debug.h"
#include "disco.h"
#include "gabble-presence-cache.h"
#include "gabble-presence.h"
#include "gabble-register.h"
#include "im-factory.h"
#include "search-factory.h"
#include "jingle-info.h"
#include "media-factory.h"
#include "muc-factory.h"
#include "namespaces.h"
#include "roster.h"
#include "util.h"
#include "vcard-manager.h"
#include "search-keys-info.h"

#include "gabble-media-channel.h"
#include "gabble-roomlist-channel.h"

#include "gabble_enums.h"
#include "sha1.h"
#include "base64.h"

#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION
#define DBUS_API_SUBJECT_TO_CHANGE
#define BUS_NAME        "org.freedesktop.Telepathy.Connection.gabble"
#define OBJECT_PATH     "/org/freedesktop/Telepathy/Connection/gabble"

#define TP_ALIAS_PAIR_TYPE (dbus_g_type_get_struct ("GValueArray", \
      G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID))
#define TP_CAPABILITY_PAIR_TYPE (dbus_g_type_get_struct ("GValueArray", \
      G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID))
#define TP_CAPABILITIES_CHANGED_MONSTER_TYPE (dbus_g_type_get_struct \
    ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \
                    G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
#define TP_GET_CAPABILITIES_MONSTER_TYPE (dbus_g_type_get_struct \
    ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \
                    G_TYPE_INVALID))
#define TP_CHANNEL_LIST_ENTRY_TYPE (dbus_g_type_get_struct ("GValueArray", \
      DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \
      G_TYPE_INVALID))

#define ERROR_IF_NOT_CONNECTED(CONN, ERROR) \
  if ((CONN)->status != TP_CONN_STATUS_CONNECTED) \
    { \
      gabble_debug (DEBUG_FLAG, "rejected request as disconnected"); \
      g_set_error (ERROR, TELEPATHY_ERRORS, NotAvailable, \
          "Connection is disconnected"); \
      return FALSE; \
    }

#define ERROR_IF_NOT_CONNECTED_ASYNC(CONN, ERROR, CONTEXT) \
  if ((CONN)->status != TP_CONN_STATUS_CONNECTED) \
    { \
      gabble_debug (DEBUG_FLAG, "rejected request as disconnected"); \
      (ERROR) = g_error_new (TELEPATHY_ERRORS, NotAvailable, \
          "Connection is disconnected"); \
      dbus_g_method_return_error ((CONTEXT), (ERROR)); \
      g_error_free ((ERROR)); \
      return; \
    }

#ifdef DEBUG_FLAG
//#define DEBUG(format, ...)
#define DEBUGGING 0
#define NODE_DEBUG(n, s)
#endif /* DEBUG_FLAG */

#ifndef EMULATOR
G_DEFINE_TYPE(GabbleConnection, gabble_connection, G_TYPE_OBJECT)
#endif

typedef struct _StatusInfo StatusInfo;

struct _StatusInfo
{
  const gchar *name;
  TpConnectionPresenceType presence_type;
  const gboolean self;
  const gboolean exclusive;
};

/* order must match PresenceId enum in gabble-connection.h */
/* in increasing order of presence */
static const StatusInfo gabble_statuses[LAST_GABBLE_PRESENCE] = {
 { "offline",   TP_CONN_PRESENCE_TYPE_OFFLINE,       TRUE, TRUE },
 { "hidden",    TP_CONN_PRESENCE_TYPE_HIDDEN,        TRUE, TRUE },
 { "xa",        TP_CONN_PRESENCE_TYPE_EXTENDED_AWAY, TRUE, TRUE },
 { "away",      TP_CONN_PRESENCE_TYPE_AWAY,          TRUE, TRUE },
 { "dnd",       TP_CONN_PRESENCE_TYPE_AWAY,          TRUE, TRUE },
 { "available", TP_CONN_PRESENCE_TYPE_AVAILABLE,     TRUE, TRUE },
 { "chat",      TP_CONN_PRESENCE_TYPE_AVAILABLE,     TRUE, TRUE }
};

/* signal enum */
enum
{
    ALIASES_CHANGED,
    CAPABILITIES_CHANGED,
    NEW_CHANNEL,
    PRESENCE_UPDATE,
    STATUS_CHANGED,
    DISCONNECTED,
    LAST_SIGNAL 
#ifdef EMULATOR    
    = LAST_SIGNAL_CON
#endif
    
};


#ifdef EMULATOR
#include "libgabble_wsd_solution.h"

	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_con,guint)
	#define signals (GET_WSD_VAR_NAME(signals,gabble_con, s)())	
	
	
	GET_STATIC_VAR_FROM_TLS(arguments,gabble_con,GHashTable*)
	#define arguments (*GET_WSD_VAR_NAME(arguments,gabble_con, s)())	
	
	GET_STATIC_VAR_FROM_TLS(gabble_connection_parent_class,gabble_con,gpointer)
	#define gabble_connection_parent_class (*GET_WSD_VAR_NAME(gabble_connection_parent_class,gabble_con,s)())
	
	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_con,GType)
	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_con,s)())
	
	//GET_STATIC_ARRAY_FROM_TLS(assumed_caps,gabble_con,gchar*)
	/*gchar** _s_gabble_con_assumed_caps() { return (gchar**)((libgabble_ImpurePtr()->_s_gabble_con_assumed_caps)); }

	#define assumed_caps (GET_WSD_VAR_NAME(assumed_caps,gabble_con, s)())	*/
	
	
			
static void gabble_connection_init (GabbleConnection *self); 
static void gabble_connection_class_init (GabbleConnectionClass *klass); 
static void gabble_connection_class_intern_init (gpointer klass) 
{ 
gabble_connection_parent_class = g_type_class_peek_parent (klass); 
gabble_connection_class_init ((GabbleConnectionClass*) klass); 
} 
EXPORT_C GType gabble_connection_get_type (void) 
{ 

if ((g_define_type_id == 0)) 
{ 
static const GTypeInfo g_define_type_info = 
	{ sizeof (GabbleConnectionClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_connection_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleConnection), 0, (GInstanceInitFunc) gabble_connection_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleConnection"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; 
	} ;
	
#else

	static guint signals[LAST_SIGNAL] = {0};

#endif	

/* properties */
enum
{
    PROP_PROTOCOL = 1,
    PROP_CONNECT_SERVER,
    PROP_PORT,
    PROP_OLD_SSL,
    PROP_REGISTER,
    PROP_LOW_BANDWIDTH,
    PROP_STREAM_SERVER,
    PROP_USERNAME,
    PROP_PASSWORD,
    PROP_RESOURCE,
    PROP_PRIORITY,
    PROP_HTTPS_PROXY_SERVER,
    PROP_HTTPS_PROXY_PORT,
    PROP_FALLBACK_CONFERENCE_SERVER,
    PROP_STUN_SERVER,
    PROP_STUN_PORT,
    PROP_STUN_RELAY_MAGIC_COOKIE,
    PROP_STUN_RELAY_SERVER,
    PROP_STUN_RELAY_UDP_PORT,
    PROP_STUN_RELAY_TCP_PORT,
    PROP_STUN_RELAY_SSLTCP_PORT,
    PROP_STUN_RELAY_USERNAME,
    PROP_STUN_RELAY_PASSWORD,
    PROP_IGNORE_SSL_ERRORS,
    PROP_ALIAS,

    LAST_PROPERTY
};

/* TP properties */
enum
{
  CONN_PROP_STUN_SERVER = 0,
  CONN_PROP_STUN_PORT,
  CONN_PROP_STUN_RELAY_MAGIC_COOKIE,
  CONN_PROP_STUN_RELAY_SERVER,
  CONN_PROP_STUN_RELAY_UDP_PORT,
  CONN_PROP_STUN_RELAY_TCP_PORT,
  CONN_PROP_STUN_RELAY_SSLTCP_PORT,
  CONN_PROP_STUN_RELAY_USERNAME,
  CONN_PROP_STUN_RELAY_PASSWORD,

  NUM_CONN_PROPS,

  INVALID_CONN_PROP,
};

const GabblePropertySignature connection_property_signatures[NUM_CONN_PROPS] = {
      { "stun-server",                  G_TYPE_STRING },
      { "stun-port",                    G_TYPE_UINT   },
      { "stun-relay-magic-cookie",      G_TYPE_STRING },
      { "stun-relay-server",            G_TYPE_STRING },
      { "stun-relay-udp-port",          G_TYPE_UINT   },
      { "stun-relay-tcp-port",          G_TYPE_UINT   },
      { "stun-relay-ssltcp-port",       G_TYPE_UINT   },
      { "stun-relay-username",          G_TYPE_STRING },
      { "stun-relay-password",          G_TYPE_STRING },
};

/* private structure */
typedef struct _GabbleConnectionPrivate GabbleConnectionPrivate;

struct _GabbleConnectionPrivate
{
  LmMessageHandler *iq_jingle_info_cb;
  LmMessageHandler *iq_search_keys_cb;
  LmMessageHandler *iq_disco_cb;
  LmMessageHandler *iq_unknown_cb;
  LmMessageHandler *stream_error_cb;

  /* telepathy properties */
  gchar *protocol;

  /* connection properties */
  gchar *connect_server;
  guint port;
  gboolean old_ssl;

  gboolean ignore_ssl_errors;
  TpConnectionStatusReason ssl_error;

  gboolean do_register;

  gboolean low_bandwidth;

  gchar *https_proxy_server;
  guint https_proxy_port;

  gchar *fallback_conference_server;

  /* authentication properties */
  gchar *stream_server;
  gchar *username;
  gchar *password;
  gchar *resource;
  gint8 priority;
  gchar *alias;

  /* reference to conference server name */
  const gchar *conference_server;

  /* channel factories */
  GPtrArray *channel_factories;
  GPtrArray *channel_requests;
  gboolean suppress_next_handler;

  /* serial number of current advertised caps */
  guint caps_serial;

  /* gobject housekeeping */
  gboolean dispose_has_run;
};

#define GABBLE_CONNECTION_GET_PRIVATE(obj) \
    ((GabbleConnectionPrivate *)obj->priv)

typedef struct _ChannelRequest ChannelRequest;

struct _ChannelRequest
{
  DBusGMethodInvocation *context;
  gchar *channel_type;
  guint handle_type;
  guint handle;
  gboolean suppress_handler;
};

static void connection_new_channel_cb (TpChannelFactoryIface *, GObject *, gpointer);
static void connection_channel_error_cb (TpChannelFactoryIface *, GObject *, GError *, gpointer);
static void connection_nickname_update_cb (GObject *, GabbleHandle, gpointer);
static void connection_presence_update_cb (GabblePresenceCache *, GabbleHandle, gpointer);
static void connection_capabilities_update_cb (GabblePresenceCache *, GabbleHandle, GabblePresenceCapabilities, GabblePresenceCapabilities, gpointer);

static void
gabble_connection_init (GabbleConnection *self)
{
  GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
      GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate);
  guint i;
  GValue val = { 0, };

  self->priv = priv;
  self->lmconn = lm_connection_new (NULL);
  self->status = TP_CONN_STATUS_NEW;
  self->handles = gabble_handle_repo_new ();
  self->disco = gabble_disco_new (self);
  self->vcard_manager = gabble_vcard_manager_new (self);
  self->search_instr = NULL;
  self->search_key_names = NULL;
  self->search_reported_fields = NULL;
  self->search_form = FALSE;
  self->search_service_jid = NULL;
  self->self_avatar_sha1 = NULL;
  self->self_handle = 0;
  g_signal_connect (self->vcard_manager, "nickname-update", G_CALLBACK
      (connection_nickname_update_cb), self);

  self->presence_cache = gabble_presence_cache_new (self);
  g_signal_connect (self->presence_cache, "nickname-update", G_CALLBACK
      (connection_nickname_update_cb), self);
  g_signal_connect (self->presence_cache, "presence-update", G_CALLBACK
      (connection_presence_update_cb), self);
  g_signal_connect (self->presence_cache, "capabilities-update", G_CALLBACK
      (connection_capabilities_update_cb), self);

  capabilities_fill_cache (self->presence_cache);

  self->roster = gabble_roster_new (self);
  g_signal_connect (self->roster, "nickname-update", G_CALLBACK
      (connection_nickname_update_cb), self);

  priv->channel_factories = g_ptr_array_sized_new (1);

  g_ptr_array_add (priv->channel_factories, self->roster);

  g_ptr_array_add (priv->channel_factories,
                   g_object_new (GABBLE_TYPE_MUC_FACTORY,
                                 "connection", self,
                                 NULL));

  g_ptr_array_add (priv->channel_factories,
                   g_object_new (GABBLE_TYPE_MEDIA_FACTORY,
                                 "connection", self,
                                 NULL));

  g_ptr_array_add (priv->channel_factories,
                   g_object_new (GABBLE_TYPE_IM_FACTORY,
                                 "connection", self,
                                 NULL));
                                 
  //add for search here	
  g_ptr_array_add (priv->channel_factories,
                    g_object_new (GABBLE_TYPE_SEARCH_FACTORY,
                                 "connection", self,
                                 NULL));                               

  for (i = 0; i < priv->channel_factories->len; i++)
    {
      GObject *factory = g_ptr_array_index (priv->channel_factories, i);
      g_signal_connect (factory, "new-channel", G_CALLBACK
          (connection_new_channel_cb), self);
      g_signal_connect (factory, "channel-error", G_CALLBACK
          (connection_channel_error_cb), self);
    }

  priv->channel_requests = g_ptr_array_new ();

  /* Set default parameters for optional parameters */
  priv->resource = g_strdup (GABBLE_PARAMS_DEFAULT_RESOURCE);
  priv->port = GABBLE_PARAMS_DEFAULT_PORT;
  priv->https_proxy_port = GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT;

  /* initialize properties mixin */
  gabble_properties_mixin_init (G_OBJECT (self), G_STRUCT_OFFSET (
        GabbleConnection, properties));

  g_value_init (&val, G_TYPE_UINT);
  g_value_set_uint (&val, GABBLE_PARAMS_DEFAULT_STUN_PORT);

  gabble_properties_mixin_change_value (G_OBJECT (self), CONN_PROP_STUN_PORT,
                                        &val, NULL);
  gabble_properties_mixin_change_flags (G_OBJECT (self), CONN_PROP_STUN_PORT,
                                        TP_PROPERTY_FLAG_READ, 0, NULL);

  g_value_unset (&val);

  priv->caps_serial = 1;
}

static void
gabble_connection_get_property (GObject    *object,
                                guint       property_id,
                                GValue     *value,
                                GParamSpec *pspec)
{
  GabbleConnection *self = (GabbleConnection *) object;
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
  const gchar *param_name;
  guint tp_property_id;

  switch (property_id) {
    case PROP_PROTOCOL:
      g_value_set_string (value, priv->protocol);
      break;
    case PROP_CONNECT_SERVER:
      g_value_set_string (value, priv->connect_server);
      break;
    case PROP_STREAM_SERVER:
      g_value_set_string (value, priv->stream_server);
      break;
    case PROP_PORT:
      g_value_set_uint (value, priv->port);
      break;
    case PROP_OLD_SSL:
      g_value_set_boolean (value, priv->old_ssl);
      break;
    case PROP_REGISTER:
      g_value_set_boolean (value, priv->do_register);
      break;
    case PROP_LOW_BANDWIDTH:
      g_value_set_boolean (value, priv->low_bandwidth);
      break;
    case PROP_USERNAME:
      g_value_set_string (value, priv->username);
      break;
    case PROP_PASSWORD:
      g_value_set_string (value, priv->password);
      break;
    case PROP_RESOURCE:
      g_value_set_string (value, priv->resource);
      break;
    case PROP_PRIORITY:
      g_value_set_int (value, priv->priority);
      break;
    case PROP_HTTPS_PROXY_SERVER:
      g_value_set_string (value, priv->https_proxy_server);
      break;
    case PROP_HTTPS_PROXY_PORT:
      g_value_set_uint (value, priv->https_proxy_port);
      break;
    case PROP_FALLBACK_CONFERENCE_SERVER:
      g_value_set_string (value, priv->fallback_conference_server);
      break;
    case PROP_IGNORE_SSL_ERRORS:
      g_value_set_boolean (value, priv->ignore_ssl_errors);
      break;
    case PROP_ALIAS:
      g_value_set_string (value, priv->alias);
      break;
    default:
      param_name = g_param_spec_get_name (pspec);

      if (gabble_properties_mixin_has_property (object, param_name,
            &tp_property_id))
        {
          GValue *tp_property_value =
            self->properties.properties[tp_property_id].value;

          if (tp_property_value)
            {
              g_value_copy (tp_property_value, value);
              return;
            }
        }

      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}

static void
gabble_connection_set_property (GObject      *object,
                                guint         property_id,
                                const GValue *value,
                                GParamSpec   *pspec)
{
  GabbleConnection *self = (GabbleConnection *) object;
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
  const gchar *param_name;
  guint tp_property_id;

  switch (property_id) {
    case PROP_PROTOCOL:
      g_free (priv->protocol);
      priv->protocol = g_value_dup_string (value);
      break;
    case PROP_CONNECT_SERVER:
      g_free (priv->connect_server);
      priv->connect_server = g_value_dup_string (value);
      break;
    case PROP_PORT:
      priv->port = g_value_get_uint (value);
      break;
    case PROP_OLD_SSL:
      priv->old_ssl = g_value_get_boolean (value);
      break;
    case PROP_REGISTER:
      priv->do_register = g_value_get_boolean (value);
      break;
    case PROP_LOW_BANDWIDTH:
      priv->low_bandwidth = g_value_get_boolean (value);
      break;
    case PROP_STREAM_SERVER:
      g_free (priv->stream_server);
      priv->stream_server = g_value_dup_string (value);
      break;
    case PROP_USERNAME:
      g_free (priv->username);
      priv->username = g_value_dup_string (value);
      break;
   case PROP_PASSWORD:
      g_free (priv->password);
      priv->password = g_value_dup_string (value);
      break;
    case PROP_RESOURCE:
      g_free (priv->resource);
      priv->resource = g_value_dup_string (value);
      break;
    case PROP_PRIORITY:
      priv->priority = CLAMP (g_value_get_int (value), G_MININT8, G_MAXINT8);
      break;
    case PROP_HTTPS_PROXY_SERVER:
      g_free (priv->https_proxy_server);
      priv->https_proxy_server = g_value_dup_string (value);
      break;
    case PROP_HTTPS_PROXY_PORT:
      priv->https_proxy_port = g_value_get_uint (value);
      break;
    case PROP_FALLBACK_CONFERENCE_SERVER:
      g_free (priv->fallback_conference_server);
      priv->fallback_conference_server = g_value_dup_string (value);
      break;
    case PROP_IGNORE_SSL_ERRORS:
      priv->ignore_ssl_errors = g_value_get_boolean (value);
      break;
   case PROP_ALIAS:
      g_free (priv->alias);
      priv->alias = g_value_dup_string (value);
      break;
    default:
      param_name = g_param_spec_get_name (pspec);

      if (gabble_properties_mixin_has_property (object, param_name,
            &tp_property_id))
        {
          gabble_properties_mixin_change_value (object, tp_property_id, value,
                                                NULL);
          gabble_properties_mixin_change_flags (object, tp_property_id,
                                                TP_PROPERTY_FLAG_READ,
                                                0, NULL);

          return;
        }

      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}

static void gabble_connection_dispose (GObject *object);
static void gabble_connection_finalize (GObject *object);

static void
gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (gabble_connection_class);
  GParamSpec *param_spec;

  object_class->get_property = gabble_connection_get_property;
  object_class->set_property = gabble_connection_set_property;

  g_type_class_add_private (gabble_connection_class, sizeof (GabbleConnectionPrivate));

  object_class->dispose = gabble_connection_dispose;
  object_class->finalize = gabble_connection_finalize;

  param_spec = g_param_spec_string ("protocol", "Telepathy identifier for protocol",
                                    "Identifier string used when the protocol "
                                    "name is required. Unused internally.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_PROTOCOL, param_spec);

  param_spec = g_param_spec_string ("connect-server", "Hostname or IP of Jabber server",
                                    "The server used when establishing a connection.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_CONNECT_SERVER, param_spec);

  param_spec = g_param_spec_uint ("port", "Jabber server port",
                                  "The port used when establishing a connection.",
                                  0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_PORT,
                                  G_PARAM_READWRITE |
                                  G_PARAM_STATIC_NAME |
                                  G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_PORT, param_spec);

  param_spec = g_param_spec_boolean ("old-ssl", "Old-style SSL tunneled connection",
                                     "Establish the entire connection to the server "
                                     "within an SSL-encrypted tunnel. Note that this "
                                     "is not the same as connecting with TLS, which "
                                     "is not yet supported.", FALSE,
                                     G_PARAM_READWRITE |
                                     G_PARAM_STATIC_NAME |
                                     G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_OLD_SSL, param_spec);

  param_spec = g_param_spec_boolean ("register", "Register account on server",
                                     "Register a new account on server.", FALSE,
                                     G_PARAM_READWRITE |
                                     G_PARAM_STATIC_NAME |
                                     G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_REGISTER, param_spec);

  param_spec = g_param_spec_boolean ("low-bandwidth", "Low bandwidth mode",
                                     "Determines whether we are in low "
                                     "bandwidth mode. This influences "
                                     "polling behaviour.", FALSE,
                                     G_PARAM_READWRITE |
                                     G_PARAM_STATIC_NAME |
                                     G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_LOW_BANDWIDTH, param_spec);

  param_spec = g_param_spec_string ("stream-server", "The server name used to initialise the stream.",
                                    "The server name used when initialising the stream, "
                                    "which is usually the part after the @ in the user's JID.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STREAM_SERVER, param_spec);

  param_spec = g_param_spec_string ("username", "Jabber username",
                                    "The username used when authenticating.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_USERNAME, param_spec);

  param_spec = g_param_spec_string ("password", "Jabber password",
                                    "The password used when authenticating.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_PASSWORD, param_spec);

  param_spec = g_param_spec_string ("resource", "Jabber resource",
                                    "The Jabber resource used when authenticating.",
                                    "Telepathy",
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_RESOURCE, param_spec);

  param_spec = g_param_spec_int ("priority", "Jabber presence priority",
                                 "The default priority used when reporting our presence.",
                                 G_MININT8, G_MAXINT8, 0,
                                 G_PARAM_READWRITE |
                                 G_PARAM_STATIC_NAME |
                                 G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_PRIORITY, param_spec);

  param_spec = g_param_spec_string ("https-proxy-server", "The server name "
                                    "used as an HTTPS proxy server",
                                    "The server name used as an HTTPS proxy "
                                    "server.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_HTTPS_PROXY_SERVER, param_spec);

  param_spec = g_param_spec_uint ("https-proxy-port", "The HTTP proxy server "
                                  "port", "The HTTP proxy server port.",
                                  0, G_MAXUINT16, 0,
                                  G_PARAM_READWRITE |
                                  G_PARAM_STATIC_NAME |
                                  G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_HTTPS_PROXY_PORT, param_spec);

  param_spec = g_param_spec_string ("fallback-conference-server",
                                    "The conference server used as fallback",
                                    "The conference server used as fallback when "
                                    "everything else fails.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_FALLBACK_CONFERENCE_SERVER,
                                   param_spec);

  param_spec = g_param_spec_string ("stun-server",
                                    "STUN server",
                                    "STUN server.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec);

  param_spec = g_param_spec_uint ("stun-port",
                                  "STUN port",
                                  "STUN port.",
                                  0, G_MAXUINT16, 0,
                                  G_PARAM_READWRITE |
                                  G_PARAM_STATIC_NAME |
                                  G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec);

  param_spec = g_param_spec_string ("stun-relay-magic-cookie",
                                    "STUN relay magic cookie",
                                    "STUN relay magic cookie.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_RELAY_MAGIC_COOKIE,
                                   param_spec);

  param_spec = g_param_spec_string ("stun-relay-server",
                                    "STUN relay server",
                                    "STUN relay server.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_RELAY_SERVER,
                                   param_spec);

  param_spec = g_param_spec_uint ("stun-relay-udp-port",
                                  "STUN relay UDP port",
                                  "STUN relay UDP port.",
                                  0, G_MAXUINT16, 0,
                                  G_PARAM_READWRITE |
                                  G_PARAM_STATIC_NAME |
                                  G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_RELAY_UDP_PORT,
                                   param_spec);

  param_spec = g_param_spec_uint ("stun-relay-tcp-port",
                                  "STUN relay TCP port",
                                  "STUN relay TCP port.",
                                  0, G_MAXUINT16, 0,
                                  G_PARAM_READWRITE |
                                  G_PARAM_STATIC_NAME |
                                  G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_RELAY_TCP_PORT,
                                   param_spec);

  param_spec = g_param_spec_uint ("stun-relay-ssltcp-port",
                                  "STUN relay SSL-TCP port",
                                  "STUN relay SSL-TCP port.",
                                  0, G_MAXUINT16, 0,
                                  G_PARAM_READWRITE |
                                  G_PARAM_STATIC_NAME |
                                  G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_RELAY_SSLTCP_PORT,
                                   param_spec);

  param_spec = g_param_spec_string ("stun-relay-username",
                                    "STUN relay username",
                                    "STUN relay username.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_RELAY_USERNAME,
                                   param_spec);

  param_spec = g_param_spec_string ("stun-relay-password",
                                    "STUN relay password",
                                    "STUN relay password.",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_STUN_RELAY_PASSWORD,
                                   param_spec);

  param_spec = g_param_spec_boolean ("ignore-ssl-errors", "Ignore SSL errors",
                                     "Continue connecting even if the server's "
                                     "SSL certificate is invalid or missing.",
                                     FALSE,
                                     G_PARAM_READWRITE |
                                     G_PARAM_STATIC_NAME |
                                     G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_IGNORE_SSL_ERRORS, param_spec);

  param_spec = g_param_spec_string ("alias",
                                    "Alias/nick for local user",
                                    "Alias/nick for local user",
                                    NULL,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_NAME |
                                    G_PARAM_STATIC_BLURB);
  g_object_class_install_property (object_class, PROP_ALIAS, param_spec);

  /* signal definitions */

  signals[ALIASES_CHANGED] =
    g_signal_new ("aliases-changed",
                  G_OBJECT_CLASS_TYPE (gabble_connection_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                  0,
                  NULL, NULL,
                  g_cclosure_marshal_VOID__BOXED,
                  G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID)))));

  signals[CAPABILITIES_CHANGED] =
    g_signal_new ("capabilities-changed",
                  G_OBJECT_CLASS_TYPE (gabble_connection_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                  0,
                  NULL, NULL,
                  g_cclosure_marshal_VOID__BOXED,
                  G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)))));

  signals[NEW_CHANNEL] =
    g_signal_new ("new-channel",
                  G_OBJECT_CLASS_TYPE (gabble_connection_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                  0,
                  NULL, NULL,
                  gabble_connection_marshal_VOID__STRING_STRING_UINT_UINT_BOOLEAN,
                  G_TYPE_NONE, 5, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);

  signals[PRESENCE_UPDATE] =
    g_signal_new ("presence-update",
                  G_OBJECT_CLASS_TYPE (gabble_connection_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                  0,
                  NULL, NULL,
                  g_cclosure_marshal_VOID__BOXED,
                  G_TYPE_NONE, 1, (dbus_g_type_get_map ("GHashTable", G_TYPE_UINT, (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)))), G_TYPE_INVALID)))));

  signals[STATUS_CHANGED] =
    g_signal_new ("status-changed",
                  G_OBJECT_CLASS_TYPE (gabble_connection_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                  0,
                  NULL, NULL,
                  gabble_connection_marshal_VOID__UINT_UINT,
                  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);

  signals[DISCONNECTED] =
    g_signal_new ("disconnected",
                  G_OBJECT_CLASS_TYPE (gabble_connection_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                  0,
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

  dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (gabble_connection_class), &dbus_glib_gabble_connection_object_info);

  gabble_properties_mixin_class_init (G_OBJECT_CLASS (gabble_connection_class),
                                      G_STRUCT_OFFSET (GabbleConnectionClass, properties_class),
                                      connection_property_signatures, NUM_CONN_PROPS,
                                      NULL);
}

static gboolean
_unref_lm_connection (gpointer data)
{
  LmConnection *conn = (LmConnection *) data;

  lm_connection_unref (conn);
  return FALSE;
}

void
gabble_connection_dispose (GObject *object)
{
  GabbleConnection *self = GABBLE_CONNECTION (object);
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
  DBusGProxy *bus_proxy;
  guint i;
  bus_proxy = tp_get_bus_proxy ();

  if (priv->dispose_has_run)
    return;

  priv->dispose_has_run = TRUE;

  gabble_debug (DEBUG_FLAG, "called");

  g_message("1 Inside gabble_connection_dispose() method");
  
  //<todo> are these asserts a valid one ?
  //As in case of lm_connection_open these will not be valid assertions ?
  //to remove these?
  g_assert ((self->status == TP_CONN_STATUS_DISCONNECTED) ||
            (self->status == TP_CONN_STATUS_NEW));
  //This is not a valid statement as the self_handle is set to a value in _gabble_connection_connect which wil call 
  //lm_connection_open(within do_connect). If the call fails then control comes here.. in which case self_handle can be other 
  //than 0)
  //g_assert (self->self_handle == 0);

  g_message("after assert");
  if (priv->channel_requests)
    {
      g_assert (priv->channel_requests->len == 0);
      g_ptr_array_free (priv->channel_requests, TRUE);
      priv->channel_requests = NULL;
    }

  g_ptr_array_foreach (priv->channel_factories, (GFunc) g_object_unref, NULL);
  g_ptr_array_free (priv->channel_factories, TRUE);
  priv->channel_factories = NULL;
  g_message("unrefed channel factories");
  if(self->search_instr)
	  {
	    g_free( self->search_instr );
	  	self->search_instr = NULL;
	  }
	 
  if(self->search_key_names)
	  {
	  	for( i=0; self->search_key_names[i]; i++ )
			{
			g_free( self->search_key_names[i] );
			self->search_key_names[i] = NULL;
			}
	  	g_free( self->search_key_names );
	  	self->search_key_names = NULL;	
	  }
  g_message("freed search keys");
  if ( self->self_avatar_sha1 )
      {
      g_free ( self->self_avatar_sha1 );
      }
  if(self->search_key_ht)
	  {
	   g_hash_table_destroy (self->search_key_ht);
	   self->search_key_ht = NULL;	
	  }	  
  if(self->search_service_jid)
	  {
	   g_free(self->search_service_jid);
	   self->search_service_jid = NULL;	
	  }
  /* unreffing channel factories frees the roster */
  self->roster = NULL;

  g_object_unref (self->disco);
  self->disco = NULL;

  g_object_unref (self->vcard_manager);
  self->vcard_manager = NULL;

  g_object_unref (self->presence_cache);
  self->presence_cache = NULL;

  /* if this is not already the case, we'll crash anyway */
  g_assert (!lm_connection_is_open (self->lmconn));
  g_assert (priv->iq_search_keys_cb == NULL);
  g_assert (priv->iq_jingle_info_cb == NULL);
  g_assert (priv->iq_disco_cb == NULL);
  g_assert (priv->iq_unknown_cb == NULL);
  g_assert (priv->stream_error_cb == NULL);

  /*
   * The Loudmouth connection can't be unref'd immediately because this
   * function might (indirectly) return into Loudmouth code which expects the
   * connection to always be there.
   */
  g_idle_add (_unref_lm_connection, self->lmconn);

  if (NULL != self->bus_name)
    {
      dbus_g_proxy_call_no_reply (bus_proxy, "ReleaseName",
                                  G_TYPE_STRING, self->bus_name,
                                  G_TYPE_INVALID);
    }

  if (G_OBJECT_CLASS (gabble_connection_parent_class)->dispose)
    G_OBJECT_CLASS (gabble_connection_parent_class)->dispose (object);
  
  g_message("out of the method");
}

void
gabble_connection_finalize (GObject *object)
{
  GabbleConnection *self = GABBLE_CONNECTION (object);
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  gabble_debug (DEBUG_FLAG, "called with %p", object);

  g_free (self->bus_name);
  g_free (self->object_path);

  g_free (priv->protocol);
  g_free (priv->connect_server);
  g_free (priv->stream_server);
  g_free (priv->username);
  g_free (priv->password);
  g_free (priv->resource);

  g_free (priv->https_proxy_server);
  g_free (priv->fallback_conference_server);

  g_free (priv->alias);

  gabble_properties_mixin_finalize (object);

  gabble_handle_repo_destroy (self->handles);

  G_OBJECT_CLASS (gabble_connection_parent_class)->finalize (object);
}

/**
 * _gabble_connection_set_properties_from_account
 *
 * Parses an account string which may be one of the following forms:
 *  username
 *  username/resource
 *  username@server
 *  username@server/resource
 * and sets the properties for username, stream server and resource
 * appropriately. Also sets the connect server to the stream server if one has
 * not yet been specified.
 */

gboolean
_gabble_connection_set_properties_from_account (GabbleConnection *conn,
                                                const gchar      *account,
                                                GError          **error)
{
  GabbleConnectionPrivate *priv;
  char *username, *server, *resource;
  gboolean result;

  g_assert (GABBLE_IS_CONNECTION (conn));
  g_assert (account != NULL);

  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  username = server = resource = NULL;
  result = TRUE;

  gabble_decode_jid (account, &username, &server, &resource);

  if (username == NULL || server == NULL ||
      *username == '\0' || *server == '\0')
    {
      g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
          "unable to get username and server from account");
      result = FALSE;
      goto OUT;
    }

  g_object_set (G_OBJECT (conn),
                "username", username,
                "stream-server", server,
                NULL);

  /* only override the default resource if we actually got one */
  if (resource)
    g_object_set (G_OBJECT (conn), "resource", resource, NULL);

OUT:
  g_free (username);
  g_free (server);
  g_free (resource);

  return result;
}

/**
 * _gabble_connection_register
 *
 * Make the connection object appear on the bus, returning the bus
 * name and object path used.
 */
gboolean
_gabble_connection_register (GabbleConnection *conn,
                             gchar           **bus_name,
                             gchar           **object_path,
                             GError          **error)
{
  DBusGConnection *bus;
  DBusGProxy *bus_proxy;
  GabbleConnectionPrivate *priv;
  const char *allowed_chars = "_1234567890"
                              "abcdefghijklmnopqrstuvwxyz"
                              "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  char *safe_proto;
  char *unique_name;
  guint request_name_result;
  GError *request_error;

  g_message("1 Inside _gabble_connection_register() method");
  g_assert (GABBLE_IS_CONNECTION (conn));

  bus = tp_get_bus ();
  bus_proxy = tp_get_bus_proxy ();
  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  safe_proto = g_strdup (priv->protocol);
  g_strcanon (safe_proto, allowed_chars, '_');

  unique_name = g_strdup_printf ("_%s_%s_%s",
                                 priv->username,
                                 priv->stream_server,
                                 priv->resource);
  g_strcanon (unique_name, allowed_chars, '_');

  conn->bus_name = g_strdup_printf (BUS_NAME ".%s.%s",
                                    safe_proto,
                                    unique_name);
  conn->object_path = g_strdup_printf (OBJECT_PATH "/%s/%s",
                                       safe_proto,
                                       unique_name);

  g_free (safe_proto);
  g_free (unique_name);

  if (!dbus_g_proxy_call (bus_proxy, "RequestName", &request_error,
                          G_TYPE_STRING, conn->bus_name,
                          G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
                          G_TYPE_INVALID,
                          G_TYPE_UINT, &request_name_result,
                          G_TYPE_INVALID))
    {
      g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
          "Error acquiring bus name %s: %s", conn->bus_name,
          request_error->message);

      g_error_free (request_error);

      g_free (conn->bus_name);
      conn->bus_name = NULL;

      return FALSE;
    }

  if (request_name_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
    {
      gchar *msg;

      switch (request_name_result)
        {
        case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
          msg = "Request has been queued, though we request non-queueing.";
          break;
        case DBUS_REQUEST_NAME_REPLY_EXISTS:
          msg = "A connection manger already has this busname.";
          break;
        case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
          msg = "Connection manager already has a connection to this account.";
          break;
        default:
          msg = "Unknown error return from ReleaseName";
        }

      g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
          "Error acquiring bus name %s: %s", conn->bus_name, msg);

      g_free (conn->bus_name);
      conn->bus_name = NULL;

      return FALSE;
    }

  gabble_debug (DEBUG_FLAG, "bus name %s", conn->bus_name);

  dbus_g_connection_register_g_object (bus, conn->object_path, G_OBJECT (conn));

  gabble_debug (DEBUG_FLAG, "object path %s", conn->object_path);

  *bus_name = g_strdup (conn->bus_name);
  *object_path = g_strdup (conn->object_path);

  return TRUE;
}


/**
 * _gabble_connection_send
 *
 * Send an LmMessage and trap network errors appropriately.
 */
gboolean
_gabble_connection_send (GabbleConnection *conn, LmMessage *msg, GError **error)
{
  GabbleConnectionPrivate *priv;
  GError *lmerror = NULL;

  g_assert (GABBLE_IS_CONNECTION (conn));

  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  if (!lm_connection_send (conn->lmconn, msg, &lmerror))
    {
      gabble_debug (DEBUG_FLAG, "failed: %s", lmerror->message);

      g_set_error (error, TELEPATHY_ERRORS, NetworkError,
          "message send failed: %s", lmerror->message);

      g_error_free (lmerror);

      return FALSE;
    }

  return TRUE;
}

typedef struct {
    GabbleConnectionMsgReplyFunc reply_func;

    GabbleConnection *conn;
    LmMessage *sent_msg;
    gpointer user_data;

    GObject *object;
    gboolean object_alive;
} GabbleMsgHandlerData;

static LmHandlerResult
message_send_reply_cb (LmMessageHandler *handler,
                       LmConnection *connection,
                       LmMessage *reply_msg,
                       gpointer user_data)
{
  GabbleMsgHandlerData *handler_data = user_data;
  LmMessageSubType sub_type;

  sub_type = lm_message_get_sub_type (reply_msg);

  /* Is it a reply to this message? If we're talking to another loudmouth,
   * they can send us messages which have the same ID as ones we send. :-O */
  if (sub_type != LM_MESSAGE_SUB_TYPE_RESULT &&
      sub_type != LM_MESSAGE_SUB_TYPE_ERROR)
    {
      return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
    }

  if (handler_data->object_alive)
    {
      return handler_data->reply_func (handler_data->conn,
                                       handler_data->sent_msg,
                                       reply_msg,
                                       handler_data->object,
                                       handler_data->user_data);
    }

  return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}

static void
message_send_object_destroy_notify_cb (gpointer data,
                                       GObject *where_the_object_was)
{
  GabbleMsgHandlerData *handler_data = data;

  handler_data->object = NULL;
  handler_data->object_alive = FALSE;
}

static void
message_send_handler_destroy_cb (gpointer data)
{
  GabbleMsgHandlerData *handler_data = data;

  lm_message_unref (handler_data->sent_msg);

  if (handler_data->object != NULL)
    {
      g_object_weak_unref (handler_data->object,
                           message_send_object_destroy_notify_cb,
                           handler_data);
    }

  g_free (handler_data);
}

/**
 * _gabble_connection_send_with_reply
 *
 * Send a tracked LmMessage and trap network errors appropriately.
 *
 * If object is non-NULL the handler will follow the lifetime of that object,
 * which means that if the object is destroyed the callback will not be invoked.
 */
gboolean
_gabble_connection_send_with_reply (GabbleConnection *conn,
                                    LmMessage *msg,
                                    GabbleConnectionMsgReplyFunc reply_func,
                                    GObject *object,
                                    gpointer user_data,
                                    GError **error)
{
  GabbleConnectionPrivate *priv;
  LmMessageHandler *handler;
  GabbleMsgHandlerData *handler_data;
  gboolean ret;
  GError *lmerror = NULL;

  g_assert (GABBLE_IS_CONNECTION (conn));

  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  lm_message_ref (msg);

  handler_data = g_new (GabbleMsgHandlerData, 1);
  handler_data->reply_func = reply_func;
  handler_data->conn = conn;
  handler_data->sent_msg = msg;
  handler_data->user_data = user_data;

  handler_data->object = object;
  handler_data->object_alive = TRUE;

  if (object != NULL)
    {
      g_object_weak_ref (object, message_send_object_destroy_notify_cb,
                         handler_data);
    }

  handler = lm_message_handler_new (message_send_reply_cb, handler_data,
                                    message_send_handler_destroy_cb);

  ret = lm_connection_send_with_reply (conn->lmconn, msg, handler, &lmerror);
  if (!ret)
    {
      gabble_debug (DEBUG_FLAG, "failed: %s", lmerror->message);

      if (error)
        {
          g_set_error (error, TELEPATHY_ERRORS, NetworkError,
              "message send failed: %s", lmerror->message);
        }

      g_error_free (lmerror);
    }

  lm_message_handler_unref (handler);

  return ret;
}

static LmHandlerResult connection_iq_disco_cb (LmMessageHandler*, LmConnection*, LmMessage*, gpointer);
static LmHandlerResult connection_iq_unknown_cb (LmMessageHandler*, LmConnection*, LmMessage*, gpointer);
static LmHandlerResult connection_stream_error_cb (LmMessageHandler*, LmConnection*, LmMessage*, gpointer);
static LmSSLResponse connection_ssl_cb (LmSSL*, LmSSLStatus, gpointer);
static void connection_open_cb (LmConnection*, gboolean, gpointer);
static void connection_auth_cb (LmConnection*, gboolean, gpointer);
static void connection_disco_cb (GabbleDisco *, GabbleDiscoRequest *, const gchar *, const gchar *, LmMessageNode *, GError *, gpointer);
static void connection_disconnected_cb (LmConnection *, LmDisconnectReason, gpointer);
static void connection_status_change (GabbleConnection *, TpConnectionStatus, TpConnectionStatusReason);

static void channel_request_cancel (gpointer data, gpointer user_data);

static void emit_one_presence_update (GabbleConnection *self, GabbleHandle handle);


static gboolean
do_connect (GabbleConnection *conn, GError **error)
{
  GError *lmerror = NULL;

  gabble_debug (DEBUG_FLAG, "calling lm_connection_open");
	g_message("**gabble do_connect: before calling lm_connection_open\n");	

  if (!lm_connection_open (conn->lmconn, connection_open_cb,
                           conn, NULL, &lmerror))
    {
      gabble_debug (DEBUG_FLAG, "lm_connection_open failed %s", lmerror->message);

      g_set_error (error, TELEPATHY_ERRORS, NetworkError,
          "lm_connection_open failed: %s", lmerror->message);

      g_signal_emit (conn, signals[DISCONNECTED], 0);
      g_error_free (lmerror);

      return FALSE;
    }

	g_message("**gabble do_connect: after calling lm_connection_open and it passed\n");	

  return TRUE;
}

static void
connect_callbacks (GabbleConnection *conn)
{
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  g_message("inside connect_callbacks\n");
  g_assert (priv->iq_search_keys_cb == NULL);
  g_assert (priv->iq_jingle_info_cb == NULL);
  g_assert (priv->iq_disco_cb == NULL);
  g_assert (priv->iq_unknown_cb == NULL);
  g_assert (priv->stream_error_cb == NULL);
  
  
  
  priv->iq_search_keys_cb = lm_message_handler_new (search_keys_iq_cb,
                                                    conn, NULL);
  lm_connection_register_message_handler (conn->lmconn,
                                          priv->iq_search_keys_cb,
                                          LM_MESSAGE_TYPE_IQ,
                                          LM_HANDLER_PRIORITY_NORMAL);

  priv->iq_jingle_info_cb = lm_message_handler_new (jingle_info_iq_callback,
                                                    conn, NULL);
  lm_connection_register_message_handler (conn->lmconn,
                                          priv->iq_jingle_info_cb,
                                          LM_MESSAGE_TYPE_IQ,
                                          LM_HANDLER_PRIORITY_NORMAL);

  priv->iq_disco_cb = lm_message_handler_new (connection_iq_disco_cb,
                                              conn, NULL);
  lm_connection_register_message_handler (conn->lmconn, priv->iq_disco_cb,
                                          LM_MESSAGE_TYPE_IQ,
                                          LM_HANDLER_PRIORITY_NORMAL);

  priv->iq_unknown_cb = lm_message_handler_new (connection_iq_unknown_cb,
                                            conn, NULL);
  lm_connection_register_message_handler (conn->lmconn, priv->iq_unknown_cb,
                                          LM_MESSAGE_TYPE_IQ,
                                          LM_HANDLER_PRIORITY_LAST);

  priv->stream_error_cb = lm_message_handler_new (connection_stream_error_cb,
                                            conn, NULL);
  lm_connection_register_message_handler (conn->lmconn, priv->stream_error_cb,
                                          LM_MESSAGE_TYPE_STREAM_ERROR,
                                          LM_HANDLER_PRIORITY_LAST);
  g_message("leaving connect_callbacks\n");
}

static void
disconnect_callbacks (GabbleConnection *conn)
{
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  g_message("[disconnect_callbacks]");
  g_assert (priv->iq_search_keys_cb != NULL);
  g_assert (priv->iq_jingle_info_cb != NULL);
  g_assert (priv->iq_disco_cb != NULL);
  g_assert (priv->iq_unknown_cb != NULL);
  g_assert (priv->stream_error_cb != NULL);
  
  lm_connection_unregister_message_handler (conn->lmconn, priv->iq_search_keys_cb,
                                            LM_MESSAGE_TYPE_IQ);
  lm_message_handler_unref (priv->iq_search_keys_cb);
  priv->iq_search_keys_cb = NULL;

  lm_connection_unregister_message_handler (conn->lmconn, priv->iq_jingle_info_cb,
                                            LM_MESSAGE_TYPE_IQ);
  lm_message_handler_unref (priv->iq_jingle_info_cb);
  priv->iq_jingle_info_cb = NULL;

  lm_connection_unregister_message_handler (conn->lmconn, priv->iq_disco_cb,
                                            LM_MESSAGE_TYPE_IQ);
  lm_message_handler_unref (priv->iq_disco_cb);
  priv->iq_disco_cb = NULL;

  lm_connection_unregister_message_handler (conn->lmconn, priv->iq_unknown_cb,
                                            LM_MESSAGE_TYPE_IQ);
  lm_message_handler_unref (priv->iq_unknown_cb);
  priv->iq_unknown_cb = NULL;

  lm_connection_unregister_message_handler (conn->lmconn, priv->stream_error_cb,
                                            LM_MESSAGE_TYPE_STREAM_ERROR);
  lm_message_handler_unref (priv->stream_error_cb);
  priv->stream_error_cb = NULL;
}

/**
 * _gabble_connection_connect
 *
 * Use the stored server & authentication details to commence
 * the stages for connecting to the server and authenticating. Will
 * re-use an existing LmConnection if it is present, or create it
 * if necessary.
 *
 * Stage 1 is _gabble_connection_connect calling lm_connection_open
 * Stage 2 is connection_open_cb calling lm_connection_authenticate
 * Stage 3 is connection_auth_cb initiating service discovery
 * Stage 4 is connection_disco_cb advertising initial presence, requesting
 *   the roster and setting the CONNECTED state
 */
static gboolean
_gabble_connection_connect (GabbleConnection *conn,
                            GError          **error)
{
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  char *jid;
  GabblePresence *presence;
  g_message("[_gabble_connection_connect]");
  g_assert (priv->port > 0 && priv->port <= G_MAXUINT16);
  g_assert (priv->stream_server != NULL);
  g_assert (priv->username != NULL);
  g_assert (priv->password != NULL);
  g_assert (priv->resource != NULL);
  g_assert (lm_connection_is_open (conn->lmconn) == FALSE);

  g_message("In _gabble_connection_connect" );
  
  jid = g_strdup_printf ("%s@%s", priv->username, priv->stream_server);
  lm_connection_set_jid (conn->lmconn, jid);

  conn->self_handle = gabble_handle_for_contact (conn->handles,
                                                 jid, FALSE);
  g_free (jid);

  if (conn->self_handle == 0)
    {
      g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
          "Invalid JID: %s@%s", priv->username, priv->stream_server);
      return FALSE;
    }
  gabble_handle_ref (conn->handles, TP_HANDLE_TYPE_CONTACT, conn->self_handle);

  /* set initial presence */
  /* TODO: some way for the user to set this */
  gabble_presence_cache_update (conn->presence_cache, conn->self_handle,
      priv->resource, GABBLE_PRESENCE_AVAILABLE, NULL, priv->priority);
  emit_one_presence_update (conn, conn->self_handle);

  /* set initial capabilities */
  presence = gabble_presence_cache_get (conn->presence_cache, conn->self_handle);

  gabble_presence_set_capabilities (presence, priv->resource,
      capabilities_get_initial_caps (), priv->caps_serial++);

  /* always override server and port if one was forced upon us */
  if (priv->connect_server != NULL)
    {
      lm_connection_set_server (conn->lmconn, priv->connect_server);
      lm_connection_set_port (conn->lmconn, priv->port);
      g_message("LM Server is %s \n",priv->connect_server );
  	  g_message("LM port is %ld \n",priv->port);
    }
  /* otherwise set the server & port to the stream server,
   * if one didn't appear from a SRV lookup */
  else if (lm_connection_get_server (conn->lmconn) == NULL)
    {
      lm_connection_set_server (conn->lmconn, priv->stream_server);
      lm_connection_set_port (conn->lmconn, priv->port);
    }

  
  if (priv->https_proxy_server)
    {
      LmProxy *proxy;

      proxy = lm_proxy_new_with_server (LM_PROXY_TYPE_HTTP,
          priv->https_proxy_server, priv->https_proxy_port);

      lm_connection_set_proxy (conn->lmconn, proxy);

      lm_proxy_unref (proxy);
    }

  if (priv->old_ssl)
    {
      LmSSL *ssl = lm_ssl_new (NULL, connection_ssl_cb, conn, NULL);
      lm_connection_set_ssl (conn->lmconn, ssl);
      lm_ssl_unref (ssl);
    }
  else //if we want to use tls (not old ssl?) then need to set tls flags.
    {
      LmSSL *ssl = lm_ssl_new (NULL, connection_ssl_cb, conn, NULL);
      lm_connection_set_ssl (conn->lmconn, ssl);

      lm_ssl_use_starttls (ssl, TRUE, TRUE);

	  lm_ssl_unref (ssl);
    }

  /* send whitespace to the server every 30 seconds */
  //lm_connection_set_keep_alive_rate (conn->lmconn, 45);

  lm_connection_set_disconnect_function (conn->lmconn,
                                         connection_disconnected_cb,
                                         conn,
                                         NULL);

  if (do_connect (conn, error))
    {
      gboolean valid;

      connection_status_change (conn,
          TP_CONN_STATUS_CONNECTING,
          TP_CONN_STATUS_REASON_REQUESTED);

      valid = gabble_handle_ref (conn->handles,
                                 TP_HANDLE_TYPE_CONTACT,
                                 conn->self_handle);
      g_message("before after valid assert" );
      g_assert (valid);
      g_message("In after valid assert" );
    }
  else
    {
      return FALSE;
    }
  g_message("Out _gabble_connection_connect" );
  return TRUE;
}



static void
connection_disconnected_cb (LmConnection *lmconn,
                            LmDisconnectReason lm_reason,
                            gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);
  g_message("[connection_disconnected_cb]\n");
  g_assert (conn->lmconn == lmconn);
 
  gabble_debug (DEBUG_FLAG, "called with reason %u", lm_reason);

  /* if we were expecting this disconnection, we're done so can tell
   * the connection manager to unref us. otherwise it's a network error
   * or some other screw up we didn't expect, so we emit the status
   * change */
  if (conn->status == TP_CONN_STATUS_DISCONNECTED)
    {
      g_message("[connection_disconnected_cb]expected; emitting DISCONNECTED");
      g_signal_emit (conn, signals[DISCONNECTED], 0);
    }
  else
    {
      g_message("[connection_disconnected_cb]unexpected; calling connection_status_change");
      connection_status_change (conn,
          TP_CONN_STATUS_DISCONNECTED,
          TP_CONN_STATUS_REASON_NETWORK_ERROR);
    }
}


/**
 * connection_status_change:
 * @conn: a #GabbleConnection
 * @status: new status to advertise
 * @reason: reason for new status
 *
 * Compares status with current status. If different, emits a signal
 * for the new status, and updates it in the #GabbleConnection.
 */
static void
connection_status_change (GabbleConnection        *conn,
                          TpConnectionStatus       status,
                          TpConnectionStatusReason reason)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (conn));

  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  g_message ("[connection_status_change]status %u reason %u", status, reason);

  g_assert (status != TP_CONN_STATUS_NEW);

  if (conn->status != status)
    {
      if ((status == TP_CONN_STATUS_DISCONNECTED) &&
          (conn->status == TP_CONN_STATUS_NEW))
        {
          g_message("[connection_status_change] line 1721");
          conn->status = status;

          /* unref our self handle if it's set */
          if (conn->self_handle != 0)
            {
              gabble_handle_unref (conn->handles, TP_HANDLE_TYPE_CONTACT,
                  conn->self_handle);
              conn->self_handle = 0;
            }

          g_message("[connection_status_change]new connection closed; emitting DISCONNECTED");
          g_signal_emit (conn, signals[DISCONNECTED], 0);
          return;
        }

      conn->status = status;

      if (status == TP_CONN_STATUS_DISCONNECTED)
        {
          /* remove the channels so we don't get any race conditions where
           * method calls are delivered to a channel after we've started
           * disconnecting */
        g_message("[connection_status_change] TP_CONN_STATUS_DISCONNECTED");

          /* trigger close_all on all channel factories */
          g_ptr_array_foreach (priv->channel_factories, (GFunc)
              tp_channel_factory_iface_close_all, NULL);

          /* cancel all queued channel requests */
          if (priv->channel_requests->len > 0)
            {
              g_ptr_array_foreach (priv->channel_requests, (GFunc)
                channel_request_cancel, NULL);
              g_ptr_array_remove_range (priv->channel_requests, 0,
                priv->channel_requests->len);
            }

          /* unref our self handle */
          gabble_handle_unref (conn->handles, TP_HANDLE_TYPE_CONTACT,
              conn->self_handle);
          conn->self_handle = 0;
        }

      g_message("[connection_status_change]emitting status-changed with status %u reason %u",
               status, reason);

      g_signal_emit (conn, signals[STATUS_CHANGED], 0, status, reason);

      if (status == TP_CONN_STATUS_CONNECTING)
        {
          /* add our callbacks */
          connect_callbacks (conn);

          /* trigger connecting on all channel factories */
          g_ptr_array_foreach (priv->channel_factories, (GFunc)
              tp_channel_factory_iface_connecting, NULL);
        }
      else if (status == TP_CONN_STATUS_CONNECTED)
        {
        /* send whitespace to the server every 30 seconds resetting to 30*/
          lm_connection_set_keep_alive_rate (conn->lmconn, 10);
          
          /* trigger connected on all channel factories */
          g_ptr_array_foreach (priv->channel_factories, (GFunc)
              tp_channel_factory_iface_connected, NULL);
        }
      else if (status == TP_CONN_STATUS_DISCONNECTED)
        {
          /* remove our callbacks */
          disconnect_callbacks (conn);

          /* trigger disconnected on all channel factories */
          g_ptr_array_foreach (priv->channel_factories, (GFunc)
              tp_channel_factory_iface_disconnected, NULL);

          /* if the connection is open, this function will close it for you.
           * if it's already closed (eg network error) then we're done, so
           * can emit DISCONNECTED and have the connection manager unref us */
          if (lm_connection_is_open (conn->lmconn))
            {
              g_message ("still open; calling lm_connection_close");
              lm_connection_close (conn->lmconn, NULL);
            }
          else
            {
              /* lm_connection_is_open() returns FALSE if LmConnection is in the
               * middle of connecting, so call this just in case */
              lm_connection_cancel_open (conn->lmconn);
              g_message ("closed; emitting DISCONNECTED");
              g_signal_emit (conn, signals[DISCONNECTED], 0);
            }
        }
    }
  else
    {
      g_warning ("%s: attempted to re-emit the current status %u reason %u",
          G_STRFUNC, status, reason);
    }
  g_message("[connection_status_change]");
  g_message ("out connection status changed ");
}

static ChannelRequest *
channel_request_new (DBusGMethodInvocation *context,
                     const char *channel_type,
                     guint handle_type,
                     guint handle,
                     gboolean suppress_handler)
{
  ChannelRequest *ret;

  g_assert (NULL != context);
  g_assert (NULL != channel_type);

  ret = g_new0 (ChannelRequest, 1);
  ret->context = context;
  ret->channel_type = g_strdup (channel_type);
  ret->handle_type = handle_type;
  ret->handle = handle;
  ret->suppress_handler = suppress_handler;

  return ret;
}

static void
channel_request_free (ChannelRequest *request)
{
  g_assert (NULL == request->context);
  g_free (request->channel_type);
  g_free (request);
}

static void
channel_request_cancel (gpointer data, gpointer user_data)
{
  ChannelRequest *request = (ChannelRequest *) data;
  GError *error = NULL;

  gabble_debug (DEBUG_FLAG, "cancelling request for %s/%d/%d", request->channel_type, request->handle_type, request->handle);

  error = g_error_new (TELEPATHY_ERRORS, Disconnected, "unable to "
      "service this channel request, we're disconnecting!");

  dbus_g_method_return_error (request->context, error);
  request->context = NULL;

  g_error_free (error);
  channel_request_free (request);
}

static GPtrArray *
find_matching_channel_requests (GabbleConnection *conn,
                                const gchar *channel_type,
                                guint handle_type,
                                guint handle,
                                gboolean *suppress_handler)
{
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  GPtrArray *requests;
  guint i;

  requests = g_ptr_array_sized_new (1);

  for (i = 0; i < priv->channel_requests->len; i++)
    {
      ChannelRequest *request = g_ptr_array_index (priv->channel_requests, i);

      if (0 != strcmp (request->channel_type, channel_type))
        continue;

      if (handle_type != request->handle_type)
        continue;

      if (handle != request->handle)
        continue;

      if (request->suppress_handler && suppress_handler)
        *suppress_handler = TRUE;

      g_ptr_array_add (requests, request);
    }

  return requests;
}

static void
connection_new_channel_cb (TpChannelFactoryIface *factory,
                           GObject *chan,
                           gpointer data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (data);
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  gchar *object_path = NULL, *channel_type = NULL;
  guint handle_type = 0, handle = 0;
  gboolean suppress_handler = priv->suppress_next_handler;
  GPtrArray *tmp;
  guint i;

  g_object_get (chan,
      "object-path", &object_path,
      "channel-type", &channel_type,
      "handle-type", &handle_type,
      "handle", &handle,
      NULL);

  gabble_debug (DEBUG_FLAG, "called for %s", object_path);

  tmp = find_matching_channel_requests (conn, channel_type, handle_type,
                                        handle, &suppress_handler);

  g_signal_emit (conn, signals[NEW_CHANNEL], 0,
                 object_path, channel_type,
                 handle_type, handle,
                 suppress_handler);

  for (i = 0; i < tmp->len; i++)
    {
      ChannelRequest *request = g_ptr_array_index (tmp, i);

      gabble_debug (DEBUG_FLAG, "completing queued request, channel_type=%s, handle_type=%u, "
          "handle=%u, suppress_handler=%u", request->channel_type,
          request->handle_type, request->handle, request->suppress_handler);

      dbus_g_method_return (request->context, object_path);
      request->context = NULL;

      g_ptr_array_remove (priv->channel_requests, request);

      channel_request_free (request);
    }

  g_ptr_array_free (tmp, TRUE);

  g_free (object_path);
  g_free (channel_type);
}

static void
connection_channel_error_cb (TpChannelFactoryIface *factory,
                             GObject *chan,
                             GError *error,
                             gpointer data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (data);
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  gchar *channel_type = NULL;
  guint handle_type = 0, handle = 0;
  GPtrArray *tmp;
  guint i;

  gabble_debug (DEBUG_FLAG, "channel_type=%s, handle_type=%u, handle=%u, error_code=%u, "
      "error_message=\"%s\"", channel_type, handle_type, handle,
      error->code, error->message);

  g_object_get (chan,
      "channel-type", &channel_type,
      "handle-type", &handle_type,
      "handle", &handle,
      NULL);

  tmp = find_matching_channel_requests (conn, channel_type, handle_type,
                                        handle, NULL);

  for (i = 0; i < tmp->len; i++)
    {
      ChannelRequest *request = g_ptr_array_index (tmp, i);

      gabble_debug (DEBUG_FLAG, "completing queued request %p, channel_type=%s, "
          "handle_type=%u, handle=%u, suppress_handler=%u",
          request, request->channel_type,
          request->handle_type, request->handle, request->suppress_handler);

      dbus_g_method_return_error (request->context, error);
      request->context = NULL;

      g_ptr_array_remove (priv->channel_requests, request);

      channel_request_free (request);
    }

  g_ptr_array_free (tmp, TRUE);
  g_free (channel_type);
}

static void
connection_presence_update_cb (GabblePresenceCache *cache, GabbleHandle handle, gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);

  emit_one_presence_update (conn, handle);
}

GabbleConnectionAliasSource
_gabble_connection_get_cached_alias (GabbleConnection *conn,
                                     GabbleHandle handle,
                                     gchar **alias)
{
  GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (conn,
      GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate);
  GabbleConnectionAliasSource ret = GABBLE_CONNECTION_ALIAS_NONE;
  GabblePresence *pres;
  const gchar *tmp;
  gchar *user = NULL, *resource = NULL;

  g_return_val_if_fail (NULL != conn, GABBLE_CONNECTION_ALIAS_NONE);
  g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), GABBLE_CONNECTION_ALIAS_NONE);
  g_return_val_if_fail (gabble_handle_is_valid (conn->handles,
        TP_HANDLE_TYPE_CONTACT, handle, NULL), GABBLE_CONNECTION_ALIAS_NONE);

  tmp = gabble_roster_handle_get_name (conn->roster, handle);
  if (NULL != tmp)
    {
      ret = GABBLE_CONNECTION_ALIAS_FROM_ROSTER;

      if (NULL != alias)
        *alias = g_strdup (tmp);

      goto OUT;
    }

  pres = gabble_presence_cache_get (conn->presence_cache, handle);
  if (NULL != pres && NULL != pres->nickname)
    {
      ret = GABBLE_CONNECTION_ALIAS_FROM_PRESENCE;

      if (NULL != alias)
        *alias = g_strdup (pres->nickname);

      goto OUT;
    }

  /* if it's our own handle, use alias passed to the connmgr, if any */
  if (handle == conn->self_handle && priv->alias != NULL)
    {
      ret = GABBLE_CONNECTION_ALIAS_FROM_CONNMGR;

      if (NULL != alias)
        *alias = g_strdup (priv->alias);

      goto OUT;
    }

  /* if we've seen a nickname in their vCard, use that */
  tmp = gabble_vcard_manager_get_cached_alias (conn->vcard_manager, handle);
  if (NULL != tmp)
    {
      ret = GABBLE_CONNECTION_ALIAS_FROM_VCARD;

      if (NULL != alias)
        *alias = g_strdup (tmp);

      goto OUT;
    }

  /* fallback to JID */
  tmp = gabble_handle_inspect (conn->handles, TP_HANDLE_TYPE_CONTACT, handle);
  g_assert (NULL != tmp);

  gabble_decode_jid (tmp, &user, NULL, &resource);

  /* MUC handles have the nickname in the resource */
  if (NULL != resource)
    {
      ret = GABBLE_CONNECTION_ALIAS_FROM_JID;

      if (NULL != alias)
        {
          *alias = resource;
          resource = NULL;
        }

      goto OUT;
    }

  /* otherwise just take their local part */
  if (NULL != user)
    {
      ret = GABBLE_CONNECTION_ALIAS_FROM_JID;

      if (NULL != alias)
        {
          *alias = user;
          user = NULL;
        }

      goto OUT;
    }

OUT:
  g_free (user);
  g_free (resource);
  return ret;
}

static void
connection_nickname_update_cb (GObject *object,
                               GabbleHandle handle,
                               gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);
  GabbleConnectionAliasSource signal_source, real_source;
  gchar *alias = NULL;
  GPtrArray *aliases;
  GValue entry = { 0, };

  if (object == G_OBJECT (conn->roster))
    {
      signal_source = GABBLE_CONNECTION_ALIAS_FROM_ROSTER;
    }
  else if (object == G_OBJECT (conn->presence_cache))
    {
      signal_source = GABBLE_CONNECTION_ALIAS_FROM_PRESENCE;
    }
   else if (object == G_OBJECT (conn->vcard_manager))
     {
       signal_source = GABBLE_CONNECTION_ALIAS_FROM_VCARD;
     }
  else
    {
      g_assert_not_reached ();
      return;
    }

  real_source = _gabble_connection_get_cached_alias (conn, handle, &alias);

  g_assert (real_source != GABBLE_CONNECTION_ALIAS_NONE);

  /* if the active alias for this handle is already known and from
   * a higher priority, this signal is not interesting so we do
   * nothing */
  if (real_source > signal_source)
    {
      gabble_debug (DEBUG_FLAG, "ignoring boring alias change for handle %u, signal from %u "
          "but source %u has alias \"%s\"", handle, signal_source,
          real_source, alias);
      goto OUT;
    }

  g_value_init (&entry, TP_ALIAS_PAIR_TYPE);
  g_value_take_boxed (&entry, dbus_g_type_specialized_construct
      (TP_ALIAS_PAIR_TYPE));

  dbus_g_type_struct_set (&entry,
      0, handle,
      1, alias,
      G_MAXUINT);

  aliases = g_ptr_array_sized_new (1);
  g_ptr_array_add (aliases, g_value_get_boxed (&entry));

  g_signal_emit (conn, signals[ALIASES_CHANGED], 0, aliases);

  g_value_unset (&entry);
  g_ptr_array_free (aliases, TRUE);

OUT:
  g_free (alias);
}

/**
 * status_is_available
 *
 * Returns a boolean to indicate whether the given gabble status is
 * available on this connection.
 */
static gboolean
status_is_available (GabbleConnection *conn, int status)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (conn));
  g_assert (status < LAST_GABBLE_PRESENCE);
  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  if (gabble_statuses[status].presence_type == TP_CONN_PRESENCE_TYPE_HIDDEN &&
      (conn->features & GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE) == 0)
    return FALSE;
  else
    return TRUE;
}

/**
 * destroy_the_bastard:
 * @data: a GValue to destroy
 *
 * destroys a GValue allocated on the heap
 */
static void
destroy_the_bastard (GValue *value)
{
  g_value_unset (value);
  g_free (value);
}

static GHashTable *
construct_presence_hash (GabbleConnection *self,
                         const GArray *contact_handles)
{
  guint i;
  GabbleHandle handle;
  GHashTable *presence_hash, *contact_status, *parameters;
  GValueArray *vals;
  GValue *message;
  GabblePresence *presence;
  GabblePresenceId status;
  const gchar *status_message;
  /* this is never set at the moment*/
  guint timestamp = 0;

  g_assert (gabble_handles_are_valid (self->handles, TP_HANDLE_TYPE_CONTACT,
        contact_handles, FALSE, NULL));

  presence_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
      (GDestroyNotify) g_value_array_free);

  for (i = 0; i < contact_handles->len; i++)
    {
      handle = g_array_index (contact_handles, GabbleHandle, i);
      presence = gabble_presence_cache_get (self->presence_cache, handle);

      if (presence)
        {
          status = presence->status;
          status_message = presence->status_message;
        }
      else
        {
          status = GABBLE_PRESENCE_OFFLINE;
          status_message = NULL;
        }

      message = g_new0 (GValue, 1);
      g_value_init (message, G_TYPE_STRING);
      g_value_set_static_string (message, status_message);

      parameters = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
          (GDestroyNotify) destroy_the_bastard);

      g_hash_table_insert (parameters, "message", message);

      contact_status = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
          (GDestroyNotify) g_hash_table_destroy);
      g_hash_table_insert (contact_status,
          (gchar *) gabble_statuses[status].name, parameters);

      vals = g_value_array_new (2);

      g_value_array_append (vals, NULL);
      g_value_init (g_value_array_get_nth (vals, 0), G_TYPE_UINT);
      g_value_set_uint (g_value_array_get_nth (vals, 0), timestamp);

      g_value_array_append (vals, NULL);
      g_value_init (g_value_array_get_nth (vals, 1),
          dbus_g_type_get_map ("GHashTable", G_TYPE_STRING,
            dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)));
      g_value_take_boxed (g_value_array_get_nth (vals, 1), contact_status);

      g_hash_table_insert (presence_hash, GINT_TO_POINTER (handle), vals);
    }

  return presence_hash;
}

/**
 * emit_presence_update:
 * @self: A #GabbleConnection
 * @contact_handles: A zero-terminated array of #GabbleHandle for
 *                    the contacts to emit presence for
 *
 * Emits the Telepathy PresenceUpdate signal with the current
 * stored presence information for the given contact.
 */
static void
emit_presence_update (GabbleConnection *self,
                      const GArray *contact_handles)
{
  GHashTable *presence_hash;

  presence_hash = construct_presence_hash (self, contact_handles);
  g_signal_emit (self, signals[PRESENCE_UPDATE], 0, presence_hash);
  g_hash_table_destroy (presence_hash);
}

/**
 * emit_one_presence_update:
 * Convenience function for calling emit_presence_update with one handle.
 */

static void
emit_one_presence_update (GabbleConnection *self,
                          GabbleHandle handle)
{
  GArray *handles = g_array_sized_new (FALSE, FALSE, sizeof (GabbleHandle), 1);

  g_array_insert_val (handles, 0, handle);
  emit_presence_update (self, handles);
  g_array_free (handles, TRUE);
}

/**
 * signal_own_presence:
 * @self: A #GabbleConnection
 * @error: pointer in which to return a GError in case of failure.
 *
 * Signal the user's stored presence to the jabber server
 *
 * Retuns: FALSE if an error occurred
 */
static gboolean
signal_own_presence (GabbleConnection *self, GError **error)
{
  
  GabblePresence *presence;
  LmMessage *message;
  LmMessageNode *node;
  gboolean ret;
  gchar *ext_string = NULL;
  GSList *features, *i;
  GabbleConnectionPrivate *priv;
  
  if ( NULL == self )
      {
      g_debug ("%s: accesing after dereferenced connection", G_STRFUNC);
      return FALSE;
      }
  
  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  presence = gabble_presence_cache_get (self->presence_cache, self->self_handle);
  message = gabble_presence_as_message (presence, priv->resource);
  node = lm_message_get_node (message);
  
  if (presence->status == GABBLE_PRESENCE_HIDDEN)
    {
      if ((self->features & GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE) != 0)
        lm_message_node_set_attribute (node, "type", "invisible");
    }

  features = capabilities_get_features (presence->caps);

  for (i = features; NULL != i; i = i->next)
    {
      const Feature *feat = (const Feature *) i->data;

      if ((NULL != feat->bundle) && g_strdiff (VERSION, feat->bundle))
        {
          if (NULL != ext_string)
            {
              gchar *tmp = ext_string;
              ext_string = g_strdup_printf ("%s %s", ext_string, feat->bundle);
              g_free (tmp);
            }
          else
            {
              ext_string = g_strdup (feat->bundle);
            }
        }
    }

  node = lm_message_node_add_child (node, "c", NULL);
  lm_message_node_set_attributes (
    node,
    "xmlns", NS_CAPS,
    "node",  NS_GABBLE_CAPS,
    "ver",   VERSION,
    NULL);

  if (NULL != ext_string)
      lm_message_node_set_attribute (node, "ext", ext_string);

  ret = _gabble_connection_send (self, message, error);

  lm_message_unref (message);

  g_free (ext_string);
  g_slist_free (features);

  return ret;
}

static LmMessage *_lm_iq_message_make_result (LmMessage *iq_message);

/**
 * _gabble_connection_send_iq_result
 *
 * Function used to acknowledge an IQ stanza.
 */
void
_gabble_connection_acknowledge_set_iq (GabbleConnection *conn,
                                       LmMessage *iq)
{
  LmMessage *result;

  g_assert (LM_MESSAGE_TYPE_IQ == lm_message_get_type (iq));
  g_assert (LM_MESSAGE_SUB_TYPE_SET == lm_message_get_sub_type (iq));

  result = _lm_iq_message_make_result (iq);

  if (NULL != result)
    {
      _gabble_connection_send (conn, result, NULL);
      lm_message_unref (result);
    }
}


/**
 * _gabble_connection_send_iq_error
 *
 * Function used to acknowledge an IQ stanza with an error.
 */
void
_gabble_connection_send_iq_error (GabbleConnection *conn,
                                  LmMessage *message,
                                  GabbleXmppError error,
                                  const gchar *errmsg)
{
  const gchar *to, *id;
  LmMessage *msg;
  LmMessageNode *iq_node;

  iq_node = lm_message_get_node (message);
  to = lm_message_node_get_attribute (iq_node, "from");
  id = lm_message_node_get_attribute (iq_node, "id");

  if (id == NULL)
    {
      NODE_DEBUG (iq_node, "can't acknowledge IQ with no id");
      return;
    }

  msg = lm_message_new_with_sub_type (to, LM_MESSAGE_TYPE_IQ,
                                      LM_MESSAGE_SUB_TYPE_ERROR);

  lm_message_node_set_attribute (msg->node, "id", id);

  lm_message_node_steal_children (msg->node, iq_node);

  gabble_xmpp_error_to_node (error, msg->node, errmsg);

  _gabble_connection_send (conn, msg, NULL);

  lm_message_unref (msg);
}

static LmMessage *
_lm_iq_message_make_result (LmMessage *iq_message)
{
  LmMessage *result;
  LmMessageNode *iq, *result_iq;
  const gchar *from_jid, *id;

  g_assert (lm_message_get_type (iq_message) == LM_MESSAGE_TYPE_IQ);
  g_assert (lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_GET ||
            lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_SET);

  iq = lm_message_get_node (iq_message);
  id = lm_message_node_get_attribute (iq, "id");

  if (id == NULL)
    {
      NODE_DEBUG (iq, "can't acknowledge IQ with no id");
      return NULL;
    }

  from_jid = lm_message_node_get_attribute (iq, "from");

  result = lm_message_new_with_sub_type (from_jid, LM_MESSAGE_TYPE_IQ,
                                         LM_MESSAGE_SUB_TYPE_RESULT);
  result_iq = lm_message_get_node (result);
  lm_message_node_set_attribute (result_iq, "id", id);

  return result;
}

/**
 * connection_iq_disco_cb
 *
 * Called by loudmouth when we get an incoming <iq>. This handler handles
 * disco-related IQs.
 */
static LmHandlerResult
connection_iq_disco_cb (LmMessageHandler *handler,
                        LmConnection *connection,
                        LmMessage *message,
                        gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);
  LmMessage *result;
  LmMessageNode *iq, *result_iq, *query, *result_query;
  const gchar *node, *suffix;
  GSList *features;
  GSList *i;
  GabblePresence *pres;
  
  if ( NULL == conn )
        {
        return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
        }

  if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_GET)
    return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;

  iq = lm_message_get_node (message);
  query = lm_message_node_get_child_with_namespace (iq, "query",
      NS_DISCO_INFO);

  if (!query)
    return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;

  node = lm_message_node_get_attribute (query, "node");

  if (node && (
      0 != strncmp (node, NS_GABBLE_CAPS "#", strlen (NS_GABBLE_CAPS) + 1) ||
      strlen(node) < strlen (NS_GABBLE_CAPS) + 2))
    {
      NODE_DEBUG (iq, "got iq disco query with unexpected node attribute");
      return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
    }

  if (node == NULL)
    suffix = NULL;
  else
    suffix = node + strlen (NS_GABBLE_CAPS) + 1;

  result = _lm_iq_message_make_result (message);
  result_iq = lm_message_get_node (result);
  result_query = lm_message_node_add_child (result_iq, "query", NULL);
  lm_message_node_set_attribute (result_query, "xmlns", NS_DISCO_INFO);

  pres = gabble_presence_cache_get (conn->presence_cache, conn->self_handle);
  gabble_debug (DEBUG_FLAG, "got disco request for bundle %s, caps are %x", node, pres->caps);
  features = capabilities_get_features (pres->caps);

  g_debug("%s: caps now %u", G_STRFUNC, pres->caps);

  for (i = features; NULL != i; i = i->next)
    {
      const Feature *feature = (const Feature *) i->data;

      if (NULL == node || !g_strdiff (suffix, feature->bundle))
        {
          LmMessageNode *node = lm_message_node_add_child (result_query,
              "feature", NULL);
          lm_message_node_set_attribute (node, "var", feature->ns);
        }
    }

  NODE_DEBUG (result_iq, "sending disco response");

  if (!lm_connection_send (conn->lmconn, result, NULL))
    gabble_debug (DEBUG_FLAG, "sending disco response failed");

  g_slist_free (features);

  return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}

/**
 * connection_iq_unknown_cb
 *
 * Called by loudmouth when we get an incoming <iq>. This handler is
 * at a lower priority than the others, and should reply with an error
 * about unsupported get/set attempts.
 */
static LmHandlerResult
connection_iq_unknown_cb (LmMessageHandler *handler,
                          LmConnection *connection,
                          LmMessage *message,
                          gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);

  g_assert (connection == conn->lmconn);

  NODE_DEBUG (message->node, "got unknown iq");

  switch (lm_message_get_sub_type (message))
    {
    case LM_MESSAGE_SUB_TYPE_GET:
    case LM_MESSAGE_SUB_TYPE_SET:
      _gabble_connection_send_iq_error (conn, message,
          XMPP_ERROR_SERVICE_UNAVAILABLE, NULL);
      break;
    default:
      break;
    }

  return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}

/**
 * connection_stream_error_cb
 *
 * Called by loudmouth when we get stream error, which means that
 * we're about to close the connection. The message contains the reason
 * for the connection hangup.
 */
static LmHandlerResult
connection_stream_error_cb (LmMessageHandler *handler,
                            LmConnection *connection,
                            LmMessage *message,
                            gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);
  LmMessageNode *conflict_node;

  g_assert (connection == conn->lmconn);

  NODE_DEBUG (message->node, "got stream error");

  conflict_node = lm_message_node_get_child (message->node, "conflict");
  if (conflict_node)
    {
      gabble_debug (DEBUG_FLAG, "found conflict node, emiting status change");

      /* Another client with the same resource just
       * appeared, we're going down. */
        connection_status_change (conn,
            TP_CONN_STATUS_DISCONNECTED,
            TP_CONN_STATUS_REASON_NAME_IN_USE);
        return LM_HANDLER_RESULT_REMOVE_MESSAGE;
    }

  return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
}


/**
 * connection_ssl_cb
 *
 * If we're doing old SSL, this function gets called if the certificate
 * is dodgy.
 */
static LmSSLResponse
connection_ssl_cb (LmSSL      *lmssl,
                   LmSSLStatus status,
                   gpointer    data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (data);
  
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  const char *reason;
  TpConnectionStatusReason tp_reason;

  g_message("inside ssl_cb\n");
  switch (status) {
    case LM_SSL_STATUS_NO_CERT_FOUND:
      reason = "The server doesn't provide a certificate.";
      tp_reason = TP_CONN_STATUS_REASON_CERT_NOT_PROVIDED;
      break;
    case LM_SSL_STATUS_UNTRUSTED_CERT:
      reason = "The certificate can not be trusted.";
      tp_reason = TP_CONN_STATUS_REASON_CERT_UNTRUSTED;
      break;
    case LM_SSL_STATUS_CERT_EXPIRED:
      reason = "The certificate has expired.";
      tp_reason = TP_CONN_STATUS_REASON_CERT_EXPIRED;
      break;
    case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
      reason = "The certificate has not been activated.";
      tp_reason = TP_CONN_STATUS_REASON_CERT_NOT_ACTIVATED;
      break;
    case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
      reason = "The server hostname doesn't match the one in the certificate.";
      tp_reason = TP_CONN_STATUS_REASON_CERT_HOSTNAME_MISMATCH;
      break;
    case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH:
      reason = "The fingerprint doesn't match the expected value.";
      tp_reason = TP_CONN_STATUS_REASON_CERT_FINGERPRINT_MISMATCH;
      break;
    case LM_SSL_STATUS_GENERIC_ERROR:
      reason = "An unknown SSL error occurred.";
      tp_reason = TP_CONN_STATUS_REASON_CERT_OTHER_ERROR;
      break;
    default:
      g_assert_not_reached();
      reason = "Unknown SSL error code from Loudmouth.";
      tp_reason = TP_CONN_STATUS_REASON_ENCRYPTION_ERROR;
      break;
  }

  gabble_debug (DEBUG_FLAG, "called: %s", reason);

  if (priv->ignore_ssl_errors)
    {
      return LM_SSL_RESPONSE_CONTINUE;
    }
  else
    {
      priv->ssl_error = tp_reason;
      return LM_SSL_RESPONSE_STOP;
    }
}

static void
do_auth (GabbleConnection *conn)
{
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  GError *error = NULL;

  gabble_debug (DEBUG_FLAG, "authenticating with username: %s, password: <hidden>, resource: %s",
           priv->username, priv->resource);

  if (!lm_connection_authenticate (conn->lmconn, priv->username, priv->password,
                                   priv->resource, connection_auth_cb,
                                   conn, NULL, &error))
    {
      gabble_debug (DEBUG_FLAG, "failed: %s", error->message);
      g_error_free (error);

      /* the reason this function can fail is through network errors,
       * authentication failures are reported to our auth_cb */
      connection_status_change (conn,
          TP_CONN_STATUS_DISCONNECTED,
          TP_CONN_STATUS_REASON_NETWORK_ERROR);
    }
}

static void
registration_finished_cb (GabbleRegister *reg,
                          gboolean success,
                          gint err_code,
                          const gchar *err_msg,
                          gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);

  g_message("registration_finished_cb\n");
  if (conn->status != TP_CONN_STATUS_CONNECTING)
    {
      g_assert (conn->status == TP_CONN_STATUS_DISCONNECTED);
      return;
    }

  gabble_debug (DEBUG_FLAG, "%s", (success) ? "succeeded" : "failed");

  g_object_unref (reg);

  if (success)
    {
      do_auth (conn);
    }
  else
    {
      gabble_debug (DEBUG_FLAG, "err_code = %d, err_msg = '%s'",
               err_code, err_msg);

      connection_status_change (conn,
          TP_CONN_STATUS_DISCONNECTED,
          (err_code == InvalidArgument) ? TP_CONN_STATUS_REASON_NAME_IN_USE :
            TP_CONN_STATUS_REASON_AUTHENTICATION_FAILED);
    }
}

static void
do_register (GabbleConnection *conn)
{
  GabbleRegister *reg;

  reg = gabble_register_new (conn);

  g_signal_connect (reg, "finished", (GCallback) registration_finished_cb,
                    conn);

  gabble_register_start (reg);
}

/**
 * connection_open_cb
 *
 * Stage 2 of connecting, this function is called by loudmouth after the
 * result of the non-blocking lm_connection_open call is known. It makes
 * a request to authenticate the user with the server, or optionally
 * registers user on the server first.
 */
static void
connection_open_cb (LmConnection *lmconn,
                    gboolean      success,
                    gpointer      data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (data);
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  g_message("[connection_open_cb]");
  if ((conn->status != TP_CONN_STATUS_CONNECTING) &&
      (conn->status != TP_CONN_STATUS_NEW))
    {
      g_assert (conn->status == TP_CONN_STATUS_DISCONNECTED);
      return;
    }

  g_assert (priv);
  g_assert (lmconn == conn->lmconn);

  if (!success)
    {
      if (lm_connection_get_proxy (lmconn))
        {
          g_message ("failed, retrying without proxy");

          lm_connection_set_proxy (lmconn, NULL);

          if (do_connect (conn, NULL))
            {
              return;
            }
        }
      else
        {
          g_message ("failed");
        }

      if (priv->ssl_error)
        {
        g_message ("[connection_open_cb] priv->ssl_error");
          connection_status_change (conn,
            TP_CONN_STATUS_DISCONNECTED,
            priv->ssl_error);
        }
      else
        {
        g_message ("[connection_open_cb] reached else");
          connection_status_change (conn,
              TP_CONN_STATUS_DISCONNECTED,
              TP_CONN_STATUS_REASON_NETWORK_ERROR);
        }

      return;
    }

  if (!priv->do_register)
    do_auth (conn);
  else
    do_register (conn);
}

/**
 * connection_auth_cb
 *
 * Stage 3 of connecting, this function is called by loudmouth after the
 * result of the non-blocking lm_connection_authenticate call is known.
 * It sends a discovery request to find the server's features.
 */
static void
connection_auth_cb (LmConnection *lmconn,
                    gboolean      success,
                    gpointer      data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (data);
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
  GError *error = NULL;

  g_message("In connection_auth_cb\n");

  if (conn->status != TP_CONN_STATUS_CONNECTING)
    {
      g_assert (conn->status == TP_CONN_STATUS_DISCONNECTED);
      return;
    }

  g_assert (priv);
  g_assert (lmconn == conn->lmconn);

  if (!success)
    {
      gabble_debug (DEBUG_FLAG, "failed");
	  g_message("Inside !success\n");

      connection_status_change (conn,
          TP_CONN_STATUS_DISCONNECTED,
          TP_CONN_STATUS_REASON_AUTHENTICATION_FAILED);

      return;
    }

  if (!gabble_disco_request_with_timeout (conn->disco, GABBLE_DISCO_TYPE_INFO,
                                          priv->stream_server, NULL, 5000,
                                          connection_disco_cb, conn,
                                          G_OBJECT (conn), &error))
    {
      gabble_debug (DEBUG_FLAG, "sending disco request failed: %s",
          error->message);
	  g_message("sending disco request failed\n");

      g_error_free (error);

      connection_status_change (conn,
          TP_CONN_STATUS_DISCONNECTED,
          TP_CONN_STATUS_REASON_NETWORK_ERROR);
    }
}

/**
 * connection_disco_cb
 *
 * Stage 4 of connecting, this function is called by GabbleDisco after the
 * result of the non-blocking server feature discovery call is known. It sends
 * the user's initial presence to the server, marking them as available,
 * and requests the roster.
 */
static void
connection_disco_cb (GabbleDisco *disco,
                     GabbleDiscoRequest *request,
                     const gchar *jid,
                     const gchar *node,
                     LmMessageNode *result,
                     GError *disco_error,
                     gpointer user_data)
{
  GabbleConnection *conn = user_data;
  GabbleConnectionPrivate *priv;
  GError *error = NULL;

	g_message("Inside connection_disco_cb\n");
	
  if ( NULL == conn )
      {
      return;
      }
  if (conn->status != TP_CONN_STATUS_CONNECTING)
    {
      g_assert (conn->status == TP_CONN_STATUS_DISCONNECTED);
      return;
    }

  g_assert (GABBLE_IS_CONNECTION (conn));
  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  if (disco_error)
    {
		g_message("got disco error, setting no features\n");
      gabble_debug (DEBUG_FLAG, "got disco error, setting no features: %s", disco_error->message);
    }
  else
    {
      LmMessageNode *iter;

      NODE_DEBUG (result, "got");

      for (iter = result->children; iter != NULL; iter = iter->next)
        {
          if (0 == strcmp (iter->name, "feature"))
            {
              const gchar *var = lm_message_node_get_attribute (iter, "var");

              if (var == NULL)
                continue;

              if (0 == strcmp (var, NS_GOOGLE_JINGLE_INFO))
                conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO;
              else if (0 == strcmp (var, NS_GOOGLE_ROSTER))
                conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER;
              else if (0 == strcmp (var, NS_PRESENCE_INVISIBLE))
                conn->features |= GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE;
              else if (0 == strcmp (var, NS_PRIVACY))
                conn->features |= GABBLE_CONNECTION_FEATURES_PRIVACY;
              else if (0 == strcmp (var, NS_SEARCH))
                conn->features |= GABBLE_CONNECTION_FEATURES_SEARCH;
            }
        }

      gabble_debug (DEBUG_FLAG, "set features flags to %d", conn->features);
    }

		g_message("before signal_own_presence\n");

  /* send presence to the server to indicate availability */
  if (!signal_own_presence (conn, &error))
    {
      gabble_debug (DEBUG_FLAG, "sending initial presence failed: %s", error->message);
		g_message("sending initial presence failed\n");
      goto ERROR;
    }

		g_message("after signal_own_presence\n");

  /* go go gadget on-line */
  connection_status_change (conn, TP_CONN_STATUS_CONNECTED, TP_CONN_STATUS_REASON_REQUESTED);

  if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO)
    {
      jingle_info_discover_servers (conn);
    }
  
  if(conn->features & GABBLE_CONNECTION_FEATURES_SEARCH)
	{
	get_search_keys_info(conn,jid);
	}

  return;

ERROR:
  if (error)
    g_error_free (error);

  connection_status_change (conn,
      TP_CONN_STATUS_DISCONNECTED,
      TP_CONN_STATUS_REASON_NETWORK_ERROR);

  return;
}


static GHashTable *
get_statuses_arguments()
{

#ifndef EMULATOR
  static GHashTable *arguments = NULL;
#endif
  

  if (arguments == NULL)
    {
      arguments = g_hash_table_new (g_str_hash, g_str_equal);

      g_hash_table_insert (arguments, "message", "s");
      g_hash_table_insert (arguments, "priority", "n");
    }

  return arguments;
}

/****************************************************************************
 *                          D-BUS EXPORTED METHODS                          *
 ****************************************************************************/


/**
 * gabble_connection_add_status
 *
 * Implements D-Bus method AddStatus
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_add_status (GabbleConnection *self,
                              const gchar *status,
                              GHashTable *parms,
                              GError **error)
{
  g_assert (GABBLE_IS_CONNECTION (self));

  ERROR_IF_NOT_CONNECTED (self, error);

  g_set_error (error, TELEPATHY_ERRORS, NotImplemented,
      "Only one status is possible at a time with this protocol");

  return FALSE;
}

static void
_emit_capabilities_changed (GabbleConnection *conn,
                            GabbleHandle handle,
                            GabblePresenceCapabilities old_caps,
                            GabblePresenceCapabilities new_caps)
{
  GPtrArray *caps_arr;
  const CapabilityConversionData *ccd;
  guint i;

  if (old_caps == new_caps)
    return;

  caps_arr = g_ptr_array_new ();

  for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
    {
      if (ccd->c2tf_fn (old_caps | new_caps))
        {
          GValue caps_monster_struct = {0, };
          guint old_tpflags = ccd->c2tf_fn (old_caps);
          guint old_caps = old_tpflags ?
            TP_CONN_CAPABILITY_FLAG_CREATE |
            TP_CONN_CAPABILITY_FLAG_INVITE : 0;
          guint new_tpflags = ccd->c2tf_fn (new_caps);
          guint new_caps = new_tpflags ?
            TP_CONN_CAPABILITY_FLAG_CREATE |
            TP_CONN_CAPABILITY_FLAG_INVITE : 0;

          if (0 == (old_tpflags ^ new_tpflags))
            continue;

          g_value_init (&caps_monster_struct,
              TP_CAPABILITIES_CHANGED_MONSTER_TYPE);
          g_value_take_boxed (&caps_monster_struct,
              dbus_g_type_specialized_construct
                (TP_CAPABILITIES_CHANGED_MONSTER_TYPE));

          dbus_g_type_struct_set (&caps_monster_struct,
              0, handle,
              1, ccd->iface,
              2, old_caps,
              3, new_caps,
              4, old_tpflags,
              5, new_tpflags,
              G_MAXUINT);

          g_ptr_array_add (caps_arr, g_value_get_boxed (&caps_monster_struct));
        }
    }

  if (caps_arr->len)
    g_signal_emit (conn, signals[CAPABILITIES_CHANGED], 0, caps_arr);

  for (i = 0; i < caps_arr->len; i++)
    {
      g_boxed_free (TP_CAPABILITIES_CHANGED_MONSTER_TYPE,
          g_ptr_array_index (caps_arr, i));
    }
  g_ptr_array_free (caps_arr, TRUE);
}

static void
connection_capabilities_update_cb (GabblePresenceCache *cache,
                                   GabbleHandle handle,
                                   GabblePresenceCapabilities old_caps,
                                   GabblePresenceCapabilities new_caps,
                                   gpointer user_data)
{
  GabbleConnection *conn = GABBLE_CONNECTION (user_data);

  _emit_capabilities_changed (conn, handle, old_caps, new_caps);
}

/**
 * gabble_connection_advertise_capabilities
 *
 * Implements D-Bus method AdvertiseCapabilities
 * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_advertise_capabilities (GabbleConnection *self,
                                          const GPtrArray *add,
                                          const gchar **remove,
                                          GPtrArray **ret,
                                          GError **error)
{
  guint i;
  GabblePresence *pres;
  GabblePresenceCapabilities add_caps = 0, remove_caps = 0, caps, save_caps;
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
  const CapabilityConversionData *ccd;

  ERROR_IF_NOT_CONNECTED (self, error);

  pres = gabble_presence_cache_get (self->presence_cache, self->self_handle);
  gabble_debug (DEBUG_FLAG, "caps before: %x", pres->caps);

  for (i = 0; i < add->len; i++)
    {
      GValue iface_flags_pair = {0, };
      gchar *iface;
      guint flags;

      g_value_init (&iface_flags_pair, TP_CAPABILITY_PAIR_TYPE);
      g_value_set_static_boxed (&iface_flags_pair, g_ptr_array_index (add, i));

      dbus_g_type_struct_get (&iface_flags_pair,
                              0, &iface,
                              1, &flags,
                              G_MAXUINT);

      for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
          if (g_str_equal (iface, ccd->iface))
            add_caps |= ccd->tf2c_fn (flags);
    }

  for (i = 0; NULL != remove[i]; i++)
    {
      for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
          if (g_str_equal (remove[i], ccd->iface))
            remove_caps |= ccd->tf2c_fn (~0);
    }

  pres = gabble_presence_cache_get (self->presence_cache, self->self_handle);
  save_caps = caps = pres->caps;

  caps |= add_caps;
  caps ^= (caps & remove_caps);

  gabble_debug (DEBUG_FLAG, "caps to add: %x", add_caps);
  gabble_debug (DEBUG_FLAG, "caps to remove: %x", remove_caps);
  gabble_debug (DEBUG_FLAG, "caps after: %x", caps);

  if (caps ^ save_caps)
    {
      gabble_debug (DEBUG_FLAG, "before != after, changing");
      gabble_presence_set_capabilities (pres, priv->resource, caps, priv->caps_serial++);
      gabble_debug (DEBUG_FLAG, "set caps: %x", pres->caps);
    }

  *ret = g_ptr_array_new ();

  for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
    {
      if (ccd->c2tf_fn (pres->caps))
        {
          GValue iface_flags_pair = {0, };

          g_value_init (&iface_flags_pair, TP_CAPABILITY_PAIR_TYPE);
          g_value_take_boxed (&iface_flags_pair,
              dbus_g_type_specialized_construct (TP_CAPABILITY_PAIR_TYPE));

          dbus_g_type_struct_set (&iface_flags_pair,
                                  0, ccd->iface,
                                  1, ccd->c2tf_fn (pres->caps),
                                  G_MAXUINT);

          g_ptr_array_add (*ret, g_value_get_boxed (&iface_flags_pair));
        }
    }

  if (caps ^ save_caps)
    {
      if (!signal_own_presence (self, error))
        return FALSE;

      _emit_capabilities_changed (self, self->self_handle, save_caps, caps);
    }

  return TRUE;
}

/**
 * gabble_connection_clear_status
 *
 * Implements D-Bus method ClearStatus
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_clear_status (GabbleConnection *self,
                                GError **error)
{
  GabbleConnectionPrivate *priv;
  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error);

  gabble_presence_cache_update (self->presence_cache, self->self_handle,
      priv->resource, GABBLE_PRESENCE_AVAILABLE, NULL, priv->priority);
  emit_one_presence_update (self, self->self_handle);
  return signal_own_presence (self, error);
}


/**
 * gabble_connection_connect
 * Implements D-Bus method Connect
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */

gboolean
gabble_connection_connect (GabbleConnection *self,
                           GError **error)
{
  g_assert(GABBLE_IS_CONNECTION (self));
  
  if (self->status == TP_CONN_STATUS_NEW)
    return _gabble_connection_connect (self, error);
  return TRUE;
}


/**
 * gabble_connection_disconnect
 *
 * Implements D-Bus method Disconnect
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_disconnect (GabbleConnection *self,
                              GError **error)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  g_message("gabble_connection_disconnect: going to change status to TP_CONN_STATUS_DISCONNECTED ")	;
  connection_status_change (self,
      TP_CONN_STATUS_DISCONNECTED,
      TP_CONN_STATUS_REASON_REQUESTED);

  return TRUE;
}


/**
 * gabble_connection_get_alias_flags
 *
 * Implements D-Bus method GetAliasFlags
 * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_alias_flags (GabbleConnection *self,
                                   guint *ret,
                                   GError **error)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  *ret = TP_CONN_ALIAS_FLAG_USER_SET;

  return TRUE;
}

//#ifndef EMULATOR
static const gchar *assumed_caps[] =
{
  TP_IFACE_CHANNEL_TYPE_TEXT,
  TP_IFACE_CHANNEL_INTERFACE_GROUP,
  NULL
};
//#endif


/**
 * gabble_connection_get_capabilities
 *
 * Implements D-Bus method GetCapabilities
 * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_capabilities (GabbleConnection *self,
                                    const GArray *handles,
                                    GPtrArray **ret,
                                    GError **error)
{
  guint i;

  ERROR_IF_NOT_CONNECTED (self, error);

  if (!gabble_handles_are_valid (self->handles,
                                 TP_HANDLE_TYPE_CONTACT,
                                 handles,
                                 TRUE,
                                 error))
    {
      return FALSE;
    }

  *ret = g_ptr_array_new ();

  for (i = 0; i < handles->len; i++)
    {
      GabbleHandle handle = g_array_index (handles, guint, i);
      GabblePresence *pres;
      const CapabilityConversionData *ccd;
      guint typeflags;
      //#ifndef EMULATOR
      const gchar **assumed;
	  //#else
	  //gchar **assumed;
	  //#endif
	  
      if (0 == handle)
        {
          /* FIXME report the magical channel types available on the connection itself */
          continue;
        }

      pres = gabble_presence_cache_get (self->presence_cache, handle);

      if (NULL != pres)
        for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
          {
            typeflags = ccd->c2tf_fn (pres->caps);

            if (typeflags)
              {
                GValue monster = {0, };

                g_value_init (&monster, TP_GET_CAPABILITIES_MONSTER_TYPE);
                g_value_take_boxed (&monster,
                    dbus_g_type_specialized_construct (
                      TP_GET_CAPABILITIES_MONSTER_TYPE));

                dbus_g_type_struct_set (&monster,
                    0, handle,
                    1, ccd->iface,
                    2, TP_CONN_CAPABILITY_FLAG_CREATE |
                        TP_CONN_CAPABILITY_FLAG_INVITE,
                    3, typeflags,
                    G_MAXUINT);

                g_ptr_array_add (*ret, g_value_get_boxed (&monster));
              }
          }

      for (assumed = assumed_caps; NULL != *assumed; assumed++)
        {
          GValue monster = {0, };

          g_value_init (&monster, TP_GET_CAPABILITIES_MONSTER_TYPE);
          g_value_take_boxed (&monster,
              dbus_g_type_specialized_construct (TP_GET_CAPABILITIES_MONSTER_TYPE));

          dbus_g_type_struct_set (&monster,
              0, handle,
              1, *assumed,
              2, TP_CONN_CAPABILITY_FLAG_CREATE |
                  TP_CONN_CAPABILITY_FLAG_INVITE,
              3, 0,
              G_MAXUINT);

          g_ptr_array_add (*ret, g_value_get_boxed (&monster));
        }
    }

  return TRUE;
}


/**
 * gabble_connection_get_interfaces
 *
 * Implements D-Bus method GetInterfaces
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_interfaces (GabbleConnection *self,
                                  gchar ***ret,
                                  GError **error)
{
  const char *interfaces[] = {
      TP_IFACE_CONN_INTERFACE_ALIASING,
      TP_IFACE_CONN_INTERFACE_CAPABILITIES,
      TP_IFACE_CONN_INTERFACE_PRESENCE,
      TP_IFACE_PROPERTIES,
      TP_IFACE_CONN_INTERFACE_AVATAR,
      NULL };
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  *ret = g_strdupv ((gchar **) interfaces);

  return TRUE;
}


/**
 * gabble_connection_get_presence
 *
 * Implements D-Bus method GetPresence
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */
void
gabble_connection_get_presence (GabbleConnection *self,
                                const GArray *contacts,
                                DBusGMethodInvocation *context)
{
  GHashTable *presence_hash;
  GError *error = NULL;

  if (!gabble_handles_are_valid (self->handles, TP_HANDLE_TYPE_CONTACT,
        contacts, FALSE, &error))
    {
      dbus_g_method_return_error (context, error);
      g_error_free (error);
    }

  presence_hash = construct_presence_hash (self, contacts);
  dbus_g_method_return (context, presence_hash);
  g_hash_table_destroy (presence_hash);
}


/**
 * gabble_connection_get_properties
 *
 * Implements D-Bus method GetProperties
 * on interface org.freedesktop.Telepathy.Properties
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_properties (GabbleConnection *self,
                                  const GArray *properties,
                                  GPtrArray **ret,
                                  GError **error)
{
  return gabble_properties_mixin_get_properties (G_OBJECT (self), properties,
      ret, error);
}


/**
 * gabble_connection_get_protocol
 *
 * Implements D-Bus method GetProtocol
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_protocol (GabbleConnection *self,
                                gchar **ret,
                                GError **error)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  *ret = g_strdup (priv->protocol);

  return TRUE;
}


/**
 * gabble_connection_get_self_handle
 *
 * Implements D-Bus method GetSelfHandle
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_self_handle (GabbleConnection *self,
                                   guint *ret,
                                   GError **error)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  *ret = self->self_handle;

  return TRUE;
}


/**
 * gabble_connection_get_status
 *
 * Implements D-Bus method GetStatus
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_status (GabbleConnection *self,
                              guint *ret,
                              GError **error)
{
  g_assert (GABBLE_IS_CONNECTION (self));

  if (self->status == TP_CONN_STATUS_NEW)
    {
      *ret = TP_CONN_STATUS_DISCONNECTED;
    }
  else
    {
      *ret = self->status;
    }

  return TRUE;
}


/**
 * gabble_connection_get_statuses
 *
 * Implements D-Bus method GetStatuses
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_get_statuses (GabbleConnection *self,
                                GHashTable **ret,
                                GError **error)
{
  GabbleConnectionPrivate *priv;
  GValueArray *status;
  int i;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  gabble_debug (DEBUG_FLAG, "called.");

  *ret = g_hash_table_new_full (g_str_hash, g_str_equal,
                                NULL, (GDestroyNotify) g_value_array_free);

  for (i=0; i < LAST_GABBLE_PRESENCE; i++)
    {
      /* don't report the invisible presence if the server
       * doesn't have the presence-invisible feature */
      if (!status_is_available (self, i))
        continue;

      status = g_value_array_new (5);

      g_value_array_append (status, NULL);
      g_value_init (g_value_array_get_nth (status, 0), G_TYPE_UINT);
      g_value_set_uint (g_value_array_get_nth (status, 0),
          gabble_statuses[i].presence_type);

      g_value_array_append (status, NULL);
      g_value_init (g_value_array_get_nth (status, 1), G_TYPE_BOOLEAN);
      g_value_set_boolean (g_value_array_get_nth (status, 1),
          gabble_statuses[i].self);

      g_value_array_append (status, NULL);
      g_value_init (g_value_array_get_nth (status, 2), G_TYPE_BOOLEAN);
      g_value_set_boolean (g_value_array_get_nth (status, 2),
          gabble_statuses[i].exclusive);

      g_value_array_append (status, NULL);
      g_value_init (g_value_array_get_nth (status, 3),
          DBUS_TYPE_G_STRING_STRING_HASHTABLE);
      g_value_set_static_boxed (g_value_array_get_nth (status, 3),
          get_statuses_arguments());

      g_hash_table_insert (*ret, (gchar*) gabble_statuses[i].name, status);
    }

  return TRUE;
}


/**
 * gabble_connection_hold_handles
 *
 * Implements D-Bus method HoldHandles
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */
void
gabble_connection_hold_handles (GabbleConnection *self,
                                guint handle_type,
                                const GArray *handles,
                                DBusGMethodInvocation *context)
{
  GabbleConnectionPrivate *priv;
  GError *error = NULL;
  gchar *sender;
  guint i;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED_ASYNC (self, error, context)

  if (!gabble_handles_are_valid (self->handles,
                                 handle_type,
                                 handles,
                                 FALSE,
                                 &error))
    {
      dbus_g_method_return_error (context, error);
      g_error_free (error);
      return;
    }

  sender = dbus_g_method_get_sender (context);
  for (i = 0; i < handles->len; i++)
    {
      GabbleHandle handle = g_array_index (handles, GabbleHandle, i);
      if (!gabble_handle_client_hold (self->handles, sender, handle,
            handle_type, &error))
        {
          dbus_g_method_return_error (context, error);
          g_error_free (error);
          return;
        }
    }

  dbus_g_method_return (context);
}


/**
 * gabble_connection_inspect_handles
 *
 * Implements D-Bus method InspectHandles
 * on interface org.freedesktop.Telepathy.Connection
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
void
gabble_connection_inspect_handles (GabbleConnection *self,
                                   guint handle_type,
                                   const GArray *handles,
                                   DBusGMethodInvocation *context)
{
  GabbleConnectionPrivate *priv;
  GError *error = NULL;
  const gchar **ret;
  guint i;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED_ASYNC (self, error, context);

  if (!gabble_handles_are_valid (self->handles,
                                 handle_type,
                                 handles,
                                 FALSE,
                                 &error))
    {
      dbus_g_method_return_error (context, error);

      g_error_free (error);

      return;
    }

  ret = g_new (const gchar *, handles->len + 1);

  for (i = 0; i < handles->len; i++)
    {
      GabbleHandle handle;
      const gchar *tmp;

      handle = g_array_index (handles, GabbleHandle, i);
      tmp = gabble_handle_inspect (self->handles, handle_type, handle);
      g_assert (tmp != NULL);

      ret[i] = tmp;
    }

  ret[i] = NULL;

  dbus_g_method_return (context, ret);

  g_free (ret);
}

/**
 * list_channel_factory_foreach_one:
 * @key: iterated key
 * @value: iterated value
 * @data: data attached to this key/value pair
 *
 * Called by the exported ListChannels function, this should iterate over
 * the handle/channel pairs in a channel factory, and to the GPtrArray in
 * the data pointer, add a GValueArray containing the following:
 *  a D-Bus object path for the channel object on this service
 *  a D-Bus interface name representing the channel type
 *  an integer representing the handle type this channel communicates with, or zero
 *  an integer handle representing the contact, room or list this channel communicates with, or zero
 */
static void
list_channel_factory_foreach_one (TpChannelIface *chan,
                                  gpointer data)
{
  GObject *channel = G_OBJECT (chan);
  GPtrArray *channels = (GPtrArray *) data;
  gchar *path, *type;
  guint handle_type, handle;
  GValue entry = { 0, };

  g_value_init (&entry, TP_CHANNEL_LIST_ENTRY_TYPE);
  g_value_take_boxed (&entry, dbus_g_type_specialized_construct
      (TP_CHANNEL_LIST_ENTRY_TYPE));

  g_object_get (channel,
      "object-path", &path,
      "channel-type", &type,
      "handle-type", &handle_type,
      "handle", &handle,
      NULL);

  dbus_g_type_struct_set (&entry,
      0, path,
      1, type,
      2, handle_type,
      3, handle,
      G_MAXUINT);

  g_ptr_array_add (channels, g_value_get_boxed (&entry));

  g_free (path);
  g_free (type);
}

/**
 * gabble_connection_list_channels
 *
 * Implements D-Bus method ListChannels
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_list_channels (GabbleConnection *self,
                                 GPtrArray **ret,
                                 GError **error)
{
  GabbleConnectionPrivate *priv;
  GPtrArray *channels;
  guint i;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  /* I think on average, each factory will have 2 channels :D */
  channels = g_ptr_array_sized_new (priv->channel_factories->len * 2);

  for (i = 0; i < priv->channel_factories->len; i++)
    {
      TpChannelFactoryIface *factory = g_ptr_array_index
        (priv->channel_factories, i);
      tp_channel_factory_iface_foreach (factory,
          list_channel_factory_foreach_one, channels);
    }

  *ret = channels;

  return TRUE;
}


/**
 * gabble_connection_list_properties
 *
 * Implements D-Bus method ListProperties
 * on interface org.freedesktop.Telepathy.Properties
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns false.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_list_properties (GabbleConnection *self,
                                   GPtrArray **ret,
                                   GError **error)
{
  return gabble_properties_mixin_list_properties (G_OBJECT (self), ret, error);
}


/**
 * gabble_connection_release_handles
 *
 * Implements D-Bus method ReleaseHandles
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */
void
gabble_connection_release_handles (GabbleConnection *self,
                                   guint handle_type,
                                   const GArray * handles,
                                   DBusGMethodInvocation *context)
{
  GabbleConnectionPrivate *priv;
  char *sender;
  GError *error = NULL;
  guint i;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED_ASYNC (self, error, context)

  if (!gabble_handles_are_valid (self->handles,
                                 handle_type,
                                 handles,
                                 FALSE,
                                 &error))
    {
      dbus_g_method_return_error (context, error);
      g_error_free (error);
      return;
    }

  sender = dbus_g_method_get_sender (context);
  for (i = 0; i < handles->len; i++)
    {
      GabbleHandle handle = g_array_index (handles, GabbleHandle, i);
      if (!gabble_handle_client_release (self->handles, sender, handle,
            handle_type, &error))
        {
          dbus_g_method_return_error (context, error);
          g_error_free (error);
          return;
        }
    }

  dbus_g_method_return (context);
}


/**
 * gabble_connection_remove_status
 *
 * Implements D-Bus method RemoveStatus
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_remove_status (GabbleConnection *self,
                                 const gchar *status,
                                 GError **error)
{
  GabblePresence *presence;
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  g_assert (GABBLE_IS_CONNECTION (self));

  ERROR_IF_NOT_CONNECTED (self, error)

  presence = gabble_presence_cache_get (self->presence_cache,
      self->self_handle);

  if (strcmp (status, gabble_statuses[presence->status].name) == 0)
    {
      gabble_presence_cache_update (self->presence_cache, self->self_handle,
          priv->resource, GABBLE_PRESENCE_AVAILABLE, NULL, priv->priority);
      emit_one_presence_update (self, self->self_handle);
      return signal_own_presence (self, error);
    }
  else
    {
      g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
          "Attempting to remove non-existent presence.");
      return FALSE;
    }
}


typedef struct _AliasesRequest AliasesRequest;

struct _AliasesRequest
{
  GabbleConnection *conn;
  DBusGMethodInvocation *request_call;
  guint pending_vcard_requests;
  GArray *contacts;
  GabbleVCardManagerRequest **vcard_requests;
  gchar **aliases;
};


static AliasesRequest *
aliases_request_new (GabbleConnection *conn,
                     DBusGMethodInvocation *request_call,
                     const GArray *contacts)
{
  AliasesRequest *request;

  request = g_slice_new0 (AliasesRequest);
  request->conn = conn;
  request->request_call = request_call;
  request->contacts = g_array_new (FALSE, FALSE, sizeof (GabbleHandle));
  g_array_insert_vals (request->contacts, 0, contacts->data, contacts->len);
  request->vcard_requests =
    g_new0 (GabbleVCardManagerRequest *, contacts->len);
  request->aliases = g_new0 (gchar *, contacts->len + 1);
  return request;
}


static void
aliases_request_free (AliasesRequest *request)
{
  guint i;

  for (i = 0; i < request->contacts->len; i++)
    {
      if (request->vcard_requests[i] != NULL)
        gabble_vcard_manager_cancel_request (request->conn->vcard_manager,
            request->vcard_requests[i]);
    }

  g_array_free (request->contacts, TRUE);
  g_free (request->vcard_requests);
  g_strfreev (request->aliases);
  g_slice_free (AliasesRequest, request);
}


static gboolean
aliases_request_try_return (AliasesRequest *request)
{
  if (request->pending_vcard_requests == 0)
    {
      dbus_g_method_return (request->request_call, request->aliases);
      return TRUE;
    }

  return FALSE;
}


static void
aliases_request_vcard_cb (GabbleVCardManager *manager,
                          GabbleVCardManagerRequest *request,
                          GabbleHandle handle,
                          LmMessageNode *vcard,
                          GError *error,
                          gpointer user_data)
{
  AliasesRequest *aliases_request = (AliasesRequest *) user_data;
  GabbleConnectionAliasSource source;
  guint i;
  gboolean found = FALSE;
  gchar *alias = NULL;

  g_assert (aliases_request->pending_vcard_requests > 0);

  /* The index of the vCard request in the vCard request array is the
   * index of the contact/alias in their respective arrays. */

  for (i = 0; i < aliases_request->contacts->len; i++)
    if (aliases_request->vcard_requests[i] == request)
      {
        found = TRUE;
        break;
      }

  g_assert (found);
  source = _gabble_connection_get_cached_alias (aliases_request->conn,
      g_array_index (aliases_request->contacts, GabbleHandle, i), &alias);
  g_assert (source != GABBLE_CONNECTION_ALIAS_NONE);
  g_assert (NULL != alias);

  aliases_request->pending_vcard_requests--;
  aliases_request->vcard_requests[i] = NULL;
  aliases_request->aliases[i] = alias;

  if (aliases_request_try_return (aliases_request))
    aliases_request_free (aliases_request);
}


/**
 * gabble_connection_request_aliases
 *
 * Implements D-Bus method RequestAliases
 * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */
void
gabble_connection_request_aliases (GabbleConnection *self,
                                   const GArray *contacts,
                                   DBusGMethodInvocation *context)
{
  guint i;
  AliasesRequest *request;
  GError *error = NULL;

  g_assert (GABBLE_IS_CONNECTION (self));

  ERROR_IF_NOT_CONNECTED_ASYNC (self, error, context)

  if (!gabble_handles_are_valid (self->handles, TP_HANDLE_TYPE_CONTACT,
        contacts, FALSE, &error))
    {
      dbus_g_method_return_error (context, error);
      g_error_free (error);
      return;
    }

  request = aliases_request_new (self, context, contacts);

  for (i = 0; i < contacts->len; i++)
    {
      GabbleHandle handle = g_array_index (contacts, GabbleHandle, i);
      GabbleConnectionAliasSource source;
      GabbleVCardManagerRequest *vcard_request;
      gchar *alias;

      source = _gabble_connection_get_cached_alias (self, handle, &alias);
      g_assert (source != GABBLE_CONNECTION_ALIAS_NONE);
      g_assert (NULL != alias);

      if (source >= GABBLE_CONNECTION_ALIAS_FROM_VCARD ||
          gabble_vcard_manager_has_cached_alias (self->vcard_manager, handle))
        {
          /* Either the alias we got was from a vCard or better, or we already
           * tried getting an alias from a vcard and failed, so there's no
           * point trying again. */
          request->aliases[i] = alias;
        }
      else
        {
          gabble_debug (DEBUG_FLAG, "requesting vCard for alias of contact %s",
              gabble_handle_inspect (self->handles, TP_HANDLE_TYPE_CONTACT,
                handle));

          g_free (alias);
          vcard_request = gabble_vcard_manager_request (self->vcard_manager,
              handle, 0, aliases_request_vcard_cb, request, G_OBJECT (self),
              &error);

          if (NULL != error)
            {
              dbus_g_method_return_error (context, error);
              g_error_free (error);
              aliases_request_free (request);
              return;
            }

          request->vcard_requests[i] = vcard_request;
          request->pending_vcard_requests++;
        }
    }

  if (aliases_request_try_return (request))
    aliases_request_free (request);
}


/**
 * gabble_connection_request_channel
 *
 * Implements D-Bus method RequestChannel
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */
void
gabble_connection_request_channel (GabbleConnection *self,
                                   const gchar *type,
                                   guint handle_type,
                                   guint handle,
                                   gboolean suppress_handler,
                                   DBusGMethodInvocation *context)
{
  GabbleConnectionPrivate *priv;
  TpChannelFactoryRequestStatus status =
    TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED;
  gchar *object_path = NULL;
  GError *error = NULL;
  guint i;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED_ASYNC (self, error, context);

  for (i = 0; i < priv->channel_factories->len; i++)
    {
      TpChannelFactoryIface *factory = g_ptr_array_index
        (priv->channel_factories, i);
      TpChannelFactoryRequestStatus cur_status;
      TpChannelIface *chan = NULL;
      ChannelRequest *request = NULL;

      priv->suppress_next_handler = suppress_handler;

      cur_status = tp_channel_factory_iface_request (factory, type,
          (TpHandleType) handle_type, handle, &chan, &error);

      priv->suppress_next_handler = FALSE;

      switch (cur_status)
        {
        case TP_CHANNEL_FACTORY_REQUEST_STATUS_DONE:
          g_assert (NULL != chan);
          g_object_get (chan, "object-path", &object_path, NULL);
          goto OUT;
        case TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED:
          gabble_debug (DEBUG_FLAG, "queueing request, channel_type=%s, handle_type=%u, "
              "handle=%u, suppress_handler=%u", type, handle_type,
              handle, suppress_handler);
          request = channel_request_new (context, type, handle_type, handle,
              suppress_handler);
          g_ptr_array_add (priv->channel_requests, request);
          return;
        case TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR:
          /* pass through error */
          goto OUT;
        default:
          /* always return the most specific error */
          if (cur_status > status)
            status = cur_status;
        }
    }

  switch (status)
    {
      case TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE:
        gabble_debug (DEBUG_FLAG, "invalid handle %u", handle);

        error = g_error_new (TELEPATHY_ERRORS, InvalidHandle,
                             "invalid handle %u", handle);

        break;

      case TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE:
        gabble_debug (DEBUG_FLAG, "requested channel is unavailable with "
                 "handle type %u", handle_type);

        error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
                             "requested channel is not available with "
                             "handle type %u", handle_type);

        break;

      case TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED:
        gabble_debug (DEBUG_FLAG, "unsupported channel type %s", type);

        error = g_error_new (TELEPATHY_ERRORS, NotImplemented,
                             "unsupported channel type %s", type);

        break;

      default:
        g_assert_not_reached ();
    }

OUT:
  if (NULL != error)
    {
      g_assert (NULL == object_path);
      dbus_g_method_return_error (context, error);
      g_error_free (error);
      return;
    }

  g_assert (NULL != object_path);
  dbus_g_method_return (context, object_path);
  g_free (object_path);
}


static void
hold_and_return_handles (DBusGMethodInvocation *context,
                         GabbleConnection *conn,
                         GArray *handles,
                         guint handle_type)
{
  GError *error = NULL;
  gchar *sender = dbus_g_method_get_sender(context);
  guint i;

  for (i = 0; i < handles->len; i++)
    {
      GabbleHandle handle = (GabbleHandle) g_array_index (handles, guint, i);
      if (!gabble_handle_client_hold (conn->handles, sender, handle, handle_type, &error))
        {
          dbus_g_method_return_error (context, error);
          g_error_free (error);
          return;
        }
    }
  dbus_g_method_return (context, handles);
}


const char *
_gabble_connection_find_conference_server (GabbleConnection *conn)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (conn));

  priv = GABBLE_CONNECTION_GET_PRIVATE (conn);

  if (priv->conference_server == NULL)
    {
      /* Find first server that has NS_MUC feature */
      const GabbleDiscoItem *item = gabble_disco_service_find (conn->disco,
          NULL, NULL, NS_MUC);
      if (item != NULL)
        priv->conference_server = item->jid;
    }

  if (priv->conference_server == NULL)
    priv->conference_server = priv->fallback_conference_server;

  return priv->conference_server;
}


static gchar *
_gabble_connection_get_canonical_room_name (GabbleConnection *conn,
                                           const gchar *name)
{
  const gchar *server;

  g_assert (GABBLE_IS_CONNECTION (conn));

  if (index (name, '@'))
    return g_strdup (name);

  server = _gabble_connection_find_conference_server (conn);

  if (server == NULL)
    return NULL;

  return g_strdup_printf ("%s@%s", name, server);
}


typedef struct _RoomVerifyContext RoomVerifyContext;

typedef struct {
    GabbleConnection *conn;
    DBusGMethodInvocation *invocation;
    gboolean errored;
    guint count;
    GArray *handles;
    RoomVerifyContext *contexts;
} RoomVerifyBatch;

struct _RoomVerifyContext {
    gchar *jid;
    guint index;
    RoomVerifyBatch *batch;
    GabbleDiscoRequest *request;
};

static void
room_verify_batch_free (RoomVerifyBatch *batch)
{
  guint i;

  g_array_free (batch->handles, TRUE);
  for (i = 0; i < batch->count; i++)
    {
      g_free(batch->contexts[i].jid);
    }
  g_free (batch->contexts);
  g_free (batch);
}

/* Frees the error and the batch. */
static void
room_verify_batch_raise_error (RoomVerifyBatch *batch,
                               GError *error)
{
  guint i;

  dbus_g_method_return_error (batch->invocation, error);
  g_error_free (error);
  batch->errored = TRUE;
  for (i = 0; i < batch->count; i++)
    {
      if (batch->contexts[i].request)
        {
          gabble_disco_cancel_request(batch->conn->disco,
                                      batch->contexts[i].request);
        }
    }
  room_verify_batch_free (batch);
}

static RoomVerifyBatch *
room_verify_batch_new (GabbleConnection *conn,
                       DBusGMethodInvocation *invocation,
                       guint count,
                       const gchar **jids)
{
  RoomVerifyBatch *batch = g_new(RoomVerifyBatch, 1);
  guint i;

  batch->errored = FALSE;
  batch->conn = conn;
  batch->invocation = invocation;
  batch->count = count;
  batch->handles = g_array_sized_new(FALSE, FALSE, sizeof(GabbleHandle), count);
  batch->contexts = g_new0(RoomVerifyContext, count);
  for (i = 0; i < count; i++)
    {
      const gchar *name = jids[i];
      gchar *qualified_name;
      GabbleHandle handle;

      batch->contexts[i].index = i;
      batch->contexts[i].batch = batch;

      qualified_name = _gabble_connection_get_canonical_room_name (conn, name);

      if (!qualified_name)
        {
          GError *error = NULL;
          gabble_debug (DEBUG_FLAG, "requested handle %s contains no conference server",
                 name);
          error = g_error_new (TELEPATHY_ERRORS, NotAvailable, "requested "
                  "room handle %s does not specify a server, but we have not discovered "
                  "any local conference servers and no fallback was provided", name);
          room_verify_batch_raise_error (batch, error);
          return NULL;
        }

      batch->contexts[i].jid = qualified_name;

      /* has the handle been verified before? */
      if (gabble_handle_for_room_exists (conn->handles, qualified_name, FALSE))
        {
          handle = gabble_handle_for_room (conn->handles, qualified_name);
        }
      else
        {
          handle = 0;
        }
      g_array_append_val (batch->handles, handle);
    }

  return batch;
}

/* If all handles in the array have been disco'd or got from cache,
free the batch and return TRUE. Else return FALSE. */
static gboolean
room_verify_batch_try_return (RoomVerifyBatch *batch)
{
  guint i;

  for (i = 0; i < batch->count; i++)
    {
      if (!g_array_index(batch->handles, GabbleHandle, i))
        {
          /* we're not ready yet */
          return FALSE;
        }
    }

  hold_and_return_handles (batch->invocation, batch->conn, batch->handles, TP_HANDLE_TYPE_ROOM);
  room_verify_batch_free (batch);
  return TRUE;
}

static void
room_jid_disco_cb (GabbleDisco *disco,
                   GabbleDiscoRequest *request,
                   const gchar *jid,
                   const gchar *node,
                   LmMessageNode *query_result,
                   GError *error,
                   gpointer user_data)
{
  RoomVerifyContext *rvctx = user_data;
  RoomVerifyBatch *batch = rvctx->batch;
  LmMessageNode *lm_node;
  gboolean found = FALSE;
  GabbleHandle handle;

  /* stop the request getting cancelled after it's already finished */
  rvctx->request = NULL;

  /* if an error is being handled already, quietly go away */
  if (batch->errored)
    {
      return;
    }

  if (error != NULL)
    {
      gabble_debug (DEBUG_FLAG, "disco reply error %s", error->message);

      /* disco will free the old error, _raise_error will free the new one */
      error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
        "can't retrieve room info: %s", error->message);

      room_verify_batch_raise_error (batch, error);

      return;
    }

  for (lm_node = query_result->children; lm_node; lm_node = lm_node->next)
    {
      const gchar *var;

      if (g_strdiff (lm_node->name, "feature"))
        continue;

      var = lm_message_node_get_attribute (lm_node, "var");

      /* for servers who consider schema compliance to be an optional bonus */
      if (var == NULL)
        var = lm_message_node_get_attribute (lm_node, "type");

      if (!g_strdiff (var, NS_MUC))
        {
          found = TRUE;
          break;
        }
    }

  if (!found)
    {
      gabble_debug (DEBUG_FLAG, "no MUC support for service name in jid %s", rvctx->jid);

      error = g_error_new (TELEPATHY_ERRORS, NotAvailable, "specified server "
          "doesn't support MUC");

      room_verify_batch_raise_error (batch, error);

      return;
    }

  handle = gabble_handle_for_room (batch->conn->handles, rvctx->jid);
  g_assert (handle != 0);

  gabble_debug (DEBUG_FLAG, "disco reported MUC support for service name in jid %s", rvctx->jid);
  g_array_index (batch->handles, GabbleHandle, rvctx->index) = handle;

  /* if this was the last callback to be run, send off the result */
  room_verify_batch_try_return (batch);
}

/**
 * room_jid_verify:
 *
 * Utility function that verifies that the service name of
 * the specified jid exists and reports MUC support.
 */
static gboolean
room_jid_verify (RoomVerifyBatch *batch,
                 guint index,
                 DBusGMethodInvocation *context)
{
  gchar *room, *service;
  gboolean ret;
  GError *error = NULL;

  room = service = NULL;
  gabble_decode_jid (batch->contexts[index].jid, &room, &service, NULL);

  g_assert (room && service);

  ret = (gabble_disco_request (batch->conn->disco, GABBLE_DISCO_TYPE_INFO,
                               service, NULL, room_jid_disco_cb,
                               batch->contexts + index,
                               G_OBJECT (batch->conn), &error) != NULL);
  if (!ret)
    {
      room_verify_batch_raise_error (batch, error);
    }

  g_free (room);
  g_free (service);

  return ret;
}


/**
 * gabble_connection_request_handles
 *
 * Implements D-Bus method RequestHandles
 * on interface org.freedesktop.Telepathy.Connection
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */
void
gabble_connection_request_handles (GabbleConnection *self,
                                   guint handle_type,
                                   const gchar **names,
                                   DBusGMethodInvocation *context)
{
  guint count = 0, i;
  const gchar **cur_name;
  GError *error = NULL;
  GArray *handles = NULL;
  RoomVerifyBatch *batch = NULL;

  for (cur_name = names; *cur_name != NULL; cur_name++)
    {
      count++;
    }

  g_assert (GABBLE_IS_CONNECTION (self));

  ERROR_IF_NOT_CONNECTED_ASYNC (self, error, context)

  if (!gabble_handle_type_is_valid (handle_type, &error))
    {
      dbus_g_method_return_error (context, error);
      g_error_free (error);
      return;
    }

  switch (handle_type)
    {
    case TP_HANDLE_TYPE_CONTACT:
      handles = g_array_sized_new(FALSE, FALSE, sizeof(GabbleHandle), count);

      for (i = 0; i < count; i++)
        {
          GabbleHandle handle;
          const gchar *name = names[i];

          if (!gabble_handle_jid_is_valid (handle_type, name, &error))
            {
              dbus_g_method_return_error (context, error);
              g_error_free (error);

              g_array_free (handles, TRUE);
              return;
            }

          handle = gabble_handle_for_contact (self->handles, name, FALSE);

          if (handle == 0)
            {
              gabble_debug (DEBUG_FLAG, "requested handle %s was invalid", name);

              error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
                                   "requested handle %s was invalid", name);
              dbus_g_method_return_error (context, error);
              g_error_free (error);

              g_array_free (handles, TRUE);
              return;
            }

          g_array_append_val(handles, handle);
        }
      hold_and_return_handles (context, self, handles, handle_type);
      g_array_free(handles, TRUE);
      break;

    case TP_HANDLE_TYPE_ROOM:
      batch = room_verify_batch_new (self, context, count, names);
      if (!batch)
        {
          /* an error occurred while setting up the batch, and we returned error
          to dbus */
          return;
        }

      /* have all the handles been verified already? If so, nothing to do */
      if (room_verify_batch_try_return (batch))
        {
          return;
        }

      for (i = 0; i < count; i++)
        {
          if (!room_jid_verify (batch, i, context))
            {
              return;
            }
        }

      /* we've set the verification process going - the callback will handle
      returning or raising error */
      break;

    case TP_HANDLE_TYPE_LIST:
      handles = g_array_sized_new(FALSE, FALSE, sizeof(GabbleHandle), count);

      for (i = 0; i < count; i++)
        {
          GabbleHandle handle;
          const gchar *name = names[i];

          handle = gabble_handle_for_list (self->handles, name);

          if (handle == 0)
            {
              gabble_debug (DEBUG_FLAG, "requested list channel %s not available", name);

              error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
                                   "requested list channel %s not available",
                                   name);
              dbus_g_method_return_error (context, error);
              g_error_free (error);

              g_array_free (handles, TRUE);
              return;
            }
          g_array_append_val(handles, handle);
        }
      hold_and_return_handles (context, self, handles, handle_type);
      g_array_free(handles, TRUE);
      break;

    default:
      gabble_debug (DEBUG_FLAG, "unimplemented handle type %u", handle_type);

      error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
                          "unimplemented handle type %u", handle_type);
      dbus_g_method_return_error (context, error);
      g_error_free (error);
    }
}


/**
 * gabble_connection_request_presence
 *
 * Implements D-Bus method RequestPresence
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_request_presence (GabbleConnection *self,
                                    const GArray *contacts,
                                    GError **error)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  if (!gabble_handles_are_valid (self->handles, TP_HANDLE_TYPE_CONTACT,
        contacts, FALSE, error))
    return FALSE;

  if (contacts->len)
    emit_presence_update (self, contacts);

  return TRUE;
}


struct _i_hate_g_hash_table_foreach
{
  GabbleConnection *conn;
  GError **error;
  gboolean retval;
};

static void
setaliases_foreach (gpointer key, gpointer value, gpointer user_data)
{
  struct _i_hate_g_hash_table_foreach *data =
    (struct _i_hate_g_hash_table_foreach *) user_data;
  GabbleHandle handle = GPOINTER_TO_INT (key);
  gchar *alias = (gchar *) value;
  GError *error = NULL;

  if (!gabble_handle_is_valid (data->conn->handles, TP_HANDLE_TYPE_CONTACT,
        handle, &error))
    {
      data->retval = FALSE;
    }
  else if (data->conn->self_handle == handle)
    {
      /* only alter the roster if we're already there, e.g. because someone
       * added us with another client
       */
      if (gabble_roster_handle_has_entry (data->conn->roster, handle)
          && !gabble_roster_handle_set_name (data->conn->roster, handle,
                                             alias, data->error))
        {
          data->retval = FALSE;
        }
    }
  else if (!gabble_roster_handle_set_name (data->conn->roster, handle, alias,
        data->error))
    {
      data->retval = FALSE;
    }

  if (data->conn->self_handle == handle)
    {
      /* User has done SetAliases on themselves - patch their vCard.
       * FIXME: because SetAliases is currently synchronous, we ignore errors
       * here, and just let the request happen in the background
       */
      gabble_vcard_manager_edit (data->conn->vcard_manager,
                                 0, NULL, NULL, G_OBJECT(data->conn), NULL,
                                 "NICKNAME", alias, NULL);
    }

  if (NULL != error)
    {
      if (NULL == *(data->error))
        {
          *(data->error) = error;
        }
      else
        {
          g_error_free (error);
        }
    }
}

/**
 * gabble_connection_set_aliases
 *
 * Implements D-Bus method SetAliases
 * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns false.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_set_aliases (GabbleConnection *self,
                               GHashTable *aliases,
                               GError **error)
{
  GabbleConnectionPrivate *priv;
  struct _i_hate_g_hash_table_foreach data = { NULL, NULL, TRUE };

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  data.conn = self;
  data.error = error;

  g_hash_table_foreach (aliases, setaliases_foreach, &data);

  return data.retval;
}


/**
 * gabble_connection_set_last_activity_time
 *
 * Implements D-Bus method SetLastActivityTime
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns false.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_set_last_activity_time (GabbleConnection *self,
                                          guint time,
                                          GError **error)
{
  GabbleConnectionPrivate *priv;

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  return TRUE;
}


static void
setstatuses_foreach (gpointer key, gpointer value, gpointer user_data)
{
  struct _i_hate_g_hash_table_foreach *data =
    (struct _i_hate_g_hash_table_foreach*) user_data;
  GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (data->conn);

  int i;

  for (i = 0; i < LAST_GABBLE_PRESENCE; i++)
    {
      if (0 == strcmp (gabble_statuses[i].name, (const gchar*) key))
        break;
    }

  if (i < LAST_GABBLE_PRESENCE)
    {
      GHashTable *args = (GHashTable *)value;
      GValue *message = g_hash_table_lookup (args, "message");
      GValue *priority = g_hash_table_lookup (args, "priority");
      const gchar *status = NULL;
      gint8 prio = priv->priority;

      if (!status_is_available (data->conn, i))
        {
          gabble_debug (DEBUG_FLAG, "requested status %s is not available", (const gchar *) key);
          g_set_error (data->error, TELEPATHY_ERRORS, NotAvailable,
              "requested status '%s' is not available on this connection",
              (const gchar *) key);
          data->retval = FALSE;
          return;
        }

      if (message)
        {
          if (!G_VALUE_HOLDS_STRING (message))
            {
              gabble_debug (DEBUG_FLAG, "got a status message which was not a string");
              g_set_error (data->error, TELEPATHY_ERRORS, InvalidArgument,
                  "Status argument 'message' requires a string");
              data->retval = FALSE;
              return;
            }
          status = g_value_get_string (message);
        }

      if (priority)
        {
          if (!G_VALUE_HOLDS_INT (priority))
            {
              gabble_debug (DEBUG_FLAG, "got a priority value which was not a signed integer");
              g_set_error (data->error, TELEPATHY_ERRORS, InvalidArgument,
                   "Status argument 'priority' requires a signed integer");
              data->retval = FALSE;
              return;
            }
          prio = CLAMP (g_value_get_int (priority), G_MININT8, G_MAXINT8);
        }

      gabble_presence_cache_update (data->conn->presence_cache, data->conn->self_handle, priv->resource, i, status, prio);
      emit_one_presence_update (data->conn, data->conn->self_handle);
      data->retval = signal_own_presence (data->conn, data->error);
    }
  else
    {
      gabble_debug (DEBUG_FLAG, "got unknown status identifier %s", (const gchar *) key);
      g_set_error (data->error, TELEPATHY_ERRORS, InvalidArgument,
          "unknown status identifier: %s", (const gchar *) key);
      data->retval = FALSE;
    }
}

/**
 * gabble_connection_set_properties
 *
 * Implements D-Bus method SetProperties
 * on interface org.freedesktop.Telepathy.Properties
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */
void
gabble_connection_set_properties (GabbleConnection *self,
                                  const GPtrArray *properties,
                                  DBusGMethodInvocation *context)
{
  gabble_properties_mixin_set_properties (G_OBJECT (self), properties, context);
}

/**
 * gabble_connection_set_status
 *
 * Implements D-Bus method SetStatus
 * on interface org.freedesktop.Telepathy.Connection.Interface.Presence
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean
gabble_connection_set_status (GabbleConnection *self,
                              GHashTable *statuses,
                              GError **error)
{
  GabbleConnectionPrivate *priv;
  struct _i_hate_g_hash_table_foreach data = { NULL, NULL, TRUE };

  g_assert (GABBLE_IS_CONNECTION (self));

  priv = GABBLE_CONNECTION_GET_PRIVATE (self);

  ERROR_IF_NOT_CONNECTED (self, error)

  if (g_hash_table_size (statuses) != 1)
    {
      gabble_debug (DEBUG_FLAG, "got more than one status");
      g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
          "Only one status may be set at a time in this protocol");
      return FALSE;
    }

  data.conn = self;
  data.error = error;
  g_hash_table_foreach (statuses, setstatuses_foreach, &data);

  return data.retval;
}

/**
 * gabble_connection_build_avatar
 *
 * Implements D-Bus method SetAvatar
 * on interface org.freedesktop.Telepathy.Connection.Avatar
 *
 * @context: The D-Bus invocation context to use to return values
 *           or throw an error.
 */

LmMessage* gabble_connection_build_avatar(GabbleConnection *self,  
        const GArray* bin_image, gchar* mime )
    {
    int i = 0; 
    LmMessage    *message;
    LmMessageNode *lm_node,*photo_node;
    LmMessageHandler* handler = NULL;
    char* base64 = NULL;
    GError* error = NULL;
    //NULL values to be checked ? never returns NULL?
    if ( bin_image )
        {
        if ( self->self_avatar_sha1 ) 
            {
            free( self->self_avatar_sha1 );
            self->self_avatar_sha1 = NULL;
            }
        self->self_avatar_sha1 = sha1_hex( bin_image->data, bin_image->len );
        base64 = base64_encode ( bin_image->len, bin_image->data );
        }  

    message = lm_message_new_with_sub_type ( NULL,
            LM_MESSAGE_TYPE_IQ,
            LM_MESSAGE_SUB_TYPE_SET );
    lm_node = lm_message_node_add_child (message->node, "vCard", NULL);
    lm_message_node_set_attribute (lm_node, "xmlns", NS_VCARD_TEMP);
    photo_node = lm_message_node_add_child (lm_node, "PHOTO", NULL); 
    lm_message_node_add_child (photo_node, "TYPE", mime);
    lm_message_node_add_child (photo_node, "BINVAL", base64);
    g_free(base64);
    return message;
    }

/**
 * gabble_connection_set_avatar
 *
 * Implements D-Bus method SetAvatar
 * on interface org.freedesktop.Telepathy.Connection
 *
 */
gboolean  
gabble_connection_set_avatar( GabbleConnection *self, const GArray* bin_image, gchar* mime_type, gchar**avatar_sha1,
        GError** err )
    {
    
    gboolean ret = FALSE; 
    LmMessage *message; 
    

    //There is no need to check bin_image and mime for NULL.. NULL is a valid value for those
    message = gabble_connection_build_avatar( self, bin_image, mime_type );

    ret = _gabble_connection_send ( self, message, err );
    
    if ( *err ) 
            {
            *avatar_sha1 = NULL;
            //ret is already false.. so false returned
            }
        else {
            if ( self->self_avatar_sha1 )
                {
                *avatar_sha1 = g_strdup( self->self_avatar_sha1 );
                }
            ret = TRUE;
            }

    lm_message_unref( message );
    
    return ret;
    }

/**
 * gabble_connection_clear_avatar
 *
 * Implements D-Bus method ClearAvatar
 * on interface org.freedesktop.Telepathy.Connection.Interface.Avatar
 *
 * @error: Used to return a pointer to a GError detailing any error
 *         that occurred, D-Bus will throw the error only if this
 *         function returns FALSE.
 *
 * Returns: TRUE if successful, FALSE if an error was thrown.
 */
gboolean 
gabble_connection_clear_avatar( GabbleConnection *self,
                              GError **error)
 {
   LmMessage *message;
   gboolean ret = TRUE;


   //Build the avatar xml with NULL as image data and NULL as mimetype
   message = gabble_connection_build_avatar( self, 
           NULL, NULL );

   ret = _gabble_connection_send ( self, message, error );

   if ( self->self_avatar_sha1 ) 
       {
       free( self->self_avatar_sha1 );
       self->self_avatar_sha1 = NULL;
       }

   lm_message_unref( message );

   return ret;
 }