Logo Search packages:      
Sourcecode: telepathy-feed version File versions  Download package

galago.c

/*
 * This file is part of telepathy-feed
 *
 * Copyright (C) 2006 Nokia Corporation. All rights reserved.
 *
 * Contact: Onne Gorter <onne.gorter@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * 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 <string.h>
#include <glib.h>

#include <libgalago/galago.h>

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

#include <libtelepathy/tp-connmgr.h>
#include <libtelepathy/tp-conn.h>
#include <libtelepathy/tp-conn-gen.h>
#include <libtelepathy/tp-conn-iface-presence-gen.h>
#include <libtelepathy/tp-chan.h>
#include <libtelepathy/tp-chan-iface-group-gen.h>

#include "galago.h"
#include "shared.h"

/* TODO:
 * - listen for connection crashing
 * - handle statuses
 */

static void
null_cb (DBusGProxy *proxy, GError *error, gpointer userdata)
{
  if (error) {
    g_warning ("Got error requesting presence: %s", error->message);
    g_error_free (error);  
  }
}

static void
get_members_cb (DBusGProxy *proxy, GArray *members, GError *error, gpointer userdata)
{
  FeedContext *context;

  g_assert (DBUS_IS_G_PROXY (proxy));
  g_assert (userdata);

  if (error) {
    g_warning (G_STRLOC ": cannot get members: %s", error->message);
    g_error_free (error);
    return;
  }

  context = userdata;

  d(g_printerr (G_STRLOC ": got %d members\n", members->len));

  tp_conn_iface_presence_request_presence_async (context->presence_proxy, members, null_cb, NULL);

  g_array_free (members, TRUE);
}

/**
 * Get a GalagoService from a service name.
 */
static GalagoService*
get_service (const char *service_name)
{
  static GHashTable *hash = NULL;
  GalagoService *service;

  g_assert (service_name != NULL);

  if (hash == NULL) {
    hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
    g_assert (hash != NULL);
  }

  service = g_hash_table_lookup (hash, service_name);
  if (service == NULL) {
    service = galago_create_service(service_name, NULL, 0) ;
    g_hash_table_insert (hash, g_strdup (service_name), service);
  }
  return service;
}

static void
get_remote_pending_members_cb (DBusGProxy *proxy, 
                         GArray     *handles, 
                         GError     *error, 
                         gpointer    userdata)
{
  FeedContext *context;
  GError *e = NULL;
  int i;
  char *protocol = NULL;
  char **ids = NULL;

  g_assert (DBUS_IS_G_PROXY (proxy));
  g_assert (userdata);

  if (error) {
    g_warning (G_STRLOC ": cannot get members: %s", error->message);
    g_error_free (error);
    return;
  }

  context = userdata;

  d(g_printerr (G_STRLOC ": got %d remote_pending members\n", handles->len));  

  tp_conn_get_protocol (DBUS_G_PROXY (context->conn), &protocol, &e);
  if (e) {
    g_warning ("Cannot get protocol: %s\n", e->message);
    g_error_free (e);
    
    goto cleanup;
  }
  g_assert (protocol);

  tp_conn_inspect_handles (DBUS_G_PROXY (context->conn), 
                           TP_CONN_HANDLE_TYPE_CONTACT, handles, &ids, &e);
  if (e) {
    g_warning ("Cannot get names for handles: %s", e->message);
    g_error_free (error);
    
    goto cleanup;
  }

  for (i = 0; i < handles->len; i++) {
    guint handle = handles->data[i];
    GalagoService *service;
    GalagoAccount *account;
    GalagoPresence *presence;
    GalagoStatus *status;
        
    d(g_printerr(G_STRLOC ": got presence for %s\n", ids[i]));
    
    service = get_service (protocol);
    account = g_hash_table_lookup (context->account_hash, 
                                   GINT_TO_POINTER (handle));
    if (!account) {
      GalagoPerson *person;

      person = galago_create_person (NULL);

      /* Set name, avatar, etc */
      account = galago_service_create_account (service, person, ids[i]);
      g_hash_table_insert (context->account_hash, GINT_TO_POINTER (handle), 
                           account);
    }
    
    presence = galago_account_create_presence (account);
    galago_presence_clear_statuses (presence);

    status = galago_status_new (GALAGO_STATUS_PENDING, "pending", "pending", TRUE);
    galago_presence_add_status (presence, status);

  }

 cleanup:
  g_array_free (handles, TRUE);
  g_strfreev (ids);
  g_free (protocol);
}

