View | Details | Raw Unified | Return to bug 27989
Collapse All | Expand All

(-)a/client/gdaemonfile.c (+3 lines)
Lines 2807-2812 retry: Link Here
2807
  if (proxy == NULL)
2807
  if (proxy == NULL)
2808
    goto out;
2808
    goto out;
2809
2809
2810
  /* File transfers can take arbitrarily long amounts of time. */
2811
  g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_MAXINT);
2812
2810
  data.progress_callback = progress_callback;
2813
  data.progress_callback = progress_callback;
2811
  data.progress_callback_data = progress_callback_data;
2814
  data.progress_callback_data = progress_callback_data;
2812
  data.context = g_main_context_new ();
2815
  data.context = g_main_context_new ();
(-)a/common/Makefile.am (-2 / +4 lines)
Lines 1-6 Link Here
1
NULL =
1
NULL =
2
2
3
lib_LTLIBRARIES=libgvfscommon.la
3
privlibdir=$(libdir)/gvfs
4
privlib_LTLIBRARIES=libgvfscommon.la
5
4
noinst_LTLIBRARIES = libgvfscommon-monitor.la
6
noinst_LTLIBRARIES = libgvfscommon-monitor.la
5
7
6
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/gvfs \
8
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/gvfs \
Lines 31-37 libgvfscommon_la_SOURCES = \ Link Here
31
	$(NULL)
33
	$(NULL)
32
34
33
# needed by cygwin (see bug #564003)
35
# needed by cygwin (see bug #564003)
34
libgvfscommon_la_LDFLAGS = -no-undefined
36
libgvfscommon_la_LDFLAGS = -no-undefined  -avoid-version
35
37
36
libgvfscommon_la_LIBADD =	\
38
libgvfscommon_la_LIBADD =	\
37
	$(GLIB_LIBS)		\
39
	$(GLIB_LIBS)		\
(-)a/configure.ac (+34 lines)
Lines 493-498 AC_SUBST(BLURAY_CFLAGS) Link Here
493
AC_SUBST(BLURAY_LIBS)
493
AC_SUBST(BLURAY_LIBS)
494
AM_CONDITIONAL(HAVE_BLURAY, [test "$msg_bluray" = "yes"])
494
AM_CONDITIONAL(HAVE_BLURAY, [test "$msg_bluray" = "yes"])
495
495
496
dnl *************************
497
dnl *** Check for libmtp ***
498
dnl *************************
499
AC_ARG_ENABLE(libmtp, AS_HELP_STRING([--disable-libmtp],[build without libmtp support]))
500
msg_libmtp=no
501
LIBMTP_LIBS=
502
LIBMTP_CFLAGS=
503
504
if test "x$enable_libmtp" != "xno" -a "x$msg_gudev" = "xyes"; then
505
  PKG_CHECK_EXISTS(libmtp, msg_libmtp=yes)
506
507
  if test "x$msg_libmtp" = "xyes"; then
508
    PKG_CHECK_MODULES(LIBMTP, libmtp >= 1.1.0)
509
    AC_DEFINE(HAVE_LIBMTP, 1, [Define to 1 if libmtp is available])
510
511
    save_libs="$LIBS"
512
    LIBS="$LIBMTP_LIBS"
513
    AC_CHECK_LIB(mtp, LIBMTP_Get_Thumbnail, have_libmtp_get_thumbnail=yes)
514
    if test "x$have_libmtp_get_thumbnail" = "xyes"; then
515
      AC_DEFINE(HAVE_LIBMTP_GET_THUMBNAIL, 1, [Define to 1 if LIBMTP_Get_Thumbnail is available])
516
    fi
517
518
    AC_CHECK_LIB(mtp, LIBMTP_Read_Event, have_libmtp_read_event=yes)
519
    if test "x$have_libmtp_read_event" = "xyes"; then
520
      AC_DEFINE(HAVE_LIBMTP_READ_EVENT, 1, [Define to 1 if LIBMTP_Read_Event is available])
521
    fi
522
    LIBS="$save_libs"
523
  fi
524
fi
525
526
AM_CONDITIONAL(USE_LIBMTP, [test "$msg_libmtp" = "yes"])
527
496
dnl ==========================================================================
528
dnl ==========================================================================
497
dnl Samba 3.0
529
dnl Samba 3.0
498
530
Lines 830-835 monitor/gdu/Makefile Link Here
830
monitor/udisks2/Makefile
862
monitor/udisks2/Makefile
831
monitor/gphoto2/Makefile
863
monitor/gphoto2/Makefile
832
monitor/afc/Makefile
864
monitor/afc/Makefile
865
monitor/mtp/Makefile
833
programs/Makefile
866
programs/Makefile
834
man/Makefile
867
man/Makefile
835
test/Makefile
868
test/Makefile
Lines 850-855 echo " Link Here
850
	FUSE support:                 $msg_fuse
883
	FUSE support:                 $msg_fuse
851
        CDDA support:                 $msg_cdda
884
        CDDA support:                 $msg_cdda
852
        Gphoto2 support:              $msg_gphoto2
885
        Gphoto2 support:              $msg_gphoto2
886
        MTP support:                  $msg_libmtp
853
	archive support:              $msg_archive
887
	archive support:              $msg_archive
854
	AFC support:                  $msg_afc
888
	AFC support:                  $msg_afc
855
        AFP support:                  $msg_afp
889
        AFP support:                  $msg_afp
(-)a/daemon/Makefile.am (+19 lines)
Lines 79-84 mount_DATA += gphoto2.mount Link Here
79
libexec_PROGRAMS += gvfsd-gphoto2
79
libexec_PROGRAMS += gvfsd-gphoto2
80
endif
80
endif
81
81
82
mount_in_files += mtp.mount.in
83
if USE_LIBMTP
84
mount_DATA += mtp.mount
85
libexec_PROGRAMS += gvfsd-mtp
86
endif
87
82
mount_in_files += obexftp.mount.in
88
mount_in_files += obexftp.mount.in
83
if USE_OBEXFTP
89
if USE_OBEXFTP
84
mount_DATA += obexftp.mount
90
mount_DATA += obexftp.mount
Lines 440-445 else Link Here
440
gvfsd_gphoto2_LDADD = $(libraries) $(GPHOTO2_LIBS) $(HAL_LIBS)
446
gvfsd_gphoto2_LDADD = $(libraries) $(GPHOTO2_LIBS) $(HAL_LIBS)
441
endif
447
endif
442
448
449
gvfsd_mtp_SOURCES = \
450
	gvfsbackendmtp.c gvfsbackendmtp.h \
451
	daemon-main.c daemon-main.h \
452
	daemon-main-generic.c
453
454
gvfsd_mtp_CPPFLAGS = \
455
	-DBACKEND_HEADER=gvfsbackendmtp.h \
456
	-DDEFAULT_BACKEND_TYPE=mtp \
457
	-DBACKEND_TYPES='"mtp", G_VFS_TYPE_BACKEND_MTP,' \
458
	$(GUDEV_CFLAGS) $(LIBMTP_CFLAGS)
459
460
gvfsd_mtp_LDADD = $(libraries) $(GUDEV_LIBS) $(LIBMTP_LIBS)
461
443
gvfsd_http_SOURCES = \
462
gvfsd_http_SOURCES = \
444
	soup-input-stream.c soup-input-stream.h \
463
	soup-input-stream.c soup-input-stream.h \
445
	gvfsbackendhttp.c gvfsbackendhttp.h \
464
	gvfsbackendhttp.c gvfsbackendhttp.h \
(-)a/daemon/gvfsbackendmtp.c (+1401 lines)
Line 0 Link Here
1
/* GIO - GLib Input, Output and Streaming Library
2
 *   MTP Backend
3
 *
4
 * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
23
#include <config.h>
24
25
#include <sys/types.h>
26
#include <sys/stat.h>
27
#include <errno.h>
28
#include <unistd.h>
29
#include <fcntl.h>
30
#include <string.h>
31
#include <stdlib.h>
32
33
#include <glib/gstdio.h>
34
#include <glib/gi18n.h>
35
#include <gio/gio.h>
36
37
#include <libmtp.h>
38
39
#include "gvfsbackendmtp.h"
40
#include "gvfsicon.h"
41
#include "gvfsjobopenforread.h"
42
#include "gvfsjobread.h"
43
#include "gvfsjobseekread.h"
44
#include "gvfsjobopenforwrite.h"
45
#include "gvfsjobwrite.h"
46
#include "gvfsjobclosewrite.h"
47
#include "gvfsjobseekwrite.h"
48
#include "gvfsjobsetdisplayname.h"
49
#include "gvfsjobqueryinfo.h"
50
#include "gvfsjobdelete.h"
51
#include "gvfsjobqueryfsinfo.h"
52
#include "gvfsjobqueryattributes.h"
53
#include "gvfsjobenumerate.h"
54
#include "gvfsdaemonprotocol.h"
55
#include "gvfsjobcreatemonitor.h"
56
#include "gvfsjobmakedirectory.h"
57
#include "gvfsmonitor.h"
58
59
60
/* ------------------------------------------------------------------------------------------------- */
61
62
/* showing debug traces */
63
#define DEBUG_SHOW_TRACES 1
64
#define DEBUG_SHOW_ENUMERATE_TRACES 0
65
66
static void
67
DEBUG (const gchar *message, ...)
68
{
69
#if DEBUG_SHOW_TRACES
70
  va_list args;
71
  va_start (args, message);
72
  g_vfprintf (stderr, message, args);
73
  va_end (args);
74
  g_fprintf (stderr, "\n");
75
  fflush (stderr);
76
#endif
77
}
78
79
static void
80
DEBUG_ENUMERATE (const gchar *message, ...)
81
{
82
#if DEBUG_SHOW_ENUMERATE_TRACES
83
  va_list args;
84
  va_start (args, message);
85
  g_vfprintf (stderr, message, args);
86
  va_end (args);
87
  g_fprintf (stderr, "\n");
88
  fflush (stderr);
89
#endif
90
}
91
92
93
/************************************************
94
 * Storage constants copied from ptp.h
95
 *
96
 * ptp.h is treated as a private header by libmtp
97
 ************************************************/
98
99
/* PTP Storage Types */
100
101
#define PTP_ST_Undefined                        0x0000
102
#define PTP_ST_FixedROM                         0x0001
103
#define PTP_ST_RemovableROM                     0x0002
104
#define PTP_ST_FixedRAM                         0x0003
105
#define PTP_ST_RemovableRAM                     0x0004
106
107
108
/************************************************
109
 * Initialization
110
 ************************************************/
111
112
G_DEFINE_TYPE (GVfsBackendMtp, g_vfs_backend_mtp, G_VFS_TYPE_BACKEND)
113
114
static void
115
g_vfs_backend_mtp_init (GVfsBackendMtp *backend)
116
{
117
  DEBUG ("(I) g_vfs_backend_mtp_init");
118
  GMountSpec *mount_spec;
119
120
  g_mutex_init (&backend->mutex);
121
  g_vfs_backend_set_display_name (G_VFS_BACKEND (backend), "mtp");
122
  g_vfs_backend_set_icon_name (G_VFS_BACKEND (backend), "multimedia-player");
123
124
  mount_spec = g_mount_spec_new ("mtp");
125
  g_vfs_backend_set_mount_spec (G_VFS_BACKEND (backend), mount_spec);
126
  g_mount_spec_unref (mount_spec);
127
128
  backend->monitors = g_hash_table_new (g_direct_hash, g_direct_equal);
129
130
  DEBUG ("(I) g_vfs_backend_mtp_init done.");
131
}
132
133
static void
134
g_vfs_backend_mtp_finalize (GObject *object)
135
{
136
  GVfsBackendMtp *backend;
137
138
  DEBUG ("(I) g_vfs_backend_mtp_finalize");
139
140
  backend = G_VFS_BACKEND_MTP (object);
141
142
  g_hash_table_unref (backend->monitors);
143
  g_mutex_clear (&backend->mutex);
144
145
  if (G_OBJECT_CLASS (g_vfs_backend_mtp_parent_class)->finalize)
146
    (*G_OBJECT_CLASS (g_vfs_backend_mtp_parent_class)->finalize) (object);
147
148
  DEBUG ("(I) g_vfs_backend_mtp_finalize done.");
149
}
150
151
152
/************************************************
153
 * Monitors
154
 ************************************************/
