aboutsummaryrefslogblamecommitdiffstats
path: root/src/empathy-tube-dispatch.c
blob: 66efa217dbfb9c2409e05db14e97c7e55c956416 (plain) (tree)


























                                                                             
                                      
 



                       


                                            
 


                                            

                                           





















                                                                        
                 
































                                                                     
                                                        





                                                   
                                                        













                                                              
                                                         






                                                 
                                                         




                                               
                                                                          













                                                                     
                            
                  
 
                                                  



                                                                     




                                               
                                                                    
     
                                 
                                              
                                                       
     
                                                                       
     

                           
                               
                                              
                                                         








                                                                               





                  
 


                      

                                                                             
 

                                     
                                                       


                                                                          

















































































































                                                                             
                         


































                                                                          




                                                                            
                                                             



























                                                                          











                                                                     


















                                                                        

                                                                       
 


















                                                                     

                         
 


                                                                         

                                                                             
 




                                                                      
 

                



                                               





                                 
/*
 * empathy-tube-dispatch.c - Source for EmpathyTubeDispatch
 * Copyright (C) 2008 Collabora Ltd.
 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
 *
 * 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 <stdio.h>
#include <stdlib.h>

#include <telepathy-glib/dbus.h>
#include <telepathy-glib/util.h>
#include <telepathy-glib/proxy-subclass.h>
#include <telepathy-glib/interfaces.h>

#include <gtk/gtk.h>
#include <glib/gi18n.h>


#include <libempathy/empathy-tube-handler.h>
#include <extensions/extensions.h>


#include "empathy-tube-dispatch.h"
#include "empathy-tube-dispatch-enumtypes.h"

#define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
#include <libempathy/empathy-debug.h>

G_DEFINE_TYPE(EmpathyTubeDispatch, empathy_tube_dispatch, G_TYPE_OBJECT)

static void empathy_tube_dispatch_set_ability (
  EmpathyTubeDispatch *tube_dispatch,
  EmpathyTubeDispatchAbility dispatchability);

/* private structure */
typedef struct _EmpathyTubeDispatchPriv EmpathyTubeDispatchPriv;

/* properties */
enum {
    PROP_OPERATION = 1,
    PROP_DISPATCHABILITY
};


struct _EmpathyTubeDispatchPriv
{
  gboolean dispose_has_run;
  EmpathyDispatchOperation *operation;
  EmpathyTubeDispatchAbility dispatchability;
  gchar *service;
  gchar *bus_name;
  gchar *object_path;
  TpDBusDaemon *dbus;
};

#define GET_PRIV(o) \
 (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
  EMPATHY_TYPE_TUBE_DISPATCH, EmpathyTubeDispatchPriv))

static void
empathy_tube_dispatch_init (EmpathyTubeDispatch *obj)
{
  EmpathyTubeDispatchPriv *priv = GET_PRIV (obj);

  priv->dispatchability = EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN;
}

static void empathy_tube_dispatch_dispose (GObject *object);
static void empathy_tube_dispatch_finalize (GObject *object);

static void
empathy_tube_dispatch_list_activatable_names_cb (TpDBusDaemon *proxy,
  const gchar **names, const GError *error, gpointer user_data,
  GObject *object)
{
  EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
  gchar **name;

  for (name = (gchar **) names; *name != NULL; name++)
    {
      if (!tp_strdiff (*name, priv->bus_name))
        {
          DEBUG ("Found tube handler. Can dispatch it");
          empathy_tube_dispatch_set_ability (self,
            EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE);
          return;
        }
    }

  DEBUG ("Didn't find tube handler. Can't dispatch it");
  empathy_tube_dispatch_set_ability (self,
    EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE);
}

static void
empathy_tube_dispatch_name_has_owner_cb (TpDBusDaemon *proxy,
  gboolean has_owner, const GError *error, gpointer user_data,
  GObject *object)
{
  EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (self);

  if (error != NULL)
    {
      DEBUG ("NameHasOwner failed. Can't dispatch tube");
      empathy_tube_dispatch_set_ability (self,
        EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE);
      return;
    }

  if (has_owner)
    {
      DEBUG ("Tube handler is running. Can dispatch it");
      empathy_tube_dispatch_set_ability (self,
        EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE);
    }
  else
    {
      DEBUG ("Tube handler is not running. Calling ListActivatableNames");
      tp_cli_dbus_daemon_call_list_activatable_names (priv->dbus, -1,
        empathy_tube_dispatch_list_activatable_names_cb, NULL, NULL,
        G_OBJECT (self));
    }
}

