1 /*************************************************************************/
2 /* Copyright (C) 2009-2014 matias <mati86dl@gmail.com> */
3 /* */
4 /* This program is free software: you can redistribute it and/or modify */
5 /* it under the terms of the GNU General Public License as published by */
6 /* the Free Software Foundation, either version 3 of the License, or */
7 /* (at your option) any later version. */
8 /* */
9 /* This program is distributed in the hope that it will be useful, */
10 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
11 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
12 /* GNU General Public License for more details. */
13 /* */
14 /* You should have received a copy of the GNU General Public License */
15 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
16 /*************************************************************************/
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #if defined(GETTEXT_PACKAGE)
23 #include <glib/gi18n-lib.h>
24 #else
25 #include <glib/gi18n.h>
26 #endif
27 #include <glib.h>
28 #include <glib-object.h>
29 #include <gmodule.h>
30 #include <gtk/gtk.h>
31
32 #include <gudev/gudev.h>
33
34 #include <libpeas/peas.h>
35 #include <libpeas-gtk/peas-gtk.h>
36
37 #include "plugins/pragha-plugin-macros.h"
38
39 #include "plugins/devices/pragha-devices-plugin.h"
40 #include "plugins/devices/pragha-device-client.h"
41
42 #include "src/pragha-playback.h"
43 #include "src/pragha-utils.h"
44 #include "src/pragha.h"
45
46 #define PRAGHA_TYPE_REMOVABLE_PLUGIN (pragha_removable_plugin_get_type ())
47 #define PRAGHA_REMOVABLE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), PRAGHA_TYPE_REMOVABLE_PLUGIN, PraghaRemovablePlugin))
48 #define PRAGHA_REMOVABLE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), PRAGHA_TYPE_REMOVABLE_PLUGIN, PraghaRemovablePlugin))
49 #define PRAGHA_IS_REMOVABLE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), PRAGHA_TYPE_REMOVABLE_PLUGIN))
50 #define PRAGHA_IS_REMOVABLE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), PRAGHA_TYPE_REMOVABLE_PLUGIN))
51 #define PRAGHA_REMOVABLE_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), PRAGHA_TYPE_REMOVABLE_PLUGIN, PraghaRemovablePluginClass))
52
53 typedef struct _PraghaRemovablePluginPrivate PraghaRemovablePluginPrivate;
54
55 struct _PraghaRemovablePluginPrivate {
56 PraghaApplication *pragha;
57
58 /* Gudev devie */
59 guint64 bus_hooked;
60 guint64 device_hooked;
61 GUdevDevice *u_device;
62
63 /* Gio Volume */
64 GVolume *volume;
65
66 /* Mount point. */
67 gchar *mount_path;
68 };
69
PRAGHA_PLUGIN_REGISTER(PRAGHA_TYPE_REMOVABLE_PLUGIN,PraghaRemovablePlugin,pragha_removable_plugin)70 PRAGHA_PLUGIN_REGISTER (PRAGHA_TYPE_REMOVABLE_PLUGIN,
71 PraghaRemovablePlugin,
72 pragha_removable_plugin)
73
74 static void
75 pragha_removable_clear_hook_device (PraghaRemovablePlugin *plugin)
76 {
77 PraghaRemovablePluginPrivate *priv = plugin->priv;
78
79 priv->bus_hooked = 0;
80 priv->device_hooked = 0;
81
82 if (priv->u_device) {
83 g_object_unref (priv->u_device);
84 priv->u_device = NULL;
85 }
86 if (priv->volume) {
87 g_object_unref (priv->volume);
88 priv->volume = NULL;
89 }
90 if (priv->mount_path) {
91 g_free(priv->mount_path);
92 priv->mount_path = NULL;
93 }
94 }
95
96 static void
pragha_block_device_add_to_library(PraghaRemovablePlugin * plugin,GMount * mount)97 pragha_block_device_add_to_library (PraghaRemovablePlugin *plugin, GMount *mount)
98 {
99 PraghaPreferences *preferences;
100 PraghaScanner *scanner;
101 GSList *library_dir = NULL;
102 GFile *mount_point;
103 gchar *mount_path;
104
105 PraghaRemovablePluginPrivate *priv = plugin->priv;
106
107 mount_point = g_mount_get_root (mount);
108 mount_path = g_file_get_path (mount_point);
109
110 preferences = pragha_application_get_preferences (priv->pragha);
111
112 library_dir = pragha_preferences_get_library_list (preferences);
113 if (!is_present_str_list (mount_path, library_dir)) {
114 library_dir = g_slist_append (library_dir, g_strdup(mount_path));
115
116 pragha_preferences_set_filename_list (preferences,
117 GROUP_LIBRARY,
118 KEY_LIBRARY_DIR,
119 library_dir);
120 }
121 priv->mount_path = g_strdup(mount_path);
122
123 scanner = pragha_application_get_scanner (priv->pragha);
124 pragha_scanner_update_library (scanner);
125
126 g_object_unref (mount_point);
127 free_str_list(library_dir);
128 g_free (mount_path);
129 }
130
131 static void
pragha_removable_drop_device_from_library(PraghaRemovablePlugin * plugin)132 pragha_removable_drop_device_from_library (PraghaRemovablePlugin *plugin)
133 {
134 PraghaPreferences *preferences;
135 PraghaScanner *scanner;
136 GSList *library_dir = NULL;
137
138 PraghaRemovablePluginPrivate *priv = plugin->priv;
139
140 preferences = pragha_application_get_preferences (priv->pragha);
141
142 library_dir = pragha_preferences_get_library_list (preferences);
143 if (is_present_str_list (priv->mount_path, library_dir)) {
144 library_dir = delete_from_str_list (priv->mount_path, library_dir);
145
146 pragha_preferences_set_filename_list (preferences,
147 GROUP_LIBRARY,
148 KEY_LIBRARY_DIR,
149 library_dir);
150
151 scanner = pragha_application_get_scanner (priv->pragha);
152 pragha_scanner_update_library (scanner);
153 }
154 free_str_list(library_dir);
155 }
156
157 /*
158 * Some functions to mount block removable.
159 */
160
161 /* Decode the ID_FS_LABEL_ENC of block device.
162 * Extentions copy of Thunar-volman code.
163 * http://git.xfce.org/xfce/thunar-volman/tree/thunar-volman/tvm-gio-extensions.c */
164
165 static GVolume *
tvm_g_volume_monitor_get_volume_for_kind(GVolumeMonitor * monitor,const gchar * kind,const gchar * identifier)166 tvm_g_volume_monitor_get_volume_for_kind (GVolumeMonitor *monitor,
167 const gchar *kind,
168 const gchar *identifier)
169 {
170 GVolume *volume = NULL;
171 GList *volumes;
172 GList *lp;
173 gchar *value;
174
175 g_return_val_if_fail (G_IS_VOLUME_MONITOR (monitor), NULL);
176 g_return_val_if_fail (kind != NULL && *kind != '\0', NULL);
177 g_return_val_if_fail (identifier != NULL && *identifier != '\0', NULL);
178
179 volumes = g_volume_monitor_get_volumes (monitor);
180
181 for (lp = volumes; volume == NULL && lp != NULL; lp = lp->next) {
182 value = g_volume_get_identifier (lp->data, kind);
183 if (value == NULL)
184 continue;
185 if (g_strcmp0 (value, identifier) == 0)
186 volume = g_object_ref (lp->data);
187 g_free (value);
188 }
189 g_list_foreach (volumes, (GFunc)g_object_unref, NULL);
190 g_list_free (volumes);
191
192 return volume;
193 }
194
195 static void
pragha_block_device_mount_finish(GVolume * volume,GAsyncResult * result,PraghaRemovablePlugin * plugin)196 pragha_block_device_mount_finish (GVolume *volume, GAsyncResult *result, PraghaRemovablePlugin *plugin)
197 {
198 GtkWidget *dialog;
199 GMount *mount;
200 GError *error = NULL;
201 gchar *name = NULL, *primary = NULL;
202
203 g_return_if_fail (G_IS_VOLUME (volume));
204 g_return_if_fail (G_IS_ASYNC_RESULT (result));
205
206 /* finish mounting the volume */
207 if (!g_volume_mount_finish (volume, result, &error)) {
208 if (error->code != G_IO_ERROR_FAILED_HANDLED &&
209 error->code != G_IO_ERROR_ALREADY_MOUNTED) {
210 name = g_volume_get_name (G_VOLUME (volume));
211 primary = g_strdup_printf (_("Unable to access \"%s\""), name);
212 g_free (name);
213
214 dialog = pragha_gudev_dialog_new (NULL, _("Removable Device"), "media-removable",
215 primary, error->message,
216 NULL, PRAGHA_DEVICE_RESPONSE_NONE);
217 g_signal_connect (dialog, "response",
218 G_CALLBACK (gtk_widget_destroy), NULL);
219
220 gtk_widget_show_all (dialog);
221
222 g_free (primary);
223 }
224 g_error_free (error);
225 }
226
227 /* get the moint point of the volume */
228 mount = g_volume_get_mount (volume);
229 if (mount != NULL) {
230 pragha_block_device_add_to_library (plugin, mount);
231 g_object_unref (mount);
232 }
233 g_object_unref (volume);
234 }
235
236 static void
pragha_block_device_mount_device(PraghaRemovablePlugin * plugin)237 pragha_block_device_mount_device (PraghaRemovablePlugin *plugin)
238 {
239 GMountOperation *mount_operation;
240
241 PraghaRemovablePluginPrivate *priv = plugin->priv;
242
243 /* try to mount the volume asynchronously */
244 mount_operation = gtk_mount_operation_new (NULL);
245 g_volume_mount (priv->volume, G_MOUNT_MOUNT_NONE, mount_operation,
246 NULL, (GAsyncReadyCallback) pragha_block_device_mount_finish,
247 plugin);
248 g_object_unref (mount_operation);
249 }
250
251 static void
pragha_block_device_detected_response(GtkWidget * dialog,gint response,gpointer user_data)252 pragha_block_device_detected_response (GtkWidget *dialog,
253 gint response,
254 gpointer user_data)
255 {
256 PraghaRemovablePlugin *plugin = user_data;
257
258 switch (response)
259 {
260 case PRAGHA_DEVICE_RESPONSE_BROWSE:
261 pragha_block_device_mount_device (plugin);
262 break;
263 case PRAGHA_DEVICE_RESPONSE_NONE:
264 pragha_removable_clear_hook_device (plugin);
265 default:
266 break;
267 }
268 gtk_widget_destroy (dialog);
269 }
270
271 static gboolean
pragha_block_device_detected(gpointer data)272 pragha_block_device_detected (gpointer data)
273 {
274 GtkWidget *dialog;
275 GVolumeMonitor *monitor;
276 GVolume *volume;
277 gchar *name = NULL, *primary = NULL;
278
279 PraghaRemovablePlugin *plugin = data;
280 PraghaRemovablePluginPrivate *priv = plugin->priv;
281
282 /* determine the GVolume corresponding to the udev removable */
283 monitor = g_volume_monitor_get ();
284 volume = tvm_g_volume_monitor_get_volume_for_kind (monitor,
285 G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE,
286 g_udev_device_get_device_file (priv->u_device));
287 g_object_unref (monitor);
288
289 /* check if we have a volume */
290 priv->volume = volume;
291 if (volume == NULL || !g_volume_can_mount (volume)) {
292 pragha_removable_clear_hook_device (plugin);
293 return FALSE;
294 }
295
296 name = g_volume_get_name (G_VOLUME (volume));
297 primary = g_strdup_printf (_("Want to manage \"%s\" volume?"), name);
298 g_free (name);
299
300 dialog = pragha_gudev_dialog_new (NULL, _("Removable Device"), "media-removable",
301 primary, NULL,
302 _("_Update library"), PRAGHA_DEVICE_RESPONSE_BROWSE);
303
304 g_signal_connect (G_OBJECT (dialog), "response",
305 G_CALLBACK (pragha_block_device_detected_response), plugin);
306
307 gtk_widget_show_all (dialog);
308
309 g_free (primary);
310
311 return FALSE;
312 }
313
314 static void
pragha_removable_plugin_device_added(PraghaDeviceClient * device_client,PraghaDeviceType device_type,GUdevDevice * u_device,gpointer user_data)315 pragha_removable_plugin_device_added (PraghaDeviceClient *device_client,
316 PraghaDeviceType device_type,
317 GUdevDevice *u_device,
318 gpointer user_data)
319 {
320 PraghaRemovablePlugin *plugin = user_data;
321 PraghaRemovablePluginPrivate *priv = plugin->priv;
322
323 if (device_type != PRAGHA_DEVICE_MOUNTABLE)
324 return;
325
326 priv->bus_hooked = g_udev_device_get_property_as_uint64 (u_device, "BUSNUM");
327 priv->device_hooked = g_udev_device_get_property_as_uint64 (u_device, "DEVNUM");
328 priv->u_device = g_object_ref (u_device);
329 priv->volume = NULL;
330
331 /*
332 * HACK: We're listening udev. Then wait 2 seconds, to ensure that GVolume also detects the device.
333 */
334 g_timeout_add_seconds(2, pragha_block_device_detected, plugin);
335 }
336
337 void
pragha_removable_plugin_device_removed(PraghaDeviceClient * device_client,PraghaDeviceType device_type,GUdevDevice * u_device,gpointer user_data)338 pragha_removable_plugin_device_removed (PraghaDeviceClient *device_client,
339 PraghaDeviceType device_type,
340 GUdevDevice *u_device,
341 gpointer user_data)
342 {
343 guint64 busnum = 0;
344 guint64 devnum = 0;
345
346 PraghaRemovablePlugin *plugin = user_data;
347 PraghaRemovablePluginPrivate *priv = plugin->priv;
348
349 if (!priv->u_device || !priv->mount_path)
350 return;
351
352 if (device_type != PRAGHA_DEVICE_MOUNTABLE)
353 return;
354
355 busnum = g_udev_device_get_property_as_uint64(u_device, "BUSNUM");
356 devnum = g_udev_device_get_property_as_uint64(u_device, "DEVNUM");
357
358 if (busnum == priv->bus_hooked && devnum == priv->device_hooked) {
359 pragha_removable_drop_device_from_library (plugin);
360 pragha_removable_clear_hook_device (plugin);
361 }
362 }
363
364 static void
pragha_plugin_activate(PeasActivatable * activatable)365 pragha_plugin_activate (PeasActivatable *activatable)
366 {
367 PraghaDeviceClient *device_client;
368 PraghaRemovablePlugin *plugin = PRAGHA_REMOVABLE_PLUGIN (activatable);
369 PraghaRemovablePluginPrivate *priv = plugin->priv;
370
371 CDEBUG(DBG_PLUGIN, "Removable plugin %s", G_STRFUNC);
372
373 priv->pragha = g_object_get_data (G_OBJECT (plugin), "object");
374
375 device_client = pragha_device_client_get();
376 g_signal_connect (G_OBJECT(device_client), "device-added",
377 G_CALLBACK(pragha_removable_plugin_device_added), plugin);
378 g_signal_connect (G_OBJECT(device_client), "device-removed",
379 G_CALLBACK(pragha_removable_plugin_device_removed), plugin);
380 g_object_unref (device_client);
381 }
382
383 static void
pragha_plugin_deactivate(PeasActivatable * activatable)384 pragha_plugin_deactivate (PeasActivatable *activatable)
385 {
386 PraghaDeviceClient *device_client;
387 PraghaRemovablePlugin *plugin = PRAGHA_REMOVABLE_PLUGIN (activatable);
388 PraghaRemovablePluginPrivate *priv = plugin->priv;
389
390 CDEBUG(DBG_PLUGIN, "Removable plugin %s", G_STRFUNC);
391
392 device_client = pragha_device_client_get();
393 g_signal_handlers_disconnect_by_func (device_client,
394 pragha_removable_plugin_device_added,
395 plugin);
396 g_signal_handlers_disconnect_by_func (device_client,
397 pragha_removable_plugin_device_removed,
398 plugin);
399 g_object_unref (device_client);
400
401 priv->pragha = NULL;
402 }