155
156
static void
157
do_create_dir_monitor (GVfsBackend *backend,
158
                       GVfsJobCreateMonitor *job,
159
                       const char *filename,
160
                       GFileMonitorFlags flags)
161
{
162
  GVfsBackendMtp *mtp_backend = G_VFS_BACKEND_MTP (backend);
163
164
  DEBUG ("(I) create_dir_monitor (%s)", filename);
165
166
  GVfsMonitor *vfs_monitor = g_vfs_monitor_new (backend);
167
168
  g_object_set_data_full (G_OBJECT (vfs_monitor), "gvfsbackendmtp:path",
169
                          g_strdup (filename), g_free);
170
171
  g_vfs_job_create_monitor_set_monitor (job, vfs_monitor);
172
  g_hash_table_insert (mtp_backend->monitors, vfs_monitor, NULL);
173
  g_object_weak_ref (G_OBJECT (vfs_monitor), (GWeakNotify)g_hash_table_remove, mtp_backend->monitors);
174
  g_object_unref (vfs_monitor);
175
  g_vfs_job_succeeded (G_VFS_JOB (job));
176
177
  DEBUG ("(I) create_dir_monitor done.");
178
}
179
180
181
static void
182
do_create_file_monitor (GVfsBackend *backend,
183
                        GVfsJobCreateMonitor *job,
184
                        const char *filename,
185
                        GFileMonitorFlags flags)
186
{
187
  GVfsBackendMtp *mtp_backend = G_VFS_BACKEND_MTP (backend);
188
189
  DEBUG ("(I) create_file_monitor (%s)", filename);
190
191
  GVfsMonitor *vfs_monitor = g_vfs_monitor_new (backend);
192
193
  g_object_set_data_full (G_OBJECT (vfs_monitor), "gvfsbackendmtp:path",
194
                          g_strdup (filename), g_free);
195
196
  g_vfs_job_create_monitor_set_monitor (job, vfs_monitor);
197
  g_hash_table_insert (mtp_backend->monitors, vfs_monitor, NULL);
198
  g_object_weak_ref (G_OBJECT (vfs_monitor), (GWeakNotify)g_hash_table_remove, mtp_backend->monitors);
199
  g_object_unref (vfs_monitor);
200
  g_vfs_job_succeeded (G_VFS_JOB (job));
201
202
  DEBUG ("(I) create_file_monitor done.");
203
}
204
205
static void
206
emit_event_internal (GVfsMonitor *monitor,
207
                     const char *path,
208
                     GFileMonitorEvent event)
209
{
210
  DEBUG ("(III) emit_event_internal (%s, %d)", path, event);
211
212
  char *dir = g_dirname (path);
213
  const char *monitored_path = g_object_get_data (G_OBJECT (monitor), "gvfsbackendmtp:path");
214
  if (g_strcmp0 (dir, monitored_path) == 0) {
215
    DEBUG ("(III) emit_event_internal: Event %d on directory %s for %s", event, dir, path);
216
    g_vfs_monitor_emit_event (monitor, event, path, NULL);
217
  } else if (g_strcmp0 (path, monitored_path) == 0) {
218
    DEBUG ("(III) emit_event_internal: Event %d on file %s", event, path);
219
    g_vfs_monitor_emit_event (monitor, event, path, NULL);
220
  }
221
  g_free (dir);
222
223
  DEBUG ("(III) emit_event_internal done.");
224
}
225
226
static void
227
emit_create_event (gpointer key,
228
                   gpointer value,
229
                   gpointer user_data)
230
{
231
  DEBUG ("(II) emit_create_event.");
232
  emit_event_internal (key, user_data, G_FILE_MONITOR_EVENT_CREATED);
233
}
234
235
static void
236
emit_delete_event (gpointer key,
237
                   gpointer value,
238
                   gpointer user_data)
239
{
240
  DEBUG ("(II) emit_delete_event.");
241
  emit_event_internal (key, user_data, G_FILE_MONITOR_EVENT_DELETED);
242
}
243
244
static void
245
emit_change_event (gpointer key,
246
                   gpointer value,
247
                   gpointer user_data)
248
{
249
  DEBUG ("(II) emit_change_event.");
250
  emit_event_internal (key, user_data, G_FILE_MONITOR_EVENT_CHANGED);
251
}
252
253
254
/************************************************
255
 * Errors
256
 ************************************************/
257
258
static void
259
fail_job (GVfsJob *job, LIBMTP_mtpdevice_t *device)
260
{
261
  LIBMTP_error_t *error = LIBMTP_Get_Errorstack (device);
262
263
  g_vfs_job_failed (job, G_IO_ERROR,
264
                    g_vfs_job_is_cancelled (job) ?
265
                      G_IO_ERROR_CANCELLED :
266
                      G_IO_ERROR_FAILED,
267
                    _("libmtp error: %s"),
268
                    g_strrstr (error->error_text, ":") + 1);
269
270
  LIBMTP_Clear_Errorstack (device);
271
}
272
273
274
/************************************************
275
 * Mounts
276
 ************************************************/
277
278
static LIBMTP_mtpdevice_t *
279
get_device (GVfsBackend *backend, const char *id, GVfsJob *job);
280
281
282
static void
283
on_uevent (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data)
284
{
285
  const char *dev_path = g_udev_device_get_device_file (device);
286
  DEBUG ("(I) on_uevent (action %s, device %s)", action, dev_path);
287
288
  if (dev_path == NULL) {
289
    return;
290
  }
291
292
  GVfsBackendMtp *op_backend = G_VFS_BACKEND_MTP (user_data);
293
294
  if (g_strcmp0 (op_backend->dev_path, dev_path) == 0 &&
295
      g_str_equal (action, "remove")) {
296
    DEBUG ("(I) on_uevent: Quiting after remove event on device %s", dev_path);
297
    /* TODO: need a cleaner way to force unmount ourselves */
298
    exit (1);
299
  }
300
301
  DEBUG ("(I) on_uevent done.");
302
}
303
304
#if HAVE_LIBMTP_READ_EVENT
305
static gpointer
306
check_event (gpointer user_data)
307
{
308
  GVfsBackendMtp *backend = user_data;
309
310
  LIBMTP_event_t event;
311
  int ret = 0;
312
  while (ret == 0) {
313
    uint32_t param1;
314
    char *path;
315
    ret = LIBMTP_Read_Event (backend->device, &event, &param1);
316
    switch (event) {
317
    case LIBMTP_EVENT_STORE_ADDED:
318
      path = g_strdup_printf ("/%u", param1);
319
      g_mutex_lock (&backend->mutex);
320
      g_hash_table_foreach (backend->monitors, emit_create_event, path);
321
      g_mutex_unlock (&backend->mutex);
322
      g_free (path);
323
      break;
324
    default:
325
      break;
326
    }
327
  }
328
  return NULL;
329
}
330
#endif
331
332
static gboolean
333
mtp_heartbeat (GVfsBackendMtp *backend)
334
{
335
  if (g_mutex_trylock (&backend->mutex)) {
336
    LIBMTP_Dump_Device_Info(backend->device);
337
    g_mutex_unlock (&backend->mutex);
338
  }
339
  return TRUE;
340
}
341
342
static void
343
do_mount (GVfsBackend *backend,
344
           GVfsJobMount *job,
345
           GMountSpec *mount_spec,
346
           GMountSource *mount_source,
347
           gboolean is_automount)
348
{
349
  GVfsBackendMtp *op_backend = G_VFS_BACKEND_MTP (backend);
350
351
  DEBUG ("(I) do_mount");
352
353
  const char *host = g_mount_spec_get (mount_spec, "host");
354
  DEBUG ("(I) do_mount: host=%s", host);
355
  if (host == NULL) {
356
    GError *error;
357
    g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No device specified"));
358
    g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
359
    g_error_free (error);
360
    return;
361
  }
362
363
  const char *subsystems[] = {"usb", NULL};
364
  op_backend->gudev_client = g_udev_client_new (subsystems);
365
  if (op_backend->gudev_client == NULL) {
366
    GError *error;
367
    g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create gudev client"));
368
    g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
369
    g_error_free (error);
370
    return;
371
  }
372
  g_signal_connect (op_backend->gudev_client, "uevent", G_CALLBACK (on_uevent), op_backend);
373
374
  /* turn usb:001,041 string into an udev device name */
375
  if (!g_str_has_prefix (host, "[usb:")) {
376
    g_vfs_job_failed (G_VFS_JOB (job),
377
                      G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
378
                      _("Unexpected host uri format."));
379
    return;
380
  }
381
382
  char *comma;
383
  char *dev_path = g_strconcat ("/dev/bus/usb/", host + 5, NULL);
384
  if ((comma = strchr (dev_path, ',')) == NULL) {
385
    g_free (dev_path);
386
    g_vfs_job_failed (G_VFS_JOB (job),
387
                      G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
388
                      _("Malformed host uri."));
389
    return;
390
  }
391
  *comma = '/';
392
  dev_path[strlen (dev_path) -1] = '\0';
393
  DEBUG ("(I) do_mount: Parsed '%s' into device name %s", host, dev_path);
394
395
  /* find corresponding GUdevDevice */
396
  if (!g_udev_client_query_by_device_file (op_backend->gudev_client, dev_path)) {
397
    g_free (dev_path);
398
    g_vfs_job_failed (G_VFS_JOB (job),
399
                      G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
400
                      _("Couldn't find matching udev device."));
401
    return;
402
  }
403
404
  op_backend->dev_path = dev_path;
405
406
  LIBMTP_Init ();
407
408
  get_device (backend, host, G_VFS_JOB (job));
409
  if (!G_VFS_JOB (job)->failed) {
410
    GMountSpec *mtp_mount_spec = g_mount_spec_new ("mtp");
411
    g_mount_spec_set (mtp_mount_spec, "host", host);
412
    g_vfs_backend_set_mount_spec (backend, mtp_mount_spec);
413
    g_mount_spec_unref (mtp_mount_spec);
414
415
    g_vfs_job_succeeded (G_VFS_JOB (job));
416
417
    op_backend->hb_id =
418
      g_timeout_add_seconds (900, (GSourceFunc)mtp_heartbeat, op_backend);
419
420
#if HAVE_LIBMTP_READ_EVENT
421
    g_thread_new ("events", check_event, backend);
422
#endif
423
  }
424
  DEBUG ("(I) do_mount done.");
425
}
426
427
428
static void
429
do_unmount (GVfsBackend *backend, GVfsJobUnmount *job,
430
            GMountUnmountFlags flags,
431
            GMountSource *mount_source)
432
{
433
  GVfsBackendMtp *op_backend;
434
435
  DEBUG ("(I) do_umount");
436
437
  op_backend = G_VFS_BACKEND_MTP (backend);
438
439
  g_source_remove (op_backend->hb_id);
440
  g_object_unref (op_backend->gudev_client);
441
  g_free (op_backend->dev_path);
442
443
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
444
  LIBMTP_Release_Device (op_backend->device);
445
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
446
447
  g_vfs_job_succeeded (G_VFS_JOB (job));
448
449
  DEBUG ("(I) do_umount done.");
450
}
451
452
453
454
455
456
457
458
/************************************************
459
 * 	  Queries
460
 * 
461
 */
