Logo Search packages:      
Sourcecode: obex-data-server version File versions

ods-manager.c

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
 *
 * Copyright (C) 2007-2008 Tadas Dailyda <tadas@dailyda.com>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sdp.h>
#include <openobex/obex.h>

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


#include "ods-bluez.h"
#include "ods-common.h"
#include "ods-error.h"
#include "ods-server.h"
#include "ods-session.h"
#include "ods-manager.h"
#include "ods-manager-dbus-glue.h"

#define ODS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ODS_TYPE_MANAGER, OdsManagerPrivate))

typedef struct OdsManagerSessionInfo_ {
      OdsSession  *session;
      /* Bluetooth specific */
      gchar       *bluetooth_address;
} OdsManagerSessionInfo;

typedef struct OdsManagerServerInfo_ {
      OdsServer   *server;
      /* Bluetooth specific */
      gchar       *bluetooth_address;
      guint32           sdp_record_handle;
} OdsManagerServerInfo;

typedef struct OdsManagerCreateBluetoothSessionData_ {
      /* arguments from function */
      OdsManager                    *manager;
      gchar                         *address;
      gint                          service;
      /* DBUS data */
      DBusGMethodInvocation   *context;
} OdsManagerCreateBluetoothSessionData;

struct OdsManagerPrivate
{
      gboolean    is_disposing;
      gboolean    disposed;
      gboolean    initialized;
      /* Session list (DBus path as key and OdsManagerSessionInfo as value) */
      GHashTable  *session_list;
      /* Server list (DBus path as key and OdsManagerServerInfo as value) */
      GHashTable  *server_list;
      OdsBluez    *bluez;
      DBusGProxy  *dbus_proxy;
      GHashTable  *listened_dbus_names;
      GHashTable  *removed_dbus_names;
};

enum {
      SESSION_CREATED,
      SESSION_REMOVED,
      DISPOSED,
      LAST_SIGNAL
};

static guint           signals [LAST_SIGNAL] = { 0, };

G_DEFINE_TYPE (OdsManager, ods_manager, G_TYPE_OBJECT)

static void     ods_manager_class_init    (OdsManagerClass *klass);
static void     ods_manager_init          (OdsManager      *manager);
static gboolean ods_manager_session_finalize (gpointer key,
                                                            OdsManagerSessionInfo *session_info,
                                                            OdsManager *manager);
static gboolean         ods_manager_server_finalize (gpointer key,
                                                            OdsManagerServerInfo *server_info,
                                                            OdsManager *manager);
static void     ods_manager_finalize      (GObject     *object);


static void
ods_manager_listened_names_add (OdsManager *manager, const gchar *dbus_owner,
                                                const gchar *dbus_path)
{
      GHashTable *object_list = NULL;
      
      if (!(object_list = g_hash_table_lookup (manager->priv->listened_dbus_names, 
                                                                                    dbus_owner))) {
            object_list = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
            g_hash_table_insert (manager->priv->listened_dbus_names, g_strdup (dbus_owner),
                                                object_list);
      }
      g_hash_table_insert (object_list, g_strdup (dbus_path), NULL);
}

static void
ods_manager_listened_names_remove (OdsManager *manager, const gchar *dbus_owner,
                                                      const gchar *dbus_path)
{
      GHashTable *object_list;

      g_message ("Removing listened DBUS name %s (object: %s)", dbus_owner, dbus_path);
      if ((object_list = g_hash_table_lookup (manager->priv->listened_dbus_names, 
                                                                              dbus_owner))) {
            if (dbus_path) {
                  g_hash_table_remove (object_list, dbus_path);
                  if (g_hash_table_size (object_list) > 0)
                        return;
            }
      }
      g_hash_table_remove (manager->priv->listened_dbus_names, dbus_owner);
      g_hash_table_remove (manager->priv->removed_dbus_names, dbus_owner);
      
      g_message ("Removed from listened DBUS names list");
      if (manager->priv->is_disposing &&
                  g_hash_table_size (manager->priv->listened_dbus_names) == 0) {
            g_message ("Manager disposed");
            manager->priv->disposed = TRUE;
            g_signal_emit (manager, signals [DISPOSED], 0);
      }

}