static void
empathy_tube_dispatch_constructed (GObject *object)
{
  EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
  TpChannel *channel;
  GHashTable *properties;
  const gchar *service;
  const gchar *channel_type;
  TpTubeType type;

  priv->dbus = tp_dbus_daemon_new (tp_get_bus ());

  channel = empathy_dispatch_operation_get_channel (priv->operation);
  properties = tp_channel_borrow_immutable_properties (channel);

  channel_type = tp_asv_get_string (properties,
    TP_IFACE_CHANNEL ".ChannelType");
  if (channel_type == NULL)
    goto failed;

  if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
    {
      type = TP_TUBE_TYPE_STREAM;
      service = tp_asv_get_string (properties,
        TP_IFACE_CHANNEL_TYPE_STREAM_TUBE  ".Service");
    }
  else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
    {
      GError *error = NULL;

      type = TP_TUBE_TYPE_DBUS;
      service = tp_asv_get_string (properties,
        TP_IFACE_CHANNEL_TYPE_DBUS_TUBE  ".ServiceName");

      if (!tp_dbus_check_valid_bus_name (service, TP_DBUS_NAME_TYPE_WELL_KNOWN,
            &error))
        {
          DEBUG ("Can't dispatch tube; invalid ServiceName %s: %s", service,
              error->message);
          g_error_free (error);
          goto failed;
        }
    }
  else
    {
      goto failed;
    }


  if (service == NULL)
    goto failed;

  priv->bus_name = empathy_tube_handler_build_bus_name (type, service);
  priv->object_path = empathy_tube_handler_build_object_path (type, service);

  priv->service = g_strdup (service);

  DEBUG ("Look for tube handler %s\n", priv->bus_name);
  tp_cli_dbus_daemon_call_name_has_owner (priv->dbus, -1, priv->bus_name,
    empathy_tube_dispatch_name_has_owner_cb, NULL, NULL, G_OBJECT (self));

  return;

failed:
  empathy_tube_dispatch_set_ability (self,
    EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE);
}

static void
empathy_tube_dispatch_set_property (GObject *object,
  guint property_id, const GValue *value, GParamSpec *pspec)
{
  EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);

  switch (property_id)
    {
      case PROP_OPERATION:
        priv->operation = g_value_dup_object (value);
        break;
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        break;
    }
}

static void
empathy_tube_dispatch_get_property (GObject *object,
  guint property_id, GValue *value, GParamSpec *pspec)
{
  EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);

  switch (property_id)
    {
      case PROP_OPERATION:
        g_value_set_object (value, priv->operation);
        break;
      case PROP_DISPATCHABILITY:
        g_value_set_enum (value, priv->dispatchability);
        break;
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        break;
    }
}