462
463
LIBMTP_mtpdevice_t *
464
get_device (GVfsBackend *backend, const char *id, GVfsJob *job) {
465
  DEBUG ("(II) get_device: %s", id);
466
467
  LIBMTP_mtpdevice_t *device = NULL;
468
469
  if (G_VFS_BACKEND_MTP (backend)->device != NULL) {
470
    DEBUG ("(II) get_device: Returning cached device %p", device);
471
    return G_VFS_BACKEND_MTP (backend)->device;
472
  }
473
474
  LIBMTP_raw_device_t * rawdevices;
475
  int numrawdevices;
476
  LIBMTP_error_number_t err;
477
478
  err = LIBMTP_Detect_Raw_Devices (&rawdevices, &numrawdevices);
479
  switch (err) {
480
  case LIBMTP_ERROR_NONE:
481
    break;
482
  case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
483
    g_vfs_job_failed (G_VFS_JOB (job),
484
                      G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
485
                      _("No MTP devices found"));
486
    goto exit;
487
  case LIBMTP_ERROR_CONNECTING:
488
    g_vfs_job_failed (G_VFS_JOB (job),
489
                      G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
490
                      _("Unable to connect to MTP device"));
491
    goto exit;
492
  case LIBMTP_ERROR_MEMORY_ALLOCATION:
493
    g_vfs_job_failed (G_VFS_JOB (job),
494
                      G_FILE_ERROR, G_FILE_ERROR_NOMEM,
495
                      _("Unable to allocate memory while detecting MTP devices"));
496
    goto exit;
497
  case LIBMTP_ERROR_GENERAL:
498
  default:
499
    g_vfs_job_failed (G_VFS_JOB (job),
500
                      G_IO_ERROR, G_IO_ERROR_FAILED,
501
                      _("Generic libmtp error"));
502
    goto exit;
503
  }
504
505
  /* Iterate over connected MTP devices */
506
  int i;
507
  for (i = 0; i < numrawdevices; i++) {
508
    char *name;
509
    name = g_strdup_printf ("[usb:%03u,%03u]",
510
                            rawdevices[i].bus_location,
511
                            rawdevices[i].devnum);
512
513
    if (strcmp (id, name) == 0) {
514
      device = LIBMTP_Open_Raw_Device_Uncached (&rawdevices[i]);
515
      if (device == NULL) {
516
        g_vfs_job_failed (G_VFS_JOB (job),
517
                          G_IO_ERROR, G_IO_ERROR_FAILED,
518
                          _("Unable to open MTP device '%s'"), name);
519
        g_free (name);
520
        goto exit;
521
      }
522
523
      DEBUG ("(II) get_device: Storing device %s", name);
524
      G_VFS_BACKEND_MTP (backend)->device = device;
525
526
      LIBMTP_Dump_Errorstack (device);
527
      LIBMTP_Clear_Errorstack (device);
528
      break;
529
    } else {
530
      g_free (name);
531
    }
532
  }
533
534
 exit:
535
  DEBUG ("(II) get_device done.");
536
  return device;
537
}
538
539
static void
540
get_device_info (GVfsBackendMtp *backend, GFileInfo *info)
541
{
542
  LIBMTP_mtpdevice_t *device = backend->device;
543
  const char *name;
544
545
  name = g_mount_spec_get (g_vfs_backend_get_mount_spec (G_VFS_BACKEND (backend)), "host");
546
547
  DEBUG_ENUMERATE ("(II) get_device_info: %s", name);
548
549
  g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
550
  g_file_info_set_name (info, name);
551
552
  char *friendlyname = LIBMTP_Get_Friendlyname (device);
553
  g_file_info_set_display_name (info, friendlyname == NULL ?
554
                                      _("Unnamed Device") : friendlyname);
555
  free (friendlyname);
556
557
  g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
558
  g_file_info_set_content_type (info, "inode/directory");
559
  g_file_info_set_size (info, 0);
560
561
  GIcon *icon = g_themed_icon_new ("multimedia-player");
562
  g_file_info_set_icon (info, icon);
563
  g_object_unref (icon);
564
565
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
566
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE);
567
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
568
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
569
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
570
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, TRUE); 
571
572
  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs");
573
574
  int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
575
  if (ret != 0) {
576
    LIBMTP_Dump_Errorstack (device);
577
    LIBMTP_Clear_Errorstack (device);
578
    DEBUG_ENUMERATE ("(II) get_device_info done with no stores.");
579
    return;
580
  }
581
  guint64 freeSpace = 0;
582
  guint64 maxSpace = 0;
583
  LIBMTP_devicestorage_t *storage;
584
  for (storage = device->storage; storage != 0; storage = storage->next) {
585
    freeSpace += storage->FreeSpaceInBytes;
586
    maxSpace += storage->MaxCapacity;
587
  }
588
589
  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, freeSpace);
590
  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, maxSpace);
591
592
  DEBUG_ENUMERATE ("(II) get_device_info done.");
593
}
594
595
static void
596
get_storage_info (LIBMTP_devicestorage_t *storage, GFileInfo *info) {
597
598
  char *id = g_strdup_printf ("%u", storage->id);
599
  g_file_info_set_name (info, id);
600
  g_free (id);
601
602
  DEBUG_ENUMERATE ("(II) get_storage_info: %s", storage->id);
603
604
  g_file_info_set_display_name (info, storage->StorageDescription);
605
  g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
606
  g_file_info_set_content_type (info, "inode/directory");
607
  g_file_info_set_size (info, 0);
608
609
  GIcon *icon;
610
  switch (storage->StorageType) {
611
  case PTP_ST_FixedROM:
612
    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
613
    icon = g_themed_icon_new_with_default_fallbacks ("drive-harddisk");
614
    break;
615
  case PTP_ST_RemovableROM:
616
    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
617
    icon = g_themed_icon_new_with_default_fallbacks ("media-memory-sd");
618
    break;
619
  case PTP_ST_RemovableRAM:
620
    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, FALSE);
621
    icon = g_themed_icon_new_with_default_fallbacks ("media-memory-sd");
622
    break;
623
  case PTP_ST_FixedRAM:
624
  default:
625
    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, FALSE);
626
    icon = g_themed_icon_new_with_default_fallbacks ("drive-harddisk");
627
    break;
628
  }
629
  g_file_info_set_icon (info, icon);
630
  g_object_unref (icon);
631
632
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
633
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE);
634
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
635
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
636
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
637
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE); 
638
639
  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, storage->FreeSpaceInBytes);
640
  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, storage->MaxCapacity);
641
  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs");
642
643
  DEBUG_ENUMERATE ("(II) get_storage_info done.");
644
}
645
646
static void
647
get_file_info (GVfsBackend *backend,
648
               LIBMTP_mtpdevice_t *device,
649
               GFileInfo *info,
650
               LIBMTP_file_t *file) {
651
  GIcon *icon = NULL;
652
  char *content_type = NULL;
653
654
  char *id = g_strdup_printf ("%u", file->item_id);
655
  g_file_info_set_name (info, id);
656
  g_free (id);
657
658
  DEBUG_ENUMERATE ("(II) get_file_info: %u", file->item_id);
659
660
  g_file_info_set_display_name (info, file->filename);
661
662
  switch (file->filetype) {
663
  case LIBMTP_FILETYPE_FOLDER:
664
    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
665
    g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
666
    g_file_info_set_content_type (info, "inode/directory");
667
    icon = g_themed_icon_new ("folder");
668
    break;
669
  default:
670
    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, FALSE);
671
    g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
672
    content_type = g_content_type_guess (file->filename, NULL, 0, NULL);
673
    g_file_info_set_content_type (info, content_type);
674
    icon = g_content_type_get_icon (content_type);
675
    break;
676
  }
677
678
679
#if HAVE_LIBMTP_GET_THUMBNAIL
680
  if (LIBMTP_FILETYPE_IS_IMAGE (file->filetype) ||
681
      LIBMTP_FILETYPE_IS_VIDEO (file->filetype) ||
682
      LIBMTP_FILETYPE_IS_AUDIOVIDEO (file->filetype)) {
683
684
    char *icon_id;
685
    GIcon *icon;
686
    GMountSpec *mount_spec;
687
688
    mount_spec = g_vfs_backend_get_mount_spec (backend);
689
    icon_id = g_strdup_printf ("%u", file->item_id);
690
    icon = g_vfs_icon_new (mount_spec,
691
                           icon_id);
692
    g_file_info_set_attribute_object (info,
693
                                      G_FILE_ATTRIBUTE_PREVIEW_ICON,
694
                                      G_OBJECT (icon));
695
    g_object_unref (icon);
696
    g_free (icon_id);
697
  }
698
#endif
699
700
  g_file_info_set_size (info, file->filesize);
701
702
  GTimeVal modtime = { file->modificationdate, 0 };
703
  g_file_info_set_modification_time (info, &modtime);
704
705
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
706
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE);
707
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, TRUE);
708
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
709
  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, TRUE);
710
  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME, file->filename);
711
712
713
  if (icon != NULL) {
714
    g_file_info_set_icon (info, icon);
715
    g_object_unref (icon);
716
  }
717
  g_free (content_type);
718
719
  DEBUG_ENUMERATE ("(II) get_file_info done.");
720
}
721
722
723
static void
724
do_enumerate (GVfsBackend *backend,
725
               GVfsJobEnumerate *job,
726
               const char *filename,
727
               GFileAttributeMatcher *attribute_matcher,
728
               GFileQueryInfoFlags flags)
729
{
730
  GVfsBackendMtp *op_backend = G_VFS_BACKEND_MTP (backend);
731
  GFileInfo *info;
732
733
  gchar **elements = g_strsplit_set (filename, "/", -1);
734
  unsigned int ne = 0;
735
  for (ne = 0; elements[ne] != NULL; ne++);
736
737
  DEBUG ("(I) do_enumerate (filename = %s, n_elements = %d) ", filename, ne);
738
739
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
740
741
  LIBMTP_mtpdevice_t *device;
742
  device = op_backend->device;
743
744
  if (ne == 2 && elements[1][0] == '\0') {
745
    LIBMTP_devicestorage_t *storage;
746
747
    int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
748
    if (ret != 0) {
749
      LIBMTP_Dump_Errorstack (device);
750
      LIBMTP_Clear_Errorstack (device);
751
      goto success;
752
    }
753
    for (storage = device->storage; storage != 0; storage = storage->next) {
754
      info = g_file_info_new ();
755
      get_storage_info (storage, info);
756
      g_vfs_job_enumerate_add_info (job, info);
757
      g_object_unref (info);
758
    }
759
  } else {
760
    LIBMTP_file_t *files;
761
762
    int pid = (ne == 2 ? -1 : strtol (elements[ne-1], NULL, 10));
763
764
    LIBMTP_Clear_Errorstack (device);
765
    files = LIBMTP_Get_Files_And_Folders (device, strtol (elements[1], NULL, 10), pid);
766
    if (files == NULL && LIBMTP_Get_Errorstack (device) != NULL) {
767
      fail_job (G_VFS_JOB (job), device);
768
      goto exit;
769
    }
770
    while (files != NULL) {
771
      LIBMTP_file_t *file = files;
772
      files = files->next;
773
774
      info = g_file_info_new ();
775
      get_file_info (backend, device, info, file);
776
      g_vfs_job_enumerate_add_info (job, info);
777
      g_object_unref (info);
778
779
      LIBMTP_destroy_file_t (file);
780
    }
781
  }
782
783
 success:
784
  g_vfs_job_enumerate_done (job);
785
  g_vfs_job_succeeded (G_VFS_JOB (job));
786
787
 exit:
788
  g_strfreev (elements);
789
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
790
  DEBUG ("(I) do_enumerate done.");
791
}
792
793
static void
794
do_query_info (GVfsBackend *backend,
795
                GVfsJobQueryInfo *job,
796
                const char *filename,
797
                GFileQueryInfoFlags flags,
798
                GFileInfo *info,
799
                GFileAttributeMatcher *matcher)