static void
ods_manager_session_list_add (OdsManager *manager, OdsSession *session,
                                                const gchar *bluetooth_address,
                                                const gchar *dbus_owner,
                                                const gchar *dbus_path)
{
      OdsManagerSessionInfo *session_info;
      
      session_info = g_new0 (OdsManagerSessionInfo, 1);
      session_info->session = session;
      session_info->bluetooth_address = g_strdup (bluetooth_address);
      g_hash_table_insert (manager->priv->session_list, g_strdup (dbus_path),
                                          session_info);
      ods_manager_listened_names_add (manager, dbus_owner, dbus_path);
}

static void
ods_manager_server_list_add (OdsManager *manager, OdsServer *server,
                                                const gchar *bluetooth_address,
                                                guint32 sdp_record_handle,
                                                const gchar *dbus_owner,
                                                const gchar *dbus_path)
{
      OdsManagerServerInfo *server_info;
      
      server_info = g_new0 (OdsManagerServerInfo, 1);
      server_info->server = server;
      server_info->bluetooth_address = g_strdup (bluetooth_address);
      server_info->sdp_record_handle = sdp_record_handle;
      g_hash_table_insert (manager->priv->server_list, g_strdup (dbus_path),
                                          server_info);
      ods_manager_listened_names_add (manager, dbus_owner, dbus_path);
}

static void
ods_manager_session_info_free (OdsManagerSessionInfo *session_info)
{
      g_free (session_info->bluetooth_address);
      g_free (session_info);
}

static void
ods_manager_server_info_free (OdsManagerServerInfo *server_info)
{
      g_free (server_info->bluetooth_address);
      g_free (server_info);
}

static void
ods_manager_session_list_remove (OdsManager *manager, OdsSession *session)
{
      gchar *session_object;
      gchar *owner;
      
      g_object_get (session, "dbus-path", &session_object, NULL);
      g_object_get (session, "owner", &owner, NULL);
      
      if (!manager->priv->is_disposing)
            g_hash_table_remove (manager->priv->session_list, session_object);
      ods_manager_listened_names_remove (manager, owner, session_object);
      
      g_free (session_object);
      g_free (owner);
}

static void
ods_manager_server_list_remove (OdsManager *manager, OdsServer *server)
{
      gchar *server_object;
      gchar *owner;
      
      g_object_get (server, "dbus-path", &server_object, NULL);
      g_object_get (server, "owner", &owner, NULL);
      
      if (!manager->priv->is_disposing)
            g_hash_table_remove (manager->priv->server_list, server_object);
      ods_manager_listened_names_remove (manager, owner, server_object);
      
      g_free (server_object);
      g_free (owner);
}

static void
session_closed_cb (OdsSession *session, OdsManager *manager)
{
      gchar *session_object;
            
      g_message ("session closed");
      g_object_get (session, "dbus-path", &session_object, NULL);
      ods_manager_session_list_remove (manager, session);
      g_signal_emit (manager, signals [SESSION_REMOVED], 0, session_object);
      
      g_free (session_object);
      g_object_unref (session);
}

static void
server_disposed_cb (OdsServer *server, OdsManager *manager)
{     
      g_message ("server closed");
      /* Free everything */
      ods_manager_server_list_remove (manager, server);

      g_object_unref (server);
}

static void
server_closed_cb (OdsServer *server, OdsManager *manager)
{
      g_signal_connect (server, "disposed", G_CALLBACK (server_disposed_cb), manager);
      g_object_run_dispose (G_OBJECT (server));
}

static void
server_started_cb (OdsServer *server, OdsManager *manager)
{
      OdsManagerServerInfo    *server_info;
      gchar                         *server_object;
      guint                         service;
      guint32                             record_handle;
      
      /* Add service record to SDP database */
      g_object_get (server, "dbus-path", &server_object, NULL);
      g_object_get (server, "service", &service, NULL);
      server_info = g_hash_table_lookup (manager->priv->server_list, server_object);
      
      if (strlen (server_info->bluetooth_address) == 0)
            return;
      
      record_handle = ods_bluez_add_service_record (manager->priv->bluez,
                                                                              server_info->bluetooth_address,
                                                                              service);
      if (record_handle == 0) {
            /* could not add SDP record */
            g_warning ("Could not add SDP record for server (%s), closing server",
                              server_object);
            /* stop server */
            g_signal_connect (server, "disposed", G_CALLBACK (server_disposed_cb), manager);
            g_object_run_dispose (G_OBJECT (server));
      } else {
            server_info->sdp_record_handle = record_handle;
      }
      
      g_free (server_object);
}

