diff options
Diffstat (limited to 'src/obexpush.c')
-rw-r--r-- | src/obexpush.c | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/src/obexpush.c b/src/obexpush.c new file mode 100644 index 0000000..c7cb725 --- /dev/null +++ b/src/obexpush.c @@ -0,0 +1,563 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ + +/* + * Copyright (C) 2004-2008 Red Hat, Inc. + * + * Caja 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. + * + * Caja 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Bastien Nocera <[email protected]> + * + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> +#include <libmatenotify/notify.h> +#include <dbus/dbus-glib.h> +#include <mateconf/mateconf-client.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <canberra-gtk.h> + +#include <string.h> + +#include "marshal.h" +#include "obexpush.h" +#include "user_share.h" +#include "user_share-private.h" + +#define DBUS_TYPE_G_STRING_VARIANT_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING)) + +static DBusGConnection *connection = NULL; +static DBusGProxy *manager_proxy = NULL; +static DBusGProxy *server_proxy = NULL; +static AcceptSetting accept_setting = -1; +static gboolean show_notifications = FALSE; + +static GtkStatusIcon *statusicon = NULL; +static guint num_notifications = 0; + +static void +hide_statusicon (void) +{ + num_notifications--; + if (num_notifications == 0) + gtk_status_icon_set_visible (statusicon, FALSE); +} + +static void +on_close_notification (NotifyNotification *notification) +{ + hide_statusicon (); + g_object_unref (notification); +} + +static void +notification_launch_action_on_file_cb (NotifyNotification *notification, + const char *action, + const char *file_uri) +{ + GdkScreen *screen; + GAppLaunchContext *ctx; + GTimeVal val; + + g_assert (action != NULL); + + g_get_current_time (&val); + +#if GTK_CHECK_VERSION(2,14,0) + ctx = G_APP_LAUNCH_CONTEXT (gdk_app_launch_context_new ()); + screen = gdk_screen_get_default (); + gdk_app_launch_context_set_screen (GDK_APP_LAUNCH_CONTEXT (ctx), screen); + gdk_app_launch_context_set_timestamp (GDK_APP_LAUNCH_CONTEXT (ctx), val.tv_sec); +#else + ctx = NULL; + screen = NULL; +#endif + + /* We launch the file viewer for the file */ + if (g_str_equal (action, "display") != FALSE) { + if (g_app_info_launch_default_for_uri (file_uri, ctx, NULL) == FALSE) { + g_warning ("Failed to launch the file viewer\n"); + } + } + + /* we open the Downloads folder */ + if (g_str_equal (action, "reveal") != FALSE) { + GFile *file; + GFile *parent; + gchar *parent_uri; + + file = g_file_new_for_uri (file_uri); + parent = g_file_get_parent (file); + parent_uri = g_file_get_uri (parent); + g_object_unref (file); + g_object_unref (parent); + + if (!g_app_info_launch_default_for_uri (parent_uri, ctx, NULL)) { + g_warning ("Failed to launch the file manager\n"); + } + + g_free (parent_uri); + } + + notify_notification_close (notification, NULL); + /* No need to call hide_statusicon(), closing the notification + * will call the close callback */ +} + +static void +show_notification (const char *filename) +{ + char *file_uri, *notification_text, *display, *mime_type; + NotifyNotification *notification; + ca_context *ctx; + GAppInfo *app; + + file_uri = g_filename_to_uri (filename, NULL, NULL); + if (file_uri == NULL) { + g_warning ("Could not make a filename from '%s'", filename); + return; + } + + display = g_filename_display_basename (filename); + /* Translators: %s is the name of the filename received */ + notification_text = g_strdup_printf(_("You received \"%s\" via Bluetooth"), display); + g_free (display); + notification = notify_notification_new (_("You received a file"), + notification_text, + "dialog-information"); + + notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT); + + mime_type = g_content_type_guess (filename, NULL, 0, NULL); + app = g_app_info_get_default_for_type (mime_type, FALSE); + if (app != NULL) { + g_object_unref (app); + notify_notification_add_action (notification, "display", _("Open File"), + (NotifyActionCallback) notification_launch_action_on_file_cb, + g_strdup (file_uri), (GFreeFunc) g_free); + } + notify_notification_add_action (notification, "reveal", _("Reveal File"), + (NotifyActionCallback) notification_launch_action_on_file_cb, + g_strdup (file_uri), (GFreeFunc) g_free); + + g_free (file_uri); + + g_signal_connect (G_OBJECT (notification), "closed", G_CALLBACK (on_close_notification), notification); + + if (!notify_notification_show (notification, NULL)) { + g_warning ("failed to send notification\n"); + } + g_free (notification_text); + + /* Now we do the audio notification */ + ctx = ca_gtk_context_get (); + ca_context_play (ctx, 0, + CA_PROP_EVENT_ID, "complete-download", + CA_PROP_EVENT_DESCRIPTION, _("File reception complete"), + NULL); +} + +static void +show_icon (void) +{ + if (statusicon == NULL) { + statusicon = gtk_status_icon_new_from_icon_name ("mate-obex-server"); + } else { + gtk_status_icon_set_visible (statusicon, TRUE); + } + num_notifications++; +} + +static gboolean +device_is_authorised (const char *bdaddr) +{ + DBusGConnection *connection; + DBusGProxy *manager; + GError *error = NULL; + GPtrArray *adapters; + gboolean retval = FALSE; + guint i; + + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); + if (connection == NULL) + return FALSE; + + manager = dbus_g_proxy_new_for_name (connection, "org.bluez", + "/", "org.bluez.Manager"); + if (manager == NULL) { + dbus_g_connection_unref (connection); + return FALSE; + } + + if (dbus_g_proxy_call (manager, "ListAdapters", &error, G_TYPE_INVALID, dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &adapters, G_TYPE_INVALID) == FALSE) { + g_object_unref (manager); + dbus_g_connection_unref (connection); + return FALSE; + } + + for (i = 0; i < adapters->len; i++) { + DBusGProxy *adapter, *device; + char *device_path; + GHashTable *props; + + g_message ("checking adapter %s", g_ptr_array_index (adapters, i)); + + adapter = dbus_g_proxy_new_for_name (connection, "org.bluez", + g_ptr_array_index (adapters, i), "org.bluez.Adapter"); + + if (dbus_g_proxy_call (adapter, "FindDevice", NULL, + G_TYPE_STRING, bdaddr, G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &device_path, G_TYPE_INVALID) == FALSE) + { + g_object_unref (adapter); + continue; + } + + device = dbus_g_proxy_new_for_name (connection, "org.bluez", device_path, "org.bluez.Device"); + + if (dbus_g_proxy_call (device, "GetProperties", NULL, + G_TYPE_INVALID, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), + &props, G_TYPE_INVALID) != FALSE) + { + GValue *value; + gboolean bonded; + + value = g_hash_table_lookup (props, "Paired"); + bonded = g_value_get_boolean (value); + g_message ("%s is %s", bdaddr, bonded ? "bonded" : "not bonded"); + + if (bonded) { + g_hash_table_destroy (props); + g_object_unref (device); + g_object_unref (adapter); + retval = TRUE; + break; + } + } + g_object_unref(adapter); + } + + g_ptr_array_free (adapters, TRUE); + + g_object_unref(manager); + dbus_g_connection_unref(connection); + + return retval; +} + +static void +transfer_started_cb (DBusGProxy *session, + const char *filename, + const char *local_path, + guint64 size, + gpointer user_data) +{ + GHashTable *dict; + DBusGProxy *server = (DBusGProxy *) user_data; + GError *error = NULL; + gboolean authorise; + + g_message ("transfer started on %s", local_path); + g_object_set_data_full (G_OBJECT (session), "filename", g_strdup (local_path), (GDestroyNotify) g_free); + + show_icon (); + + /* First transfer of the session */ + if (g_object_get_data (G_OBJECT (session), "bdaddr") == NULL) { + const char *bdaddr; + + if (dbus_g_proxy_call (server, "GetServerSessionInfo", &error, + DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path (session), G_TYPE_INVALID, + DBUS_TYPE_G_STRING_VARIANT_HASHTABLE, &dict, G_TYPE_INVALID) == FALSE) { + g_printerr ("Getting Server session info failed: %s\n", + error->message); + g_error_free (error); + return; + } + + bdaddr = g_hash_table_lookup (dict, "BluetoothAddress"); + g_message ("transfer started for device %s", bdaddr); + + g_object_set_data_full (G_OBJECT (session), "bdaddr", g_strdup (bdaddr), (GDestroyNotify) g_free); + /* Initial accept method is undefined, we do that lower down */ + g_object_set_data (G_OBJECT (session), "accept-method", GINT_TO_POINTER (-1)); + g_hash_table_destroy (dict); + } + + /* Accept method changed */ + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session), "accept-method")) != accept_setting) { + const char *bdaddr; + + bdaddr = g_object_get_data (G_OBJECT (session), "bdaddr"); + + if (bdaddr == NULL) { + g_warning ("Couldn't get the Bluetooth address for the device, rejecting the transfer"); + authorise = FALSE; + } else if (accept_setting == ACCEPT_ALWAYS) { + authorise = TRUE; + } else if (accept_setting == ACCEPT_BONDED) { + authorise = device_is_authorised (bdaddr); + } else { + //FIXME implement + g_warning ("\"Ask\" authorisation method not implemented"); + authorise = FALSE; + } + g_object_set_data (G_OBJECT (session), "authorise", GINT_TO_POINTER (authorise)); + } + + g_message ("accept_setting: %d", accept_setting); + + authorise = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session), "authorise")); + if (authorise != FALSE) { + if (dbus_g_proxy_call (session, "Accept", &error, G_TYPE_INVALID, G_TYPE_INVALID) == FALSE) { + g_printerr ("Failed to accept file transfer: %s\n", error->message); + g_error_free (error); + return; + } + g_message ("authorised transfer"); + } else { + if (dbus_g_proxy_call (session, "Reject", &error, G_TYPE_INVALID, G_TYPE_INVALID) == FALSE) { + g_printerr ("Failed to reject file transfer: %s\n", error->message); + g_error_free (error); + return; + } + g_message ("rejected transfer"); + g_object_set_data (G_OBJECT (session), "filename", NULL); + } +} + +static void +transfer_completed_cb (DBusGProxy *session, + gpointer user_data) +{ + MateConfClient *client; + gboolean display_notify; + const char *filename; + + filename = (const char *) g_object_get_data (G_OBJECT (session), "filename"); + + g_message ("file finish transfer: %s", filename); + + if (filename == NULL) + return; + + client = mateconf_client_get_default (); + display_notify = mateconf_client_get_bool (client, FILE_SHARING_BLUETOOTH_OBEXPUSH_NOTIFY, NULL); + g_object_unref (client); + + if (display_notify) { + show_notification (filename); + } else { + hide_statusicon (); + } + g_object_set_data (G_OBJECT (session), "filename", NULL); +} + +static void +cancelled_cb (DBusGProxy *session, + gpointer user_data) +{ + //FIXME implement properly, we never actually finished the transfer + g_message ("transfered was cancelled by the sender"); + transfer_completed_cb (session, user_data); + hide_statusicon (); +} + +static void +error_occurred_cb (DBusGProxy *session, + const char *error_name, + const char *error_message, + gpointer user_data) +{ + //FIXME implement properly + g_message ("transfer error occurred: %s", error_message); + transfer_completed_cb (session, user_data); +} + +static void +session_created_cb (DBusGProxy *server, const char *session_path, gpointer user_data) +{ + DBusGProxy *session; + + session = dbus_g_proxy_new_for_name (connection, + "org.openobex", + session_path, + "org.openobex.ServerSession"); + + dbus_g_proxy_add_signal (session, "TransferStarted", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(session, "TransferStarted", + G_CALLBACK (transfer_started_cb), server, NULL); + dbus_g_proxy_add_signal (session, "TransferCompleted", + G_TYPE_INVALID, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(session, "TransferCompleted", + G_CALLBACK (transfer_completed_cb), server, NULL); + dbus_g_proxy_add_signal (session, "Cancelled", + G_TYPE_INVALID, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(session, "Cancelled", + G_CALLBACK (cancelled_cb), server, NULL); + dbus_g_proxy_add_signal (session, "ErrorOccurred", + G_TYPE_INVALID, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(session, "ErrorOccurred", + G_CALLBACK (error_occurred_cb), server, NULL); +} + +void +obexpush_up (void) +{ + GError *err = NULL; + char *download_dir, *server; + + server = NULL; + if (manager_proxy == NULL) { + manager_proxy = dbus_g_proxy_new_for_name (connection, + "org.openobex", + "/org/openobex", + "org.openobex.Manager"); + if (dbus_g_proxy_call (manager_proxy, "CreateBluetoothServer", + &err, G_TYPE_STRING, "00:00:00:00:00:00", G_TYPE_STRING, "opp", G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &server, G_TYPE_INVALID) == FALSE) { + g_printerr ("Creating Bluetooth ObexPush server failed: %s\n", + err->message); + g_error_free (err); + g_object_unref (manager_proxy); + manager_proxy = NULL; + return; + } + } + + download_dir = lookup_download_dir (); + + if (server_proxy == NULL) { + server_proxy = dbus_g_proxy_new_for_name (connection, + "org.openobex", + server, + "org.openobex.Server"); + g_free (server); + + dbus_g_proxy_add_signal (server_proxy, "SessionCreated", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(server_proxy, "SessionCreated", + G_CALLBACK (session_created_cb), NULL, NULL); + } + + if (dbus_g_proxy_call (server_proxy, "Start", &err, + G_TYPE_STRING, download_dir, G_TYPE_BOOLEAN, TRUE, G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID, + G_TYPE_INVALID) == FALSE) { + if (g_error_matches (err, DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION) != FALSE && + dbus_g_error_has_name (err, "org.openobex.Error.Started") != FALSE) { + g_error_free (err); + g_message ("already started, ignoring error"); + g_free (download_dir); + return; + } + g_printerr ("Starting Bluetooth ObexPush server failed: %s\n", + err->message); + g_error_free (err); + g_free (download_dir); + g_object_unref (server_proxy); + server_proxy = NULL; + g_object_unref (manager_proxy); + manager_proxy = NULL; + return; + } + + g_free (download_dir); +} + +static void +obexpush_stop (gboolean stop_manager) +{ + GError *err = NULL; + + if (server_proxy == NULL) + return; + + if (dbus_g_proxy_call (server_proxy, "Close", &err, G_TYPE_INVALID, G_TYPE_INVALID) == FALSE) { + if (g_error_matches (err, DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION) == FALSE || + dbus_g_error_has_name (err, "org.openobex.Error.NotStarted") == FALSE) { + g_printerr ("Stopping Bluetooth ObexPush server failed: %s\n", + err->message); + g_error_free (err); + return; + } + g_error_free (err); + } + + if (stop_manager != FALSE) { + g_object_unref (server_proxy); + server_proxy = NULL; + g_object_unref (manager_proxy); + manager_proxy = NULL; + } + + //FIXME stop all the notifications +} + +void +obexpush_down (void) +{ + obexpush_stop (TRUE); +} + +void +obexpush_restart (void) +{ + obexpush_stop (FALSE); + obexpush_up (); +} + +gboolean +obexpush_init (void) +{ + GError *err = NULL; + + connection = dbus_g_bus_get (DBUS_BUS_SESSION, &err); + if (connection == NULL) { + g_printerr ("Connecting to session bus failed: %s\n", + err->message); + g_error_free (err); + return FALSE; + } + + dbus_g_object_register_marshaller (marshal_VOID__STRING_STRING_UINT64, + G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INVALID); + + if (!notify_init("mate-user-share")) { + g_warning("Unable to initialize the notification system\n"); + } + + dbus_connection_set_exit_on_disconnect (dbus_g_connection_get_connection (connection), FALSE); + + return TRUE; +} + +void +obexpush_set_accept_files_policy (AcceptSetting setting) +{ + accept_setting = setting; +} + +void +obexpush_set_notify (gboolean enabled) +{ + show_notifications = enabled; +} |