800
{
801
  DEBUG ("(I) do_query_info (filename = %s) ", filename);
802
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
803
804
  gchar **elements = g_strsplit_set (filename, "/", -1);
805
  unsigned int ne = 0;
806
  for (ne = 0; elements[ne] != NULL; ne++);
807
808
  LIBMTP_mtpdevice_t *device;
809
  device = G_VFS_BACKEND_MTP (backend)->device;
810
811
  if (ne == 2 && elements[1][0] == '\0') {
812
    get_device_info (G_VFS_BACKEND_MTP (backend), info);
813
  } else if (ne < 3) {
814
    LIBMTP_devicestorage_t *storage;
815
    int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
816
    if (ret != 0) {
817
      LIBMTP_Dump_Errorstack (device);
818
      LIBMTP_Clear_Errorstack (device);
819
      g_vfs_job_failed (G_VFS_JOB (job),
820
                        G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
821
                        _("No storage volumes found"));
822
      goto exit;
823
    }
824
    for (storage = device->storage; storage != 0; storage = storage->next) {
825
      if (storage->id == strtol (elements[ne-1], NULL, 10)) {
826
        DEBUG ("(III) found storage %u", storage->id);
827
        get_storage_info (storage, info);
828
      }
829
    }
830
  } else {
831
    LIBMTP_file_t *file = NULL;
832
    char *endptr;
833
    if (strtol (elements[ne-1], &endptr, 10) == 0 ||
834
        *endptr != '\0') {
835
      DEBUG ("(II) try get files and folders");
836
      int parent_id = -1;
837
      if (ne > 3) {
838
        parent_id = strtol (elements[ne-2], NULL, 10);
839
      }
840
      LIBMTP_file_t *i = LIBMTP_Get_Files_And_Folders (device, strtol (elements[1], NULL, 10),
841
                                                       parent_id);
842
      while (i != NULL) {
843
        DEBUG ("(II) backup query (entity = %s, name = %s) ", i->filename, elements[ne-1]);
844
        if (strcmp (i->filename, elements[ne-1]) == 0) {
845
          file = i;
846
          i = i->next;
847
          break;
848
        } else {
849
          LIBMTP_file_t *tmp = i;
850
          i = i->next;
851
          LIBMTP_destroy_file_t (tmp);
852
        }
853
      }
854
      while (i != NULL) {
855
        LIBMTP_file_t *tmp = i;
856
        i = i->next;
857
        LIBMTP_destroy_file_t (tmp);
858
      }
859
    } else {
860
      file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10));
861
    }
862
863
    if (file != NULL) {
864
      get_file_info (backend, device, info, file);
865
      LIBMTP_destroy_file_t (file);
866
    } else {
867
      fail_job (G_VFS_JOB (job), device);
868
      goto exit;
869
    }
870
  }
871
872
  g_vfs_job_succeeded (G_VFS_JOB (job));
873
874
 exit:
875
  g_strfreev (elements);
876
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
877
  DEBUG ("(I) do_query_info done.");
878
}
879
880
881
static void
882
do_query_fs_info (GVfsBackend *backend,
883
		  GVfsJobQueryFsInfo *job,
884
		  const char *filename,
885
		  GFileInfo *info,
886
		  GFileAttributeMatcher *attribute_matcher)
887
{
888
  DEBUG ("(I) do_query_fs_info (filename = %s) ", filename);
889
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
890
891
  gchar **elements = g_strsplit_set (filename, "/", -1);
892
  unsigned int ne = 0;
893
  for (ne = 0; elements[ne] != NULL; ne++);
894
895
  LIBMTP_mtpdevice_t *device;
896
  device = G_VFS_BACKEND_MTP (backend)->device;
897
898
  if (ne == 2 && elements[1][0] == '\0') {
899
    get_device_info (G_VFS_BACKEND_MTP (backend), info);
900
  } else {
901
    LIBMTP_devicestorage_t *storage;
902
    int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
903
    if (ret != 0) {
904
      LIBMTP_Dump_Errorstack (device);
905
      LIBMTP_Clear_Errorstack (device);
906
      g_vfs_job_failed (G_VFS_JOB (job),
907
                        G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
908
                        _("No storage volumes found"));
909
      goto exit;
910
    }
911
    for (storage = device->storage; storage != 0; storage = storage->next) {
912
      if (storage->id == strtol (elements[1], NULL, 10)) {
913
        get_storage_info (storage, info);
914
      }
915
    }
916
  }
917
918
  g_vfs_job_succeeded (G_VFS_JOB (job));
919
920
 exit:
921
  g_strfreev (elements);
922
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
923
924
  DEBUG ("(I) do_query_fs_info done.");
925
}
926
927
928
/************************************************
929
 * 	  Operations
930
 * 
931
 */
932
933
typedef struct {
934
  GFileProgressCallback progress_callback;
935
  gpointer progress_callback_data;
936
  GVfsJob *job;
937
} MtpProgressData;
938
939
940
static int mtp_progress (uint64_t const sent, uint64_t const total,
941
                  MtpProgressData const * const data)
942
{
943
  if (data->progress_callback) {
944
    data->progress_callback (sent, total, data->progress_callback_data);
945
  }
946
  return g_vfs_job_is_cancelled (data->job);
947
}
948
949
static void
950
do_make_directory (GVfsBackend *backend,
951
                    GVfsJobMakeDirectory *job,
952
                    const char *filename)
953
{
954
  DEBUG ("(I) do_make_directory (filename = %s) ", filename);
955
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
956
957
  gchar **elements = g_strsplit_set (filename, "/", -1);
958
  unsigned int ne = 0;
959
  for (ne = 0; elements[ne] != NULL; ne++);
960
961
  if (ne < 3) {
962
    g_vfs_job_failed (G_VFS_JOB (job),
963
                      G_IO_ERROR, G_IO_ERROR_FAILED,
964
                      _("Cannot make directory in this location"));
965
    goto exit;
966
  }
967
968
  LIBMTP_mtpdevice_t *device;
969
  device = G_VFS_BACKEND_MTP (backend)->device;
970
971
  int parent_id = 0;
972
  if (ne > 3) {
973
    parent_id = strtol (elements[ne-2], NULL, 10);
974
  }
975
976
  int ret = LIBMTP_Create_Folder (device, elements[ne-1], parent_id, strtol (elements[1], NULL, 10));
977
  if (ret == 0) {
978
    fail_job (G_VFS_JOB (job), device);
979
    goto exit;
980
  }
981
982
  g_vfs_job_succeeded (G_VFS_JOB (job));
983
984
  g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors, emit_create_event, (char *)filename);
985
986
 exit:
987
  g_strfreev (elements);
988
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
989
990
  DEBUG ("(I) do_make_directory done.");
991
}
992
993
994
static void
995
do_pull (GVfsBackend *backend,
996
         GVfsJobPull *job,
997
         const char *source,
998
         const char *local_path,
999
         GFileCopyFlags flags,
1000
         gboolean remove_source,
1001
         GFileProgressCallback progress_callback,
1002
         gpointer progress_callback_data)
1003
{
1004
  DEBUG ("(I) do_pull (filename = %s, local_path = %s) ", source, local_path);
1005
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
1006
1007
  GFileInfo *info = NULL;
1008
  gchar **elements = g_strsplit_set (source, "/", -1);
1009
  unsigned int ne = 0;
1010
  for (ne = 0; elements[ne] != NULL; ne++);
1011
1012
  if (ne < 3) {
1013
    g_vfs_job_failed (G_VFS_JOB (job),
1014
                      G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1015
                      _("Not a regular file"));
1016
    goto exit;
1017
  }
1018
1019
  LIBMTP_mtpdevice_t *device;
1020
  device = G_VFS_BACKEND_MTP (backend)->device;
1021
1022
  LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10));
1023
  if (file == NULL) {
1024
    g_vfs_job_failed (G_VFS_JOB (job),
1025
                      G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1026
                      _("File does not exist"));
1027
    goto exit;
1028
  }
1029
1030
  info = g_file_info_new ();
1031
  get_file_info (backend, device, info, file);
1032
  LIBMTP_destroy_file_t (file);
1033
  file = NULL;
1034
  if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
1035
    GError *error;
1036
    GFile *file = g_file_new_for_path (local_path);
1037
    g_assert (file != NULL);
1038
    if (file) {
1039
      error = NULL;
1040
      if (g_file_make_directory (file, G_VFS_JOB (job)->cancellable, &error)) {
1041
        g_vfs_job_succeeded (G_VFS_JOB (job));
1042
      } else if (error->code == G_IO_ERROR_EXISTS) {
1043
        g_vfs_job_succeeded (G_VFS_JOB (job));
1044
        g_error_free (error);
1045
      } else {
1046
        g_vfs_job_failed_from_error (G_VFS_JOB (job), error); 
1047
        DEBUG ("(II) pull dir failed: %s", error->message);
1048
        g_error_free (error);
1049
      }
1050
      g_object_unref (file);
1051
    }
1052
  } else {
1053
      MtpProgressData *mtp_progress_data = g_new0 (MtpProgressData, 1);
1054
      mtp_progress_data->progress_callback = progress_callback;
1055
      mtp_progress_data->progress_callback_data = progress_callback_data;
1056
      mtp_progress_data->job = G_VFS_JOB (job);
1057
      int ret = LIBMTP_Get_File_To_File (device,
1058
                                         strtol (elements[ne-1], NULL, 10),
1059
                                         local_path,
1060
                                         (LIBMTP_progressfunc_t)mtp_progress,
1061
                                         mtp_progress_data);
1062
      g_free (mtp_progress_data);
1063
      if (ret != 0) {
1064
        fail_job (G_VFS_JOB (job), device);
1065
        goto exit;
1066
      }
1067
    g_vfs_job_succeeded (G_VFS_JOB (job));
1068
  }
1069
1070
 exit:
1071
  if (info != NULL) {
1072
    g_object_unref (info);
1073
  }
1074
  g_strfreev (elements);
1075
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
1076
1077
  DEBUG ("(I) do_pull done.");
1078
}
1079
1080
1081
static void
1082
do_push (GVfsBackend *backend,
1083
         GVfsJobPush *job,
1084
         const char *destination,
1085
         const char *local_path,
1086
         GFileCopyFlags flags,
1087
         gboolean remove_source,
1088
         GFileProgressCallback progress_callback,
1089
         gpointer progress_callback_data)
1090
{
1091
  DEBUG ("(I) do_push (filename = %s, local_path = %s) ", destination, local_path);
1092
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
1093
1094
  GFile *file = NULL;
1095
  GFileInfo *info = NULL;
1096
  gchar **elements = g_strsplit_set (destination, "/", -1);
1097
  unsigned int ne = 0;
1098
  for (ne = 0; elements[ne] != NULL; ne++);
1099
1100
  if (ne < 3) {
1101
    g_vfs_job_failed (G_VFS_JOB (job),
1102
                      G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1103
                      _("Cannot write to this location"));
1104
    goto exit;
1105
  }
1106
1107
  LIBMTP_mtpdevice_t *device;
1108
  device = G_VFS_BACKEND_MTP (backend)->device;
1109
1110
  int parent_id = 0;
1111
1112
  if (ne > 3) {
1113
    parent_id = strtol (elements[ne-2], NULL, 10);
1114
  }
1115
1116
  file = g_file_new_for_path (local_path);
1117
  if (!file) {
1118
    g_vfs_job_failed (G_VFS_JOB (job),
1119
                      G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1120
                      _("File not found"));
1121
    goto exit;
1122
  }
1123
1124
  if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE,
1125
                              G_VFS_JOB (job)->cancellable) ==
1126
      G_FILE_TYPE_DIRECTORY) {
1127
    /*
1128
     * It happens to be the case that we can reuse do_make_directory
1129
     * here.
1130
     */
1131
    return do_make_directory (backend, G_VFS_JOB_MAKE_DIRECTORY (job),
1132
                              elements[ne-1]);
1133
  }
1134
1135
  GError *error = NULL;
1136
  info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
1137
                            G_FILE_QUERY_INFO_NONE,
1138
                            G_VFS_JOB (job)->cancellable,
1139
                            &error);
1140
  if (!info) {
1141
    g_vfs_job_failed_from_error (G_VFS_JOB (job), error); 
1142
    g_error_free (error);
1143
    goto exit;
1144
  }
1145
1146
  LIBMTP_file_t *mtpfile = LIBMTP_new_file_t ();
1147
  mtpfile->filename = strdup (elements[ne-1]);
1148
  mtpfile->parent_id = parent_id;
1149
  mtpfile->storage_id = strtol (elements[1], NULL, 10);
1150
  mtpfile->filetype = LIBMTP_FILETYPE_UNKNOWN; 
1151
  mtpfile->filesize = g_file_info_get_size (info);
