hythmbox-2.98/plugins/mtpdevice/rb-mtp-source.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rb-mtp-source.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  *  Copyright (C) 2006 Peter Grundstrรถm  <pete@openfestis.org>
   3  *
   4  *  This program is free software; you can redistribute it and/or modify
   5  *  it under the terms of the GNU General Public License as published by
   6  *  the Free Software Foundation; either version 2 of the License, or
   7  *  (at your option) any later version.
   8  *
   9  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
  10  *  GStreamer plugins to be used and distributed together with GStreamer
  11  *  and Rhythmbox. This permission is above and beyond the permissions granted
  12  *  by the GPL license by which Rhythmbox is covered. If you modify this code
  13  *  you may extend this exception to your version of the code, but you are not
  14  *  obligated to do so. If you do not wish to do so, delete this exception
  15  *  statement from your version.
  16  *
  17  *  This program is distributed in the hope that it will be useful,
  18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20  *  GNU General Public License for more details.
  21  *
  22  *  You should have received a copy of the GNU General Public License
  23  *  along with this program; if not, write to the Free Software
  24  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
  25  *
  26  */
  27 
  28 #include <config.h>
  29 
  30 #include <string.h>
  31 #include <gtk/gtk.h>
  32 #include <glib/gi18n.h>
  33 #include <gst/gst.h>
  34 
  35 #if defined(HAVE_GUDEV)
  36 #define G_UDEV_API_IS_SUBJECT_TO_CHANGE
  37 #include <gudev/gudev.h>
  38 #endif
  39 
  40 #include "rhythmdb.h"
  41 #include "rb-debug.h"
  42 #include "rb-file-helpers.h"
  43 #include "rb-builder-helpers.h"
  44 #include "rb-removable-media-manager.h"
  45 #include "rb-static-playlist-source.h"
  46 #include "rb-transfer-target.h"
  47 #include "rb-device-source.h"
  48 #include "rb-util.h"
  49 #include "rb-refstring.h"
  50 #include "rhythmdb.h"
  51 #include "rb-dialog.h"
  52 #include "rb-shell-player.h"
  53 #include "rb-player.h"
  54 #include "rb-encoder.h"
  55 #include "rb-sync-settings.h"
  56 #include "rb-gst-media-types.h"
  57 #include "rb-ext-db.h"
  58 
  59 #include "rb-mtp-source.h"
  60 #include "rb-mtp-thread.h"
  61 
  62 static void rb_mtp_source_constructed (GObject *object);
  63 static void rb_mtp_source_dispose (GObject *object);
  64 static void rb_mtp_source_finalize (GObject *object);
  65 static void rb_mtp_device_source_init (RBDeviceSourceInterface *interface);
  66 static void rb_mtp_source_transfer_target_init (RBTransferTargetInterface *interface);
  67 
  68 static void rb_mtp_source_set_property (GObject *object,
  69 			                guint prop_id,
  70 			                const GValue *value,
  71 			                GParamSpec *pspec);
  72 static void rb_mtp_source_get_property (GObject *object,
  73 			                guint prop_id,
  74 			                GValue *value,
  75 			                GParamSpec *pspec);
  76 
  77 static void impl_delete (RBSource *asource);
  78 static RBTrackTransferBatch *impl_paste (RBSource *asource, GList *entries);
  79 static gboolean impl_show_popup (RBDisplayPage *page);
  80 static gboolean impl_uri_is_source (RBSource *asource, const char *uri);
  81 
  82 static gboolean impl_track_added (RBTransferTarget *target,
  83 				  RhythmDBEntry *entry,
  84 				  const char *dest,
  85 				  guint64 filesize,
  86 				  const char *media_type);
  87 static gboolean impl_track_add_error (RBTransferTarget *target,
  88 				      RhythmDBEntry *entry,
  89 				      const char *dest,
  90 				      GError *error);
  91 static char *impl_build_dest_uri (RBTransferTarget *target,
  92 				  RhythmDBEntry *entry,
  93 				  const char *media_type,
  94 				  const char *extension);
  95 
  96 static void impl_eject (RBDeviceSource *source);
  97 static gboolean impl_can_eject (RBDeviceSource *source);
  98 
  99 static void impl_selected (RBDisplayPage *page);
 100 static void mtp_device_open_cb (LIBMTP_mtpdevice_t *device, RBMtpSource *source);
 101 static void mtp_tracklist_cb (LIBMTP_track_t *tracks, RBMtpSource *source);
 102 static RhythmDB * get_db_for_source (RBMtpSource *source);
 103 
 104 static void		impl_get_entries	(RBMediaPlayerSource *source, const char *category, GHashTable *map);
 105 static guint64		impl_get_capacity	(RBMediaPlayerSource *source);
 106 static guint64		impl_get_free_space	(RBMediaPlayerSource *source);
 107 static void		impl_delete_entries	(RBMediaPlayerSource *source,
 108 						 GList *entries,
 109 						 RBMediaPlayerSourceDeleteCallback callback,
 110 						 gpointer callback_data,
 111 						 GDestroyNotify destroy_data);
 112 static void		impl_show_properties	(RBMediaPlayerSource *source, GtkWidget *info_box, GtkWidget *notebook);
 113 
 114 static void prepare_player_source_cb (RBPlayer *player,
 115 				      const char *stream_uri,
 116 				      GstElement *src,
 117 				      RBMtpSource *source);
 118 static void prepare_encoder_source_cb (RBEncoderFactory *factory,
 119 				       const char *stream_uri,
 120 				       GObject *src,
 121 				       RBMtpSource *source);
 122 static void prepare_encoder_sink_cb (RBEncoderFactory *factory,
 123 				     const char *stream_uri,
 124 				     GObject *sink,
 125 				     RBMtpSource *source);
 126 #if defined(HAVE_GUDEV)
 127 static GMount *find_mount_for_device (GUdevDevice *device);
 128 #endif
 129 
 130 typedef struct
 131 {
 132 	gboolean tried_open;
 133 	RBMtpThread *device_thread;
 134 	LIBMTP_raw_device_t raw_device;
 135 	GHashTable *entry_map;
 136 	GHashTable *track_transfer_map;
 137 #if defined(HAVE_GUDEV)
 138 	GUdevDevice *udev_device;
 139 	GVolume *remount_volume;
 140 #else
 141 	char *udi;
 142 #endif
 143 	uint16_t supported_types[LIBMTP_FILETYPE_UNKNOWN+1];
 144 	gboolean album_art_supported;
 145 	RBExtDB *art_store;
 146 
 147 	/* device information */
 148 	char *manufacturer;
 149 	char *serial;
 150 	char *device_version;
 151 	char *model_name;
 152 	guint64 capacity;
 153 	guint64 free_space;		/* updated by callbacks */
 154 
 155 } RBMtpSourcePrivate;
 156 
 157 G_DEFINE_DYNAMIC_TYPE_EXTENDED(
 158 	RBMtpSource,
 159 	rb_mtp_source,
 160 	RB_TYPE_MEDIA_PLAYER_SOURCE,
 161 	0,
 162 	G_IMPLEMENT_INTERFACE_DYNAMIC (RB_TYPE_DEVICE_SOURCE, rb_mtp_device_source_init)
 163 	G_IMPLEMENT_INTERFACE_DYNAMIC (RB_TYPE_TRANSFER_TARGET, rb_mtp_source_transfer_target_init))
 164 
 165 #define MTP_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_MTP_SOURCE, RBMtpSourcePrivate))
 166 
 167 enum
 168 {
 169 	PROP_0,
 170 	PROP_RAW_DEVICE,
 171 	PROP_UDEV_DEVICE,
 172 	PROP_UDI,
 173 	PROP_DEVICE_SERIAL
 174 };
 175 
 176 static void
 177 rb_mtp_source_class_init (RBMtpSourceClass *klass)
 178 {
 179 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 180 	RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
 181 	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
 182 	RBMediaPlayerSourceClass *mps_class = RB_MEDIA_PLAYER_SOURCE_CLASS (klass);
 183 
 184 	object_class->constructed = rb_mtp_source_constructed;
 185 	object_class->dispose = rb_mtp_source_dispose;
 186 	object_class->finalize = rb_mtp_source_finalize;
 187 	object_class->set_property = rb_mtp_source_set_property;
 188 	object_class->get_property = rb_mtp_source_get_property;
 189 
 190 	page_class->show_popup = impl_show_popup;
 191 	page_class->selected = impl_selected;
 192 
 193 	source_class->impl_can_rename = (RBSourceFeatureFunc) rb_true_function;
 194 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
 195 	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_true_function;
 196 	source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
 197 	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
 198 	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
 199 	source_class->impl_delete = impl_delete;
 200 	source_class->impl_paste = impl_paste;
 201 	source_class->impl_uri_is_source = impl_uri_is_source;
 202 
 203 	mps_class->impl_get_entries = impl_get_entries;
 204 	mps_class->impl_get_capacity = impl_get_capacity;
 205 	mps_class->impl_get_free_space = impl_get_free_space;
 206 	mps_class->impl_delete_entries = impl_delete_entries;
 207 	mps_class->impl_show_properties = impl_show_properties;
 208 
 209 	g_object_class_install_property (object_class,
 210 					 PROP_RAW_DEVICE,
 211 					 g_param_spec_pointer ("raw-device",
 212 							       "raw-device",
 213 							       "libmtp raw device",
 214 							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 215 #if defined(HAVE_GUDEV)
 216 	g_object_class_install_property (object_class,
 217 					 PROP_UDEV_DEVICE,
 218 					 g_param_spec_object ("udev-device",
 219 							      "udev-device",
 220 							      "GUdev device object",
 221 							      G_UDEV_TYPE_DEVICE,
 222 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 223 #else
 224 	g_object_class_install_property (object_class,
 225 					 PROP_UDI,
 226 					 g_param_spec_string ("udi",
 227 						 	      "udi",
 228 							      "udi",
 229 							      NULL,
 230 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 231 #endif
 232 	g_object_class_override_property (object_class, PROP_DEVICE_SERIAL, "serial");
 233 
 234 	g_type_class_add_private (klass, sizeof (RBMtpSourcePrivate));
 235 }
 236 
 237 static void
 238 rb_mtp_device_source_init (RBDeviceSourceInterface *interface)
 239 {
 240 	interface->can_eject = impl_can_eject;
 241 	interface->eject = impl_eject;
 242 }
 243 
 244 static void
 245 rb_mtp_source_transfer_target_init (RBTransferTargetInterface *interface)
 246 {
 247 	interface->build_dest_uri = impl_build_dest_uri;
 248 	interface->track_added = impl_track_added;
 249 	interface->track_add_error = impl_track_add_error;
 250 }
 251 
 252 static void
 253 rb_mtp_source_class_finalize (RBMtpSourceClass *klass)
 254 {
 255 }
 256 
 257 static void
 258 rb_mtp_source_name_changed_cb (RBMtpSource *source,
 259 			       GParamSpec *spec,
 260 			       gpointer data)
 261 {
 262 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 263 	char *name = NULL;
 264 
 265 	g_object_get (source, "name", &name, NULL);
 266 	rb_mtp_thread_set_device_name (priv->device_thread, name);
 267 	g_free (name);
 268 }
 269 
 270 static void
 271 rb_mtp_source_init (RBMtpSource *source)
 272 {
 273 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 274 
 275 	priv->entry_map = g_hash_table_new_full (g_direct_hash,
 276 						 g_direct_equal,
 277 						 NULL,
 278 						 (GDestroyNotify) LIBMTP_destroy_track_t);
 279 
 280 	priv->track_transfer_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 281 }
 282 
 283 
 284 static void
 285 open_device (RBMtpSource *source)
 286 {
 287 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 288 
 289 	rb_debug ("actually opening device");
 290 	priv->device_thread = rb_mtp_thread_new ();
 291 	rb_mtp_thread_open_device (priv->device_thread,
 292 				   &priv->raw_device,
 293 				   (RBMtpOpenCallback)mtp_device_open_cb,
 294 				   g_object_ref (source),
 295 				   g_object_unref);
 296 }
 297 
 298 #if defined(HAVE_GUDEV)
 299 static void
 300 unmount_done_cb (GObject *object, GAsyncResult *result, gpointer psource)
 301 {
 302 	GMount *mount;
 303 	RBMtpSource *source;
 304 	gboolean ok;
 305 	GError *error = NULL;
 306 	RBMtpSourcePrivate *priv;
 307 
 308 	mount = G_MOUNT (object);
 309 	source = RB_MTP_SOURCE (psource);
 310 	priv = MTP_SOURCE_GET_PRIVATE (source);
 311 
 312 	ok = g_mount_unmount_with_operation_finish (mount, result, &error);
 313 	if (ok) {
 314 		rb_debug ("successfully unmounted mtp device");
 315 		priv->remount_volume = g_mount_get_volume (mount);
 316 
 317 		open_device (source);
 318 	} else {
 319 		g_warning ("Unable to unmount MTP device: %s", error->message);
 320 		g_error_free (error);
 321 	}
 322 
 323 	g_object_unref (mount);
 324 	g_object_unref (source);
 325 }
 326 
 327 #endif
 328 
 329 static gboolean
 330 ensure_loaded (RBMtpSource *source)
 331 {
 332 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 333 #if defined(HAVE_GUDEV)
 334 	GMount *mount;
 335 #endif
 336 	if (priv->tried_open) {
 337 		RBSourceLoadStatus status;
 338 		g_object_get (source, "load-status", &status, NULL);
 339 		return (status == RB_SOURCE_LOAD_STATUS_LOADED);
 340 	}
 341 	priv->tried_open = TRUE;
 342 
 343 	/* try to open the device.  if gvfs has mounted it, unmount it first */
 344 #if defined(HAVE_GUDEV)
 345 	mount = find_mount_for_device (priv->udev_device);
 346 	if (mount != NULL) {
 347 		rb_debug ("device is already mounted, waiting until activated");
 348 		g_mount_unmount_with_operation (mount,
 349 						G_MOUNT_UNMOUNT_NONE,
 350 						NULL,
 351 						NULL,
 352 						unmount_done_cb,
 353 						g_object_ref (source));
 354 		/* mount gets unreffed in callback */
 355 	} else {
 356 		rb_debug ("device isn't mounted");
 357 		open_device (source);
 358 	}
 359 #else
 360 	open_device (source);
 361 #endif
 362 	return FALSE;
 363 }
 364 
 365 static void
 366 impl_selected (RBDisplayPage *page)
 367 {
 368 	ensure_loaded (RB_MTP_SOURCE (page));
 369 }
 370 
 371 static void
 372 rb_mtp_source_constructed (GObject *object)
 373 {
 374 	RBMtpSource *source;
 375 	RBEntryView *tracks;
 376 	RBShell *shell;
 377 	RBShellPlayer *shell_player;
 378 	GObject *player_backend;
 379 	GtkIconTheme *theme;
 380 	GdkPixbuf *pixbuf;
 381 	gint size;
 382 
 383 	RB_CHAIN_GOBJECT_METHOD (rb_mtp_source_parent_class, constructed, object);
 384 	source = RB_MTP_SOURCE (object);
 385 
 386 	tracks = rb_source_get_entry_view (RB_SOURCE (source));
 387 	rb_entry_view_append_column (tracks, RB_ENTRY_VIEW_COL_RATING, FALSE);
 388 	rb_entry_view_append_column (tracks, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
 389 
 390 	/* the source element needs our cooperation */
 391 	g_object_get (source, "shell", &shell, NULL);
 392 	g_object_get (shell, "shell-player", &shell_player, NULL);
 393 	g_object_get (shell_player, "player", &player_backend, NULL);
 394 	g_object_unref (shell_player);
 395 
 396 	g_signal_connect_object (player_backend,
 397 				 "prepare-source",
 398 				 G_CALLBACK (prepare_player_source_cb),
 399 				 source, 0);
 400 
 401 	g_object_unref (player_backend);
 402 	g_object_unref (shell);
 403 
 404 	g_signal_connect_object (rb_encoder_factory_get (),
 405 				 "prepare-source",
 406 				 G_CALLBACK (prepare_encoder_source_cb),
 407 				 source, 0);
 408 	g_signal_connect_object (rb_encoder_factory_get (),
 409 				 "prepare-sink",
 410 				 G_CALLBACK (prepare_encoder_sink_cb),
 411 				 source, 0);
 412 
 413 	/* icon */
 414 	theme = gtk_icon_theme_get_default ();
 415 	gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
 416 	pixbuf = gtk_icon_theme_load_icon (theme, "multimedia-player", size, 0, NULL);
 417 
 418 	g_object_set (source, "pixbuf", pixbuf, NULL);
 419 	g_object_unref (pixbuf);
 420 
 421 }
 422 
 423 static void
 424 rb_mtp_source_set_property (GObject *object,
 425 			    guint prop_id,
 426 			    const GValue *value,
 427 			    GParamSpec *pspec)
 428 {
 429 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (object);
 430 	LIBMTP_raw_device_t *raw_device;
 431 
 432 	switch (prop_id) {
 433 	case PROP_RAW_DEVICE:
 434 		raw_device = g_value_get_pointer (value);
 435 		priv->raw_device = *raw_device;
 436 		break;
 437 #if defined(HAVE_GUDEV)
 438 	case PROP_UDEV_DEVICE:
 439 		priv->udev_device = g_value_dup_object (value);
 440 		break;
 441 #else
 442 	case PROP_UDI:
 443 		priv->udi = g_value_dup_string (value);
 444 		break;
 445 #endif
 446 	default:
 447 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 448 		break;
 449 	}
 450 }
 451 
 452 static void
 453 rb_mtp_source_get_property (GObject *object,
 454 			    guint prop_id,
 455 			    GValue *value,
 456 			    GParamSpec *pspec)
 457 {
 458 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (object);
 459 
 460 	switch (prop_id) {
 461 	case PROP_RAW_DEVICE:
 462 		g_value_set_pointer (value, &priv->raw_device);
 463 		break;
 464 #if defined(HAVE_GUDEV)
 465 	case PROP_UDEV_DEVICE:
 466 		g_value_set_object (value, priv->udev_device);
 467 		break;
 468 #else
 469 	case PROP_UDI:
 470 		g_value_set_string (value, priv->udi);
 471 		break;
 472 #endif
 473 	case PROP_DEVICE_SERIAL:
 474 		g_value_set_string (value, priv->serial);
 475 		break;
 476 	default:
 477 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 478 		break;
 479 	}
 480 }
 481 
 482 #if defined(HAVE_GUDEV)
 483 static void
 484 remount_done_cb (GObject *object, GAsyncResult *result, gpointer no)
 485 {
 486 	gboolean ok;
 487 	GError *error = NULL;
 488 
 489 	ok = g_volume_mount_finish (G_VOLUME (object), result, &error);
 490 	if (ok) {
 491 		rb_debug ("volume remounted successfully");
 492 	} else {
 493 		g_warning ("Unable to remount MTP device: %s", error->message);
 494 		g_error_free (error);
 495 	}
 496 	g_object_unref (object);
 497 }
 498 #endif
 499 
 500 static void
 501 rb_mtp_source_dispose (GObject *object)
 502 {
 503 	RBMtpSource *source = RB_MTP_SOURCE (object);
 504 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 505 	RhythmDBEntryType *entry_type;
 506 	RhythmDB *db;
 507 
 508 	if (priv->device_thread != NULL) {
 509 		g_object_unref (priv->device_thread);
 510 		priv->device_thread = NULL;
 511 	}
 512 
 513 #if defined(HAVE_GUDEV)
 514 	if (priv->remount_volume != NULL) {
 515 		rb_debug ("remounting gvfs volume for mtp device");
 516 		/* the callback will unref remount_volume */
 517 		g_volume_mount (priv->remount_volume,
 518 				G_MOUNT_MOUNT_NONE,
 519 				NULL,
 520 				NULL,
 521 				remount_done_cb,
 522 				NULL);
 523 		priv->remount_volume = NULL;
 524 	}
 525 #endif
 526 	if (priv->art_store != NULL) {
 527 		g_object_unref (priv->art_store);
 528 		priv->art_store = NULL;
 529 	}
 530 
 531 	db = get_db_for_source (source);
 532 
 533 	g_object_get (G_OBJECT (source), "entry-type", &entry_type, NULL);
 534 	rhythmdb_entry_delete_by_type (db, entry_type);
 535 	g_object_unref (entry_type);
 536 
 537 	rhythmdb_commit (db);
 538 	g_object_unref (db);
 539 
 540 	G_OBJECT_CLASS (rb_mtp_source_parent_class)->dispose (object);
 541 }
 542 
 543 static void
 544 rb_mtp_source_finalize (GObject *object)
 545 {
 546 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (object);
 547 
 548 	g_hash_table_destroy (priv->entry_map);
 549 	g_hash_table_destroy (priv->track_transfer_map);		/* probably need to destroy the tracks too.. */
 550 
 551 #if defined(HAVE_GUDEV)
 552 	if (priv->udev_device) {
 553 		g_object_unref (G_OBJECT (priv->udev_device));
 554 	}
 555 #else
 556 	g_free (priv->udi);
 557 #endif
 558 	g_free (priv->manufacturer);
 559 	g_free (priv->device_version);
 560 	g_free (priv->model_name);
 561 	g_free (priv->serial);
 562 
 563 	G_OBJECT_CLASS (rb_mtp_source_parent_class)->finalize (object);
 564 }
 565 
 566 RBSource *
 567 rb_mtp_source_new (RBShell *shell,
 568 		   GObject *plugin,
 569 #if defined(HAVE_GUDEV)
 570 		   GUdevDevice *udev_device,
 571 #else
 572 		   const char *udi,
 573 #endif
 574 		   LIBMTP_raw_device_t *device)
 575 {
 576 	RBMtpSource *source = NULL;
 577 	RhythmDBEntryType *entry_type;
 578 	RhythmDB *db = NULL;
 579 	GSettings *settings;
 580 	char *name = NULL;
 581 
 582 	g_object_get (shell, "db", &db, NULL);
 583 	name = g_strdup_printf ("MTP-%u-%d", device->bus_location, device->devnum);
 584 
 585 	entry_type = g_object_new (RHYTHMDB_TYPE_ENTRY_TYPE,
 586 				   "db", db,
 587 				   "name", name,
 588 				   "save-to-disk", FALSE,
 589 				   "category", RHYTHMDB_ENTRY_NORMAL,
 590 				   NULL);
 591 	g_free (name);
 592 	g_object_unref (db);
 593 
 594 	settings = g_settings_new ("org.gnome.rhythmbox.plugins.mtpdevice");
 595 	source = RB_MTP_SOURCE (g_object_new (RB_TYPE_MTP_SOURCE,
 596 					      "plugin", plugin,
 597 					      "entry-type", entry_type,
 598 					      "shell", shell,
 599 					      "visibility", TRUE,
 600 					      "raw-device", device,
 601 #if defined(HAVE_GUDEV)
 602 					      "udev-device", udev_device,
 603 #else
 604 					      "udi", udi,
 605 #endif
 606 					      "load-status", RB_SOURCE_LOAD_STATUS_LOADING,
 607 					      "settings", g_settings_get_child (settings, "source"),
 608 					      "toolbar-path", "/MTPSourceToolBar",
 609 					      "name", _("Media Player"),
 610 					      NULL));
 611 	g_object_unref (settings);
 612 
 613 	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
 614 
 615 	return RB_SOURCE (source);
 616 }
 617 
 618 static void
 619 update_free_space_cb (LIBMTP_mtpdevice_t *device, RBMtpSource *source)
 620 {
 621 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 622 	LIBMTP_devicestorage_t *storage;
 623 	int ret;
 624 
 625 	ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
 626 	if (ret != 0) {
 627 		rb_mtp_thread_report_errors (priv->device_thread, FALSE);
 628 	}
 629 
 630 	/* probably need a lock for this.. */
 631 	priv->free_space = 0;
 632 	for (storage = device->storage; storage != NULL; storage = storage->next) {
 633 		priv->free_space += storage->FreeSpaceInBytes;
 634 	}
 635 }
 636 
 637 static void
 638 queue_free_space_update (RBMtpSource *source)
 639 {
 640 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 641 	rb_mtp_thread_queue_callback (priv->device_thread,
 642 				      (RBMtpThreadCallback) update_free_space_cb, source, NULL);
 643 }
 644 
 645 static void
 646 entry_set_string_prop (RhythmDB *db,
 647 		       RhythmDBEntry *entry,
 648 		       RhythmDBPropType propid,
 649 		       const char *str)
 650 {
 651 	GValue value = {0,};
 652 
 653 	if (str == NULL || (g_utf8_validate (str, -1, NULL) == FALSE)) {
 654 		str = _("Unknown");
 655 	}
 656 
 657 	g_value_init (&value, G_TYPE_STRING);
 658 	g_value_set_static_string (&value, str);
 659 	rhythmdb_entry_set (RHYTHMDB (db), entry, propid, &value);
 660 	g_value_unset (&value);
 661 }
 662 
 663 static RhythmDBEntry *
 664 add_mtp_track_to_db (RBMtpSource *source,
 665 		     RhythmDB *db,
 666 		     LIBMTP_track_t *track)
 667 {
 668 	RhythmDBEntry *entry = NULL;
 669 	RhythmDBEntryType *entry_type;
 670 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 671 	char *name = NULL;
 672 
 673 	/* ignore everything except audio (allow audio/video types too, since they're probably pretty common) */
 674 	if (!(LIBMTP_FILETYPE_IS_AUDIO (track->filetype) || LIBMTP_FILETYPE_IS_AUDIOVIDEO (track->filetype))) {
 675 		rb_debug ("ignoring non-audio item %d (filetype %s)",
 676 			  track->item_id,
 677 			  LIBMTP_Get_Filetype_Description (track->filetype));
 678 		return NULL;
 679 	}
 680 
 681 	/* Set URI */
 682 	g_object_get (G_OBJECT (source), "entry-type", &entry_type, NULL);
 683 	name = g_strdup_printf ("xrbmtp://%i/%s", track->item_id, track->filename);
 684 	entry = rhythmdb_entry_new (RHYTHMDB (db), entry_type, name);
 685 	g_free (name);
 686         g_object_unref (entry_type);
 687 
 688 	if (entry == NULL) {
 689 		rb_debug ("cannot create entry %i", track->item_id);
 690 		g_object_unref (G_OBJECT (db));
 691 		return NULL;
 692 	}
 693 
 694 	/* Set track number */
 695 	if (track->tracknumber != 0) {
 696 		GValue value = {0, };
 697 		g_value_init (&value, G_TYPE_ULONG);
 698 		g_value_set_ulong (&value, track->tracknumber);
 699 		rhythmdb_entry_set (RHYTHMDB (db), entry,
 700 				    RHYTHMDB_PROP_TRACK_NUMBER,
 701 				    &value);
 702 		g_value_unset (&value);
 703 	}
 704 
 705 	/* Set length */
 706 	if (track->duration != 0) {
 707 		GValue value = {0, };
 708 		g_value_init (&value, G_TYPE_ULONG);
 709 		g_value_set_ulong (&value, track->duration/1000);
 710 		rhythmdb_entry_set (RHYTHMDB (db), entry,
 711 				    RHYTHMDB_PROP_DURATION,
 712 				    &value);
 713 		g_value_unset (&value);
 714 	}
 715 
 716 	/* Set file size */
 717 	if (track->filesize != 0) {
 718 		GValue value = {0, };
 719 		g_value_init (&value, G_TYPE_UINT64);
 720 		g_value_set_uint64 (&value, track->filesize);
 721 		rhythmdb_entry_set (RHYTHMDB (db), entry,
 722 				    RHYTHMDB_PROP_FILE_SIZE,
 723 				    &value);
 724 		g_value_unset (&value);
 725 	}
 726 
 727 	/* Set playcount */
 728 	if (track->usecount != 0) {
 729 		GValue value = {0, };
 730 		g_value_init (&value, G_TYPE_ULONG);
 731 		g_value_set_ulong (&value, track->usecount);
 732 		rhythmdb_entry_set (RHYTHMDB (db), entry,
 733 					       RHYTHMDB_PROP_PLAY_COUNT,
 734 					       &value);
 735 		g_value_unset (&value);
 736 	}
 737 	/* Set rating */
 738 	if (track->rating != 0) {
 739 		GValue value = {0, };
 740 		g_value_init (&value, G_TYPE_DOUBLE);
 741 		g_value_set_double (&value, track->rating/20);
 742 		rhythmdb_entry_set (RHYTHMDB (db), entry,
 743 					       RHYTHMDB_PROP_RATING,
 744 					       &value);
 745 		g_value_unset (&value);
 746 	}
 747 	/* Set release date */
 748 	if (track->date != NULL && track->date[0] != '\0') {
 749 		GTimeVal tv;
 750 		if (g_time_val_from_iso8601 (track->date, &tv)) {
 751 			GDate d;
 752 			GValue value = {0, };
 753 			g_value_init (&value, G_TYPE_ULONG);
 754 			g_date_set_time_val (&d, &tv);
 755 			g_value_set_ulong (&value, g_date_get_julian (&d));
 756 			rhythmdb_entry_set (RHYTHMDB (db), entry, RHYTHMDB_PROP_DATE, &value);
 757 			g_value_unset (&value);
 758 		}
 759 	}
 760 
 761 	/* Set title */
 762 	entry_set_string_prop (RHYTHMDB (db), entry, RHYTHMDB_PROP_TITLE, track->title);
 763 
 764 	/* Set album, artist and genre from MTP */
 765 	entry_set_string_prop (RHYTHMDB (db), entry, RHYTHMDB_PROP_ARTIST, track->artist);
 766 	entry_set_string_prop (RHYTHMDB (db), entry, RHYTHMDB_PROP_ALBUM, track->album);
 767 	entry_set_string_prop (RHYTHMDB (db), entry, RHYTHMDB_PROP_GENRE, track->genre);
 768 
 769 	g_hash_table_insert (priv->entry_map, entry, track);
 770 	rhythmdb_commit (RHYTHMDB (db));
 771 
 772 	return entry;
 773 }
 774 
 775 typedef struct {
 776 	RBMtpSource *source;
 777 	char *name;
 778 	guint16 *types;
 779 	guint16 num_types;
 780 } DeviceOpenedData;
 781 
 782 static gboolean
 783 device_opened_idle (DeviceOpenedData *data)
 784 {
 785 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (data->source);
 786 	int i;
 787 	GstEncodingTarget *target;
 788 	GList *profiles = NULL;
 789 
 790 	if (data->name != NULL) {
 791 		g_object_set (data->source, "name", data->name, NULL);
 792 	}
 793 
 794 	/* when the source name changes after this, try to update the device name */
 795 	g_signal_connect (G_OBJECT (data->source), "notify::name",
 796 			  (GCallback)rb_mtp_source_name_changed_cb, NULL);
 797 
 798 	rb_media_player_source_load (RB_MEDIA_PLAYER_SOURCE (data->source));
 799 
 800 	for (i = 0; i < data->num_types; i++) {
 801 		const char *mediatype;
 802 		gboolean prepend;
 803 		if (i <= LIBMTP_FILETYPE_UNKNOWN) {
 804 			priv->supported_types[data->types[i]] = 1;
 805 		}
 806 
 807 		mediatype = NULL;
 808 		prepend = FALSE;
 809 		switch (data->types[i]) {
 810 		case LIBMTP_FILETYPE_WAV:
 811 			/*mediatype = "audio/x-wav";*/
 812 			/* don't bother including this? */
 813 			break;
 814 		case LIBMTP_FILETYPE_MP3:
 815 			mediatype = "audio/mpeg";
 816 			prepend = TRUE;		/* always goes first if supported */
 817 			break;
 818 		case LIBMTP_FILETYPE_WMA:
 819 			mediatype = "audio/x-wma";
 820 			break;
 821 		case LIBMTP_FILETYPE_OGG:
 822 			mediatype = "audio/x-vorbis";
 823 			break;
 824 		case LIBMTP_FILETYPE_MP4:
 825 		case LIBMTP_FILETYPE_M4A:
 826 		case LIBMTP_FILETYPE_AAC:
 827 			mediatype = "audio/x-aac";
 828 			break;
 829 		case LIBMTP_FILETYPE_WMV:
 830 			mediatype = "audio/x-ms-wmv";		/* media type? */
 831 			break;
 832 		case LIBMTP_FILETYPE_ASF:
 833 			mediatype = "video/x-ms-asf";		/* media type? */
 834 			break;
 835 		case LIBMTP_FILETYPE_FLAC:
 836 			mediatype = "audio/x-flac";
 837 			break;
 838 
 839 		case LIBMTP_FILETYPE_JPEG:
 840 			rb_debug ("JPEG (album art) supported");
 841 			priv->album_art_supported = TRUE;
 842 			break;
 843 
 844 		default:
 845 			rb_debug ("unknown libmtp filetype %s supported", LIBMTP_Get_Filetype_Description (data->types[i]));
 846 			break;
 847 		}
 848 
 849 		if (mediatype != NULL) {
 850 			GstEncodingProfile *profile;
 851 			profile = rb_gst_get_encoding_profile (mediatype);
 852 			if (profile != NULL) {
 853 				rb_debug ("media type %s supported", mediatype);
 854 				if (prepend) {
 855 					profiles = g_list_prepend (profiles, profile);
 856 				} else {
 857 					profiles = g_list_append (profiles, profile);
 858 				}
 859 			} else {
 860 				rb_debug ("no encoding profile for supported media type %s", mediatype);
 861 			}
 862 		}
 863 	}
 864 
 865 	if (priv->album_art_supported) {
 866 		priv->art_store = rb_ext_db_new ("album-art");
 867 	}
 868 
 869 	target = gst_encoding_target_new ("mtpdevice", "device", "", profiles);
 870 	g_object_set (data->source, "encoding-target", target, NULL);
 871 
 872 	g_object_unref (data->source);
 873 	free (data->types);
 874 	g_free (data->name);
 875 	g_free (data);
 876 
 877 	return FALSE;
 878 }
 879 
 880 static gboolean
 881 device_open_failed_idle (RBMtpSource *source)
 882 {
 883 	/* libmtp doesn't give us a useful error message in this case, so
 884 	 * all we can offer is this generic message.
 885 	 */
 886 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 887 	rb_error_dialog (NULL,
 888 			 _("Media player device error"),
 889 			 /* Translators: first %s is the device manufacturer,
 890 			  * second is the product name.
 891 			  */
 892 			 _("Unable to open the %s %s device"),
 893 			 priv->raw_device.device_entry.vendor,
 894 			 priv->raw_device.device_entry.product);
 895 	rb_display_page_delete_thyself (RB_DISPLAY_PAGE (source));
 896 	g_object_unref (source);
 897 	return FALSE;
 898 }
 899 
 900 static gboolean
 901 device_open_ignore_idle (DeviceOpenedData *data)
 902 {
 903 	rb_display_page_delete_thyself (RB_DISPLAY_PAGE (data->source));
 904 	g_object_unref (data->source);
 905 	free (data->types);
 906 	g_free (data->name);
 907 	g_free (data);
 908 	return FALSE;
 909 }
 910 
 911 /* this callback runs on the device handling thread, so it can call libmtp directly */
 912 static void
 913 mtp_device_open_cb (LIBMTP_mtpdevice_t *device, RBMtpSource *source)
 914 {
 915 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
 916 	gboolean has_audio = FALSE;
 917 	DeviceOpenedData *data;
 918 
 919 	if (device == NULL) {
 920 		/* can't delete the source on this thread, so move it to the main thread */
 921 		g_idle_add ((GSourceFunc) device_open_failed_idle, g_object_ref (source));
 922 		return;
 923 	}
 924 
 925 	/* set the source name to match the device, ignoring some
 926 	 * particular broken device names.
 927 	 */
 928 	data = g_new0 (DeviceOpenedData, 1);
 929 	data->source = g_object_ref (source);
 930 	data->name = LIBMTP_Get_Friendlyname (device);
 931 	if (data->name == NULL || strcmp (data->name, "?????") == 0) {
 932 		g_free (data->name);
 933 		data->name = LIBMTP_Get_Modelname (device);
 934 	}
 935 	if (data->name == NULL) {
 936 		data->name = g_strdup (_("Digital Audio Player"));
 937 	}
 938 
 939 	/* get some other device information that doesn't change */
 940 	priv->manufacturer = LIBMTP_Get_Manufacturername (device);
 941 	priv->device_version = LIBMTP_Get_Deviceversion (device);
 942 	priv->model_name = LIBMTP_Get_Modelname (device);
 943 	priv->serial = LIBMTP_Get_Serialnumber (device);
 944 
 945 	/* calculate the device capacity */
 946 	priv->capacity = 0;
 947 	if (LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == 0) {
 948 		LIBMTP_devicestorage_t *storage;
 949 		for (storage = device->storage;
 950 		     storage != NULL;
 951 		     storage = storage->next) {
 952 			priv->capacity += storage->MaxCapacity;
 953 		}
 954 	}
 955 
 956 	update_free_space_cb (device, RB_MTP_SOURCE (source));
 957 
 958 	/* figure out the set of formats supported by the device, ensuring there's at least
 959 	 * one audio format aside from WAV.  the purpose of this is to exclude cameras and other
 960 	 * MTP devices that aren't interesting to us.
 961 	 */
 962 	if (LIBMTP_Get_Supported_Filetypes (device, &data->types, &data->num_types) != 0) {
 963 		rb_mtp_thread_report_errors (priv->device_thread, FALSE);
 964 	} else {
 965 		int i;
 966 		for (i = 0; i < data->num_types; i++) {
 967 			if (data->types[i] != LIBMTP_FILETYPE_WAV && LIBMTP_FILETYPE_IS_AUDIO (data->types[i])) {
 968 				has_audio = TRUE;
 969 				break;
 970 			}
 971 		}
 972 	}
 973 
 974 	if (has_audio == FALSE) {
 975 		rb_debug ("device doesn't support any audio formats");
 976 		g_idle_add ((GSourceFunc) device_open_ignore_idle, data);
 977 		return;
 978 	}
 979 
 980 	g_idle_add ((GSourceFunc) device_opened_idle, data);
 981 
 982 	/* now get the track list */
 983 	rb_mtp_thread_get_track_list (priv->device_thread, (RBMtpTrackListCallback) mtp_tracklist_cb, g_object_ref (source), g_object_unref);
 984 }
 985 
 986 static gboolean
 987 device_loaded_idle (RBMtpSource *source)
 988 {
 989 	g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
 990 	rb_transfer_target_transfer (RB_TRANSFER_TARGET (source), NULL, FALSE);
 991 	return FALSE;
 992 }
 993 
 994 static void
 995 mtp_tracklist_cb (LIBMTP_track_t *tracks, RBMtpSource *source)
 996 {
 997 	RhythmDB *db = NULL;
 998 	LIBMTP_track_t *track;
 999 
1000 	/* add tracks to database */
1001 	db = get_db_for_source (source);
1002 	for (track = tracks; track != NULL; track = track->next) {
1003 		add_mtp_track_to_db (source, db, track);
1004 	}
1005 	g_object_unref (db);
1006 
1007 	g_idle_add ((GSourceFunc) device_loaded_idle, source);
1008 }
1009 
1010 static char *
1011 gdate_to_char (GDate* date)
1012 {
1013 	return g_strdup_printf ("%04i%02i%02iT000000.0",
1014 				g_date_get_year (date),
1015 				g_date_get_month (date),
1016 				g_date_get_day (date));
1017 }
1018 
1019 static LIBMTP_filetype_t
1020 media_type_to_filetype (RBMtpSource *source, const char *media_type)
1021 {
1022 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1023 
1024 	if (!strcmp (media_type, "audio/mpeg")) {
1025 		return LIBMTP_FILETYPE_MP3;
1026 	}  else if (!strcmp (media_type, "audio/x-wav")) {
1027 		return  LIBMTP_FILETYPE_WAV;
1028 	} else if (!strcmp (media_type, "audio/x-vorbis")) {
1029 		return LIBMTP_FILETYPE_OGG;
1030 	} else if (!strcmp (media_type, "audio/x-aac")) {
1031 		/* try a few different filetypes that might work */
1032 		if (priv->supported_types[LIBMTP_FILETYPE_M4A])
1033 			return LIBMTP_FILETYPE_M4A;
1034 		else if (!priv->supported_types[LIBMTP_FILETYPE_AAC] && priv->supported_types[LIBMTP_FILETYPE_MP4])
1035 			return LIBMTP_FILETYPE_MP4;
1036 		else
1037 			return LIBMTP_FILETYPE_AAC;
1038 
1039 	} else if (!strcmp (media_type, "audio/x-wma")) {
1040 		return LIBMTP_FILETYPE_WMA;
1041 	} else if (!strcmp (media_type, "video/x-ms-asf")) {
1042 		return LIBMTP_FILETYPE_ASF;
1043 	} else if (!strcmp (media_type, "audio/x-flac")) {
1044 		return LIBMTP_FILETYPE_FLAC;
1045 	} else {
1046 		rb_debug ("\"%s\" is not a supported media_type", media_type);
1047 		return LIBMTP_FILETYPE_UNKNOWN;
1048 	}
1049 }
1050 
1051 static void
1052 impl_delete (RBSource *source)
1053 {
1054 	GList *sel;
1055 	RBEntryView *songs;
1056 
1057 	songs = rb_source_get_entry_view (source);
1058 	sel = rb_entry_view_get_selected_entries (songs);
1059 	impl_delete_entries (RB_MEDIA_PLAYER_SOURCE (source), sel, NULL, NULL, NULL);
1060 	rb_list_destroy_free (sel, (GDestroyNotify) rhythmdb_entry_unref);
1061 }
1062 
1063 static gboolean
1064 impl_uri_is_source (RBSource *source, const char *uri)
1065 {
1066 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1067 	char *source_uri;
1068 	gboolean result;
1069 
1070 	if (g_str_has_prefix (uri, "gphoto2://") == FALSE)
1071 		return FALSE;
1072 
1073 	source_uri = g_strdup_printf ("gphoto2://[usb:%03d,%03d]/", 
1074 				      priv->raw_device.bus_location,
1075 				      priv->raw_device.devnum);
1076 	result = g_str_has_prefix (uri, source_uri);
1077 	g_free (source_uri);
1078 	return result;
1079 }
1080 
1081 static RBTrackTransferBatch *
1082 impl_paste (RBSource *source, GList *entries)
1083 {
1084 	gboolean defer;
1085 	defer = (ensure_loaded (RB_MTP_SOURCE (source)) == FALSE);
1086 	return rb_transfer_target_transfer (RB_TRANSFER_TARGET (source), entries, defer);
1087 }
1088 
1089 static gboolean
1090 impl_show_popup (RBDisplayPage *page)
1091 {
1092 	_rb_display_page_show_popup (page, "/MTPSourcePopup");
1093 	return TRUE;
1094 }
1095 
1096 static RhythmDB *
1097 get_db_for_source (RBMtpSource *source)
1098 {
1099 	RBShell *shell = NULL;
1100 	RhythmDB *db = NULL;
1101 
1102 	g_object_get (source, "shell", &shell, NULL);
1103 	g_object_get (shell, "db", &db, NULL);
1104 	g_object_unref (shell);
1105 
1106 	return db;
1107 }
1108 
1109 static void
1110 art_request_cb (RBExtDBKey *key, const char *filename, GValue *data, RBMtpSource *source)
1111 {
1112 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1113 
1114 	if (G_VALUE_HOLDS (data, GDK_TYPE_PIXBUF)) {
1115 		GdkPixbuf *pixbuf;
1116 		const char *album_name;
1117 
1118 		pixbuf = GDK_PIXBUF (g_value_get_object (data));
1119 
1120 		album_name = rb_ext_db_key_get_field (key, "album");
1121 		rb_mtp_thread_set_album_image (priv->device_thread, album_name, pixbuf);
1122 		queue_free_space_update (source);
1123 	}
1124 }
1125 
1126 static gboolean
1127 impl_track_added (RBTransferTarget *target,
1128 		  RhythmDBEntry *entry,
1129 		  const char *dest,
1130 		  guint64 filesize,
1131 		  const char *media_type)
1132 {
1133 	LIBMTP_track_t *track = NULL;
1134 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (target);
1135 	RhythmDB *db;
1136 
1137 	track = g_hash_table_lookup (priv->track_transfer_map, dest);
1138 	if (track == NULL) {
1139 		rb_debug ("track-added called, but can't find a track for dest URI %s", dest);
1140 		return FALSE;
1141 	}
1142 	g_hash_table_remove (priv->track_transfer_map, dest);
1143 
1144 	if (strcmp (track->album, _("Unknown")) != 0) {
1145 		rb_mtp_thread_add_to_album (priv->device_thread, track, track->album);
1146 
1147 		if (priv->album_art_supported) {
1148 			RBExtDBKey *key;
1149 
1150 			/* need to do this in an idle handler? */
1151 			key = rb_ext_db_key_create_lookup ("album", track->album);
1152 			rb_ext_db_key_add_field (key, "artist", track->artist);
1153 			rb_ext_db_request (priv->art_store,
1154 					   key,
1155 					   (RBExtDBRequestCallback) art_request_cb,
1156 					   g_object_ref (target),
1157 					   (GDestroyNotify) g_object_unref);
1158 			rb_ext_db_key_free (key);
1159 		}
1160 	}
1161 
1162 	db = get_db_for_source (RB_MTP_SOURCE (target));
1163 	add_mtp_track_to_db (RB_MTP_SOURCE (target), db, track);
1164 	g_object_unref (db);
1165 
1166 	queue_free_space_update (RB_MTP_SOURCE (target));
1167 	return FALSE;
1168 }
1169 
1170 static gboolean
1171 impl_track_add_error (RBTransferTarget *target,
1172 		      RhythmDBEntry *entry,
1173 		      const char *dest,
1174 		      GError *error)
1175 {
1176 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (target);
1177 	/* we don't actually do anything with the error here, we just need to clean up the transfer map */
1178 	LIBMTP_track_t *track = g_hash_table_lookup (priv->track_transfer_map, dest);
1179 	if (track != NULL) {
1180 		LIBMTP_destroy_track_t (track);
1181 		g_hash_table_remove (priv->track_transfer_map, dest);
1182 	} else {
1183 		rb_debug ("track-add-error called, but can't find a track for dest URI %s", dest);
1184 	}
1185 
1186 	return TRUE;
1187 }
1188 
1189 static void
1190 sanitize_for_mtp (char *str)
1191 {
1192 	rb_sanitize_path_for_msdos_filesystem (str);
1193 	g_strdelimit (str, "/", '_');
1194 }
1195 
1196 static void
1197 prepare_encoder_sink_cb (RBEncoderFactory *factory,
1198 			 const char *stream_uri,
1199 			 GObject *sink,
1200 			 RBMtpSource *source)
1201 {
1202 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1203 	RhythmDBEntry *entry;
1204 	RhythmDB *db;
1205 	LIBMTP_track_t *track;
1206 	char **bits;
1207 	char *extension;
1208 	char *track_str;
1209 	LIBMTP_filetype_t filetype;
1210 	gulong track_id;
1211 	GDate d;
1212 	char **folder_path;
1213 
1214 	/* make sure this stream is for a file on our device */
1215 	if (g_str_has_prefix (stream_uri, "xrbmtp://") == FALSE)
1216 		return;
1217 
1218 	/* extract the entry ID, extension, and MTP filetype from the URI */
1219 	bits = g_strsplit (stream_uri + strlen ("xrbmtp://"), "/", 3);
1220 	track_id = strtoul (bits[0], NULL, 0);
1221 	extension = g_strdup (bits[1]);
1222 	filetype = strtoul (bits[2], NULL, 0);
1223 	g_strfreev (bits);
1224 
1225 	db = get_db_for_source (source);
1226 	entry = rhythmdb_entry_lookup_by_id (db, track_id);
1227 	g_object_unref (db);
1228 	if (entry == NULL) {
1229 		g_free (extension);
1230 		return;
1231 	}
1232 
1233 	track = LIBMTP_new_track_t ();
1234 	track->title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
1235 	track->album = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ALBUM);
1236 	track->artist = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ARTIST);
1237 	track->genre = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_GENRE);
1238 
1239 	/* build up device filename */
1240 	if (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER) > 0) {
1241 		track_str = g_strdup_printf ("%.2lu.%.2lu ",
1242 					     rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER),
1243 					     rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
1244 	} else {
1245 		track_str = g_strdup_printf ("%.2lu ",
1246 					     rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
1247 	}
1248 	
1249 	track->filename = g_strdup_printf ("%s%s - %s.%s",
1250 					   track_str,
1251 					   rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
1252 					   rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE),
1253 					   extension);
1254 	g_free (track_str);
1255 	g_free (extension);
1256 
1257 	/* construct folder path: artist/album */
1258 	folder_path = g_new0 (char *, 3);
1259 	folder_path[0] = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ALBUM_ARTIST);
1260 	if (folder_path[0] == NULL || folder_path[0][0] == '\0') {
1261 		g_free (folder_path[0]);
1262 		folder_path[0] = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ARTIST);
1263 	}
1264 	folder_path[1] = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ALBUM);
1265 
1266 	/* ensure the filename is safe for FAT filesystems and doesn't contain slashes */
1267 	sanitize_for_mtp (track->filename);
1268 	sanitize_for_mtp (folder_path[0]);
1269 	sanitize_for_mtp (folder_path[1]);
1270 
1271 	if (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE) > 0) {
1272 		g_date_set_julian (&d, rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE));
1273 		track->date = gdate_to_char (&d);
1274 	}
1275 	track->tracknumber = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER);
1276 	track->duration = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION) * 1000;
1277 	track->rating = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_RATING) * 20;
1278 	track->usecount = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_PLAY_COUNT);
1279 
1280 	track->filetype = filetype;
1281 
1282 	g_object_set (sink,
1283 		      "device-thread", priv->device_thread,
1284 		      "folder-path", folder_path,
1285 		      "mtp-track", track,
1286 		      NULL);
1287 	rhythmdb_entry_unref (entry);
1288 	g_strfreev (folder_path);
1289 
1290 	g_hash_table_insert (priv->track_transfer_map, g_strdup (stream_uri), track);
1291 }
1292 
1293 static char *
1294 impl_build_dest_uri (RBTransferTarget *target,
1295 		     RhythmDBEntry *entry,
1296 		     const char *media_type,
1297 		     const char *extension)
1298 {
1299 	gulong id;
1300 	char *uri;
1301 	LIBMTP_filetype_t filetype;
1302 
1303 	if (media_type == NULL) {
1304 		media_type = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MEDIA_TYPE);
1305 	}
1306 	filetype = media_type_to_filetype (RB_MTP_SOURCE (target), media_type);
1307 	rb_debug ("using libmtp filetype %d (%s) for source media type %s",
1308 		  filetype,
1309 		  LIBMTP_Get_Filetype_Description (filetype),
1310 		  media_type);
1311 
1312 	/* the prepare-sink callback needs the entry ID to set up the
1313 	 * upload data, and we want to use the supplied extension for
1314 	 * the filename on the device.
1315 	 *
1316 	 * this is pretty ugly - it'd be much nicer to have a source-defined
1317 	 * structure that got passed around (or was accessible from) the various
1318 	 * hooks and methods called during the track transfer process.  probably
1319 	 * something to address in my horribly stalled track transfer rewrite..
1320 	 *
1321 	 * the structure would either be created when queuing the track for transfer,
1322 	 * or here; passed to any prepare-source or prepare-sink callbacks for the
1323 	 * encoder; and then passed to whatever gets called when the transfer is complete.
1324 	 */
1325 	id = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_ENTRY_ID);
1326 	if (extension == NULL) {
1327 		extension = "";
1328 	}
1329 	uri = g_strdup_printf ("xrbmtp://%lu/%s/%d", id, extension, filetype);
1330 	return uri;
1331 }
1332 
1333 
1334 static void
1335 impl_get_entries (RBMediaPlayerSource *source, const char *category, GHashTable *map)
1336 {
1337 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1338 	GHashTableIter iter;
1339 	gpointer key, value;
1340 	gboolean podcast;
1341 
1342 	/* sync category mapping is a bit hackish here, as MTP doesn't categorise
1343 	 * tracks itself.  matching specific genres is about the best we can do.
1344 	 */
1345 	podcast = (g_str_equal (category, SYNC_CATEGORY_PODCAST));
1346 
1347 	g_hash_table_iter_init (&iter, priv->entry_map);
1348 	while (g_hash_table_iter_next (&iter, &key, &value)) {
1349 		LIBMTP_track_t *track = value;
1350 
1351 		if ((g_strcmp0 (track->genre, "Podcast") == 0) == podcast) {
1352 			RhythmDBEntry *entry = key;
1353 			_rb_media_player_source_add_to_map (map, entry);
1354 		}
1355 	}
1356 }
1357 
1358 static guint64
1359 impl_get_capacity	(RBMediaPlayerSource *source)
1360 {
1361 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1362 	return priv->capacity;
1363 }
1364 
1365 static guint64
1366 impl_get_free_space	(RBMediaPlayerSource *source)
1367 {
1368 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1369 	/* probably need a lock for this */
1370 	return priv->free_space;
1371 }
1372 
1373 typedef struct {
1374 	gboolean actually_free;
1375 	GHashTable *check_folders;
1376 	RBMediaPlayerSource *source;
1377 	RBMediaPlayerSourceDeleteCallback callback;
1378 	gpointer callback_data;
1379 	GDestroyNotify destroy_data;
1380 } TracksDeletedCallbackData;
1381 
1382 static void
1383 free_delete_data (TracksDeletedCallbackData *data)
1384 {
1385 	if (data->actually_free == FALSE) {
1386 		return;
1387 	}
1388 
1389 	g_hash_table_destroy (data->check_folders);
1390 	g_object_unref (data->source);
1391 	if (data->destroy_data) {
1392 		data->destroy_data (data->callback_data);
1393 	}
1394 	g_free (data);
1395 }
1396 
1397 static gboolean
1398 delete_done_idle_cb (TracksDeletedCallbackData *data)
1399 {
1400 	if (data->callback) {
1401 		data->callback (data->source, data->callback_data);
1402 	}
1403 
1404 	data->actually_free = TRUE;
1405 	free_delete_data (data);
1406 	return FALSE;
1407 }
1408 
1409 static void
1410 delete_done_cb (LIBMTP_mtpdevice_t *device, TracksDeletedCallbackData *data)
1411 {
1412 	LIBMTP_folder_t *folders;
1413 	LIBMTP_file_t *files;
1414 
1415 	data->actually_free = FALSE;
1416 	update_free_space_cb (device, RB_MTP_SOURCE (data->source));
1417 
1418 	/* if any of the folders we just deleted from are now empty, delete them */
1419 	folders = LIBMTP_Get_Folder_List (device);
1420 	files = LIBMTP_Get_Filelisting_With_Callback (device, NULL, NULL);
1421 	if (folders != NULL) {
1422 		GHashTableIter iter;
1423 		gpointer key;
1424 		g_hash_table_iter_init (&iter, data->check_folders);
1425 		while (g_hash_table_iter_next (&iter, &key, NULL)) {
1426 			LIBMTP_folder_t *f;
1427 			LIBMTP_folder_t *c;
1428 			LIBMTP_file_t *file;
1429 			uint32_t folder_id = GPOINTER_TO_UINT(key);
1430 
1431 			while (folder_id != device->default_music_folder && folder_id != 0) {
1432 
1433 				f = LIBMTP_Find_Folder (folders, folder_id);
1434 				if (f == NULL) {
1435 					rb_debug ("unable to find folder %u", folder_id);
1436 					break;
1437 				}
1438 
1439 				/* don't delete folders with children that we didn't just delete */
1440 				for (c = f->child; c != NULL; c = c->sibling) {
1441 					if (g_hash_table_lookup (data->check_folders,
1442 								 GUINT_TO_POINTER (c->folder_id)) == NULL) {
1443 						break;
1444 					}
1445 				}
1446 				if (c != NULL) {
1447 					rb_debug ("folder %s has children", f->name);
1448 					break;
1449 				}
1450 
1451 				/* don't delete folders that contain files */
1452 				for (file = files; file != NULL; file = file->next) {
1453 					if (file->parent_id == folder_id) {
1454 						break;
1455 					}
1456 				}
1457 
1458 				if (file != NULL) {
1459 					rb_debug ("folder %s contains at least one file: %s", f->name, file->filename);
1460 					break;
1461 				}
1462 
1463 				/* ok, the folder is empty */
1464 				rb_debug ("deleting empty folder %s", f->name);
1465 				LIBMTP_Delete_Object (device, f->folder_id);
1466 
1467 				/* if the folder we just deleted has siblings, the parent
1468 				 * can't be empty.
1469 				 */
1470 				if (f->sibling != NULL) {
1471 					rb_debug ("folder %s has siblings, can't delete parent", f->name);
1472 					break;
1473 				}
1474 				folder_id = f->parent_id;
1475 			}
1476 		}
1477 
1478 		LIBMTP_destroy_folder_t (folders);
1479 	} else {
1480 		rb_debug ("unable to get device folder list");
1481 	}
1482 
1483 	/* clean up the file list */
1484 	while (files != NULL) {
1485 		LIBMTP_file_t *n;
1486 
1487 		n = files->next;
1488 		LIBMTP_destroy_file_t (files);
1489 		files = n;
1490 	}
1491 
1492 	g_idle_add ((GSourceFunc) delete_done_idle_cb, data);
1493 }
1494 
1495 static void
1496 impl_delete_entries	(RBMediaPlayerSource *source,
1497 			 GList *entries,
1498 			 RBMediaPlayerSourceDeleteCallback callback,
1499 			 gpointer user_data,
1500 			 GDestroyNotify destroy_data)
1501 {
1502 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1503 	RhythmDB *db;
1504 	GList *i;
1505 	TracksDeletedCallbackData *cb_data;
1506 
1507 	cb_data = g_new0 (TracksDeletedCallbackData, 1);
1508 	cb_data->source = g_object_ref (source);
1509 	cb_data->callback_data = user_data;
1510 	cb_data->callback = callback;
1511 	cb_data->destroy_data = destroy_data;
1512 	cb_data->check_folders = g_hash_table_new (g_direct_hash, g_direct_equal);
1513 
1514 	db = get_db_for_source (RB_MTP_SOURCE (source));
1515 	for (i = entries; i != NULL; i = i->next) {
1516 		LIBMTP_track_t *track;
1517 		const char *uri;
1518 		const char *album_name;
1519 		RhythmDBEntry *entry;
1520 
1521 		entry = i->data;
1522 		uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
1523 		track = g_hash_table_lookup (priv->entry_map, entry);
1524 		if (track == NULL) {
1525 			rb_debug ("Couldn't find track on mtp-device! (%s)", uri);
1526 			continue;
1527 		}
1528 
1529 		album_name = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
1530 		if (g_strcmp0 (album_name, _("Unknown")) != 0) {
1531 			rb_mtp_thread_remove_from_album (priv->device_thread, track, album_name);
1532 		}
1533 		rb_mtp_thread_delete_track (priv->device_thread, track);
1534 
1535 		g_hash_table_insert (cb_data->check_folders,
1536 				     GUINT_TO_POINTER (track->parent_id),
1537 				     GINT_TO_POINTER (1));
1538 
1539 		g_hash_table_remove (priv->entry_map, entry);
1540 		rhythmdb_entry_delete (db, entry);
1541 	}
1542 
1543 	/* callback when all tracks have been deleted */
1544 	rb_mtp_thread_queue_callback (priv->device_thread,
1545 				      (RBMtpThreadCallback) delete_done_cb,
1546 				      cb_data,
1547 				      (GDestroyNotify) free_delete_data);
1548 
1549 	rhythmdb_commit (db);
1550 }
1551 
1552 static void
1553 impl_show_properties (RBMediaPlayerSource *source, GtkWidget *info_box, GtkWidget *notebook)
1554 {
1555 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1556 	GtkBuilder *builder;
1557 	GtkWidget *widget;
1558 	GHashTableIter iter;
1559 	gpointer key, value;
1560 	int num_podcasts;
1561 	char *device_name;
1562 	char *builder_file;
1563 	GObject *plugin;
1564 	char *text;
1565 	GList *output_formats;
1566 	GList *t;
1567 	GString *str;
1568 
1569 	g_object_get (source, "plugin", &plugin, NULL);
1570 	builder_file = rb_find_plugin_data_file (G_OBJECT (plugin), "mtp-info.ui");
1571 	g_object_unref (plugin);
1572 
1573 	if (builder_file == NULL) {
1574 		g_warning ("Couldn't find mtp-info.ui");
1575 		return;
1576 	}
1577 
1578 	builder = rb_builder_load (builder_file, NULL);
1579 	g_free (builder_file);
1580 
1581 	if (builder == NULL) {
1582 		rb_debug ("Couldn't load mtp-info.ui");
1583 		return;
1584 	}
1585 
1586 	/* 'basic' tab stuff */
1587 
1588 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "mtp-basic-info"));
1589 	gtk_box_pack_start (GTK_BOX (info_box), widget, TRUE, TRUE, 0);
1590 
1591 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "entry-mtp-name"));
1592 	g_object_get (source, "name", &device_name, NULL);
1593 	gtk_entry_set_text (GTK_ENTRY (widget), device_name);
1594 	g_free (device_name);
1595 	g_signal_connect (widget, "focus-out-event",
1596 			  (GCallback)rb_mtp_source_name_changed_cb, source);
1597 
1598 	num_podcasts = 0;
1599 	g_hash_table_iter_init (&iter, priv->entry_map);
1600 	while (g_hash_table_iter_next (&iter, &key, &value)) {
1601 		LIBMTP_track_t *track = value;
1602 		if (g_strcmp0 (track->genre, "Podcast") == 0) {
1603 			num_podcasts++;
1604 		}
1605 	}
1606 
1607 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "mtp-num-tracks"));
1608 	text = g_strdup_printf ("%d", g_hash_table_size (priv->entry_map) - num_podcasts);
1609 	gtk_label_set_text (GTK_LABEL (widget), text);
1610 	g_free (text);
1611 
1612 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "mtp-num-podcasts"));
1613 	text = g_strdup_printf ("%d", num_podcasts);
1614 	gtk_label_set_text (GTK_LABEL (widget), text);
1615 	g_free (text);
1616 
1617 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "mtp-num-playlists"));
1618 	text = g_strdup_printf ("%d", 0);						/* correct, but wrong */
1619 	gtk_label_set_text (GTK_LABEL (widget), text);
1620 	g_free (text);
1621 
1622 	/* 'advanced' tab stuff */
1623 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "mtp-advanced-tab"));
1624 	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widget, gtk_label_new (_("Advanced")));
1625 
1626 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-mtp-model-value"));
1627 	gtk_label_set_text (GTK_LABEL (widget), priv->model_name);
1628 
1629 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-serial-number-value"));
1630 	gtk_label_set_text (GTK_LABEL (widget), priv->serial);
1631 
1632 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-firmware-version-value"));
1633 	gtk_label_set_text (GTK_LABEL (widget), priv->device_version);
1634 
1635 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-manufacturer-value"));
1636 	gtk_label_set_text (GTK_LABEL (widget), priv->manufacturer);
1637 
1638 	str = g_string_new ("");
1639 	output_formats = rb_transfer_target_get_format_descriptions (RB_TRANSFER_TARGET (source));
1640 	for (t = output_formats; t != NULL; t = t->next) {
1641 		if (t != output_formats) {
1642 			g_string_append (str, "\n");
1643 		}
1644 		g_string_append (str, t->data);
1645 	}
1646 	rb_list_deep_free (output_formats);
1647 
1648 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-audio-formats-value"));
1649 	gtk_label_set_text (GTK_LABEL (widget), str->str);
1650 	g_string_free (str, TRUE);
1651 
1652 	g_object_unref (builder);
1653 }
1654 
1655 static void
1656 prepare_source (RBMtpSource *source, const char *stream_uri, GObject *src)
1657 {
1658 	RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source);
1659 	RhythmDBEntry *entry;
1660 	RhythmDB *db;
1661 
1662 	/* make sure this stream is for a file on our device */
1663 	if (g_str_has_prefix (stream_uri, "xrbmtp://") == FALSE)
1664 		return;
1665 
1666 	db = get_db_for_source (source);
1667 	entry = rhythmdb_entry_lookup_by_location (db, stream_uri);
1668 	g_object_unref (db);
1669 	if (entry == NULL)
1670 		return;
1671 
1672 	if (_rb_source_check_entry_type (RB_SOURCE (source), entry) == FALSE) {
1673 		rhythmdb_entry_unref (entry);
1674 		return;
1675 	}
1676 
1677 	rb_debug ("setting device-thread for stream %s", stream_uri);
1678 	g_object_set (src, "device-thread", priv->device_thread, NULL);
1679 	rhythmdb_entry_unref (entry);
1680 }
1681 
1682 static void
1683 prepare_player_source_cb (RBPlayer *player,
1684 			  const char *stream_uri,
1685 			  GstElement *src,
1686 			  RBMtpSource *source)
1687 {
1688 	prepare_source (source, stream_uri, G_OBJECT (src));
1689 }
1690 
1691 static void
1692 prepare_encoder_source_cb (RBEncoderFactory *factory,
1693 			   const char *stream_uri,
1694 			   GObject *src,
1695 			   RBMtpSource *source)
1696 {
1697 	prepare_source (source, stream_uri, src);
1698 }
1699 
1700 static gboolean
1701 impl_can_eject (RBDeviceSource *source)
1702 {
1703 	return TRUE;
1704 }
1705 
1706 static void
1707 impl_eject (RBDeviceSource *source)
1708 {
1709 	rb_display_page_delete_thyself (RB_DISPLAY_PAGE (source));
1710 }
1711 
1712 #if defined(HAVE_GUDEV)
1713 
1714 static GMount *
1715 find_mount_for_device (GUdevDevice *device)
1716 {
1717 	GMount *mount = NULL;
1718 	const char *device_file;
1719 	GVolumeMonitor *volmon;
1720 	GList *mounts;
1721 	GList *i;
1722 
1723 	device_file = g_udev_device_get_device_file (device);
1724 	if (device_file == NULL) {
1725 		return NULL;
1726 	}
1727 
1728 	volmon = g_volume_monitor_get ();
1729 	mounts = g_volume_monitor_get_mounts (volmon);
1730 	g_object_unref (volmon);
1731 
1732 	for (i = mounts; i != NULL; i = i->next) {
1733 		GVolume *v;
1734 
1735 		v = g_mount_get_volume (G_MOUNT (i->data));
1736 		if (v != NULL) {
1737 			char *devname = NULL;
1738 			gboolean match;
1739 
1740 			devname = g_volume_get_identifier (v, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
1741 			g_object_unref (v);
1742 			if (devname == NULL)
1743 				continue;
1744 
1745 			match = g_str_equal (devname, device_file);
1746 			g_free (devname);
1747 
1748 			if (match) {
1749 				mount = G_MOUNT (i->data);
1750 				g_object_ref (G_OBJECT (mount));
1751 				break;
1752 			}
1753 		}
1754 	}
1755 	g_list_foreach (mounts, (GFunc)g_object_unref, NULL);
1756 	g_list_free (mounts);
1757 	return mount;
1758 }
1759 
1760 #endif
1761 
1762 void
1763 _rb_mtp_source_register_type (GTypeModule *module)
1764 {
1765 	rb_mtp_source_register_type (module);
1766 }