summaryrefslogtreecommitdiff
path: root/sendto/plugins/upnp/upnp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sendto/plugins/upnp/upnp.c')
-rw-r--r--sendto/plugins/upnp/upnp.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/sendto/plugins/upnp/upnp.c b/sendto/plugins/upnp/upnp.c
new file mode 100644
index 0000000..b381587
--- /dev/null
+++ b/sendto/plugins/upnp/upnp.c
@@ -0,0 +1,320 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Copyright (C) 2008 Zeeshan Ali (Khattak)
+ * Copyright (C) 2006 Peter Enseleit
+ *
+ * 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 av.
+ *
+ * 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.
+ *
+ * Author: Zeeshan Ali (Khattak) <[email protected]>
+ * Peter Enseleit <[email protected]>
+ * Roberto Majadas <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <libgupnp/gupnp.h>
+#include "caja-sendto-plugin.h"
+
+#define MEDIA_SERVER "urn:schemas-upnp-org:device:MediaServer:1"
+#define CDS "urn:schemas-upnp-org:service:ContentDirectory"
+
+enum {
+ UDN_COL,
+ NAME_COL,
+ INTERFACE_COL,
+ NUM_COLS
+};
+
+static GtkWidget *combobox;
+static GtkTreeModel *model;
+static GUPnPContextManager *context_manager;
+
+static gboolean
+find_device (const gchar *udn,
+ GtkTreeIter *iter)
+{
+ gboolean found = FALSE;
+
+ if (!gtk_tree_model_get_iter_first (model, iter))
+ return FALSE;
+
+ do {
+ gchar *tmp;
+
+ gtk_tree_model_get (model,
+ iter,
+ UDN_COL, &tmp,
+ -1);
+
+ if (tmp != NULL && strcmp (tmp, udn) == 0)
+ found = TRUE;
+
+ g_free (tmp);
+ } while (!found && gtk_tree_model_iter_next (model, iter));
+
+ return found;
+}
+
+static gboolean
+check_required_actions (GUPnPServiceIntrospection *introspection)
+{
+ if (gupnp_service_introspection_get_action (introspection,
+ "CreateObject") == NULL)
+ return FALSE;
+ if (gupnp_service_introspection_get_action (introspection,
+ "ImportResource") == NULL)
+ return FALSE;
+ return TRUE;
+}
+
+static void
+get_introspection_cb (GUPnPServiceInfo *service_info,
+ GUPnPServiceIntrospection *introspection, const GError *error,
+ gpointer user_data)
+{
+ GUPnPDeviceInfo *device_info;
+ gchar *name;
+ const gchar *udn, *interface;
+ GtkTreeIter iter;
+ GUPnPContext *context;
+
+ device_info = GUPNP_DEVICE_INFO (user_data);
+
+ if (introspection != NULL) {
+ /* If introspection is available, make sure required actions
+ * are implemented.
+ */
+ if (!check_required_actions (introspection))
+ goto error;
+ }
+
+ udn = gupnp_device_info_get_udn (device_info);
+ if (G_UNLIKELY (udn == NULL))
+ goto error;
+
+ /* First check if the device is already added */
+ if (find_device (udn, &iter))
+ goto error;
+
+ name = gupnp_device_info_get_friendly_name (device_info);
+ if (name == NULL)
+ name = g_strdup (udn);
+
+ context = gupnp_device_info_get_context (device_info);
+ interface = gssdp_client_get_interface (GSSDP_CLIENT (context));
+
+ gtk_list_store_insert_with_values (GTK_LIST_STORE (model), NULL, -1,
+ UDN_COL, udn,
+ NAME_COL, name,
+ INTERFACE_COL, interface,
+ -1);
+
+ g_free (name);
+
+error:
+ /* We don't need the proxy objects anymore */
+ g_object_unref (service_info);
+ g_object_ref (device_info);
+}
+
+static void
+device_proxy_available_cb (GUPnPControlPoint *cp,
+ GUPnPDeviceProxy *proxy)
+{
+ GUPnPServiceInfo *info;
+
+ info = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (proxy), CDS);
+ if (G_UNLIKELY (info == NULL)) {
+ /* No ContentDirectory implemented? Not interesting. */
+ return;
+ }
+
+ gupnp_service_info_get_introspection_async (info,
+ get_introspection_cb,
+ g_object_ref (proxy));
+}
+
+static void
+device_proxy_unavailable_cb (GUPnPControlPoint *cp,
+ GUPnPDeviceProxy *proxy)
+{
+ GtkTreeIter iter;
+ const gchar *udn;
+
+ udn = gupnp_device_info_get_udn (GUPNP_DEVICE_INFO (proxy));
+ if (udn == NULL)
+ return;
+
+ /* First check if the device is already added */
+ if (find_device (udn, &iter))
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+}
+
+static void
+on_context_available (GUPnPContextManager *context_manager,
+ GUPnPContext *context,
+ gpointer user_data)
+{
+ GUPnPControlPoint *cp;
+
+ cp = gupnp_control_point_new (context, MEDIA_SERVER);
+
+ g_signal_connect (cp,
+ "device-proxy-available",
+ G_CALLBACK (device_proxy_available_cb),
+ NULL);
+ g_signal_connect (cp,
+ "device-proxy-unavailable",
+ G_CALLBACK (device_proxy_unavailable_cb),
+ NULL);
+
+ gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
+
+ /* Let context manager take care of the control point life cycle */
+ gupnp_context_manager_manage_control_point (context_manager, cp);
+ g_object_unref (cp);
+}
+
+static gboolean
+init (NstPlugin *plugin)
+{
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ char *upload_cmd;
+
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ upload_cmd = g_find_program_in_path ("gupnp-upload");
+ if (upload_cmd == NULL)
+ return FALSE;
+ g_free (upload_cmd);
+
+ context_manager = gupnp_context_manager_new (NULL, 0);
+ g_assert (context_manager != NULL);
+ g_signal_connect (context_manager, "context-available",
+ G_CALLBACK (on_context_available), NULL);
+
+ combobox = gtk_combo_box_new ();
+
+ store = gtk_list_store_new (NUM_COLS,
+ G_TYPE_STRING, /* UDN */
+ G_TYPE_STRING, /* Name */
+ G_TYPE_STRING); /* Network Interface */
+ model = GTK_TREE_MODEL (store);
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combobox), model);
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox),
+ renderer,
+ TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox),
+ renderer,
+ "text", NAME_COL);
+
+ return TRUE;
+}
+
+static GtkWidget*
+get_contacts_widget (NstPlugin *plugin)
+{
+ return combobox;
+}
+
+static gboolean
+send_files (NstPlugin *plugin,
+ GtkWidget *contact_widget,
+ GList *file_list)
+{
+ gchar *upload_cmd, *udn, *interface;
+ GPtrArray *argv;
+ gboolean ret;
+ GList *l;
+ GtkTreeIter iter;
+ GError *err = NULL;
+
+ if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &iter))
+ return FALSE;
+
+ gtk_tree_model_get (model, &iter, UDN_COL, &udn, INTERFACE_COL,
+ &interface, -1);
+
+ upload_cmd = g_find_program_in_path ("gupnp-upload");
+ if (upload_cmd == NULL)
+ return FALSE;
+
+ argv = g_ptr_array_new ();
+ g_ptr_array_add (argv, upload_cmd);
+ g_ptr_array_add (argv, "-t");
+ g_ptr_array_add (argv, "15"); /* discovery timeout (seconds) */
+ g_ptr_array_add (argv, "-e");
+ g_ptr_array_add (argv, interface);
+ g_ptr_array_add (argv, udn);
+ for (l = file_list ; l; l=l->next) {
+ gchar *file_path;
+
+ file_path = g_filename_from_uri (l->data, NULL, NULL);
+ g_ptr_array_add (argv, file_path);
+ }
+ g_ptr_array_add (argv, NULL);
+
+ ret = g_spawn_async (NULL, (gchar **) argv->pdata,
+ NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
+
+ if (ret == FALSE) {
+ g_warning ("Could not send files to MediaServer: %s",
+ err->message);
+ g_error_free (err);
+ }
+
+ g_ptr_array_free (argv, TRUE);
+ g_free (upload_cmd);
+ g_free (udn);
+
+ return ret;
+}
+
+static gboolean
+destroy (NstPlugin *plugin)
+{
+ gtk_widget_destroy (combobox);
+ g_object_unref (model);
+
+ g_object_unref (context_manager);
+
+ return TRUE;
+}
+
+static
+NstPluginInfo plugin_info = {
+ "folder-remote",
+ "upnp",
+ N_("UPnP Media Server"),
+ NULL,
+ CAJA_CAPS_NONE,
+ init,
+ get_contacts_widget,
+ NULL,
+ send_files,
+ destroy
+};
+
+NST_INIT_PLUGIN (plugin_info)
+