1152
1153
  MtpProgressData *mtp_progress_data = g_new0 (MtpProgressData, 1);
1154
  mtp_progress_data->progress_callback = progress_callback;
1155
  mtp_progress_data->progress_callback_data = progress_callback_data;
1156
  mtp_progress_data->job = G_VFS_JOB (job);
1157
  int ret = LIBMTP_Send_File_From_File (device, local_path, mtpfile,
1158
                                        (LIBMTP_progressfunc_t)mtp_progress,
1159
                                        mtp_progress_data);
1160
  g_free (mtp_progress_data);
1161
  LIBMTP_destroy_file_t (mtpfile);
1162
  if (ret != 0) {
1163
    fail_job (G_VFS_JOB (job), device);
1164
    goto exit;
1165
  }
1166
1167
  g_vfs_job_succeeded (G_VFS_JOB (job));
1168
1169
  g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
1170
                        emit_create_event,
1171
                        (char *)destination);
1172
1173
 exit:
1174
  if (file) {
1175
    g_object_unref (file);
1176
  }
1177
  if (info) {
1178
    g_object_unref (info);
1179
  }
1180
  g_strfreev (elements);
1181
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
1182
1183
  DEBUG ("(I) do_push done.");
1184
}
1185
1186
1187
static void
1188
do_delete (GVfsBackend *backend,
1189
            GVfsJobDelete *job,
1190
            const char *filename)
1191
{
1192
  DEBUG ("(I) do_delete (filename = %s) ", filename);
1193
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
1194
1195
  gchar **elements = g_strsplit_set (filename, "/", -1);
1196
  unsigned int ne = 0;
1197
  for (ne = 0; elements[ne] != NULL; ne++);
1198
1199
  if (ne < 3) {
1200
    g_vfs_job_failed (G_VFS_JOB (job),
1201
                      G_IO_ERROR, G_IO_ERROR_FAILED,
1202
                      _("Cannot delete this entity"));
1203
    goto exit;
1204
  }
1205
1206
  LIBMTP_mtpdevice_t *device;
1207
  device = G_VFS_BACKEND_MTP (backend)->device;
1208
1209
  int ret = LIBMTP_Delete_Object (device, strtol (elements[ne-1], NULL, 10));
1210
  if (ret != 0) {
1211
    fail_job (G_VFS_JOB (job), device);
1212
    goto exit;
1213
  }
1214
  g_vfs_job_succeeded (G_VFS_JOB (job));
1215
1216
  g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
1217
                        emit_delete_event,
1218
                        (char *)filename);
1219
1220
 exit:
1221
  g_strfreev (elements);
1222
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
1223
1224
  DEBUG ("(I) do_delete done.");
1225
}
1226
1227
1228
static void
1229
do_set_display_name (GVfsBackend *backend,
1230
                      GVfsJobSetDisplayName *job,
1231
                      const char *filename,
1232
                      const char *display_name)
1233
{
1234
  DEBUG ("(I) do_set_display_name '%s' --> '%s' ", filename, display_name);
1235
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
1236
1237
  gchar **elements = g_strsplit_set (filename, "/", -1);
1238
  unsigned int ne = 0;
1239
  for (ne = 0; elements[ne] != NULL; ne++);
1240
1241
  if (ne < 3) {
1242
    g_vfs_job_failed (G_VFS_JOB (job),
1243
                      G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1244
                      _("Can't rename volume"));
1245
    goto exit;
1246
  }
1247
1248
  LIBMTP_mtpdevice_t *device;
1249
  device = G_VFS_BACKEND_MTP (backend)->device;
1250
1251
  LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10));
1252
  int ret = LIBMTP_Set_File_Name (device, file, display_name);
1253
  if (ret != 0) {
1254
    fail_job (G_VFS_JOB (job), device);
1255
    goto exit;
1256
  }
1257
  LIBMTP_destroy_file_t (file);
1258
  file = NULL;
1259
  g_vfs_job_set_display_name_set_new_path (job, filename);
1260
  g_vfs_job_succeeded (G_VFS_JOB (job));
1261
1262
  g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
1263
                        emit_change_event,
1264
                        (char *)filename);
1265
1266
 exit:
1267
  g_strfreev (elements);
1268
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
1269
1270
  DEBUG ("(I) do_set_display_name done.");
1271
}
1272
1273
1274
#if HAVE_LIBMTP_GET_THUMBNAIL
1275
static void
1276
do_open_icon_for_read (GVfsBackend *backend,
1277
                       GVfsJobOpenIconForRead *job,
1278
                       const char *icon_id)
1279
{
1280
  DEBUG ("(I) do_open_icon_for_read (%s)", icon_id);
1281
  g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
1282
1283
  guint id = strtol (icon_id, NULL, 10);
1284
1285
  if (id > 0) {
1286
    unsigned char *data;
1287
    unsigned int size;
1288
    int ret = LIBMTP_Get_Thumbnail (G_VFS_BACKEND_MTP (backend)->device, id,
1289
                                    &data, &size);
1290
    if (ret == 0) {
1291
      DEBUG ("File %u has thumbnail: %u", id, size);
1292
      GByteArray *bytes = g_byte_array_sized_new (size);
1293
      g_byte_array_append (bytes, data, size);
1294
      free (data);
1295
      g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), FALSE);
1296
      g_vfs_job_open_for_read_set_handle (G_VFS_JOB_OPEN_FOR_READ (job), bytes);
1297
      g_vfs_job_succeeded (G_VFS_JOB (job));
1298
    } else {
1299
      LIBMTP_filesampledata_t *sample_data = LIBMTP_new_filesampledata_t ();
1300
      ret = LIBMTP_Get_Representative_Sample (G_VFS_BACKEND_MTP (backend)->device,
1301
                                              id, sample_data);
1302
      if (ret == 0) {
1303
        DEBUG ("File %u has sampledata: %u", id, size);
1304
        GByteArray *bytes = g_byte_array_sized_new (sample_data->size);
1305
        g_byte_array_append (bytes, (const guint8 *)sample_data->data, sample_data->size);
1306
        LIBMTP_destroy_filesampledata_t (sample_data);
1307
        g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), FALSE);
1308
        g_vfs_job_open_for_read_set_handle (G_VFS_JOB_OPEN_FOR_READ (job), bytes);
1309
        g_vfs_job_succeeded (G_VFS_JOB (job));
1310
      } else {
1311
        DEBUG ("File %u has no thumbnail:", id);
1312
        g_vfs_job_failed (G_VFS_JOB (job),
1313
                          G_IO_ERROR,
1314
                          G_IO_ERROR_NOT_FOUND,
1315
                          _("No thumbnail for entity '%s'"),
1316
                          icon_id);
1317
      }
1318
    }
1319
  } else {
1320
    g_vfs_job_failed (G_VFS_JOB (job),
1321
                      G_IO_ERROR,
1322
                      G_IO_ERROR_INVALID_ARGUMENT,
1323
                      _("Malformed icon identifier '%s'"),
1324
                      icon_id);
1325
  }
1326
  g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
1327
1328
  DEBUG ("(I) do_open_icon_for_read done.");
1329
}
1330
1331
static gboolean
1332
try_read (GVfsBackend *backend,
1333
          GVfsJobRead *job,
1334
          GVfsBackendHandle handle,
1335
          char *buffer,
1336
          gsize bytes_requested)
1337
{
1338
  GByteArray *bytes = handle;
1339
1340
  DEBUG ("(I) try_read (%u %lu)", bytes->len, bytes_requested);
1341
1342
  gsize bytes_to_copy =  MIN (bytes->len, bytes_requested);
1343
  if (bytes_to_copy == 0) {
1344
    goto out;
1345
  }
1346
  memcpy (buffer, bytes->data, bytes_to_copy);
1347
  g_byte_array_remove_range (bytes, 0, bytes_to_copy);
1348
1349
 out:
1350
  g_vfs_job_read_set_size (job, bytes_to_copy);
1351
  g_vfs_job_succeeded (G_VFS_JOB (job));
1352
1353
  DEBUG ("(I) try_read done.");
1354
  return TRUE;
1355
}
1356
1357
static void
1358
do_close_read (GVfsBackend *backend,
1359
                GVfsJobCloseRead *job,
1360
                GVfsBackendHandle handle)
1361
{
1362
  DEBUG ("(I) do_close_read");
1363
  g_byte_array_unref (handle);
1364
  g_vfs_job_succeeded (G_VFS_JOB (job));
1365
  DEBUG ("(I) do_close_read done.");
1366
}
1367
#endif /* HAVE_LIBMTP_GET_THUMBNAIL */
1368
1369
1370
/************************************************
1371
 * 	  Class init
1372
 *
1373
 */
1374
1375
1376
static void
1377
g_vfs_backend_mtp_class_init (GVfsBackendMtpClass *klass)
1378
{
1379
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1380
  GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
1381
1382
  gobject_class->finalize = g_vfs_backend_mtp_finalize;
1383
1384
  backend_class->mount = do_mount;
1385
  backend_class->unmount = do_unmount;
1386
  backend_class->query_info = do_query_info;
1387
  backend_class->enumerate = do_enumerate;
1388
  backend_class->query_fs_info = do_query_fs_info;
1389
  backend_class->pull = do_pull;
1390
  backend_class->push = do_push;
1391
  backend_class->make_directory = do_make_directory;
1392
  backend_class->delete = do_delete;
1393
  backend_class->set_display_name = do_set_display_name;
1394
  backend_class->create_dir_monitor = do_create_dir_monitor;
1395
  backend_class->create_file_monitor = do_create_file_monitor;
1396
#if HAVE_LIBMTP_GET_THUMBNAIL
1397
  backend_class->open_icon_for_read = do_open_icon_for_read;
1398
  backend_class->try_read = try_read;
1399
  backend_class->close_read = do_close_read;
1400
#endif
1401
}
(-)a/daemon/gvfsbackendmtp.h (+69 lines)
Line 0 Link Here
1
/* GIO - GLib Input, Output and Streaming Library
2
 *   MTP Backend
3
 * 
4
 * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
#ifndef __G_VFS_BACKEND_MTP_H__
23
#define __G_VFS_BACKEND_MTP_H__
24
25
#include <gvfsbackend.h>
26
#include <gmountspec.h>
27
#ifdef HAVE_GUDEV
28
#include <gudev/gudev.h>
29
#endif
30
#include <libmtp.h>
31
32
G_BEGIN_DECLS
33
34
#define G_VFS_TYPE_BACKEND_MTP         (g_vfs_backend_mtp_get_type ())
35
#define G_VFS_BACKEND_MTP(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_MTP, GVfsBackendMtp))
36
#define G_VFS_BACKEND_MTP_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_MTP, GVfsBackendMtpClass))
37
#define G_VFS_IS_BACKEND_MTP(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_MTP))
38
#define G_VFS_IS_BACKEND_MTP_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_MTP))
39
#define G_VFS_BACKEND_MTP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_MTP, GVfsBackendMtpClass))
40
41
typedef struct _GVfsBackendMtp        GVfsBackendMtp;
42
typedef struct _GVfsBackendMtpClass   GVfsBackendMtpClass;
43
44
struct _GVfsBackendMtp
45
{
46
  GVfsBackend parent_instance;
47
48
#ifdef HAVE_GUDEV
49
  GUdevClient *gudev_client;
50
#endif
51
52
  GMutex mutex;
53
  LIBMTP_mtpdevice_t *device;
54
  char *dev_path;
55
56
  GHashTable *monitors;
57
  guint hb_id;
58
};
59
60
struct _GVfsBackendMtpClass
61
{
62
  GVfsBackendClass parent_class;
63
};
64
65
GType g_vfs_backend_mtp_get_type (void) G_GNUC_CONST;
66
67
G_END_DECLS
68
69
#endif /* __G_VFS_BACKEND_MTP_H__ */
(-)a/daemon/mtp.mount.in (+4 lines)
Line 0 Link Here
1
[Mount]
2
Type=mtp
3
Exec=@libexecdir@/gvfsd-mtp
4
AutoMount=false
(-)a/monitor/Makefile.am (-1 / +5 lines)
Lines 1-4 Link Here
1
DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2
1
DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp
2
SUBDIRS = proxy
2
SUBDIRS = proxy
3
3
4
if USE_HAL
4
if USE_HAL
Lines 20-22 endif Link Here
20
if USE_AFC
20
if USE_AFC
21
SUBDIRS += afc
21
SUBDIRS += afc
22
endif
22
endif
23
24
if USE_LIBMTP
25
SUBDIRS += mtp
26
endif
(-)a/monitor/gphoto2/ggphoto2volumemonitor.c (+7 lines)
Lines 201-206 gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean Link Here
201
	return;