/**
 * Map Telepathy status names to the Galago status enum. Note that this is wrong
 * and we should be using Presence:GetStatus() to get the status IDs from there.
 */
static GalagoStatusType
name_to_status (const char *status)
{
  g_assert (status != NULL);

#define MAP(s, i) if (strcmp (status, s) == 0) return i;

  MAP ("available", GALAGO_STATUS_AVAILABLE);
  MAP ("away", GALAGO_STATUS_AWAY);
  MAP ("brb", GALAGO_STATUS_AWAY);
  MAP ("busy", GALAGO_STATUS_AWAY);
  MAP ("dnd", GALAGO_STATUS_AWAY);
  MAP ("xa", GALAGO_STATUS_EXTENDED_AWAY);
  MAP ("hidden", GALAGO_STATUS_HIDDEN);
  MAP ("offline", GALAGO_STATUS_OFFLINE);
  return GALAGO_STATUS_UNSET;
#undef MAP
}

static void
populate_presence (gpointer key, gpointer value, gpointer user_data)
{
  char *id = key;
  GHashTable *params = value;
  GalagoPresence *presence = user_data;
  GalagoStatus *status;
  GValue *message;
  const char *s;

  /* TODO: get code from GetStatus */
  /* TODO at some point: pass idle time */
  status = galago_status_new (name_to_status (id), id, id, TRUE);

  if (params) {
    message = g_hash_table_lookup (params, "message");
    s = message ? g_value_get_string (message) : NULL;
    if (s && s[0] != '\0') {
      galago_object_set_attr_string (GALAGO_OBJECT (status), "message", s);
    }
  }

  galago_presence_add_status (presence, status);
}

/* A temporary hack until libtelepathy exposes useful API for single inspect calls */
static gboolean
my_tp_conn_inspect_handle (DBusGProxy *proxy, const guint IN_handle_type, const guint IN_handle, char ** OUT_arg2, GError **error)
{
  GArray *arr;
  char **names;
  gboolean ret;

  arr = g_array_new (FALSE, FALSE, sizeof(guint));
  g_array_append_val (arr, IN_handle);

  ret = tp_conn_inspect_handles (proxy, IN_handle_type, arr, &names, error);
  if (ret) {
    *OUT_arg2 = names[0];
    g_free (names);
  }
  g_array_free (arr, TRUE);
  return ret;
}

static void
presence_handle (gpointer key, gpointer value, gpointer user_data)
{
  GError *error = NULL;
  guint handle;
  GValueArray *values;
  char *id = NULL, *protocol = NULL;
  GHashTable *presences;
  FeedContext *context = user_data;

  g_assert (context);
  
  handle = GPOINTER_TO_INT (key);
  values = value;

  tp_conn_get_protocol (DBUS_G_PROXY (context->conn), &protocol, &error);
  if (error) {
    g_warning ("Cannot get protocol: %s\n", error->message);
    g_error_free (error);
    return;
  }
  g_assert (protocol);

  my_tp_conn_inspect_handle (DBUS_G_PROXY (context->conn), TP_CONN_HANDLE_TYPE_CONTACT, handle, &id, &error);
  if (!id) {
    g_warning ("Cannot get name for handle: %s", error->message);
    g_error_free (error);
    return;
  }
  d(g_printerr(G_STRLOC ": got presence for %s\n", id));

  presences = g_value_get_boxed (g_value_array_get_nth (values, 1));

  {
    GalagoService *service;
    GalagoPerson *person;
    GalagoAccount *account;
    GalagoPresence *presence;
    
    service = get_service (protocol);

    account = g_hash_table_lookup (context->account_hash, GINT_TO_POINTER (handle));
    if (!account) {
      person = galago_create_person (NULL);
      
      /* Set name, avatar, etc */
      account = galago_service_create_account (service, person, id);
      g_hash_table_insert (context->account_hash, GINT_TO_POINTER (handle), account);
    }

    presence = galago_account_create_presence (account);
    galago_presence_clear_statuses (presence);
    g_hash_table_foreach (presences, populate_presence, presence);
  }
  g_free (id);
  g_free (protocol);
}