static void
server_stopped_cb (OdsServer *server, OdsManager *manager)
{
      OdsManagerServerInfo    *server_info;
      gchar                         *server_object;
            
      g_message ("server stopped");
      g_object_get (server, "dbus-path", &server_object, NULL);
      
      /* Remove SDP record (Bluetooth specific) */
      server_info = g_hash_table_lookup (manager->priv->server_list, server_object);
      if (!server_info)
            goto out;

      if (server_info->sdp_record_handle && strlen (server_info->bluetooth_address) > 0) {
            ods_bluez_remove_service_record (manager->priv->bluez,
                                                                  server_info->bluetooth_address,
                                                                  server_info->sdp_record_handle);
      }
      
out:  
      g_free (server_object);
}

static void
session_cancelled_cb (OdsSession *session, OdsManager *manager)
{
      GError *error = NULL;
      
      g_message ("session cancelled");
      g_signal_connect (session, "disconnected",
                                    G_CALLBACK (session_closed_cb), manager);
      if (ods_session_disconnect_internal (session, &error) == -1) {
            /* shouldn't ever happen */
            g_clear_error (&error);
      }
}

static void
ods_manager_finalize_object (gpointer *object, OdsManager *manager)
{
      OdsManagerSessionInfo   *session_info;
      OdsManagerServerInfo    *server_info;
            
      /* Determine whether we have to close a session or a server */
      if ((session_info = g_hash_table_lookup (manager->priv->session_list, 
                                                                  object))) {
            g_warning ("Finalizing session");
            ods_manager_session_finalize (NULL, session_info, manager);
      } else if ((server_info = g_hash_table_lookup (manager->priv->server_list,
                                                                              object))) {
            g_warning ("Finalizing server");
            ods_manager_server_finalize (NULL, server_info, manager);
      }
}

static void
dbus_name_owner_changed_cb (DBusGProxy *dbus_proxy, const gchar *name,
                                          const gchar *old_owner, const gchar *new_owner,
                                          OdsManager *manager)
{
      GHashTable  *object_list;
      GList       *object_list_dup;
      
      if (*new_owner != '\0')
            return;
      /* Lookup this name */
      object_list = g_hash_table_lookup (manager->priv->listened_dbus_names, name);
      if (object_list == NULL || 
                  (g_hash_table_lookup (manager->priv->removed_dbus_names, name) != NULL))
            return;
      g_warning ("DBUS NAME REMOVED: %s", name);
      /* insert into removed_dbus_names cause NameOwnerChanged signal 
       * might be received twice. Use manager as bogus value */
      g_hash_table_insert (manager->priv->removed_dbus_names, g_strdup (name), manager);
      
      /* Now we finalize all objects (sessions and servers) */
      object_list_dup = ods_hash_table_get_keys (object_list);
      g_list_foreach (object_list_dup, (GFunc) ods_manager_finalize_object, manager);
      g_list_free (object_list_dup);
}

static void
client_socket_connected_cb (gint fd, GError *error,
                                          OdsManagerCreateBluetoothSessionData *data)
{
      gboolean          name_exists = TRUE;
      GError                  *error_local = NULL;
      OdsSession        *session;
      gchar             *sender = NULL;
      gchar             *session_object = NULL;
      
      
      if (fd == -1) {
            /* Could not connect, return error */
            dbus_g_method_return_error (data->context, error);
            g_free (data->address);
            g_free (data);
            return;
      }
      sender = dbus_g_method_get_sender (data->context);
      g_message ("Session created by: %s", sender);
      /* It might be that sender disconnected from bus while connecting socket */
      org_freedesktop_DBus_name_has_owner (data->manager->priv->dbus_proxy,
                                                                  sender, &name_exists, &error_local);
      if (error_local) {
            /* ignore this */
            g_clear_error (&error_local);
      }
      if (!name_exists) {
            shutdown (fd, SHUT_RDWR);
            g_message ("Session owner disconnected from bus, discarding session");
            g_set_error (&error_local, ODS_ERROR, ODS_ERROR_FAILED,
                                    "Session owner disconnected from bus");
            dbus_g_method_return_error (data->context, error_local);
            g_free (data->address);
            g_free (data);
            return;
      }
      /* create session object and return it's object path */
      session = ods_session_new (fd, data->service, sender);
      
      /* add session to session list */
      g_object_get (session, "dbus-path", &session_object, NULL);
      ods_manager_session_list_add (data->manager, session, data->address,
                                                      sender, session_object);

      /* deal with signals */
      g_signal_connect (session, "closed", 
                                    G_CALLBACK (session_closed_cb), 
                                    data->manager);
      g_signal_emit (data->manager, signals [SESSION_CREATED], 0, session_object);
      
      /* return session object path */
      dbus_g_method_return (data->context, session_object);
      
      g_free (sender);
      g_free (session_object);
      g_free (data->address);
      g_free (data);
}