201
	return;
202
      }
202
      }
203
#endif /* HAVE_AFC */
203
#endif /* HAVE_AFC */
204
#ifdef HAVE_LIBMTP
205
    if (g_udev_device_get_property_as_boolean (device, "ID_MTP_DEVICE"))
206
      {
207
	/* g_debug ("ignoring device, is MTP"); */
208
	return;
209
      }
210
#endif /* HAVE_LIBMTP */
204
211
205
    usb_bus_num = g_udev_device_get_property (device, "BUSNUM");
212
    usb_bus_num = g_udev_device_get_property (device, "BUSNUM");
206
    if (usb_bus_num == NULL) {
213
    if (usb_bus_num == NULL) {
(-)a/monitor/mtp/Makefile.am (+51 lines)
Line 0 Link Here
1
2
NULL =
3
4
libexec_PROGRAMS = gvfs-mtp-volume-monitor
5
6
gvfs_mtp_volume_monitor_SOURCES =
7
8
gvfs_mtp_volume_monitor_SOURCES +=			\
9
        mtp-volume-monitor-daemon.c \
10
	gmtpvolume.c	gmtpvolume.h	\
11
	gmtpvolumemonitor.c	gmtpvolumemonitor.h	\
12
	$(NULL)
13
14
gvfs_mtp_volume_monitor_CFLAGS =		\
15
	-DG_LOG_DOMAIN=\"GVFS-MTP\"		\
16
	-I$(top_srcdir)/common                  \
17
	-I$(top_srcdir)/monitor/proxy           \
18
	$(GLIB_CFLAGS)                          \
19
	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\
20
	-DGVFS_LOCALEDIR=\""$(localedir)"\"	\
21
	-DG_UDEV_API_IS_SUBJECT_TO_CHANGE	\
22
	$(NULL)
23
24
gvfs_mtp_volume_monitor_CFLAGS += $(GUDEV_CFLAGS)
25
26
gvfs_mtp_volume_monitor_LDFLAGS =	\
27
	$(NULL)
28
29
gvfs_mtp_volume_monitor_LDADD  =		     			      \
30
	$(GLIB_LIBS)                                 			      \
31
	$(top_builddir)/common/libgvfscommon.la 			      \
32
	$(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \
33
	$(NULL)
34
35
gvfs_mtp_volume_monitor_LDADD += $(GUDEV_LIBS)
36
37
38
remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
39
remote_volume_monitors_DATA = mtp.monitor
40
41
servicedir       = $(datadir)/dbus-1/services
42
service_in_files = org.gtk.Private.MTPVolumeMonitor.service.in
43
service_DATA     = $(service_in_files:.service.in=.service)
44
45
$(service_DATA): $(service_in_files) Makefile
46
	$(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
47
48
clean-local:
49
	rm -f *~ *.loT $(service_DATA)
50
51
EXTRA_DIST = $(service_in_files) mtp.monitor
(-)a/monitor/mtp/gmtpvolume.c (+433 lines)
Line 0 Link Here
1
/* GIO - GLib Input, Output and Streaming Library
2
 *   Volume Monitor for MTP Backend
3
 *
4
 * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
#include <config.h>
23
24
#include <string.h>
25
#include <sys/wait.h>
26
#include <unistd.h>
27
28
#include <glib.h>
29
#include <glib/gi18n-lib.h>
30
#include <gio/gio.h>
31
32
#include "gmtpvolume.h"
33
34
G_LOCK_DEFINE_STATIC (mtp_volume);
35
36
struct _GMtpVolume {
37
  GObject parent;
38
39
  GVolumeMonitor *volume_monitor; /* owned by volume monitor */
40
41
  char *device_path;
42
  GUdevDevice *device;
43
44
  GFile *activation_root;
45
46
  char *name;
47
  char *icon;
48
};
49
50
static void g_mtp_volume_volume_iface_init (GVolumeIface *iface);
51
52
G_DEFINE_TYPE_EXTENDED (GMtpVolume, g_mtp_volume, G_TYPE_OBJECT, 0,
53
                        G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
54
                                               g_mtp_volume_volume_iface_init))
55
56
static void
57
g_mtp_volume_finalize (GObject *object)
58
{
59
  GMtpVolume *volume;
60
61
  volume = G_MTP_VOLUME (object);
62
63
  if (volume->device != NULL)
64
    g_object_unref (volume->device);
65
66
  if (volume->activation_root != NULL)
67
    g_object_unref (volume->activation_root);
68
69
  if (volume->volume_monitor != NULL)
70
    g_object_remove_weak_pointer (G_OBJECT (volume->volume_monitor), (gpointer) &(volume->volume_monitor));
71
72
  g_free (volume->name);
73
  g_free (volume->icon);
74
75
  if (G_OBJECT_CLASS (g_mtp_volume_parent_class)->finalize)
76
    (*G_OBJECT_CLASS (g_mtp_volume_parent_class)->finalize) (object);
77
}
78
79
static void
80
g_mtp_volume_class_init (GMtpVolumeClass *klass)
81
{
82
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
83
84
  gobject_class->finalize = g_mtp_volume_finalize;
85
}
86
87
static void
88
g_mtp_volume_init (GMtpVolume *mtp_volume)
89
{
90
}
91
92
static int hexdigit (char c)
93
{
94
  if (c >= 'a')
95
    return c - 'a' + 10;
96
  if (c >= 'A')
97
   return c - 'A' + 10;
98
  g_return_val_if_fail (c >= '0' && c <= '9', 0);
99
  return c - '0';
100
}
101
102
/* Do not free result, it's a static buffer */
103
static const char*
104
udev_decode_string (const char* encoded)
105
{
106
  static char decoded[4096];
107
  int len;
108
  const char* s;
109
110
  if (encoded == NULL)
111
    return NULL;
112
113
  for (len = 0, s = encoded; *s && len < sizeof (decoded) - 1; ++len, ++s) {
114
    /* need to check for NUL terminator in advance */
115
    if (s[0] == '\\' && s[1] == 'x' && s[2] >= '0' && s[3] >= '0') {
116
      decoded[len] = (hexdigit (s[2]) << 4) | hexdigit (s[3]);
117
      s += 3;
118
    } else {
119
      decoded[len] = *s;
120
    }
121
  }
122
  decoded[len] = '\0';
123
  return decoded;
124
}
125
126
static void
127
set_volume_name (GMtpVolume *v)
128
{
129
  const char *gphoto_name;
130
  const char *product = NULL;
131
  const char *vendor;
132
  const char *model;
133
134
  /* our preference: ID_MTP > ID_MEDIA_PLAYER_{VENDOR,PRODUCT} > product >
135
   * ID_{VENDOR,MODEL} */
136
137
  gphoto_name = g_udev_device_get_property (v->device, "ID_MTP");
138
  if (gphoto_name != NULL && strcmp (gphoto_name, "1") != 0) {
139
    v->name = g_strdup (gphoto_name);
140
    return;
141
  }
142
143
  vendor = g_udev_device_get_property (v->device, "ID_MEDIA_PLAYER_VENDOR");
144
  if (vendor == NULL)
145
    vendor = g_udev_device_get_property (v->device, "ID_VENDOR_ENC");
146
  model = g_udev_device_get_property (v->device, "ID_MEDIA_PLAYER_MODEL");
147
  if (model == NULL) {
148
    model = g_udev_device_get_property (v->device, "ID_MODEL_ENC");
149
    product = g_udev_device_get_sysfs_attr (v->device, "product");
150
  }
151
152
  v->name = NULL;
153
  if (product != NULL && strlen (product) > 0) {
154
    v->name = g_strdup (product);
155
  } else if (vendor == NULL) {
156
    if (model != NULL)
157
      v->name = g_strdup (udev_decode_string (model));
158
  } else {
159
    if (model != NULL) {
160
      /* we can't call udev_decode_string() twice in one g_strdup_printf(),
161
       * it returns a static buffer */
162
      gchar *temp = g_strdup_printf ("%s %s", vendor, model);
163
      v->name = g_strdup (udev_decode_string (temp));
164
      g_free (temp);
165
    } else {
166
      if (g_udev_device_has_property (v->device, "ID_MEDIA_PLAYER")) {
167
        /* Translators: %s is the device vendor */
168
        v->name = g_strdup_printf (_("%s Audio Player"), udev_decode_string (vendor));
169
      } else {
170
        /* Translators: %s is the device vendor */
171
        v->name = g_strdup_printf (_("%s Camera"), udev_decode_string (vendor));
172
      }
173
    }
174
  }
175
176
  if (v->name == NULL)
177
    v->name = g_strdup (_("Camera"));
178
}
179
180
static void
181
set_volume_icon (GMtpVolume *volume)
182
{
183
  if (g_udev_device_has_property (volume->device, "ID_MEDIA_PLAYER_ICON_NAME"))
184
    volume->icon = g_strdup (g_udev_device_get_property (volume->device, "ID_MEDIA_PLAYER_ICON_NAME"));
185
  else if (g_udev_device_has_property (volume->device, "ID_MEDIA_PLAYER"))
186
    volume->icon = g_strdup ("multimedia-player");
187
  else
188
    volume->icon = g_strdup ("camera-photo");
189
}
190
191
GMtpVolume *
192
g_mtp_volume_new (GVolumeMonitor   *volume_monitor,
193
                  GUdevDevice      *device,
194
                  GUdevClient      *gudev_client,
195
                  GFile            *activation_root)
196
{
197
  GMtpVolume *volume;
198
  const char *device_path;
199
200
  g_return_val_if_fail (volume_monitor != NULL, NULL);
201
  g_return_val_if_fail (device != NULL, NULL);
202
  g_return_val_if_fail (gudev_client != NULL, NULL);
203
  g_return_val_if_fail (activation_root != NULL, NULL);
204
205
  if (!g_udev_device_has_property (device, "ID_MTP_DEVICE"))
206
    return NULL;
207
  device_path = g_udev_device_get_device_file (device);
208
209
  volume = g_object_new (G_TYPE_MTP_VOLUME, NULL);
210
  volume->volume_monitor = volume_monitor;
211
  g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(volume->volume_monitor));
212
  volume->device_path = g_strdup (device_path);
213
  volume->device = g_object_ref (device);
214
  volume->activation_root = g_object_ref (activation_root);
215
216
  set_volume_name (volume);
217
  set_volume_icon (volume);
218
  /* we do not really need to listen for changes */
219
220
  return volume;
221
}
222
223
void
224
g_mtp_volume_removed (GMtpVolume *volume)
225
{
226
}
227
228
static GIcon *
229
g_mtp_volume_get_icon (GVolume *volume)
230
{
231
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
232
  GIcon *icon;
233
234
  G_LOCK (mtp_volume);
235
  icon = g_themed_icon_new (mtp_volume->icon);
236
  G_UNLOCK (mtp_volume);
237
  return icon;
238
}
239
240
static char *
241
g_mtp_volume_get_name (GVolume *volume)
242
{
243
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
244
  char *name;
245
246
  G_LOCK (mtp_volume);
247
  name = g_strdup (mtp_volume->name);
248
  G_UNLOCK (mtp_volume);
249
250
  return name;
251
}
252
253
static char *
254
g_mtp_volume_get_uuid (GVolume *volume)
255
{
256
  return NULL;
257
}
258
259
static gboolean
260
g_mtp_volume_can_mount (GVolume *volume)
261
{
262
  return TRUE;
263
}
264
265
static gboolean
266
g_mtp_volume_can_eject (GVolume *volume)
267
{
268
  return FALSE;
269
}
270
271
static gboolean
272
g_mtp_volume_should_automount (GVolume *volume)
273
{
274
  return TRUE;
275
}
276
277
static GDrive *
278
g_mtp_volume_get_drive (GVolume *volume)
279
{
280
  return NULL;
281
}
282
283
static GMount *
284
g_mtp_volume_get_mount (GVolume *volume)
285
{
286
  return NULL;
287
}
288
289
gboolean
290
g_mtp_volume_has_path (GMtpVolume  *volume,
291
                      const char  *sysfs_path)
292
{
293
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
294
  gboolean res;
295
296
  G_LOCK (mtp_volume);
297
  res = FALSE;
298
  if (mtp_volume->device != NULL)
299
    res = strcmp (g_udev_device_get_sysfs_path (mtp_volume->device), sysfs_path) == 0;
300
  G_UNLOCK (mtp_volume);
301
  return res;
302
}
303
304
typedef struct
305
{
306
  GMtpVolume *enclosing_volume;
307
  GAsyncReadyCallback  callback;
308
  gpointer user_data;
309
} ActivationMountOp;
310
311
static void
312
mount_callback (GObject *source_object,
313
                        GAsyncResult *res,
314
                        gpointer user_data)
315
{
316
  ActivationMountOp *data = user_data;
317
  data->callback (G_OBJECT (data->enclosing_volume), res, data->user_data);
318
  g_free (data);
319
}
320
321
static void
322
g_mtp_volume_mount (GVolume             *volume,
323
                        GMountMountFlags     flags,
324
                        GMountOperation     *mount_operation,
325
                        GCancellable        *cancellable,
326
                        GAsyncReadyCallback  callback,
327
                        gpointer             user_data)
328
{
329
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
330
  ActivationMountOp *data;
331
332
  /*g_warning ("mtp_volume_mount (can_mount=%d foreign=%p device_path=%s)",
333
              g_mtp_volume_can_mount (volume),
334
              mtp_volume->activation_root,
335
              mtp_volume->device_path);*/
336
337
  G_LOCK (mtp_volume);
338
339
  data = g_new0 (ActivationMountOp, 1);
340
  data->enclosing_volume = mtp_volume;
341
  data->callback = callback;
342
  data->user_data = user_data;
343
344
  g_file_mount_enclosing_volume (mtp_volume->activation_root,
345
                                 0,
346
                                 mount_operation,
347
                                 cancellable,
348
                                 mount_callback,
349
                                 data);
350
351
  G_UNLOCK (mtp_volume);
352
}
353
354
static gboolean
355
g_mtp_volume_mount_finish (GVolume       *volume,
356
                           GAsyncResult  *result,
357
                           GError       **error)
358
{
359
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
360
  gboolean res;
361
362
  G_LOCK (mtp_volume);
363
  res = g_file_mount_enclosing_volume_finish (mtp_volume->activation_root, result, error);
364
  G_UNLOCK (mtp_volume);
365
366
  return res;
367
}
368
369
static char *
370
g_mtp_volume_get_identifier (GVolume    *volume,
371
                             const char *kind)
372
{
373
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
374
  char *id;
375
376
  G_LOCK (mtp_volume);
377
  id = NULL;
378
  if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
379
    id = g_strdup (mtp_volume->device_path);
380
  G_UNLOCK (mtp_volume);
381
382
  return id;
383
}
384
385
static char **
386
g_mtp_volume_enumerate_identifiers (GVolume *volume)
387
{
388
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
389
  GPtrArray *res;
390
391
  G_LOCK (mtp_volume);
392
393
  res = g_ptr_array_new ();
394
395
  if (mtp_volume->device_path && *mtp_volume->device_path != 0)
396
    g_ptr_array_add (res,
397
                     g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE));
398
399
  /* Null-terminate */
400
  g_ptr_array_add (res, NULL);
401
402
  G_UNLOCK (mtp_volume);
403
404
  return (char **)g_ptr_array_free (res, FALSE);
405
}
406
407
static GFile *
408
g_mtp_volume_get_activation_root (GVolume *volume)
409
{
410
  GMtpVolume *mtp_volume = G_MTP_VOLUME (volume);
411
412
  return g_object_ref (mtp_volume->activation_root);
413
}
414
415
static void
416
g_mtp_volume_volume_iface_init (GVolumeIface *iface)
417
{
418
  iface->get_name = g_mtp_volume_get_name;
419
  iface->get_icon = g_mtp_volume_get_icon;
420
  iface->get_uuid = g_mtp_volume_get_uuid;
421
  iface->get_drive = g_mtp_volume_get_drive;
422
  iface->get_mount = g_mtp_volume_get_mount;
423
  iface->can_mount = g_mtp_volume_can_mount;
424
  iface->can_eject = g_mtp_volume_can_eject;
425
  iface->should_automount = g_mtp_volume_should_automount;
426
  iface->mount_fn = g_mtp_volume_mount;
427
  iface->mount_finish = g_mtp_volume_mount_finish;
428
  iface->eject = NULL;
429
  iface->eject_finish = NULL;
430
  iface->get_identifier = g_mtp_volume_get_identifier;
431
  iface->enumerate_identifiers = g_mtp_volume_enumerate_identifiers;
432
  iface->get_activation_root = g_mtp_volume_get_activation_root;
433
}
(-)a/monitor/mtp/gmtpvolume.h (+59 lines)
Line 0 Link Here
1
/* GIO - GLib Input, Output and Streaming Library
2
 *   Volume Monitor for MTP Backend
3
 *
4
 * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
#ifndef __G_MTP_VOLUME_H__
23
#define __G_MTP_VOLUME_H__
24
25
#include <glib-object.h>
26
#include <gio/gio.h>
27
28
#include <gudev/gudev.h>
29
#include "gmtpvolumemonitor.h"
30
31
G_BEGIN_DECLS
32
33
#define G_TYPE_MTP_VOLUME        (g_mtp_volume_get_type ())
34
#define G_MTP_VOLUME(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MTP_VOLUME, GMtpVolume))
35
#define G_MTP_VOLUME_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MTP_VOLUME, GMtpVolumeClass))
36
#define G_IS_MTP_VOLUME(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MTP_VOLUME))
37
#define G_IS_MTP_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MTP_VOLUME))
38
39
typedef struct _GMtpVolumeClass GMtpVolumeClass;
40
41
struct _GMtpVolumeClass {
42
   GObjectClass parent_class;
43
};
44
45
GType g_mtp_volume_get_type (void) G_GNUC_CONST;
46
47
GMtpVolume *g_mtp_volume_new      (GVolumeMonitor *volume_monitor,
48
                                   GUdevDevice    *device,
49
                                   GUdevClient    *gudev_client,
50
                                   GFile          *activation_root);
51
52
gboolean    g_mtp_volume_has_path (GMtpVolume     *volume,
53
                                   const char     *path);
54
55
void        g_mtp_volume_removed  (GMtpVolume     *volume);
56
57
G_END_DECLS
58
59
#endif /* __G_MTP_VOLUME_H__ */
(-)a/monitor/mtp/gmtpvolumemonitor.c (+329 lines)
Line 0 Link Here
1
/* GIO - GLib Input, Output and Streaming Library
2
 *   Volume Monitor for MTP Backend
3
 *
4
 * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
#include <config.h>
23
24
#include <limits.h>
25
#include <string.h>
26
#include <stdlib.h>
27
28
#include <glib.h>
29
#include <glib/gi18n-lib.h>
30
#include <gio/gio.h>
31
32
#include "gmtpvolumemonitor.h"
33
#include "gmtpvolume.h"
34
35
#include <gio/gunixmounts.h>
36
37
G_LOCK_DEFINE_STATIC(vm_lock);
38
39
static GMtpVolumeMonitor *the_volume_monitor = NULL;
40
41
struct _GMtpVolumeMonitor {
42
  GNativeVolumeMonitor parent;
43
44
  GUnixMountMonitor *mount_monitor;
45
46
  GUdevClient *gudev_client;
47
48
  GList *last_devices;
49
50
  GList *device_volumes;
51
};
52
53
static void on_uevent (GUdevClient *client, 
54
                       gchar *action,
55
                       GUdevDevice *device,
56
                       gpointer user_data);
57
58
G_DEFINE_TYPE (GMtpVolumeMonitor, g_mtp_volume_monitor, G_TYPE_VOLUME_MONITOR)
59
60
static void
61
list_free (GList *objects)
62
{
63
  g_list_foreach (objects, (GFunc)g_object_unref, NULL);
64
  g_list_free (objects);
65
}
66
67
static void
68
g_mtp_volume_monitor_dispose (GObject *object)
69
{
70
  G_LOCK (vm_lock);
71
  the_volume_monitor = NULL;
72
  G_UNLOCK (vm_lock);
73
74
  if (G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->dispose)
75
    (*G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->dispose) (object);
76
}
77
78
static void
79
g_mtp_volume_monitor_finalize (GObject *object)
80
{
81
  GMtpVolumeMonitor *monitor;
82
83
  monitor = G_MTP_VOLUME_MONITOR (object);
84
85
  g_signal_handlers_disconnect_by_func (monitor->gudev_client, on_uevent, monitor);
86
87
  g_object_unref (monitor->gudev_client);
88
89
  list_free (monitor->last_devices);
90
  list_free (monitor->device_volumes);
91
92
  if (G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->finalize)
93
    (*G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->finalize) (object);
94
}
95
96
static GList *
97
get_mounts (GVolumeMonitor *volume_monitor)
98
{
99
  return NULL;
100
}
101
102
static GList *
103
get_volumes (GVolumeMonitor *volume_monitor)
104
{
105
  GMtpVolumeMonitor *monitor;
106
  GList *l;
107
108
  monitor = G_MTP_VOLUME_MONITOR (volume_monitor);
109
110
  G_LOCK (vm_lock);
111
112
  l = g_list_copy (monitor->device_volumes);
113
  g_list_foreach (l, (GFunc)g_object_ref, NULL);
114
115
  G_UNLOCK (vm_lock);
116
117
  return l;
118
}
119
120
static GList *
121
get_connected_drives (GVolumeMonitor *volume_monitor)
122
{
123
  return NULL;
124
}
125
126
static GVolume *
127
get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
128
{
129
  return NULL;
130
}
131
132
static GMount *
133
get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
134
{
135
  return NULL;
136
}
137
138
static void
139
gudev_add_device (GMtpVolumeMonitor *monitor, GUdevDevice *device, gboolean do_emit)
140
{
141
  GMtpVolume *volume;
142
  const char *usb_bus_num, *usb_device_num, *uri;
143
  GFile *activation_mount_root;
144
145
  usb_bus_num = g_udev_device_get_property (device, "BUSNUM");
146
  if (usb_bus_num == NULL) {
147
    g_warning ("device %s has no BUSNUM property, ignoring", g_udev_device_get_device_file (device));
148
    return;
149
  }
150
151
  usb_device_num = g_udev_device_get_property (device, "DEVNUM");
152
  if (usb_device_num == NULL) {
153
    g_warning ("device %s has no DEVNUM property, ignoring", g_udev_device_get_device_file (device));
154
    return;
155
  }
156
157
  g_debug ("gudev_add_device: device %s (bus: %i, device: %i)",
158
           g_udev_device_get_device_file (device),
159
           usb_bus_num, usb_device_num);
160
161
  uri = g_strdup_printf ("mtp://[usb:%s,%s]", usb_bus_num, usb_device_num);
162
  activation_mount_root = g_file_new_for_uri (uri);
163
  g_free (uri);
164
165
  volume = g_mtp_volume_new (G_VOLUME_MONITOR (monitor),
166
                             device,
167
                             monitor->gudev_client,
168
                             activation_mount_root);
169
  if (volume != NULL) {
170
    monitor->device_volumes = g_list_prepend (monitor->device_volumes, volume);
171
    if (do_emit)
172
      g_signal_emit_by_name (monitor, "volume_added", volume);
173
  }
174
175
  if (activation_mount_root != NULL)
176
    g_object_unref (activation_mount_root);
177
}
178
179
static void
180
gudev_remove_device (GMtpVolumeMonitor *monitor, GUdevDevice *device)
181
{
182
  GList *l, *ll;
183
  const gchar* sysfs_path;
184
185
  sysfs_path = g_udev_device_get_sysfs_path (device);
186
187
  g_debug ("gudev_remove_device: %s", g_udev_device_get_device_file (device));
188
189
  for (l = monitor->device_volumes; l != NULL; l = ll) {
190
    GMtpVolume *volume = G_MTP_VOLUME (l->data);
191
192
    ll = l->next;
193
194
    if (g_mtp_volume_has_path (volume, sysfs_path)) {
195
      g_debug ("gudev_remove_device: found volume %s, deleting", sysfs_path);
196
      g_signal_emit_by_name (monitor, "volume_removed", volume);
197
      g_signal_emit_by_name (volume, "removed");
198
      g_mtp_volume_removed (volume);
199
      monitor->device_volumes = g_list_remove (monitor->device_volumes, volume);
200
      g_object_unref (volume);
201
    }
202
  }
203
}
204
205
static void
206
on_uevent (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data)
207
{
208
  GMtpVolumeMonitor *monitor = G_MTP_VOLUME_MONITOR (user_data);
209
210
  g_debug ("on_uevent: action=%s, device=%s", action, g_udev_device_get_device_file(device));
211
212
  /* filter out uninteresting events */