static void
empathy_tube_dispatch_class_init (
  EmpathyTubeDispatchClass *empathy_tube_dispatch_class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (empathy_tube_dispatch_class);
  GParamSpec *param_spec;

  g_type_class_add_private (empathy_tube_dispatch_class,
    sizeof (EmpathyTubeDispatchPriv));

  object_class->set_property = empathy_tube_dispatch_set_property;
  object_class->get_property = empathy_tube_dispatch_get_property;

  object_class->constructed = empathy_tube_dispatch_constructed;
  object_class->dispose = empathy_tube_dispatch_dispose;
  object_class->finalize = empathy_tube_dispatch_finalize;

  param_spec = g_param_spec_object ("operation",
    "operation", "The telepathy connection",
    EMPATHY_TYPE_DISPATCH_OPERATION,
    G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  g_object_class_install_property (object_class, PROP_OPERATION, param_spec);

  param_spec = g_param_spec_enum ("dispatchability",
    "dispatchability",
    "Whether or not there is a handler to dispatch the operation to",
    EMPATHY_TYPE_TUBE_DISPATCH_ABILITY, EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN,
    G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  g_object_class_install_property (object_class, PROP_DISPATCHABILITY,
    param_spec);

}

void
empathy_tube_dispatch_dispose (GObject *object)
{
  EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (self);

  if (priv->dispose_has_run)
    return;

  priv->dispose_has_run = TRUE;

  /* release any references held by the object here */
  if (priv->operation != NULL)
    g_object_unref (priv->operation);

  priv->operation = NULL;

  if (priv->dbus != NULL)
    g_object_unref (priv->dbus);

  priv->dbus = NULL;


  if (G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose)
    G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose (object);
}

void
empathy_tube_dispatch_finalize (GObject *object)
{
  EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (self);

  g_free (priv->bus_name);
  g_free (priv->object_path);
  g_free (priv->service);

  /* free any data held directly by the object here */

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

EmpathyTubeDispatch *
empathy_tube_dispatch_new (EmpathyDispatchOperation *operation)
{
  return EMPATHY_TUBE_DISPATCH (g_object_new (EMPATHY_TYPE_TUBE_DISPATCH,
      "operation", operation, NULL));
}

EmpathyTubeDispatchAbility
empathy_tube_dispatch_is_dispatchable (EmpathyTubeDispatch *tube_dispatch)
{
  EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);

  return priv->dispatchability;
}

static void
empathy_tube_dispatch_set_ability (EmpathyTubeDispatch *tube_dispatch,
  EmpathyTubeDispatchAbility dispatchability)
{
  EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);

  if (priv->dispatchability == dispatchability)
    return;

  priv->dispatchability = dispatchability;
  g_object_notify (G_OBJECT (tube_dispatch), "dispatchability");
}

static void
empathy_tube_dispatch_show_error (EmpathyTubeDispatch *self, gchar *message)
{
  GtkWidget *dialog;

  dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
      GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "%s", message);

  gtk_dialog_run (GTK_DIALOG (dialog));

  gtk_widget_destroy (dialog);
}

static void
empathy_tube_dispatch_handle_tube_cb (TpProxy *proxy, const GError *error,
  gpointer user_data, GObject *object)
{
  EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object);
  EmpathyTubeDispatchPriv *priv = GET_PRIV (self);

  if (error != NULL)
    {
      gchar *msg = g_strdup_printf (
        _("Unable to start application for service %s: %s"),
          priv->service,  error->message);

      empathy_tube_dispatch_show_error (self, msg);
      g_free (msg);
    }

  /* Remove the ref we were holding because of the dispatching */
  g_object_unref (object);
}

static void
empathy_tube_do_dispatch (EmpathyTubeDispatch *self)
{
  EmpathyTubeDispatchPriv *priv = GET_PRIV (self);
  TpChannel *channel;
  TpProxy *connection;
  TpProxy *thandler;
  gchar   *object_path;
  guint    handle_type;
  guint    handle;

  channel = empathy_dispatch_operation_get_channel (priv->operation);

  /* Create the proxy for the tube handler */
  thandler = g_object_new (TP_TYPE_PROXY,
    "dbus-connection", tp_get_bus (),
    "bus-name", priv->bus_name,
    "object-path", priv->object_path,
    NULL);

  tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER);

  /* Give the tube to the handler */
  g_object_get (channel,
    "connection", &connection,
    "object-path", &object_path,
    "handle_type", &handle_type,
    "handle", &handle,
    NULL);

  emp_cli_tube_handler_call_handle_tube (thandler, -1,
    connection->bus_name, connection->object_path,
    object_path, handle_type, handle,
    empathy_tube_dispatch_handle_tube_cb, NULL, NULL, G_OBJECT (self));

  g_object_unref (thandler);
  g_object_unref (connection);
  g_free (object_path);
}

void
empathy_tube_dispatch_handle (EmpathyTubeDispatch *tube_dispatch)
{
  EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch);

  /* Keep ourselves alive untill the dispatching is finished */
  g_object_ref (tube_dispatch);

  /* If we can't claim it, don't do anything */
  if (!empathy_dispatch_operation_claim (priv->operation))
    goto done;

  if (priv->dispatchability != EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE)
    {
      gchar *msg;
      TpChannel *channel;

      channel = empathy_dispatch_operation_get_channel (priv->operation);

      msg = g_strdup_printf (
        _("An invitation was offered for service %s, but you don't have the "
          "needed application to handle it"), priv->service);

      empathy_tube_dispatch_show_error (tube_dispatch, msg);

      g_free (msg);

      tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);

      goto done;
    }
  else
    {
      empathy_tube_do_dispatch (tube_dispatch);
    }

  return;
done:
  g_object_unref (tube_dispatch);
}