diff --git a/plugins/ipod/Makefile.am b/plugins/ipod/Makefile.am
index e8361f4..2c18466 100644
--- a/plugins/ipod/Makefile.am
+++ b/plugins/ipod/Makefile.am
@@ -4,6 +4,8 @@ plugin_LTLIBRARIES = libipod.la
 libipod_la_SOURCES = \
 	rb-ipod-db.c				\
 	rb-ipod-db.h				\
+	rb-ipod-helpers.c			\
+	rb-ipod-helpers.h			\
 	rb-ipod-static-playlist-source.c	\
 	rb-ipod-static-playlist-source.h	\
 	rb-ipod-source.c			\
@@ -47,7 +49,7 @@ uixml_DATA = ipod-ui.xml
 plugin_DATA = $(plugin_in_files:.rb-plugin.in=.rb-plugin)
 
 gladedir = $(plugindir)
-glade_DATA = ipod-info.glade
+glade_DATA = ipod-info.glade ipod-init.glade
 
 EXTRA_DIST = $(uixml_DATA) $(plugin_in_files) $(glade_DATA)
 
diff --git a/plugins/ipod/ipod-init.glade b/plugins/ipod/ipod-init.glade
new file mode 100644
index 0000000..4cdbec8
--- /dev/null
+++ b/plugins/ipod/ipod-init.glade
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkDialog" id="ipod_init">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">iPod detected</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkTable" id="table1">
+            <property name="visible">True</property>
+            <property name="border_width">12</property>
+            <property name="n_rows">4</property>
+            <property name="n_columns">2</property>
+            <property name="column_spacing">12</property>
+            <property name="row_spacing">12</property>
+            <child>
+              <widget class="GtkEntry" id="name_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+                <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkComboBox" id="model_combo">
+                <property name="visible">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="name_label">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">_Name:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">name_entry</property>
+              </widget>
+              <packing>
+                <property name="top_attach">3</property>
+                <property name="bottom_attach">4</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="model_label">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">_Model:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">model_combo</property>
+              </widget>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="text">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Rhythmbox has detected a device that is probably an uninitialised or corrupted iPod. It must be initialised before Rhythmbox can use it, but this will destroy any song metadata already present. If you wish Rhythmbox to initialise the iPod, please fill in the information below. If the device is not an iPod, or you do not wish to initialise it, please click cancel.</property>
+                <property name="wrap">True</property>
+                <property name="selectable">True</property>
+              </widget>
+              <packing>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="dialog_header">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">&lt;span size="x-large" weight="bold"&gt;Do you want to initialize your iPod?&lt;/span&gt;</property>
+                <property name="use_markup">True</property>
+              </widget>
+              <packing>
+                <property name="right_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="cancel_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="initialise_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label" translatable="yes">_Initialise</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">-3</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
diff --git a/plugins/ipod/rb-ipod-helpers.c b/plugins/ipod/rb-ipod-helpers.c
new file mode 100644
index 0000000..662f1b5
--- /dev/null
+++ b/plugins/ipod/rb-ipod-helpers.c
@@ -0,0 +1,535 @@
+/*
+ * rb-ipod-helpers.c
+ *
+ * Copyright (C) 2006 James "Doc" Livingston
+ * Copyright (C) 2008 Christophe Fergeau <teuf@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gpod/itdb.h>
+#ifdef HAVE_HAL
+#include <libhal.h>
+#include <dbus/dbus.h>
+#endif
+
+#include "rb-ipod-helpers.h"
+
+#include "rb-debug.h"
+#include "rb-dialog.h"
+
+
+enum {
+      COL_INFO = 0,
+};
+
+static gchar *ipod_info_to_string (const Itdb_IpodInfo *info)
+{
+	if (info->capacity >= 1) {   /* size in GB */
+		return g_strdup_printf ("%2.0f GB %s", info->capacity,
+					itdb_info_get_ipod_model_name_string (info->ipod_model));
+	} else if (info->capacity > 0) {   /* size in MB */
+		return g_strdup_printf ("%3.0f MB %s", info->capacity * 1024,
+					itdb_info_get_ipod_model_name_string (info->ipod_model));
+	} else {   /* no capacity information available */
+		return g_strdup_printf ("%s",
+					itdb_info_get_ipod_model_name_string (info->ipod_model));
+	}
+}
+
+static double 
+get_rounded_ipod_capacity (const char *mountpoint)
+{
+        guint64 capacity;
+
+        capacity = rb_ipod_helpers_get_capacity (mountpoint);
+        capacity += (1000*1000*500 - 1);
+        capacity -= (capacity % (1000*1000*500));
+        return (double)capacity/(1000.0*1000*1000);
+}
+
+static void
+set_cell (GtkCellLayout   *cell_layout,
+	  GtkCellRenderer *cell,
+	  GtkTreeModel    *tree_model,
+	  GtkTreeIter     *iter,
+	  gpointer         data)
+{
+	gboolean header;
+	gchar *text;
+	Itdb_IpodInfo *info;
+
+	gtk_tree_model_get (tree_model, iter, COL_INFO, &info, -1);
+	g_return_if_fail (info);
+
+	header = gtk_tree_model_iter_has_child (tree_model, iter);
+
+	if (header) {
+		text = g_strdup (
+				 itdb_info_get_ipod_generation_string (info->ipod_generation));
+	} else {
+		text = ipod_info_to_string (info);
+	}
+
+	g_object_set (cell,
+		      "sensitive", !header,
+		      "text", text,
+		      NULL);
+	g_free (text);
+}
+
+static gboolean
+model_equals (gconstpointer a, gconstpointer b)
+{
+	const Itdb_IpodInfo *lhs = (const Itdb_IpodInfo *)a;
+	const Itdb_IpodInfo *rhs = (const Itdb_IpodInfo *)b;
+
+	return !((lhs->capacity == rhs->capacity) 
+		 && (lhs->ipod_model == rhs->ipod_model) 
+		 && (lhs->ipod_generation == rhs->ipod_generation));
+}
+
+static GHashTable *
+build_model_table (const char *mount_path)
+{
+	const Itdb_IpodInfo *table;
+	const Itdb_IpodInfo *model_info;
+	GHashTable *models;
+	gdouble ipod_capacity;
+
+	ipod_capacity = get_rounded_ipod_capacity (mount_path);
+	models = g_hash_table_new_full (g_int_hash, g_int_equal,
+					NULL, (GDestroyNotify)g_list_free);
+	table = itdb_info_get_ipod_info_table ();
+	for (model_info = table;
+	     model_info->model_number != NULL;
+	     model_info++) {
+		GList *infos;
+
+		infos = g_hash_table_lookup (models,
+					     &model_info->ipod_generation);
+		if (g_list_find_custom (infos, model_info, model_equals)) {
+			continue;
+		}
+		if (model_info->capacity == ipod_capacity) {
+			/* Steal the key from the hash table since we don't 
+			 * want 'infos' to be g_list_freed by the hash
+			 * table destroy notify function
+			 */
+			g_hash_table_steal (models,
+					    &model_info->ipod_generation);
+			infos = g_list_prepend (infos, (gpointer)model_info);
+
+			g_hash_table_insert (models, 
+					     (gpointer)&model_info->ipod_generation, 
+					     infos);
+		}
+	}
+
+	return models;
+}
+
+struct FillModelContext {
+	GtkWidget *combo;
+	GtkTreeStore *store;
+	const Itdb_IpodInfo *ipod_info;
+};
+
+static void 
+fill_one_generation (gpointer key, gpointer value, gpointer data)
+{
+	GList *infos;
+	GList *it;
+	Itdb_IpodGeneration generation;
+	gboolean first;
+	GtkTreeIter iter;
+	struct FillModelContext *ctx;
+
+	ctx = (struct FillModelContext *)data;
+	infos = (GList *)value;
+	generation = *(Itdb_IpodGeneration*)key;
+
+	first = TRUE;
+	for (it = infos; it != NULL; it = it->next) {
+		const Itdb_IpodInfo *info;
+		GtkTreeIter iter_child;
+
+		info = (const Itdb_IpodInfo *)it->data;
+		g_assert (info->ipod_generation == generation);
+
+		if (first) {
+			gtk_tree_store_append (ctx->store, &iter, NULL);
+			gtk_tree_store_set (ctx->store, &iter, 
+					    COL_INFO, info, -1);
+			first = FALSE;
+		}
+		gtk_tree_store_append (ctx->store, &iter_child, &iter);
+		gtk_tree_store_set (ctx->store, &iter_child, 
+				    COL_INFO, info, -1);
+		if (info == ctx->ipod_info) {
+			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (ctx->combo), 
+						       &iter_child);
+		}
+	}
+}
+
+static void
+fill_model_combo (GtkWidget *combo, const char *mount_path)
+{
+	GHashTable *models;
+	Itdb_Device *device;
+	GtkTreeStore *store;
+	const Itdb_IpodInfo *ipod_info;
+	GtkCellRenderer *renderer;
+	struct FillModelContext ctx;
+
+	device = itdb_device_new ();
+	itdb_device_set_mountpoint (device, mount_path);
+	itdb_device_read_sysinfo (device);
+	ipod_info = itdb_device_get_ipod_info (device);
+	itdb_device_free (device);
+
+	store = gtk_tree_store_new (1, G_TYPE_POINTER);
+	gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store));
+
+	ctx.combo = combo;
+	ctx.store = store;
+	ctx.ipod_info = ipod_info;
+	models = build_model_table (mount_path);
+	g_hash_table_foreach (models, fill_one_generation, &ctx);
+	g_hash_table_destroy (models);
+	g_object_unref (store);
+
+	gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
+	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo),
+					    renderer,
+					    set_cell,
+					    NULL, NULL);
+}
+
+gboolean
+rb_ipod_helpers_show_first_time_dialog (GMount *mount, const char *glade_file)
+{
+	/* could be an uninitialised iPod, ask the user */
+	GladeXML *xml;
+	GtkWidget *dialog;
+	GtkWidget *widget;
+	GtkTreeModel *tree_model;
+	GtkTreeIter iter;
+	int response;
+	char *mountpoint;
+	const Itdb_IpodInfo *info;
+	char *ipod_name;
+	GFile *root;
+	GError *error = NULL;
+
+	root = g_mount_get_root (mount);
+	if (root == NULL) {
+		return FALSE;      
+	}
+	mountpoint = g_file_get_path (root);
+	g_object_unref (G_OBJECT (root));
+
+	if (mountpoint == NULL) {
+		return FALSE;
+	}
+
+	/* create message dialog with model-number combo box
+	 * and asking whether they want to initialise the iPod
+	 */
+	xml = glade_xml_new (glade_file, NULL, NULL);
+	dialog = glade_xml_get_widget (xml, "ipod_init");
+	widget = glade_xml_get_widget (xml, "model_combo");
+	fill_model_combo (widget, mountpoint);
+
+	rb_debug ("showing init dialog for ipod mount on '%s'", mountpoint);
+
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+	if (response != GTK_RESPONSE_ACCEPT) {
+		gtk_widget_destroy (dialog);
+		g_free (mountpoint);
+		return FALSE;
+	}
+
+	/* get model number and name */
+	tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+	gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+	gtk_tree_model_get (tree_model, &iter, COL_INFO, &info, -1);
+	widget = glade_xml_get_widget (xml, "name_entry");
+	ipod_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+
+	gtk_widget_destroy (dialog);
+
+	rb_debug ("attempting to init ipod on '%s', with model '%s' and name '%s'",
+		  mountpoint, info->model_number, ipod_name);
+	if (!itdb_init_ipod (mountpoint, info->model_number, ipod_name, &error)) {
+		rb_error_dialog (NULL, _("Unable to initialise new iPod"), "%s", error->message);
+
+		g_free (mountpoint);
+		g_free (ipod_name);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_free (mountpoint);
+	g_free (ipod_name);
+
+	return TRUE;
+}
+
+static gchar *
+rb_ipod_helpers_get_itunesdb_path (GMount *mount)
+{
+        GFile *root;
+        gchar *mount_point;
+        gchar *result = NULL;
+
+        root = g_mount_get_root (mount);
+        if (root != NULL) {
+                mount_point = g_file_get_path (root);
+                if (mount_point != NULL) {
+                        result = itdb_get_itunesdb_path (mount_point);
+                }
+
+                g_free (mount_point);
+                g_object_unref (root);
+        }
+
+        return result;
+}
+
+static guint64 get_fs_property (const char *mountpoint, const char *attr)
+{
+        GFile *root;
+        GFileInfo *info;
+        guint64 value;
+
+        root = g_file_new_for_path (mountpoint);
+        info = g_file_query_filesystem_info (root, attr, NULL, NULL);
+        g_object_unref (G_OBJECT (root));
+        if (info == NULL) {
+                return 0;
+        }
+        if (!g_file_info_has_attribute (info, attr)) {
+                g_object_unref (G_OBJECT (info));
+                return 0;
+        }
+        value = g_file_info_get_attribute_uint64 (info, attr);
+        g_object_unref (G_OBJECT (info));
+
+        return value;
+}
+
+guint64
+rb_ipod_helpers_get_capacity (const char *mountpoint)
+{
+        return  get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
+}
+
+guint64
+rb_ipod_helpers_get_free_space (const char *mountpoint)
+{
+        return get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
+}
+
+char *
+rb_ipod_helpers_get_device (RBSource *source)
+{
+        GMount *mount;
+        GVolume *volume;
+        char *device;
+
+        g_object_get (RB_SOURCE (source), "mount", &mount, NULL);
+        volume = g_mount_get_volume (mount);
+        device = g_volume_get_identifier (volume,
+                                          G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+        g_object_unref (G_OBJECT (volume));
+        g_object_unref (G_OBJECT (mount));
+
+        return device;
+}
+
+static gboolean
+rb_ipod_helpers_mount_has_ipod_db (GMount *mount)
+{
+        char *itunesdb_path;
+        gboolean result;
+
+        itunesdb_path = rb_ipod_helpers_get_itunesdb_path (mount);
+
+        if (itunesdb_path != NULL) {
+                result = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
+        } else {
+                result = FALSE;
+        }
+        g_free (itunesdb_path);
+
+        return result;
+}
+
+#ifdef HAVE_HAL
+static gboolean
+hal_udi_is_ipod (const char *udi)
+{
+	LibHalContext *ctx;
+	DBusConnection *conn;
+	char *parent_udi;
+	char **methods_list;
+	guint i;
+	gboolean result;
+	DBusError error;
+	gboolean inited = FALSE;
+
+	result = FALSE;
+	dbus_error_init (&error);
+
+	conn = NULL;
+	parent_udi = NULL;
+	methods_list = NULL;
+
+	ctx = libhal_ctx_new ();
+	if (ctx == NULL) {
+		/* FIXME: should we return an error somehow so that we can
+		 * fall back to a check for iTunesDB presence instead ?
+		 */
+		rb_debug ("cannot connect to HAL");
+		goto end;
+	}
+	conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+	if (conn == NULL || dbus_error_is_set (&error))
+		goto end;
+
+	libhal_ctx_set_dbus_connection (ctx, conn);
+	if (!libhal_ctx_init (ctx, &error) || dbus_error_is_set (&error))
+		goto end;
+
+	inited = TRUE;
+	parent_udi = libhal_device_get_property_string (ctx, udi,
+							"info.parent", &error);
+	if (parent_udi == NULL || dbus_error_is_set (&error))
+		goto end;
+	methods_list = libhal_device_get_property_strlist (ctx, parent_udi,
+							   "portable_audio_player.access_method.protocols", &error);
+	if (methods_list == NULL || dbus_error_is_set (&error))
+		goto end;
+	for (i = 0; methods_list[i] != NULL; i++) {
+		if (strcmp ("ipod", methods_list[i]) == 0) {
+			result = TRUE;
+			break;
+		}
+	}
+
+end:
+	g_free (parent_udi);
+	libhal_free_string_array (methods_list);
+
+	if (dbus_error_is_set (&error)) {
+		rb_debug ("Error: %s\n", error.message);
+		dbus_error_free (&error);
+		dbus_error_init (&error);
+	}
+
+	if (ctx) {
+		if (inited)
+			libhal_ctx_shutdown (ctx, &error);
+		libhal_ctx_free(ctx);
+	}
+
+	dbus_error_free (&error);
+
+	return result;
+}
+
+gboolean
+rb_ipod_helpers_is_ipod (GMount *mount)
+{
+	gchar *udi;
+        gboolean result;
+	GVolume *volume;
+
+	volume = g_mount_get_volume (mount);
+	if (volume == NULL)
+		return FALSE;
+
+	udi = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_HAL_UDI);
+	g_object_unref (volume);
+
+        if (udi == NULL) {
+                return FALSE;
+        }
+
+        result = hal_udi_is_ipod (udi);
+        g_free (udi);
+
+	return result;
+}
+
+#else
+
+gboolean
+rb_ipod_helpers_is_ipod (GMount *mount)
+{
+	GFile *root;
+	gchar *mount_point;
+	gboolean result = FALSE;
+
+	root = g_mount_get_root (mount);
+	if (root != NULL) {
+		gchar *device_dir;
+
+		mount_point = g_file_get_path (root);
+		if (mount_point != NULL) {
+			device_dir = itdb_get_device_dir (mount_point);
+			if (device_dir != NULL)  {
+				result = g_file_test (device_dir,
+						      G_FILE_TEST_IS_DIR);
+				g_free (device_dir);
+			}
+		}
+
+		g_free (mount_point);
+		g_object_unref (root);
+	}
+
+	return result;
+}
+#endif
+
+gboolean rb_ipod_helpers_needs_init (GMount *mount)
+{
+	/* This function is a useless one-liner for now, but it should check
+	 * for the existence of the firsttime file on the ipod to tell if
+	 * the ipod is new or not
+	 */
+	return (!rb_ipod_helpers_mount_has_ipod_db (mount));
+}
diff --git a/plugins/ipod/rb-ipod-helpers.h b/plugins/ipod/rb-ipod-helpers.h
new file mode 100644
index 0000000..2eef13c
--- /dev/null
+++ b/plugins/ipod/rb-ipod-helpers.h
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (C) 2008 Christophe Fergeau  <teuf@gnome.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#ifndef __RB_IPOD_HELPERS_H
+#define __RB_IPOD_HELPERS_H
+
+#include <glib.h>
+#include <rb-source.h>
+
+G_BEGIN_DECLS
+gboolean rb_ipod_helpers_show_first_time_dialog (GMount *mount,
+						 const char *glade_file);
+
+guint64 rb_ipod_helpers_get_capacity (const char *mountpoint);
+guint64 rb_ipod_helpers_get_free_space (const char *mountpoint);
+char *rb_ipod_helpers_get_device (RBSource *source);
+gboolean rb_ipod_helpers_is_ipod (GMount *mount);
+gboolean rb_ipod_helpers_needs_init (GMount *mount);
+G_END_DECLS
+
+#endif /* __RB_IPOD_HELPERS_H */
diff --git a/plugins/ipod/rb-ipod-plugin.c b/plugins/ipod/rb-ipod-plugin.c
index c330a84..e7f30a3 100644
--- a/plugins/ipod/rb-ipod-plugin.c
+++ b/plugins/ipod/rb-ipod-plugin.c
@@ -36,6 +36,7 @@
 #include <glib.h>
 #include <glib-object.h>
 
+#include "rb-ipod-helpers.h"
 #include "rb-removable-media-manager.h"
 #include "rb-sourcelist.h"
 #include "rb-source.h"
@@ -230,19 +231,33 @@ rb_ipod_plugin_source_deleted (RBiPodSource *source, RBIpodPlugin *plugin)
 static RBSource *
 create_source_cb (RBRemovableMediaManager *rmm, GMount *mount, RBIpodPlugin *plugin)
 {
-	if (rb_ipod_is_mount_ipod (mount)) {
-		RBSource *src;
-		src = RB_SOURCE (rb_ipod_source_new (plugin->shell, mount));
-
-		plugin->ipod_sources = g_list_prepend (plugin->ipod_sources, src);
-		g_signal_connect_object (G_OBJECT (src),
-					 "deleted", G_CALLBACK (rb_ipod_plugin_source_deleted),
-					 plugin, 0);
+	RBSource *src;
+	if (!rb_ipod_helpers_is_ipod (mount)) {
+		return NULL;
+	}
 
-		return src;
+	if (rb_ipod_helpers_needs_init (mount)) {
+		gboolean inited;
+		gchar *glade_file;
+		glade_file = rb_plugin_find_file (RB_PLUGIN (plugin),
+						  "ipod-init.glade");
+		inited = rb_ipod_helpers_show_first_time_dialog (mount,
+								 glade_file);
+		g_free (glade_file);
+		if (!inited) {
+			return NULL;
+		}
 	}
 
-	return NULL;
+	src = RB_SOURCE (rb_ipod_source_new (plugin->shell,
+					     mount));
+
+	plugin->ipod_sources = g_list_prepend (plugin->ipod_sources, src);
+	g_signal_connect_object (G_OBJECT (src),
+				 "deleted", G_CALLBACK (rb_ipod_plugin_source_deleted),
+				 plugin, 0);
+
+	return src;
 }
 
 static void
diff --git a/plugins/ipod/rb-ipod-source.c b/plugins/ipod/rb-ipod-source.c
index d2c562b..1583b4b 100644
--- a/plugins/ipod/rb-ipod-source.c
+++ b/plugins/ipod/rb-ipod-source.c
@@ -35,15 +35,12 @@
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 #include <gtk/gtk.h>
-#ifdef HAVE_HAL
-#include <libhal.h>
-#include <dbus/dbus.h>
-#endif
 #include <gpod/itdb.h>
 
 #include "eel-gconf-extensions.h"
 #include "rb-ipod-source.h"
 #include "rb-ipod-db.h"
+#include "rb-ipod-helpers.h"
 #include "rb-debug.h"
 #include "rb-file-helpers.h"
 #include "rb-glade-helpers.h"
@@ -71,10 +68,6 @@ static void rb_ipod_load_songs (RBiPodSource *source);
 static void impl_delete_thyself (RBSource *source);
 static GList* impl_get_ui_actions (RBSource *source);
 
-#ifdef HAVE_HAL
-static gboolean hal_udi_is_ipod (const char *udi);
-#endif
-
 #ifdef ENABLE_IPOD_WRITING
 static GList * impl_get_mime_types (RBRemovableMediaSource *source);
 static gboolean impl_track_added (RBRemovableMediaSource *source,
@@ -284,8 +277,6 @@ rb_ipod_source_new (RBShell *shell,
 	char *name;
 	char *path;
 
-	g_assert (rb_ipod_is_mount_ipod (mount));
-
 	volume = g_mount_get_volume (mount);
 	path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
 	g_object_unref (volume);
@@ -961,148 +952,6 @@ rb_ipod_load_songs (RBiPodSource *source)
 	g_object_unref (mount);
 }
 
-static gchar *
-rb_ipod_get_itunesdb_path (GMount *mount)
-{
-	GFile *root;
-	gchar *mount_point;
-	gchar *result = NULL;
-
-	root = g_mount_get_root (mount);
-	if (root != NULL) {
-		mount_point = g_file_get_path (root);
-		if (mount_point != NULL) {
-			result = itdb_get_itunes_dir (mount_point);
-		}
-
-		g_free (mount_point);
-		g_object_unref (root);
-	}
-
-	return result;
-}
-
-static gboolean
-rb_ipod_mount_has_ipod_db (GMount *mount)
-{
-	char *itunesdb_path;
-	gboolean result;
-
-	itunesdb_path = rb_ipod_get_itunesdb_path (mount);
-
-	if (itunesdb_path != NULL) {
-		result = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
-	} else {
-		result = FALSE;
-	}
-	g_free (itunesdb_path);
-
-	return result;
-}
-
-gboolean
-rb_ipod_is_mount_ipod (GMount *mount)
-{
-#ifdef HAVE_HAL
-	gchar *udi;
-	GVolume *volume;
-
-	volume = g_mount_get_volume (mount);
-	if (volume == NULL)
-		return FALSE;
-
-	udi = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_HAL_UDI);
-	g_object_unref (volume);
-
-	if (udi != NULL) {
-		gboolean result;
-
-		result = hal_udi_is_ipod (udi);
-		g_free (udi);
-		if (result == FALSE) {
-			return FALSE;
-		}
-	}
-#endif
-
-	return rb_ipod_mount_has_ipod_db (mount);
-}
-
-#ifdef HAVE_HAL
-
-static gboolean
-hal_udi_is_ipod (const char *udi)
-{
-	LibHalContext *ctx;
-	DBusConnection *conn;
-	char *parent_udi;
-	char **methods_list;
-	guint i;
-	gboolean result;
-	DBusError error;
-	gboolean inited = FALSE;
-
-	result = FALSE;
-	dbus_error_init (&error);
-
-	conn = NULL;
-	parent_udi = NULL;
-	methods_list = NULL;
-
-	ctx = libhal_ctx_new ();
-	if (ctx == NULL) {
-		/* FIXME: should we return an error somehow so that we can
-		 * fall back to a check for iTunesDB presence instead ?
-		 */
-		rb_debug ("cannot connect to HAL");
-		goto end;
-	}
-	conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
-	if (conn == NULL || dbus_error_is_set (&error))
-		goto end;
-
-	libhal_ctx_set_dbus_connection (ctx, conn);
-	if (!libhal_ctx_init (ctx, &error) || dbus_error_is_set (&error))
-		goto end;
-
-	inited = TRUE;
-	parent_udi = libhal_device_get_property_string (ctx, udi,
-							"info.parent", &error);
-	if (parent_udi == NULL || dbus_error_is_set (&error))
-		goto end;
-	methods_list = libhal_device_get_property_strlist (ctx, parent_udi,
-							   "portable_audio_player.access_method.protocols", &error);
-	if (methods_list == NULL || dbus_error_is_set (&error))
-		goto end;
-	for (i = 0; methods_list[i] != NULL; i++) {
-		if (strcmp ("ipod", methods_list[i]) == 0) {
-			result = TRUE;
-			break;
-		}
-	}
-
-end:
-	g_free (parent_udi);
-	libhal_free_string_array (methods_list);
-
-	if (dbus_error_is_set (&error)) {
-		rb_debug ("Error: %s\n", error.message);
-		dbus_error_free (&error);
-		dbus_error_init (&error);
-	}
-
-	if (ctx) {
-		if (inited)
-			libhal_ctx_shutdown (ctx, &error);
-		libhal_ctx_free(ctx);
-	}
-
-	dbus_error_free (&error);
-
-	return result;
-}
-#endif
-
 static GList*
 impl_get_ui_actions (RBSource *source)
 {
@@ -1745,57 +1594,6 @@ ipod_name_changed_cb (GtkWidget     *widget,
 	return FALSE;
 }
 
-static guint64 get_fs_property (const char *mountpoint, const char *attr)
-{
-        GFile *root;
-        GFileInfo *info;
-        guint64 value;
-
-        root = g_file_new_for_path (mountpoint);
-        info = g_file_query_filesystem_info (root, attr, NULL, NULL);
-        g_object_unref (G_OBJECT (root));
-        if (info == NULL) {
-                return 0;
-        }
-        if (!g_file_info_has_attribute (info, attr)) {
-                g_object_unref (G_OBJECT (info));
-                return 0;
-        }
-        value = g_file_info_get_attribute_uint64 (info, attr);
-        g_object_unref (G_OBJECT (info));
-
-        return value;
-}
-
-static guint64
-get_ipod_capacity (const char *mountpoint)
-{
-        return  get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
-}
-
-static guint64
-get_ipod_free_space (const char *mountpoint)
-{
-	return get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
-}
-
-static char *
-get_ipod_device (RBiPodSource *source)
-{
-        GMount *mount;
-        GVolume *volume;
-        char *device;
-
-        g_object_get (RB_SOURCE (source), "mount", &mount, NULL);
-        volume = g_mount_get_volume (mount);
-        device = g_volume_get_identifier (volume,
-                                          G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
-        g_object_unref (G_OBJECT (volume));
-        g_object_unref (G_OBJECT (mount));
-
-        return device;
-}
-
 void
 rb_ipod_source_show_properties (RBiPodSource *source)
 {
@@ -1848,10 +1646,10 @@ rb_ipod_source_show_properties (RBiPodSource *source)
  	gtk_label_set_text (GTK_LABEL (label), mp);
 
 	label = glade_xml_get_widget (xml, "progressbar-ipod-usage");
-	used = g_format_size_for_display (get_ipod_capacity (mp) - get_ipod_free_space (mp));
-	capacity = g_format_size_for_display (get_ipod_capacity(mp));
+	used = g_format_size_for_display (rb_ipod_helpers_get_capacity (mp) - rb_ipod_helpers_get_free_space (mp));
+	capacity = g_format_size_for_display (rb_ipod_helpers_get_capacity(mp));
 	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (label), 
-				       (double)(get_ipod_capacity (mp) - get_ipod_free_space (mp))/(double)get_ipod_capacity (mp));
+				       (double)(rb_ipod_helpers_get_capacity (mp) - rb_ipod_helpers_get_free_space (mp))/(double)rb_ipod_helpers_get_capacity (mp));
 	/* Translators: this is used to display the amount of storage space
 	 * used and the total storage space on an iPod.
 	 */
@@ -1862,7 +1660,7 @@ rb_ipod_source_show_properties (RBiPodSource *source)
 	g_free (used);
 
 	label = glade_xml_get_widget (xml, "label-device-node-value");
-	text = get_ipod_device (source);
+	text = rb_ipod_helpers_get_device (RB_SOURCE(source));
 	gtk_label_set_text (GTK_LABEL (label), text);
 	g_free (text);
 
diff --git a/plugins/ipod/rb-ipod-source.h b/plugins/ipod/rb-ipod-source.h
index 505a4e6..8944721 100644
--- a/plugins/ipod/rb-ipod-source.h
+++ b/plugins/ipod/rb-ipod-source.h
@@ -53,12 +53,11 @@ typedef struct
 	RBRemovableMediaSourceClass parent;
 } RBiPodSourceClass;
 
-RBRemovableMediaSource *	rb_ipod_source_new		(RBShell *shell, GMount *mount);
+RBRemovableMediaSource *rb_ipod_source_new		(RBShell *shell,
+                                                         GMount *mount);
 GType			rb_ipod_source_get_type		(void);
 GType                   rb_ipod_source_register_type    (GTypeModule *module);
 
-gboolean		rb_ipod_is_mount_ipod		(GMount *mount);
-
 void			rb_ipod_source_new_playlist	(RBiPodSource *source);
 void			rb_ipod_source_remove_playlist	(RBiPodSource *ipod_source,
 							 RBSource *source);