213
  if (!g_udev_device_has_property (device, "ID_MTP_DEVICE"))
214
    {
215
      g_debug ("on_uevent: discarding, not ID_MTP");
216
      return;
217
    }
218
219
  if (strcmp (action, "add") == 0)
220
     gudev_add_device (monitor, device, TRUE); 
221
  else if (strcmp (action, "remove") == 0)
222
     gudev_remove_device (monitor, device); 
223
}
224
225
static void
226
gudev_coldplug_devices (GMtpVolumeMonitor *monitor)
227
{
228
  GList *usb_devices, *l;
229
230
  usb_devices = g_udev_client_query_by_subsystem (monitor->gudev_client, "usb");
231
  for (l = usb_devices; l != NULL; l = l->next) {
232
    GUdevDevice *d = l->data;
233
    if (g_udev_device_has_property (d, "ID_MTP_DEVICE"))
234
        gudev_add_device (monitor, d, FALSE);
235
  }
236
}
237
238
static GObject *
239
g_mtp_volume_monitor_constructor (GType                  type,
240
                                  guint                  n_construct_properties,
241
                                  GObjectConstructParam *construct_properties)
242
{
243
  GObject *object;
244
  GMtpVolumeMonitor *monitor;
245
  GMtpVolumeMonitorClass *klass;
246
  GObjectClass *parent_class;
247
248
  G_LOCK (vm_lock);
249
  if (the_volume_monitor != NULL) {
250
    object = g_object_ref (the_volume_monitor);
251
    G_UNLOCK (vm_lock);
252
    return object;
253
  }
254
  G_UNLOCK (vm_lock);
255
256
  /*g_warning ("creating vm singleton");*/
257
258
  object = NULL;
259
260
  /* Invoke parent constructor. */
261
  klass = G_MTP_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_MTP_VOLUME_MONITOR));