static void
on_presence_update (DBusGProxy *proxy, GHashTable *hash, gpointer user_data)
{
  g_hash_table_foreach (hash, presence_handle, user_data);
}

static gboolean
my_tp_conn_request_handle (DBusGProxy *proxy, const guint IN_handle_type, const char * IN_name, guint *OUT_arg2, GError **error)
{
  const char *names[2] = { NULL, NULL };
  GArray *arr;

  names[0] = IN_name;

  if (tp_conn_request_handles (proxy, IN_handle_type, names, &arr, error)) {
    *OUT_arg2 = g_array_index (arr, guint, 0);
    g_array_free (arr, TRUE);
    return TRUE;
  } else {
    return FALSE;
  }
}

static void
connection_online_slurp (FeedContext *context)
{
  GError *error = NULL;
  DBusGProxy *group_proxy;
  TpChan *chan;
  guint handle;
  
  g_assert (context != NULL);
  g_assert (context->conn != NULL);
    
  /* TODO: get the status codes and put them somewhere safe */

  if (context->presence_proxy == NULL) {
    context->presence_proxy = tp_conn_get_interface (context->conn,
                                                     TELEPATHY_CONN_IFACE_PRESENCE_QUARK);
    if (context->presence_proxy == NULL) {
      g_warning ("Cannot get presence interface for connection");
      return;
    }
    g_object_add_weak_pointer (G_OBJECT (context->presence_proxy), (gpointer)&context->presence_proxy);
    dbus_g_proxy_connect_signal (context->presence_proxy, "PresenceUpdate",
                                 G_CALLBACK (on_presence_update),
                                 context, NULL);
  }

  if (!my_tp_conn_request_handle (DBUS_G_PROXY (context->conn), TP_CONN_HANDLE_TYPE_LIST,
                               "subscribe", &handle, &error)) {
    g_warning ("Cannot get handle: %s", error->message);
    g_error_free (error);
    return;
  }
  
  chan = tp_conn_new_channel(gconnection, context->conn,
                             dbus_g_proxy_get_bus_name (DBUS_G_PROXY (context->conn)),
                             TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
                             TP_CONN_HANDLE_TYPE_LIST, handle,
                             FALSE);
  if (!chan) {
    g_warning ("Cannot open channel");
    return;
  }
  
  group_proxy = tp_chan_get_interface(chan, TELEPATHY_CHAN_IFACE_GROUP_QUARK);
  g_assert (group_proxy);
  tp_chan_iface_group_get_members_async (group_proxy, get_members_cb, context);
  tp_chan_iface_group_get_remote_pending_members_async 
    (group_proxy, get_remote_pending_members_cb, context);
}

static void
on_status_change (DBusGProxy *proxy, guint status, guint reason, FeedContext *context)
{
  g_assert (context);

  d(g_printerr ("Connection status changed to %d\n", status));

  if (status == TP_CONN_STATUS_CONNECTED) {
    if (context->account_hash == NULL) {
      context->account_hash = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
    }
    connection_online_slurp (context);
  } else if (status == TP_CONN_STATUS_DISCONNECTED) {
    if (context->account_hash) {
      g_hash_table_destroy (context->account_hash);
      context->account_hash = NULL;
    }
  }
}

/**
 * For a given connection, either send to the server the presence information
 * now, or wait for the connection to go online and then send it.
 */
void
send_presence_for_connection (FeedContext *context)
{
  GError *error = NULL;
  TelepathyConnectionStatus status;
  
  g_assert (context != NULL);
  g_assert (context->conn != NULL);
  
  if (!tp_conn_get_status (DBUS_G_PROXY (context->conn), &status, &error)) {
    g_warning ("Cannot get status: %s", error->message);
    g_error_free (error);
    return;
  }

  /* Get told when the status changes */
  dbus_g_proxy_connect_signal (DBUS_G_PROXY (context->conn), "StatusChanged",
                               G_CALLBACK (on_status_change),
                               context, NULL);
  
  /* If connected already, do it now */
  if (status == TP_CONN_STATUS_CONNECTED) {
    connection_online_slurp (context);
  }
}

Generated by  Doxygen 1.6.0   Back to index