static gboolean
bluetooth_init (OdsManager *manager)
{
      if (manager->priv->bluez == NULL)
            manager->priv->bluez = ods_bluez_new ();
      else if (!ods_bluez_is_initialized (manager->priv->bluez)) {
            g_message ("Reinitializing OdsBluez object");
            /* Try to initialize bluez again */
            if (G_IS_OBJECT (manager->priv->bluez))
                  g_object_unref (manager->priv->bluez);
            manager->priv->bluez = ods_bluez_new ();
      }
      
      return ods_bluez_is_initialized (manager->priv->bluez);
}

/**
 * ods_manager_create_bluetooth_session:
 * @manager: This class instance
 * @address: Bluetooth address of remote device to connect to
 * @pattern: OBEX UUID (commonly "opp", "ftp", etc.)
 *
 * Creates and auto-connects Bluetooth session.
 *
 * Return value: 
 **/
gboolean
ods_manager_create_bluetooth_session (OdsManager *manager,
                              const gchar *address,
                              const gchar *pattern,
                              DBusGMethodInvocation *context)
{
      GError            *error = NULL;
      gboolean    valid;
      bdaddr_t    *bd_address;
      gint        service;
      gchar       **parsed;
      
      
      g_return_val_if_fail (manager != NULL, FALSE);
      g_return_val_if_fail (ODS_IS_MANAGER (manager), FALSE);
      
      /* check address validity */
      bd_address = strtoba (address);
      /* can be only real address */
      valid = bachk (address) == 0 &&
                  bacmp (bd_address, BDADDR_ANY) &&
                  bacmp (bd_address, BDADDR_ALL);
      free (bd_address);
      
      if (!valid) {
            g_set_error (&error, ODS_ERROR,     ODS_ERROR_INVALID_ARGUMENTS,
                                    "Invalid Bluetooth address");
            dbus_g_method_return_error (context, error);
            g_clear_error (&error);             
            return FALSE;
      }
      
      /* check pattern validity (has to be supported/known service */
      
      parsed = g_strsplit (pattern, ":", 2);
      
      if (!g_ascii_strcasecmp (parsed[0], ODS_MANAGER_FTP_STR))
            service = ODS_SERVICE_FTP;
      else if (!g_ascii_strcasecmp (parsed[0], ODS_MANAGER_PBAP_STR))
            service = ODS_SERVICE_PBAP;
      else if (!g_ascii_strcasecmp (parsed[0], ODS_MANAGER_OPP_STR))
            service = ODS_SERVICE_OPP;
      else {
            g_set_error (&error, ODS_ERROR,     ODS_ERROR_INVALID_ARGUMENTS,
                                    "Invalid pattern");
            dbus_g_method_return_error (context, error);
            g_clear_error (&error);
            g_strfreev (parsed);
            return FALSE;
      }
      
      /* initialize Bluetooth */
      if (!bluetooth_init (manager)) {
            g_set_error (&error, ODS_ERROR,     ODS_ERROR_TRANSPORT_NOT_AVAILABLE,
                                    "Bluez DBus interface not available");
            dbus_g_method_return_error (context, error);
            g_clear_error (&error);
            g_strfreev (parsed);
            return FALSE;
      }
      /* connect Bluetooth transport (ods-bluez.c) */
      OdsManagerCreateBluetoothSessionData *cb_data;/* data to pass to callback */
      
      cb_data = g_new0 (OdsManagerCreateBluetoothSessionData, 1);
      cb_data->manager = manager;
      cb_data->address = g_strdup (address);
      cb_data->service = service;
      cb_data->context = context;
      
      g_message ("Parsed[0]: %s, Parsed[1]: %s", parsed[0], parsed[1]);
      ods_bluez_get_client_socket (manager->priv->bluez, address, parsed[0],
                                                      (g_strv_length (parsed)>1 ? atoi (parsed[1]) : 0),
                                                      (OdsBluezFunc) client_socket_connected_cb,
                                                      cb_data);
      g_strfreev (parsed);
      
      /* Session object will be created in client_socket_connected_cb */
      return TRUE;
}