262
  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
263
  object = parent_class->constructor (type,
264
                                      n_construct_properties,
265
                                      construct_properties);
266
267
  monitor = G_MTP_VOLUME_MONITOR (object);
268
269
  const char *subsystems[] = { "usb", NULL };
270
  monitor->gudev_client = g_udev_client_new (subsystems);
271
272
  g_signal_connect (monitor->gudev_client, 
273
                    "uevent", G_CALLBACK (on_uevent), 
274
                    monitor);
275
276
  gudev_coldplug_devices (monitor);
277
278
  G_LOCK (vm_lock);
279
  the_volume_monitor = monitor;
280
  G_UNLOCK (vm_lock);
281
282
  return object;
283
}
284
285
static void
286
g_mtp_volume_monitor_init (GMtpVolumeMonitor *monitor)
287
{
288
}
289
290
static gboolean
291
is_supported (void)
292
{
293
  /* Today's Linux desktops pretty much need udev to have anything working, so
294
   * assume it's there */
295
  return TRUE;
296
}
297
298
static void
299
g_mtp_volume_monitor_class_init (GMtpVolumeMonitorClass *klass)
300
{
301
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
302
  GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
303
304
  gobject_class->constructor = g_mtp_volume_monitor_constructor;
305
  gobject_class->finalize = g_mtp_volume_monitor_finalize;
306
  gobject_class->dispose = g_mtp_volume_monitor_dispose;
307
308
  monitor_class->get_mounts = get_mounts;
309
  monitor_class->get_volumes = get_volumes;
310
  monitor_class->get_connected_drives = get_connected_drives;
311
  monitor_class->get_volume_for_uuid = get_volume_for_uuid;
312
  monitor_class->get_mount_for_uuid = get_mount_for_uuid;
313
  monitor_class->is_supported = is_supported;
314
}
315
316
/**
317
 * g_mtp_volume_monitor_new:
318
 *
319
 * Returns:  a new #GVolumeMonitor.
320
 **/
321
GVolumeMonitor *
322
g_mtp_volume_monitor_new (void)
323
{
324
  GMtpVolumeMonitor *monitor;
325
326
  monitor = g_object_new (G_TYPE_MTP_VOLUME_MONITOR, NULL);
327
328
  return G_VOLUME_MONITOR (monitor);
329
}
(-)a/monitor/mtp/gmtpvolumemonitor.h (+53 lines)
Line 0 Link Here
1
/* GIO - GLib Input, Output and Streaming Library
2
 *   Volume Monitor for MTP Backend
3
 *
4
 * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
#ifndef __G_MTP_VOLUME_MONITOR_H__
23
#define __G_MTP_VOLUME_MONITOR_H__
24
25
#include <glib-object.h>
26
#include <gio/gio.h>
27
28
G_BEGIN_DECLS
29
30
#define G_TYPE_MTP_VOLUME_MONITOR        (g_mtp_volume_monitor_get_type ())
31
#define G_MTP_VOLUME_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MTP_VOLUME_MONITOR, GMtpVolumeMonitor))
32
#define G_MTP_VOLUME_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MTP_VOLUME_MONITOR, GMtpVolumeMonitorClass))
33
#define G_IS_MTP_VOLUME_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MTP_VOLUME_MONITOR))
34
#define G_IS_MTP_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MTP_VOLUME_MONITOR))
35
36
typedef struct _GMtpVolumeMonitor GMtpVolumeMonitor;
37
typedef struct _GMtpVolumeMonitorClass GMtpVolumeMonitorClass;
38
39
/* Forward definitions */
40
typedef struct _GMtpVolume GMtpVolume;
41
42
struct _GMtpVolumeMonitorClass {
43
  GVolumeMonitorClass parent_class;
44
};
45
46
GType g_mtp_volume_monitor_get_type (void) G_GNUC_CONST;
47
48
GVolumeMonitor *g_mtp_volume_monitor_new          (void);
49
void            g_mtp_volume_monitor_force_update (GMtpVolumeMonitor *monitor);
50
51
G_END_DECLS
52
53
#endif /* __G_MTP_VOLUME_MONITOR_H__ */
(-)a/monitor/mtp/mtp-volume-monitor-daemon.c (+36 lines)
Line 0 Link Here
1
/* GIO - GLib Input, Output and Streaming Library
2
 *   Volume Monitor for MTP Backend
3
 *
4
 * Copyright (C) 2012 Philip Langdale <philipl@overt.org>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19
 * Boston, MA 02111-1307, USA.
20
 */
21
22
#include <config.h>
23
24
#include <gvfsproxyvolumemonitordaemon.h>
25
26
#include "gmtpvolumemonitor.h"
27
28
int
29
main (int argc, char *argv[])
30
{
31
  g_vfs_proxy_volume_monitor_daemon_init ();
32
  return g_vfs_proxy_volume_monitor_daemon_main (argc,
33
                                                 argv,
34
                                                 "org.gtk.Private.MTPVolumeMonitor",
35
                                                 G_TYPE_MTP_VOLUME_MONITOR);
36
}
(-)a/monitor/mtp/mtp.monitor (+4 lines)
Line 0 Link Here
1
[RemoteVolumeMonitor]
2
Name=GProxyVolumeMonitorMTP
3
DBusName=org.gtk.Private.MTPVolumeMonitor
4
IsNative=false
(-)a/monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in (+3 lines)
Line 0 Link Here
1
[D-BUS Service]
2
Name=org.gtk.Private.MTPVolumeMonitor
3
Exec=@libexecdir@/gvfs-mtp-volume-monitor
(-)a/programs/gvfs-ls.c (-3 / +9 lines)
Lines 78-84 type_to_string (GFileType type) Link Here
78
static void
78
static void
79
show_info (GFileInfo *info)
79
show_info (GFileInfo *info)
80
{
80
{
81
  const char *name, *type;
81
  const char *name, *display_name, *type;
82
  goffset size;
82
  goffset size;
83
  char **attributes;
83
  char **attributes;
84
  int i;
84
  int i;
Lines 91-102 show_info (GFileInfo *info) Link Here
91
  if (name == NULL)
91
  if (name == NULL)
92
    name = "";
92
    name = "";
93
93
94
  display_name = g_file_info_get_display_name (info);
95
  if (display_name == NULL)
96
    display_name = name;
97
94
  size = g_file_info_get_size (info);
98
  size = g_file_info_get_size (info);
95
  type = type_to_string (g_file_info_get_file_type (info));
99
  type = type_to_string (g_file_info_get_file_type (info));
96
  if (show_long)
100
  if (show_long)
97
    g_print ("%s\t%"G_GUINT64_FORMAT"\t(%s)", name, (guint64)size, type);
101
    g_print ("%s\t%s\t\t\t%"G_GUINT64_FORMAT"\t(%s)", name, display_name, (guint64)size, type);
98
  else
102
  else
99
    g_print ("%s", name);
103
    g_print ("%s", display_name);
100
104
101
  first_attr = TRUE;
105
  first_attr = TRUE;
102
  attributes = g_file_info_list_attributes (info, NULL);
106
  attributes = g_file_info_list_attributes (info, NULL);
Lines 106-111 show_info (GFileInfo *info) Link Here
106
110
107
      if (!show_long ||
111
      if (!show_long ||
108
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_NAME) == 0 ||
112
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_NAME) == 0 ||
113
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) == 0 ||
109
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_SIZE) == 0 ||
114
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_SIZE) == 0 ||
110
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_TYPE) == 0 ||
115
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_TYPE) == 0 ||
111
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) == 0)
116
	  strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) == 0)
Lines 426-431 main (int argc, char *argv[]) Link Here
426
    }
431
    }
427
432
428
  attributes = g_strconcat (G_FILE_ATTRIBUTE_STANDARD_NAME ","
433
  attributes = g_strconcat (G_FILE_ATTRIBUTE_STANDARD_NAME ","
434
                            G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
429
			    G_FILE_ATTRIBUTE_STANDARD_TYPE ","
435
			    G_FILE_ATTRIBUTE_STANDARD_TYPE ","
430
			    G_FILE_ATTRIBUTE_STANDARD_SIZE ","
436
			    G_FILE_ATTRIBUTE_STANDARD_SIZE ","
431
			    G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
437
			    G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
(-)a/gvfs/po/POTFILES.in (+1 lines)
Lines 32-37 daemon/gvfsbackendftp.c Link Here
32
daemon/gvfsbackendgphoto2.c
32
daemon/gvfsbackendgphoto2.c
33
daemon/gvfsbackendhttp.c
33
daemon/gvfsbackendhttp.c
34
daemon/gvfsbackendlocaltest.c
34
daemon/gvfsbackendlocaltest.c
35
daemon/gvfsbackendmtp.c
35
daemon/gvfsbackendnetwork.c
36
daemon/gvfsbackendnetwork.c
36
daemon/gvfsbackendobexftp.c
37
daemon/gvfsbackendobexftp.c
37
daemon/gvfsbackendrecent.c
38
daemon/gvfsbackendrecent.c

Return to bug 27989