gboolean
ods_manager_create_bluetooth_server (OdsManager *manager,
                                                            const gchar *source_address,
                                                            const gchar *pattern,
                                                            gboolean require_pairing,
                                                            DBusGMethodInvocation *context)
{
      GError            *error = NULL;
      gboolean    valid;
      bdaddr_t    *bd_address;
      gint        service;
      guint8            channel;
      gint        fd;
      gint        sockopt;
      OdsServer   *server;
      gchar       *sender;
      gchar       *server_object;
      
      g_return_val_if_fail (manager != NULL, FALSE);
      g_return_val_if_fail (ODS_IS_MANAGER (manager), FALSE);
      
      /* check address validity */
      bd_address = strtoba (source_address);
      /* can be real address or BDADDR_ANY */
      valid = bachk (source_address) == 0 &&
                  bacmp (bd_address, BDADDR_ALL);
      free (bd_address);
      
      if (!valid) {
            g_set_error (&error, ODS_ERROR,     ODS_ERROR_INVALID_ARGUMENTS,
                                    "Invalid Bluetooth address");
            dbus_g_method_return_error (context, error);
            g_clear_error (&error);             
            return FALSE;
      }
      
      /* check pattern validity (has to be supported/known service */
      if (!g_ascii_strcasecmp (pattern, ODS_MANAGER_FTP_STR)) {
            service = ODS_SERVICE_FTP;
            channel = ODS_FTP_RFCOMM_CHANNEL;
      } else if (!g_ascii_strcasecmp (pattern, ODS_MANAGER_OPP_STR)) {
            service = ODS_SERVICE_OPP;
            channel = ODS_OPP_RFCOMM_CHANNEL;
      } else if (!g_ascii_strcasecmp (pattern, ODS_MANAGER_PBAP_STR)) {
            service = ODS_SERVICE_PBAP;
            channel = ODS_PBAP_RFCOMM_CHANNEL;
      } else {
            g_set_error (&error, ODS_ERROR,     ODS_ERROR_INVALID_ARGUMENTS,
                                    "Invalid pattern");
            dbus_g_method_return_error (context, error);
            g_clear_error (&error);
            return FALSE;
      }
      
      /* initialize Bluetooth */
      if (!bluetooth_init (manager)) {
            g_set_error (&error, ODS_ERROR,     ODS_ERROR_TRANSPORT_NOT_AVAILABLE,
                                    "Bluez DBus interface not available");
            dbus_g_method_return_error (context, error);
            g_clear_error (&error);
            return FALSE;
      }
      /* create server socket */
      fd = ods_bluez_get_server_socket (manager->priv->bluez, source_address,
                                                            channel);
      if (fd == -1) {
            /* could not create server socket */
            g_set_error (&error, ODS_ERROR, ODS_ERROR_FAILED,
                                    "Could not create server socket");
            dbus_g_method_return_error (context, error);
            g_clear_error (&error);
            return FALSE;
      }
      g_message ("server socket created");
      
      /* require_pairing */
      if (require_pairing) {
            sockopt = RFCOMM_LM_SECURE;
            if (setsockopt (fd, SOL_RFCOMM, RFCOMM_LM, &sockopt, sizeof (sockopt)) < 0) {
                  g_set_error (&error, ODS_ERROR, ODS_ERROR_FAILED,
                                          "Setting RFCOMM link mode failed");
                  dbus_g_method_return_error (context, error);
                  g_clear_error (&error);
                  return FALSE;
            }
      }
                                                                              
      /* create server object and return it's object path */
      sender = dbus_g_method_get_sender (context);
      g_message ("Server created by: %s", sender);
      server = ods_server_new (fd, service, sender);
      
      /* add server to server list */
      g_object_get (server, "dbus-path", &server_object, NULL);
      ods_manager_server_list_add (manager, server, source_address, 0, sender,
                                                      server_object);
      
      /* deal with signals */
      g_signal_connect (server, "started", G_CALLBACK (server_started_cb), manager);
      g_signal_connect (server, "stopped", G_CALLBACK (server_stopped_cb), manager);
      g_signal_connect (server, "closed", G_CALLBACK (server_closed_cb), manager);
      
      /* return server object path */
      dbus_g_method_return (context, server_object);
      
      g_free (sender);
      g_free (server_object);
      return TRUE;
}

GHashTable *
ods_manager_get_session_info (OdsManager *manager, gchar *session_object)
{
      GHashTable *info;
      OdsManagerSessionInfo   *session_info;
      
      info = g_hash_table_new ((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal);
      session_info = g_hash_table_lookup (manager->priv->session_list, session_object);
      if (session_info) {
            g_hash_table_insert (info, "BluetoothAddress", 
                                                g_strdup (session_info->bluetooth_address));
      }
      return info;
}

GHashTable *
ods_manager_get_server_info (OdsManager *manager, gchar *server_object)
{
      GHashTable *info;
      OdsManagerServerInfo    *server_info;
      
      info = g_hash_table_new ((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal);
      server_info = g_hash_table_lookup (manager->priv->server_list, server_object);
      if (server_info) {
            g_hash_table_insert (info, "BluetoothAddress", 
                                                g_strdup (server_info->bluetooth_address));
      }
      return info;
}

/**
 * ods_manager_class_init:
 * @klass: The OdsManagerClass
 **/
static void
ods_manager_class_init (OdsManagerClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);

      object_class->finalize = ods_manager_finalize;

      signals [SESSION_CREATED] =
            g_signal_new ("session-created",
                        G_TYPE_FROM_CLASS (object_class), 
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (OdsManagerClass, session_created),
                        NULL, 
                        NULL, 
                        g_cclosure_marshal_VOID__STRING,
                        G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH);
                        
      signals [SESSION_REMOVED] =
            g_signal_new ("session-removed",
                        G_TYPE_FROM_CLASS (object_class), 
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (OdsManagerClass, session_removed),
                        NULL, 
                        NULL, 
                        g_cclosure_marshal_VOID__STRING,
                        G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH);
      
      signals [DISPOSED] =
            g_signal_new ("disposed",
                        G_TYPE_FROM_CLASS (object_class), 
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (OdsManagerClass, disposed),
                        NULL, 
                        NULL, 
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE, 0);

      g_type_class_add_private (klass, sizeof (OdsManagerPrivate));
      
      GError *error = NULL;

      /* Init the DBus connection, per-klass */
      klass->connection = dbus_g_bus_get (ODS_DBUS_BUS, &error);
      if (klass->connection == NULL)
      {
            g_warning("Unable to connect to dbus: %s", error->message);
            g_clear_error (&error);
            return;
      }

      /* &dbus_glib_ods_manager_object_info is provided in the 
       * dbus/ods-manager-dbus-glue.h file */
      dbus_g_object_type_install_info (ODS_TYPE_MANAGER, &dbus_glib_ods_manager_object_info);
      /* also register global error domain */
      dbus_g_error_domain_register (ODS_ERROR, ODS_ERROR_DBUS_INTERFACE, ODS_TYPE_ERROR);
      
}

/**
 * ods_manager_init:
 * @manager: This class instance
 **/
static void
ods_manager_init (OdsManager *manager)
{
      OdsManagerClass *klass = ODS_MANAGER_GET_CLASS (manager);
      manager->priv = ODS_MANAGER_GET_PRIVATE (manager);
      
      manager->priv->session_list = g_hash_table_new_full (g_str_hash, g_str_equal,
                                    g_free, (GDestroyNotify) ods_manager_session_info_free);
      manager->priv->server_list = g_hash_table_new_full (g_str_hash, g_str_equal,
                                    g_free, (GDestroyNotify) ods_manager_server_info_free);
      manager->priv->initialized = TRUE;/* For future use */
      
      manager->priv->dbus_proxy = dbus_g_proxy_new_for_name (klass->connection, 
                                                                                          DBUS_SERVICE_DBUS,
                                                                                          DBUS_PATH_DBUS,   
                                                                                          DBUS_INTERFACE_DBUS);
      dbus_g_proxy_add_signal (manager->priv->dbus_proxy, "NameOwnerChanged",
                                                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                                                G_TYPE_INVALID);
      dbus_g_proxy_connect_signal (manager->priv->dbus_proxy, "NameOwnerChanged",
                                                      G_CALLBACK (dbus_name_owner_changed_cb),
                                                      manager, NULL);
      
      manager->priv->listened_dbus_names = g_hash_table_new_full (g_str_hash,
                                          g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref);
      manager->priv->removed_dbus_names = g_hash_table_new_full (g_str_hash,
                                          g_str_equal, g_free, NULL);

      dbus_g_connection_register_g_object (klass->connection, 
                                          ODS_MANAGER_DBUS_PATH, 
                                          G_OBJECT (manager));
}

static gboolean
ods_manager_session_finalize (gpointer key, OdsManagerSessionInfo *session_info,
                                                OdsManager *manager)
{
      g_message ("attempting to close session");
      g_signal_connect (session_info->session, "cancelled", 
                                    G_CALLBACK (session_cancelled_cb), manager);
      ods_session_cancel_internal (session_info->session);
      /* Even if there was nothing to cancel, we will get
       * CANCELLED signal and disconnection will happen in 
       * session_cancelled_cb */

      return TRUE;
}

static gboolean
ods_manager_server_finalize (gpointer key, OdsManagerServerInfo *server_info,
                                                OdsManager *manager)
{
      OdsServer *server = server_info->server;
      
      /* STOPPED signal will not be emitted, call teh callback now */
      server_stopped_cb (server, manager);
      
      g_signal_connect (server, "disposed", G_CALLBACK (server_disposed_cb), manager);
      ods_server_dispose (server);

      return TRUE;
}

void
ods_manager_dispose (OdsManager     *manager)
{
      g_return_if_fail (manager != NULL);
      g_return_if_fail (ODS_IS_MANAGER (manager));

      g_return_if_fail (manager->priv != NULL);
      if (manager->priv->disposed)
            return;
      
      g_message ("Disposing manager");
      manager->priv->is_disposing = TRUE;
      /* check if there is nothing to dispose */
      if (g_hash_table_size (manager->priv->listened_dbus_names) == 0) {
            g_message ("Manager disposed at once");
            manager->priv->disposed = TRUE;
            g_signal_emit (manager, signals [DISPOSED], 0);
      } else {
            g_hash_table_foreach_remove (manager->priv->session_list,
                                                (GHRFunc) ods_manager_session_finalize, manager);
            g_hash_table_foreach_remove (manager->priv->server_list,
                                                (GHRFunc) ods_manager_server_finalize, manager);
      }
}

/**
 * ods_manager_finalize:
 * @object: The object to finalize
 *
 * Finalise the manager, by unref'ing all the depending modules.
 **/
static void
ods_manager_finalize (GObject *object)
{
      OdsManager  *manager;

      g_return_if_fail (object != NULL);
      g_return_if_fail (ODS_IS_MANAGER (object));

      manager = ODS_MANAGER (object);

      g_return_if_fail (manager->priv != NULL);
      g_return_if_fail (manager->priv->disposed);
      
      g_message ("Finalizing manager");
      g_hash_table_unref (manager->priv->listened_dbus_names);
      g_hash_table_unref (manager->priv->session_list);
      g_hash_table_unref (manager->priv->server_list);
      if (G_IS_OBJECT (manager->priv->bluez))
            g_object_unref (manager->priv->bluez);
      g_object_unref (G_OBJECT (manager->priv->dbus_proxy));

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

/**
 * ods_manager_new:
 *
 * Return value: a new OdsManager object.
 **/
OdsManager *
ods_manager_new (void)
{
      OdsManager *manager;
      manager = g_object_new (ODS_TYPE_MANAGER, NULL);
      return ODS_MANAGER (manager);
}

/**
 * ods_manager_is_initialized:
 * @manager: OdsManager instance
 *
 * Checks if object was initialized succesfully. Might not be initialized
 * if OdsBluez was not initialized successfully
 * 
 * Return value: TRUE for success, FALSE otherwise.
 **/
gboolean
ods_manager_is_initialized (OdsManager *manager)
{
      return manager->priv->initialized;
}

Generated by  Doxygen 1.6.0   Back to index