Index: podcast/test-podcast-parse.c
===================================================================
--- podcast/test-podcast-parse.c	(revision 5467)
+++ podcast/test-podcast-parse.c	(working copy)
@@ -1,6 +1,7 @@
 
 #include "config.h"
 
+#include <locale.h>
 #include <glib.h>
 #include <glib/gi18n.h>
 
@@ -47,6 +48,7 @@
 	GDate date = {0,};
 	char datebuf[1024];
 
+	setlocale (LC_ALL, "");
 	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
@@ -66,7 +68,6 @@
 	g_date_strftime (datebuf, 1024, "%F %T", &date);
 
 	g_print ("Podcast title: %s\n", data->title);
-	g_print ("Summary: %s\n", data->summary);
 	g_print ("Description: %s\n", data->description);
 	g_print ("Author: %s\n", data->author);
 	g_print ("Date: %s\n", datebuf);
Index: podcast/plugin.symbols
===================================================================
--- podcast/plugin.symbols	(revision 0)
+++ podcast/plugin.symbols	(revision 0)
@@ -0,0 +1,4 @@
+NP_GetMIMEDescription
+NP_GetValue
+NP_Initialize
+NP_Shutdown
Index: podcast/rb-podcast-manager.c
===================================================================
--- podcast/rb-podcast-manager.c	(revision 5467)
+++ podcast/rb-podcast-manager.c	(working copy)
@@ -833,7 +833,7 @@
 	RBPodcastThreadInfo *info;
 	gchar *valid_url;
 
-	if (g_str_has_prefix (url, "feed://")) {
+	if (g_str_has_prefix (url, "feed://") || g_str_has_prefix (url, "itpc://")) {
 		char *tmp;
 
 		tmp = g_strdup_printf ("http://%s", url + strlen ("feed://"));
@@ -915,7 +915,7 @@
 {
 	RBPodcastChannel *feed = g_new0 (RBPodcastChannel, 1);
 
-	if (rb_podcast_parse_load_feed (feed, info->url)) {
+	if (rb_podcast_parse_load_feed (feed, info->url) && (feed->is_opml == FALSE)) {
 		RBPodcastManagerParseResult *result;
 
 		result = g_new0 (RBPodcastManagerParseResult, 1);
@@ -927,6 +927,16 @@
 				 (GSourceFunc) rb_podcast_manager_parse_complete_cb,
 				 result,
 				 (GDestroyNotify) rb_podcast_manager_free_parse_result);
+	} else if (feed->is_opml) {
+		GList *l;
+
+		rb_debug ("Loading OPML feeds from %s", info->url);
+
+		for (l = feed->posts; l != NULL; l = l->next) {
+			RBPodcastItem *item = l->data;
+			rb_podcast_manager_subscribe_feed (info->pd, item->url);
+		}
+		rb_podcast_parse_channel_free (feed);
 	}
 
 	g_object_unref (info->pd);
@@ -1568,8 +1578,6 @@
 {
 	GValue description_val = { 0, };
 	GValue title_val = { 0, };
-	GValue subtitle_val = { 0, };
-	GValue summary_val = { 0, };
 	GValue lang_val = { 0, };
 	GValue copyright_val = { 0, };
 	GValue image_val = { 0, };
@@ -1587,7 +1595,7 @@
 
 	GList *lst_songs;
 
-	if (data->title	== NULL) {
+	if (data->title == NULL) {
 		g_list_free (data->posts);
 		g_free (data);
 		return;
@@ -1640,13 +1648,6 @@
 	rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_ARTIST, &author_val);
 	g_value_unset (&author_val);
 
-	if (data->subtitle) {
-		g_value_init (&subtitle_val, G_TYPE_STRING);
-		g_value_set_string (&subtitle_val, (gchar *) data->subtitle);
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_SUBTITLE, &subtitle_val);
-		g_value_unset (&subtitle_val);
-	}
-
 	if (data->description) {
 		g_value_init (&description_val, G_TYPE_STRING);
 		g_value_set_string (&description_val, (gchar *) data->description);
@@ -1654,13 +1655,6 @@
 		g_value_unset (&description_val);
 	}
 
-	if (data->summary) {
-		g_value_init (&summary_val, G_TYPE_STRING);
-		g_value_set_string (&summary_val, (gchar *) data->summary);
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_SUMMARY, &summary_val);
-		g_value_unset (&summary_val);
-	}
-
 	if (data->lang) {
 		g_value_init (&lang_val, G_TYPE_STRING);
 		g_value_set_string (&lang_val, (gchar *) data->lang);
Index: podcast/rb-podcast-parse.c
===================================================================
--- podcast/rb-podcast-parse.c	(revision 5467)
+++ podcast/rb-podcast-parse.c	(working copy)
@@ -22,427 +22,116 @@
 
 #include "config.h"
 
-#define _XOPEN_SOURCE
-#define __EXTENSIONS__  /* get strptime */
 #include <string.h>
-#include <time.h>
 
-#include <libxml/entities.h>
-#include <libxml/SAX.h>
-#include <libxml/parserInternals.h>
+#include <totem-pl-parser.h>
 #include <libgnomevfs/gnome-vfs.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
 #include "rb-debug.h"
 #include "rb-podcast-parse.h"
+#include "rb-file-helpers.h"
 
-#define BUFFER_SIZE 256
-
-struct RBPoadcastLoadContext
-{
-	guint in_unknown_elt;
-	xmlParserCtxtPtr xmlctx;
-	GString *prop_value;
-	RBPodcastChannel *channel_data;
-	RBPodcastItem *item_data;
-
-	enum {
-		RB_PODCAST_PARSER_STATE_START,
-		RB_PODCAST_PARSER_STATE_RSS,
-		RB_PODCAST_PARSER_STATE_CHANNEL,
-		RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY,
-		RB_PODCAST_PARSER_STATE_IMG,
-		RB_PODCAST_PARSER_STATE_IMG_PROPERTY,
-		RB_PODCAST_PARSER_STATE_ITEM,
-		RB_PODCAST_PARSER_STATE_ITEM_PROPERTY,
-		RB_PODCAST_PARSER_STATE_END,
-	} state;
-};
-
-static gboolean rb_validate_channel_propert (const char *name);
-static gboolean rb_validate_item_propert (const char *name);
-static uintmax_t rb_podcast_parse_date (const char* date_str);
-static gulong rb_podcast_parse_time (const char *time_str);
-static void rb_podcast_parser_start_element (struct RBPoadcastLoadContext* ctx, const char *name, const char **attrs);
-static void rb_podcast_parser_end_element (struct RBPoadcastLoadContext* ctx, const char *name);
-static void rb_podcast_parser_characters (struct RBPoadcastLoadContext* ctx, const char *data, guint len);
-static void rb_set_channel_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
-static void rb_set_item_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
-
-static RBPodcastItem *
-rb_podcast_initializa_item ()
-{
-	RBPodcastItem *data = g_new0 (RBPodcastItem, 1);
-	return data;
-}
-
 static void
-rb_set_channel_value (struct RBPoadcastLoadContext *ctx,
-		      const char *name,
-		      const char *value)
+playlist_metadata_foreach (const char *key,
+			   const char *value,
+			   gpointer data)
 {
-	xmlChar *dvalue;
+	RBPodcastChannel *channel = (RBPodcastChannel *) data;
 
-	if (value == NULL)
-		return;
-
-	if (name == NULL)
-		return;
-
-	dvalue = xmlCharStrdup (value);
-	g_strstrip ((char *)dvalue);
-
-	if (!strcmp (name, "title")) {
-		ctx->channel_data->title = dvalue;
-	} else if (!strcmp (name, "language")) {
-		ctx->channel_data->lang = dvalue;
-	} else if (!strcmp (name, "itunes:subtitle")) {
-		ctx->channel_data->subtitle = dvalue;
-	} else if (!strcmp (name, "itunes:summary")) {
-		ctx->channel_data->summary = dvalue;
-	} else if (!strcmp (name, "description")) {
-		ctx->channel_data->description = dvalue;
-	} else if (!strcmp (name, "generator")) {
-		if (ctx->channel_data->author == NULL)
-			ctx->channel_data->author = dvalue;
-	} else if (!strcmp (name, "itunes:author")) {
-		g_free (ctx->channel_data->author);
-		ctx->channel_data->author = dvalue;
-	} else if (!strcmp (name, "webMaster")) {
-		ctx->channel_data->contact = dvalue;
-	} else if (!strcmp (name, "pubDate")) {
-		ctx->channel_data->pub_date = rb_podcast_parse_date ((char *)dvalue);
-		g_free (dvalue);
-	} else if (!strcmp (name, "copyright")) {
-		ctx->channel_data->copyright = dvalue;
-	} else if (!strcmp (name, "img")) {
-		ctx->channel_data->img = dvalue;
-	} else {
-		g_free (dvalue);
+	if (strcmp (key, TOTEM_PL_PARSER_FIELD_TITLE) == 0) {
+		channel->title = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_LANGUAGE) == 0) {
+		channel->lang = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DESCRIPTION) == 0) {
+		channel->description = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_AUTHOR) == 0) {
+		channel->author = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_CONTACT) == 0) {
+		channel->contact = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_IMAGE_URL) == 0) {
+		channel->img = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_PUB_DATE) == 0) {
+		channel->pub_date = totem_pl_parser_parse_date (value, FALSE);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_COPYRIGHT) == 0) {
+		channel->copyright = g_strdup (value);
 	}
 }
 
 static void
-rb_set_item_value (struct RBPoadcastLoadContext *ctx,
-		   const char *name,
-		   const char *value)
+playlist_started (TotemPlParser *parser,
+		  const char *uri,
+		  GHashTable *metadata,
+		  gpointer data)
 {
-	xmlChar *dvalue;
-
-	dvalue = xmlCharStrdup (value);
-	g_strstrip ((char *)dvalue);
-
-	if (!strcmp (name, "title")) {
-		ctx->item_data->title = dvalue;
-	} else if (!strcmp (name, "url")) {
-		ctx->item_data->url = dvalue;
-	} else if (!strcmp (name, "pubDate")) {
-		ctx->item_data->pub_date = rb_podcast_parse_date ((char *)dvalue);
-		g_free (dvalue);
-	} else if (!strcmp (name, "description")) {
-		ctx->item_data->description = dvalue;
-	} else if (!strcmp (name, "author")) {
-		ctx->item_data->author = dvalue;
-	} else if (!strcmp (name, "itunes:duration")) {
-		ctx->item_data->duration = rb_podcast_parse_time ((char *)dvalue);
-		g_free (dvalue);
-	} else if (!strcmp (name, "length")) {
-		ctx->item_data->filesize = g_ascii_strtoull ((char *)dvalue, NULL, 10);
-	} else {
-		g_free (dvalue);
-	}
+	g_hash_table_foreach (metadata, (GHFunc) playlist_metadata_foreach, data);
 }
 
 static void
-rb_insert_item (struct RBPoadcastLoadContext *ctx)
+playlist_ended (TotemPlParser *parser,
+		const char *uri,
+		gpointer data)
 {
-	RBPodcastItem *data = ctx->item_data;
+	RBPodcastChannel *channel = (RBPodcastChannel *) data;
 
-	rb_debug ("Inserting item as post");
-
-	if (!data->url) {
-		rb_debug ("Item does not have a URL, skipping");
-		return;
-	}
-
-	ctx->channel_data->posts = g_list_prepend (ctx->channel_data->posts, ctx->item_data);
+	channel->posts = g_list_reverse (channel->posts);
 }
 
-static gboolean
-rb_validate_channel_propert (const char *name)
-{
-	if (name == NULL) {
-		return FALSE;
-	}
-
-	if (!strcmp (name, "title") ||
-	    !strcmp (name, "language") ||
-	    !strcmp (name, "itunes:subtitle") ||
-	    !strcmp (name, "itunes:summary") ||
-	    !strcmp (name, "description") ||
-	    !strcmp (name, "generator") ||
-	    !strcmp (name, "itunes:author") ||
-	    !strcmp (name, "webMaster") ||
-	    !strcmp (name, "lastBuildDate") ||
-	    !strcmp (name, "pubDate") ||
-	    !strcmp (name, "copyright")) {
-		return TRUE;
-	} else {
-		return FALSE;
-	}
-
-}
-
-static gboolean
-rb_validate_item_propert (const char *name)
-{
-	if (name == NULL) {
-		return FALSE;
-	}
-
-	if (!strcmp (name, "title") ||
-	    !strcmp (name, "url") ||
-	    !strcmp (name, "pubDate") ||
-	    !strcmp (name, "description") ||
-	    !strcmp (name, "author") ||
-	    !strcmp (name, "itunes:duration") ) {
-
-		return TRUE;
-	} else {
-		return FALSE;
-	}
-}
-
 static void
-rb_podcast_parser_start_element (struct RBPoadcastLoadContext *ctx,
-				 const char *name,
-				 const char **attrs)
+entry_metadata_foreach (const char *key,
+			const char *value,
+			gpointer data)
 {
+	RBPodcastItem *item = (RBPodcastItem *) data;
 
-	rb_debug ("Start element: %s state: %d", name, ctx->state);
-
-	switch (ctx->state) {
-        case RB_PODCAST_PARSER_STATE_START:
-		{
-			if (!strcmp (name, "rss")) {
-				ctx->state = RB_PODCAST_PARSER_STATE_RSS;
-			} else {
-				ctx->in_unknown_elt++;
-			}
-
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_RSS:
-		{
-			if (!strcmp (name, "channel")) {
-				ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL;
-			} else {
-				ctx->in_unknown_elt++;
-			}
-
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_CHANNEL:
-		{
-			if (strcmp (name, "image") == 0)
-			{
-				ctx->state = RB_PODCAST_PARSER_STATE_IMG;
-			} else if (strcmp (name, "itunes:image") == 0) {
-				for (; attrs && *attrs; attrs +=2) {
-					if (!strcmp (*attrs, "href")) {
-						const char *href_value = *(attrs + 1);
-						rb_set_channel_value (ctx, "img", href_value);
-					}
-				}
-
-				ctx->state = RB_PODCAST_PARSER_STATE_IMG;
-
-			} else if (!strcmp (name, "item")) {
-				ctx->item_data = rb_podcast_initializa_item ();
-				ctx->state = RB_PODCAST_PARSER_STATE_ITEM;
-			} else if (!rb_validate_channel_propert (name)) {
-				rb_debug ("Unknown property");
-				ctx->in_unknown_elt++;
-			} else {
-				ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY;
-			}
-
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_ITEM:
-		{
-			if (!strcmp (name, "enclosure")) {
-				for (; *attrs; attrs +=2) {
-					if (!strcmp (*attrs, "url")) {
-						const char *url_value = *(attrs + 1);
-						rb_set_item_value (ctx, "url", url_value);
-					} else if (!strcmp (*attrs, "length")) {
-						const char *length_value = *(attrs + 1);
-						rb_set_item_value (ctx, "length", length_value);
-					}
-				}
-
-				ctx->state = RB_PODCAST_PARSER_STATE_ITEM_PROPERTY;
-
-			} else if (!rb_validate_item_propert (name)) {
-				ctx->in_unknown_elt++;
-			} else {
-				ctx->state = RB_PODCAST_PARSER_STATE_ITEM_PROPERTY;
-			}
-
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_IMG:
-		{
-			if (strcmp (name, "url") != 0) {
-				ctx->in_unknown_elt++;
-			} else {
-				ctx->state = RB_PODCAST_PARSER_STATE_IMG_PROPERTY;
-			}
-
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY:
-        case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY:
-        case RB_PODCAST_PARSER_STATE_IMG_PROPERTY:
-		rb_debug ("nested element inside property; treating as unknown");
-		ctx->in_unknown_elt++;
-		break;
-
-        case RB_PODCAST_PARSER_STATE_END:
-		break;
-	default:
-		g_warning ("Unknown podcast parser state: %d", ctx->state);
-		break;
+	if (strcmp (key, TOTEM_PL_PARSER_FIELD_TITLE) == 0) {
+		item->title = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_URL) == 0) {
+		item->url = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DESCRIPTION) == 0) {
+		item->description = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_AUTHOR) == 0) {
+		item->author = g_strdup (value);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_PUB_DATE) == 0) {
+		item->pub_date = totem_pl_parser_parse_date (value, FALSE);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DURATION) == 0) {
+		item->duration = totem_pl_parser_parse_duration (value, FALSE);
+	} else if (strcmp (key, TOTEM_PL_PARSER_FIELD_FILESIZE) == 0) {
+		item->filesize = g_ascii_strtoull (value, NULL, 10);
 	}
 }
 
 static void
-rb_podcast_parser_end_element (struct RBPoadcastLoadContext *ctx,
-			       const char *name)
+entry_parsed (TotemPlParser *parser,
+	      const char *uri,
+	      GHashTable *metadata,
+	      gpointer data)
 {
-	rb_debug ("End element: %s state: %d", name, ctx->state);
+	RBPodcastChannel *channel = (RBPodcastChannel *) data;
+	RBPodcastItem *item;
 
-	if (ctx->in_unknown_elt > 0) {
-		ctx->in_unknown_elt--;
-		rb_debug ("Unknown element");
-		return;
-	}
-
-	switch (ctx->state) {
-        case RB_PODCAST_PARSER_STATE_START:
-		ctx->state = RB_PODCAST_PARSER_STATE_END;
-		break;
-
-        case RB_PODCAST_PARSER_STATE_RSS:
-		ctx->state = RB_PODCAST_PARSER_STATE_START;
-		break;
-
-        case RB_PODCAST_PARSER_STATE_CHANNEL:
-		ctx->state = RB_PODCAST_PARSER_STATE_RSS;
-		break;
-
-        case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY:
-		{
-			rb_set_channel_value (ctx, name, ctx->prop_value->str);
-			ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL;
-			g_string_truncate (ctx->prop_value, 0);
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_ITEM:
-		{
-			rb_insert_item (ctx);
-			ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL;
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY:
-		{
-			rb_set_item_value (ctx, name, ctx->prop_value->str);
-			ctx->state = RB_PODCAST_PARSER_STATE_ITEM;
-			g_string_truncate (ctx->prop_value, 0);
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_IMG_PROPERTY:
-		{
-			rb_set_channel_value (ctx, "img", ctx->prop_value->str);
-			ctx->state = RB_PODCAST_PARSER_STATE_IMG;
-			g_string_truncate (ctx->prop_value, 0);
-			break;
-		}
-
-        case RB_PODCAST_PARSER_STATE_IMG:
-		ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL;
-		break;
-
-        case RB_PODCAST_PARSER_STATE_END:
-		break;
-
-	default:
-		g_warning ("Unknown podcast parser state: %d", ctx->state);
-		break;
-	}
+	item = g_new0 (RBPodcastItem, 1);
+	g_hash_table_foreach (metadata, (GHFunc) entry_metadata_foreach, item);
+	channel->posts = g_list_prepend (channel->posts, item);
 }
 
-static void
-rb_podcast_parser_characters (struct RBPoadcastLoadContext *ctx,
-			      const char *data,
-			      guint len)
-{
-	switch (ctx->state) {
-        case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY:
-        case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY:
-        case RB_PODCAST_PARSER_STATE_IMG_PROPERTY:
-		g_string_append_len (ctx->prop_value, data, len);
-           	break;
-        case RB_PODCAST_PARSER_STATE_START:
-        case RB_PODCAST_PARSER_STATE_IMG:
-        case RB_PODCAST_PARSER_STATE_RSS:
-        case RB_PODCAST_PARSER_STATE_CHANNEL:
-        case RB_PODCAST_PARSER_STATE_ITEM:
-        case RB_PODCAST_PARSER_STATE_END:
-		break;
-	default:
-		g_warning ("Unknown podcast parser state: %d", ctx->state);
-		break;
-	}
-}
-
 gboolean
 rb_podcast_parse_load_feed (RBPodcastChannel *data,
 			    const char *file_name)
 {
-	xmlParserCtxtPtr parser;
-	xmlSAXHandlerPtr sax_handler = NULL;
 	GnomeVFSResult result;
 	GnomeVFSFileInfo *info;
-	gint file_size;
-	gchar *buffer = NULL;
-	const char *query_string;
+	TotemPlParser *plparser;
 
-	struct RBPoadcastLoadContext *ctx = NULL;
+	data->url = g_strdup (file_name);
 
-	data->url = xmlCharStrdup (file_name);
-
-	/* if the URL has a .rss or .xml extension (before the query string),
+	/* if the URL has a .rss, .xml or .atom extension (before the query string),
 	 * don't bother checking the MIME type.
 	 */
-	query_string = strchr (file_name, '?');
-	if (query_string == NULL) {
-		query_string = file_name + strlen (file_name);
-	}
-
-	if (strncmp (query_string - 4, ".rss", 4) == 0 ||
-	    strncmp (query_string - 4, ".xml", 4) == 0) {
-		rb_debug ("not checking mime type for %s", file_name);
+	if (rb_uri_could_be_podcast (file_name, &data->is_opml)) {
+		rb_debug ("not checking mime type for %s (should be %s file)", file_name,
+			  data->is_opml ? "OPML" : "Podcast");
 	} else {
 		gboolean invalid_mime_type;
 
@@ -451,22 +140,33 @@
 
 		result = gnome_vfs_get_file_info (file_name, info, GNOME_VFS_FILE_INFO_DEFAULT);
 
+		if ((result != GNOME_VFS_OK)) {
+			if (info->mime_type != NULL) {
+				rb_debug ("Invalid mime-type in podcast feed %s", info->mime_type);
+			} else {
+				rb_debug ("Couldn't get mime type for %s: %s", file_name,
+					  gnome_vfs_result_to_string (result));
+			}
+			gnome_vfs_file_info_unref (info);
+			return TRUE;
+		}
+
 		if (info != NULL
 		    && info->mime_type != NULL
 		    && strstr (info->mime_type, "html") == NULL
 		    && strstr (info->mime_type, "xml") == NULL
-		    && strstr (info->mime_type, "rss") == NULL) {
+		    && strstr (info->mime_type, "rss") == NULL
+		    && strstr (info->mime_type, "opml") == NULL) {
 			invalid_mime_type = TRUE;
+		} else if (info != NULL
+			   && info->mime_type != NULL
+			   && strstr (info->mime_type, "opml") != NULL) {
+			data->is_opml = TRUE;
+			invalid_mime_type = FALSE;
 		} else {
 			invalid_mime_type = FALSE;
 		}
 
-		if ((result != GNOME_VFS_OK)) {
-			rb_debug ("Invalid mime-type in podcast feed %s", info->mime_type);
-			gnome_vfs_file_info_unref (info);
-			return TRUE;
-		}
-
 		if (invalid_mime_type) {
 			GtkWidget *dialog;
 
@@ -492,173 +192,22 @@
 			return FALSE;
 	}
 
-	/* first download file by gnome_vfs for use gnome network configuration */
-	rb_debug ("reading podcast feed %s", file_name);
-	result = gnome_vfs_read_entire_file (file_name, &file_size, &buffer);
-	if (result != GNOME_VFS_OK)
-		return TRUE;
+	plparser = totem_pl_parser_new ();
+	g_object_set (plparser, "recurse", FALSE, NULL);
+	g_signal_connect (G_OBJECT (plparser), "entry-parsed", G_CALLBACK (entry_parsed), data);
+	g_signal_connect (G_OBJECT (plparser), "playlist-started", G_CALLBACK (playlist_started), data);
+	g_signal_connect (G_OBJECT (plparser), "playlist-ended", G_CALLBACK (playlist_ended), data);
 
-	/* initializing parse */
-	sax_handler = g_new0 (xmlSAXHandler, 1);
-	sax_handler->startElement = (startElementSAXFunc) rb_podcast_parser_start_element;
-	sax_handler->endElement = (endElementSAXFunc) rb_podcast_parser_end_element;
-	sax_handler->characters = (charactersSAXFunc) rb_podcast_parser_characters;
-	xmlSubstituteEntitiesDefault (1);
-
-	ctx = g_new0 (struct RBPoadcastLoadContext, 1);
-	ctx->in_unknown_elt = 0;
-	ctx->channel_data = data;
-	ctx->prop_value = g_string_sized_new (512);
-
-	parser = xmlCreateMemoryParserCtxt (buffer, file_size);
-	if (parser == NULL) {
-		g_free (sax_handler);
-		g_free (buffer);
-		g_string_free (ctx->prop_value, TRUE);
-		g_free (ctx);
+	if (totem_pl_parser_parse (plparser, file_name, FALSE) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
+		rb_debug ("Parsing %s as a Podcast failed", file_name);
+		g_object_unref (plparser);
 		return FALSE;
 	}
+	rb_debug ("Parsing %s as a Podcast succeeded", file_name);
 
-	ctx->xmlctx = parser;
-	parser->userData = ctx;
-	parser->sax = sax_handler;
-	xmlParseDocument (parser);
-
-	g_free (sax_handler);
-	parser->sax = NULL;
-	xmlFreeParserCtxt (parser);
-
-	g_free (buffer);
-	g_string_free (ctx->prop_value, TRUE);
-	g_free (ctx);
-
-	data->posts = g_list_reverse (data->posts);
 	return TRUE;
 }
 
-static uintmax_t
-rb_podcast_parse_date (const char *date_str)
-{
-	struct tm tm;
-	char *result;
-
-	/* RFC 2822 date format */
-	result = strptime (date_str, "%a, %d %b %Y %T", &tm);
-
-	/* same as above, but without comma */
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%a %d %b %Y %T", &tm);
-	}
-
-	/* close-to-RFC 2822, but with extra 0 */
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%a, %d %b %Y 0%T", &tm);
-	}
-
-	/* close-to-RFC 2822, but with no seconds */
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%a, %d %b %Y %R", &tm);
-	}
-
-	/* format without weekday */
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%d %b %Y %T", &tm);
-	}
-
-	/* reversed day and long month */
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%a, %B %d %Y %T", &tm);
-	}
-
-	/* ISO date like */
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%Y-%m-%d %T", &tm);
-	}
-
-	/* ISO date like without timezone */
-	if (result == NULL) {
-	memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%Y-%m-%d", &tm);
-	}
-
-	/* Broken weekday short names */
-	if (result == NULL) {
-		char *tmp;
-
-		/* strip off the erroneous weekday */
-		tmp = strstr (date_str, ",");
-		if (tmp != NULL) {
-			tmp++;
-			memset (&tm, 0, sizeof (struct tm));
-			result = strptime (tmp, "%d %b %Y %T", &tm);
-		}
-	}
-
-	/* format with timezone offset from GMT */
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (date_str, "%a %b %d %T %z %Y", &tm);
-	}
-
-	/* format with timezone name */
-	if (result == NULL) {
-		char *tmp;
-
-		memset (&tm, 0, sizeof (struct tm));
-
-		/* match first part of time string */
-		result = strptime (date_str, "%a %b %d %T ", &tm);
-
-		/* look for anything with a timezone name-like format
-		   i.e. at least one all caps alphabetical character */
-		if (result != NULL) {
-			size_t n;
-
-			n = strspn(result, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-			tmp = result+n;
-
-			/* make sure there was at least one character that matched */
-			if ((tmp != NULL) && n > 0)
-				/* remaining part must be the year */
-				result = strptime (tmp, "%Y", &tm);
-			else
-				result = NULL;
-		}
-	}
-
-	if (result == NULL) {
-		rb_debug ("unable to convert date string %s", date_str);
-	}
-
-	return (uintmax_t) ( (result==NULL) ? 0 : mktime (&tm) );
-}
-
-static gulong
-rb_podcast_parse_time (const char *time_str)
-{
-	struct tm tm;
-	char *result;
-
-	memset (&tm, 0, sizeof (struct tm));
-	result = strptime (time_str, "%H:%M:%S", &tm);
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		result = strptime (time_str, "%M:%S", &tm);
-	}
-	if (result == NULL) {
-		memset (&tm, 0, sizeof (struct tm));
-		rb_debug ("unable to convert duration string %s", time_str);
-	}
-
-	return ((tm.tm_hour * 60 + tm.tm_min) * 60 + tm.tm_sec);
-}
-
 void
 rb_podcast_parse_channel_free (RBPodcastChannel *data)
 {
@@ -671,8 +220,6 @@
 	g_free (data->url);
 	g_free (data->title);
 	g_free (data->lang);
-	g_free (data->subtitle);
-	g_free (data->summary);
 	g_free (data->description);
 	g_free (data->author);
 	g_free (data->contact);
Index: podcast/Makefile.am
===================================================================
--- podcast/Makefile.am	(revision 5467)
+++ podcast/Makefile.am	(working copy)
@@ -6,6 +6,9 @@
 	rb-podcast-parse.c				\
 	rb-podcast-parse.h
 
+librbpodcast_parse_la_LIBADD =				\
+	$(top_builddir)/lib/librb.la
+
 librbpodcast_la_SOURCES =				\
 	rb-feed-podcast-properties-dialog.c		\
 	rb-feed-podcast-properties-dialog.h		\
@@ -23,9 +26,10 @@
 	test-podcast-parse.c
 test_podcast_parse_LDADD =				\
 	librbpodcast_parse.la				\
-	$(RHYTHMBOX_LIBS)
+	$(RHYTHMBOX_LIBS)				\
+	$(TOTEM_PLPARSER_LIBS)
 
-INCLUDES =						\
+AM_CFLAGS =						\
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
 	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
 	-I$(top_srcdir) 				\
@@ -36,7 +40,25 @@
 	-I$(top_srcdir)/metadata			\
 	-I$(top_srcdir)/library				\
 	-I$(top_builddir)/lib 				\
-	$(RHYTHMBOX_CFLAGS)
+	$(RHYTHMBOX_CFLAGS)				\
+	$(TOTEM_PLPARSER_CFLAGS)
 
 librbpodcast_la_LDFLAGS = -export-dynamic
 
+if ENABLE_BROWSER_PLUGIN
+plugindir = ${libdir}/mozilla/plugins
+plugin_LTLIBRARIES = librhythmbox-itms-detection-plugin.la
+librhythmbox_itms_detection_plugin_la_SOURCES = rhythmbox-itms-plugin.cpp
+librhythmbox_itms_detection_plugin_la_CXXFLAGS = $(BROWSER_PLUGIN_CFLAGS)
+librhythmbox_itms_detection_plugin_la_LIBADD = $(BROWSER_PLUGIN_LIBS)
+
+librhythmbox_itms_detection_plugin_la_LDFLAGS = \
+	-avoid-version	\
+	-export-symbols $(srcdir)/plugin.symbols \
+	-module		\
+	$(AM_LDFLAGS)
+
+endif
+
+EXTRA_DIST = rhythmbox-itms-plugin.cpp plugin.symbols
+
Index: podcast/rhythmbox-itms-plugin.cpp
===================================================================
--- podcast/rhythmbox-itms-plugin.cpp	(revision 0)
+++ podcast/rhythmbox-itms-plugin.cpp	(revision 0)
@@ -0,0 +1,215 @@
+/*
+ *  Copyright © 2007 Bastien Nocera <hadess@hadess.net>
+ *  Copyright © 2005 Jorn Baayen <jbaayen@gnome.org>
+ *  Copyright © 2005 Christian Persch
+ *
+ *  Based on the work of:
+ *
+ *  Copyright © 2004 Bastien Nocera <hadess@hadess.net>
+ *  Copyright © 2002 David A. Schleef <ds@schleef.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib.h>
+#include <npupp.h>
+
+static NPNetscapeFuncs mozilla_functions;
+
+static NPError
+plugin_new_instance (NPMIMEType mime_type,
+		     NPP instance,
+		     guint16 mode,
+		     gint16 argc,
+		     char **argn,
+		     char **argv,
+		     NPSavedData *saved)
+{
+	return NPERR_INVALID_INSTANCE_ERROR;
+}
+
+static NPError
+plugin_destroy_instance (NPP instance,
+			 NPSavedData **save)
+{
+	return NPERR_NO_ERROR;
+}
+
+static NPError
+plugin_new_stream (NPP instance,
+		   NPMIMEType type,
+		   NPStream *stream_ptr,
+		   NPBool seekable,
+		   guint16 *stype)
+{
+	return NPERR_INVALID_PARAM;
+}
+
+static NPError
+plugin_stream_as_file (NPP instance,
+		       NPStream* stream,
+		       const char *filename)
+{
+	return NPERR_INVALID_PARAM;
+}
+
+static NPError
+plugin_destroy_stream (NPP instance,
+		       NPStream *stream,
+		       NPError reason)
+{
+	return NPERR_NO_ERROR;
+}
+
+static int32
+plugin_write_ready (NPP instance,
+		    NPStream *stream)
+{
+	return 0;
+}
+
+static int32
+plugin_write (NPP instance,
+	      NPStream *stream,
+	      int32 offset,
+	      int32 len,
+	      gpointer buffer)
+{
+	return -1;
+}
+
+static NPError
+plugin_get_value (NPP instance,
+		  NPPVariable variable,
+		  gpointer value)
+{
+	NPError err = NPERR_NO_ERROR;
+
+	switch (variable) {
+	case NPPVpluginNameString:
+		*((char **) value) = "iTunes Application Detector";
+		break;
+
+	case NPPVpluginDescriptionString:
+		*((char **) value) = "This plug-in detects the presence of iTunes when opening iTunes Store URLs in a web page with Firefox.";
+		break;
+
+	case NPPVpluginNeedsXEmbed:
+		*((NPBool *) value) = PR_FALSE;
+		break;
+
+	default:
+		err = NPERR_INVALID_PARAM;
+		break;
+	}
+
+	return err;
+}
+
+NPError
+NP_GetValue (void *future,
+	     NPPVariable variable,
+	     gpointer value)
+{
+	return plugin_get_value (NULL, variable, value);
+}
+
+char *
+NP_GetMIMEDescription (void)
+{
+	return "application/itunes-plugin::;";
+}
+
+NPError
+NP_Initialize (NPNetscapeFuncs *moz_funcs,
+               NPPluginFuncs *plugin_funcs)
+{
+	if (moz_funcs == NULL || plugin_funcs == NULL)
+		return NPERR_INVALID_FUNCTABLE_ERROR;
+
+	if ((moz_funcs->version >> 8) > NP_VERSION_MAJOR)
+		return NPERR_INCOMPATIBLE_VERSION_ERROR;
+	if (moz_funcs->size < sizeof (NPNetscapeFuncs))
+		return NPERR_INVALID_FUNCTABLE_ERROR;
+	if (plugin_funcs->size < sizeof (NPPluginFuncs))
+		return NPERR_INVALID_FUNCTABLE_ERROR;
+
+	/*
+	 * Copy all of the fields of the Mozilla function table into our
+	 * copy so we can call back into Mozilla later.  Note that we need
+	 * to copy the fields one by one, rather than assigning the whole
+	 * structure, because the Mozilla function table could actually be
+	 * bigger than what we expect.
+	 */
+	mozilla_functions.size             = moz_funcs->size;
+	mozilla_functions.version          = moz_funcs->version;
+	mozilla_functions.geturl           = moz_funcs->geturl;
+	mozilla_functions.posturl          = moz_funcs->posturl;
+	mozilla_functions.requestread      = moz_funcs->requestread;
+	mozilla_functions.newstream        = moz_funcs->newstream;
+	mozilla_functions.write            = moz_funcs->write;
+	mozilla_functions.destroystream    = moz_funcs->destroystream;
+	mozilla_functions.status           = moz_funcs->status;
+	mozilla_functions.uagent           = moz_funcs->uagent;
+	mozilla_functions.memalloc         = moz_funcs->memalloc;
+	mozilla_functions.memfree          = moz_funcs->memfree;
+	mozilla_functions.memflush         = moz_funcs->memflush;
+	mozilla_functions.reloadplugins    = moz_funcs->reloadplugins;
+	mozilla_functions.getJavaEnv       = moz_funcs->getJavaEnv;
+	mozilla_functions.getJavaPeer      = moz_funcs->getJavaPeer;
+	mozilla_functions.geturlnotify     = moz_funcs->geturlnotify;
+	mozilla_functions.posturlnotify    = moz_funcs->posturlnotify;
+	mozilla_functions.getvalue         = moz_funcs->getvalue;
+	mozilla_functions.setvalue         = moz_funcs->setvalue;
+	mozilla_functions.invalidaterect   = moz_funcs->invalidaterect;
+	mozilla_functions.invalidateregion = moz_funcs->invalidateregion;
+	mozilla_functions.forceredraw      = moz_funcs->forceredraw;
+	mozilla_functions.geturl           = moz_funcs->geturl;
+
+	/*
+	 * Set up a plugin function table that Mozilla will use to call
+	 * into us.  Mozilla needs to know about our version and size and
+	 * have a UniversalProcPointer for every function we implement.
+	 */
+
+	plugin_funcs->size = sizeof (NPPluginFuncs);
+	plugin_funcs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
+	plugin_funcs->newp = NewNPP_NewProc (plugin_new_instance);
+	plugin_funcs->destroy = NewNPP_DestroyProc (plugin_destroy_instance);
+	plugin_funcs->setwindow = NewNPP_SetWindowProc (NULL);
+	plugin_funcs->newstream = NewNPP_NewStreamProc (plugin_new_stream);
+	plugin_funcs->destroystream = NewNPP_DestroyStreamProc (plugin_destroy_stream);
+	plugin_funcs->asfile = NewNPP_StreamAsFileProc (plugin_stream_as_file);
+	plugin_funcs->writeready = NewNPP_WriteReadyProc (plugin_write_ready);
+	plugin_funcs->write = NewNPP_WriteProc (plugin_write);
+	plugin_funcs->print = NewNPP_PrintProc (NULL);
+	plugin_funcs->event = NewNPP_HandleEventProc (NULL);
+	plugin_funcs->urlnotify = NewNPP_URLNotifyProc (NULL);
+	plugin_funcs->javaClass = NULL;
+	plugin_funcs->getvalue = NewNPP_GetValueProc (plugin_get_value);
+	plugin_funcs->setvalue = NewNPP_SetValueProc (NULL);
+
+	return NPERR_NO_ERROR;
+}
+
+NPError
+NP_Shutdown (void)
+{
+	return NPERR_NO_ERROR;
+}
Index: podcast/rb-podcast-parse.h
===================================================================
--- podcast/rb-podcast-parse.h	(revision 5467)
+++ podcast/rb-podcast-parse.h	(working copy)
@@ -23,36 +23,34 @@
 #define RB_PODCAST_PARSE_H
 
 #include <glib.h>
-#include <libxml/xmlstring.h>
-#include <inttypes.h>
 
 typedef struct
 {
-	xmlChar* title;
-	xmlChar* url;
-	xmlChar* description;
-	xmlChar* author;
-	uintmax_t pub_date;
+	char* title;
+	char* url;
+	char* description;
+	char* author;
+	guint64 pub_date;
 	gulong duration;
 	guint64 filesize;
-}RBPodcastItem;
+} RBPodcastItem;
 
 typedef struct
 {
-	xmlChar* url;
-	xmlChar* title;
-	xmlChar* lang;
-    	xmlChar* subtitle;
-    	xmlChar* summary;
-	xmlChar* description;
-	xmlChar* author;
-	xmlChar* contact;
-	xmlChar* img;
-	uintmax_t pub_date;
-    	xmlChar* copyright;
+	char* url;
+	char* title;
+	char* lang;
+    	char* description;
+	char* author;
+	char* contact;
+	char* img;
+	guint64 pub_date;
+    	char* copyright;
 
+    	gboolean is_opml;
+
 	GList *posts;
-}RBPodcastChannel;
+} RBPodcastChannel;
 
 gboolean rb_podcast_parse_load_feed	(RBPodcastChannel *data, const char *file_name);
 void rb_podcast_parse_channel_free 	(RBPodcastChannel *data);
Index: configure.ac
===================================================================
--- configure.ac	(revision 5467)
+++ configure.ac	(working copy)
@@ -19,8 +19,9 @@
 
 AC_ISC_POSIX
 AC_PROG_CC
+AC_PROG_CXX
 AC_STDC_HEADERS
-AM_PROG_LIBTOOL
+AM_PROG_LIBTOOL()
 AC_C_BIGENDIAN
 AC_CHECK_SIZEOF(long)
 
@@ -35,7 +36,7 @@
 LIBGPOD_REQS=0.4
 MUSICBRAINZ_REQS=2.1.0
 NCB_MIN_REQS=2.9.0
-TOTEM_PLPARSER_REQS=1.1.5
+TOTEM_PLPARSER_REQS=2.21.0
 VALA_REQS=0.0.8
 
 AC_MSG_CHECKING([for GNU extension fwrite_unlocked])
@@ -967,7 +968,58 @@
 AM_PATH_CHECK([], have_check=yes, have_check=no)
 AM_CONDITIONAL([HAVE_CHECK],[test "x$have_check" = "xyes"])
 
+dnl ================================================================
+dnl Browser plugin
+dnl ================================================================
 
+AC_ARG_ENABLE([browser-plugin],
+	[AS_HELP_STRING([--enable-browser-plugin],[compile the iTunes detection browser plugin])],
+	[],[enable_browser_plugin=autodetect])
+
+if test "$enable_browser_plugin" != "no" ; then
+	AC_MSG_CHECKING([which gecko to use])
+
+	AC_ARG_WITH([gecko],
+		[AS_HELP_STRING([--with-gecko],[Which gecko engine to use (default: autodetect)])])
+
+	GECKOS="xulrunner firefox mozilla-firefox seamonkey mozilla"
+	gecko=$with_gecko
+
+	if test -z "$with_gecko"; then
+		dnl Autodetect gecko
+		for g in $GECKOS; do
+			if $PKG_CONFIG --exists $g-plugin; then
+				gecko=$g
+				break;
+			fi
+		done
+	elif ! $PKG_CONFIG --exists $gecko-plugin; then
+		AC_MSG_ERROR([Gecko "$gecko" not found])
+	fi
+
+	if test -z "$gecko" -a "$enable_browser_plugin" = "autodetect"; then
+		dnl No gecko found, disable plugin
+		AC_MSG_WARN([No gecko found, disabling plugin])
+		enable_browser_plugin=no
+	elif test -z "$gecko"; then
+		AC_MSG_ERROR([No gecko found])
+	elif ! ( echo "$GECKOS" | egrep "(^| )$gecko(\$| )" > /dev/null); then
+		AC_MSG_ERROR([Unknown gecko "$gecko" specified])
+	else
+		enable_browser_plugin=yes
+	fi
+
+	AC_MSG_RESULT([$gecko])
+fi
+
+if test "$enable_browser_plugin" = "yes" ; then
+	PKG_CHECK_MODULES([BROWSER_PLUGIN],
+		[glib-2.0 $gecko-plugin],
+		[],[enable_browser_plugins=no])
+fi
+
+AM_CONDITIONAL([ENABLE_BROWSER_PLUGIN], test x$enable_browser_plugin = xyes)
+
 dnl ================================================================
 dnl end-game
 dnl ================================================================
@@ -1168,4 +1220,10 @@
 	AC_MSG_NOTICE([   FM radio support disabled])
 fi
 
+if test "x$enable_browser_plugin" != xno; then
+	AC_MSG_NOTICE([** iTunes detection browser plugin (for podcasts) enabled])
+else
+	AC_MSG_NOTICE([   iTunes detection browser plugin (for podcasts) disabled])
+fi
+
 AC_MSG_NOTICE([End options])
Index: lib/rb-file-helpers.c
===================================================================
--- lib/rb-file-helpers.c	(revision 5467)
+++ lib/rb-file-helpers.c	(working copy)
@@ -774,6 +774,65 @@
 	return g_utf8_strrchr (text_uri, -1, GNOME_VFS_URI_PATH_CHR)[1] == '.';
 }
 
+gboolean
+rb_uri_could_be_podcast (const char *uri, gboolean *is_opml)
+{
+	const char *query_string;
+
+	if (is_opml != NULL)
+		*is_opml = FALSE;
+
+	/* Check the scheme is a possible one first */
+	if (g_str_has_prefix (uri, "http") == FALSE &&
+	    g_str_has_prefix (uri, "itpc:") == FALSE &&
+	    g_str_has_prefix (uri, "itms:") == FALSE) {
+	    	rb_debug ("'%s' can't be a Podcast or OPML file, not the right scheme", uri);
+	    	return FALSE;
+	}
+
+	/* Now, check whether the iTunes Music Store link
+	 * is a podcast */
+	if (g_str_has_prefix (uri, "itms:") != FALSE
+	    && strstr (uri, "phobos.apple.com") != NULL
+	    && strstr (uri, "viewPodcast") != NULL)
+		return TRUE;
+
+	query_string = strchr (uri, '?');
+	if (query_string == NULL) {
+		query_string = uri + strlen (uri);
+	}
+
+	/* FIXME hacks */
+	if (strstr (uri, "rss") != NULL ||
+	    strstr (uri, "atom") != NULL ||
+	    strstr (uri, "feed") != NULL) {
+	    	rb_debug ("'%s' should be Podcast file, HACK", uri);
+	    	return TRUE;
+	} else if (strstr (uri, "opml") != NULL) {
+		rb_debug ("'%s' should be an OPML file, HACK", uri);
+		if (is_opml != NULL)
+			*is_opml = TRUE;
+		return TRUE;
+	}
+
+	if (strncmp (query_string - 4, ".rss", 4) == 0 ||
+	    strncmp (query_string - 4, ".xml", 4) == 0 ||
+	    strncmp (query_string - 5, ".atom", 5) == 0 ||
+	    strncmp (uri, "itpc", 4) == 0 ||
+	    (strstr (uri, "phobos.apple.com/") != NULL && strstr (uri, "viewPodcast") != NULL) ||
+	    strstr (uri, "itunes.com/podcast") != NULL) {
+	    	rb_debug ("'%s' should be Podcast file", uri);
+	    	return TRUE;
+	} else if (strncmp (query_string - 5, ".opml", 5) == 0) {
+		rb_debug ("'%s' should be an OPML file", uri);
+		if (is_opml != NULL)
+			*is_opml = TRUE;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 char *
 rb_uri_make_hidden (const char *text_uri)
 {
Index: lib/rb-file-helpers.h
===================================================================
--- lib/rb-file-helpers.h	(revision 5467)
+++ lib/rb-file-helpers.h	(working copy)
@@ -44,6 +44,7 @@
 gboolean	rb_uri_is_writable	(const char *uri);
 gboolean	rb_uri_is_local		(const char *uri);
 gboolean	rb_uri_is_hidden	(const char *uri);
+gboolean	rb_uri_could_be_podcast (const char *uri, gboolean *is_opml);
 char *		rb_uri_make_hidden      (const char *uri);
 char *		rb_uri_get_dir_name	(const char *uri);
 char *		rb_uri_get_short_path_name (const char *uri);
Index: shell/rb-shell.c
===================================================================
--- shell/rb-shell.c	(revision 5467)
+++ shell/rb-shell.c	(working copy)
@@ -3208,20 +3208,11 @@
 	gboolean source_is_entry;
 } PlaylistParseData;
 
-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0)
 static void
 handle_playlist_entry_cb (TotemPlParser *playlist,
 			  const char *uri,
 			  GHashTable *metadata,
 			  PlaylistParseData *data)
-#else
-static void
-handle_playlist_entry_cb (TotemPlParser *playlist,
-			  const char *uri,
-			  const char *title,
-			  const char *genre,
-			  PlaylistParseData *data)
-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */
 {
 	RBSource *source;
 
@@ -3270,6 +3261,14 @@
 	entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
 	playlist_source = NULL;
 
+	/* If the URI points to a Podcast, pass it on to
+	 * the Podcast source */
+	if (rb_uri_could_be_podcast (uri, NULL)) {
+		rb_podcast_source_add_feed (shell->priv->podcast_source, uri);
+		rb_shell_select_source (shell, RB_SOURCE (shell->priv->podcast_source));
+		return TRUE;
+	}
+
 	if (entry == NULL) {
 		TotemPlParser *parser;
 		TotemPlParserResult result;
@@ -3283,15 +3282,9 @@
 		rb_debug ("adding uri %s, play %d", uri, play);
 		parser = totem_pl_parser_new ();
 
-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0)
 		g_signal_connect_data (G_OBJECT (parser), "entry-parsed",
 				       G_CALLBACK (handle_playlist_entry_cb),
 				       &data, NULL, 0);
-#else
-		g_signal_connect_data (G_OBJECT (parser), "entry",
-				       G_CALLBACK (handle_playlist_entry_cb),
-				       &data, NULL, 0);
-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */
 
 		totem_pl_parser_add_ignored_mimetype (parser, "x-directory/normal");
 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
Index: shell/rb-playlist-manager.c
===================================================================
--- shell/rb-playlist-manager.c	(revision 5467)
+++ shell/rb-playlist-manager.c	(working copy)
@@ -502,20 +502,11 @@
 	return quark;
 }
 
-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0)
 static void
 handle_playlist_entry_cb (TotemPlParser *playlist,
 			  const char *uri_maybe,
 			  GHashTable *metadata,
 			  RBPlaylistManager *mgr)
-#else
-static void
-handle_playlist_entry_cb (TotemPlParser *playlist,
-			  const char *uri_maybe,
-			  const char *title,
-			  const char *genre,
-			  RBPlaylistManager *mgr)
-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */
 {
 	char *uri;
 #if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0)
@@ -550,19 +541,29 @@
 }
 
 static void
-playlist_load_start_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr)
+playlist_load_started_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr)
 {
-	rb_debug ("loading new playlist %s", title);
+	const char *title;
 
+	rb_debug ("loading new playlist %s", uri);
+
+	title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
+	if (title == NULL)
+		title = _("Unnamed playlist");
+
 	mgr->priv->loading_playlist =
 			RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE));
 }
 
 static void
-playlist_load_end_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr)
+playlist_load_ended_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr)
 {
-	rb_debug ("finished loading playlist %s", title);
+	const char *title;
 
+	rb_debug ("finished loading playlist %s", uri);
+
+	title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE);
+
 	if (title) {
 		g_object_set (mgr->priv->loading_playlist, "name", title, NULL);
 		mgr->priv->loading_playlist = NULL;
@@ -591,22 +592,16 @@
 	{
 		TotemPlParser *parser = totem_pl_parser_new ();
 
-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0)
 		g_signal_connect_object (parser, "entry-parsed",
 					 G_CALLBACK (handle_playlist_entry_cb),
 					 mgr, 0);
-#else
-		g_signal_connect_object (parser, "entry",
-					 G_CALLBACK (handle_playlist_entry_cb),
-					 mgr, 0);
-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */
 
-		g_signal_connect_object (parser, "playlist-start",
-					 G_CALLBACK (playlist_load_start_cb),
+		g_signal_connect_object (parser, "playlist-started",
+					 G_CALLBACK (playlist_load_started_cb),
 					 mgr, 0);
 
-		g_signal_connect_object (parser, "playlist-end",
-					 G_CALLBACK (playlist_load_end_cb),
+		g_signal_connect_object (parser, "playlist-ended",
+					 G_CALLBACK (playlist_load_ended_cb),
 					 mgr, 0);
 
 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
Index: shell/rb-shell-player.c
===================================================================
--- shell/rb-shell-player.c	(revision 5467)
+++ shell/rb-shell-player.c	(working copy)
@@ -120,10 +120,10 @@
 
 static void rb_shell_player_entry_activated_cb (RBEntryView *view,
 						RhythmDBEntry *entry,
-						RBShellPlayer *playa);
+						RBShellPlayer *player);
 static void rb_shell_player_property_row_activated_cb (RBPropertyView *view,
 						       const char *name,
-						       RBShellPlayer *playa);
+						       RBShellPlayer *player);
 static void rb_shell_player_sync_volume (RBShellPlayer *player, gboolean notify);
 static void rb_shell_player_sync_replaygain (RBShellPlayer *player,
                                              RhythmDBEntry *entry);
@@ -1299,20 +1299,11 @@
 	PlaybackStartType play_type;
 } OpenLocationThreadData;
 
-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0)
 static void
 playlist_entry_cb (TotemPlParser *playlist,
 		   const char *uri,
 		   GHashTable *metadata,
 		   RBShellPlayer *player)
-#else
-static void
-playlist_entry_cb (TotemPlParser *playlist,
-		   const char *uri,
-		   const char *title,
-		   const char *genre,
-		   RBShellPlayer *player)
-#endif
 {
 	rb_debug ("adding stream url %s", uri);
 	g_queue_push_tail (player->priv->playlist_urls, g_strdup (uri));
@@ -1326,15 +1317,9 @@
 
 	playlist = totem_pl_parser_new ();
 
-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0)
 	g_signal_connect_data (G_OBJECT (playlist), "entry-parsed",
 			       G_CALLBACK (playlist_entry_cb),
 			       data->player, NULL, 0);
-#else
-	g_signal_connect_data (G_OBJECT (playlist), "entry",
-			       G_CALLBACK (playlist_entry_cb),
-			       data->player, NULL, 0);
-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */
 
 	totem_pl_parser_add_ignored_mimetype (playlist, "x-directory/normal");
 
@@ -1415,6 +1400,8 @@
 		gboolean ret = TRUE;
 
 		crossfade = rb_shell_player_get_crossfade (player, play_type);
+
+		g_object_ref (entry);
 		ret = ret && rb_player_open (player->priv->mmplayer, location, entry, (GDestroyNotify) rhythmdb_entry_unref, error);
 
 		ret = ret && rb_player_play (player->priv->mmplayer, crossfade, error);
@@ -2340,7 +2327,7 @@
 static void
 rb_shell_player_entry_activated_cb (RBEntryView *view,
 				    RhythmDBEntry *entry,
-				    RBShellPlayer *playa)
+				    RBShellPlayer *player)
 {
 	gboolean was_from_queue = FALSE;
 	RhythmDBEntry *prev_entry = NULL;
@@ -2359,44 +2346,44 @@
 
 	/* skip entries with no playback uri */
 	playback_uri = rhythmdb_entry_get_playback_uri (entry);
-	if (playback_uri == NULL) {
+	if (playback_uri == NULL)
 		return;
-	}
+
 	g_free (playback_uri);
 
 	/* figure out where the previous entry came from */
-	if ((playa->priv->queue_source != NULL) &&
-	    (playa->priv->current_playing_source == RB_SOURCE (playa->priv->queue_source))) {
-		prev_entry = rb_shell_player_get_playing_entry (playa);
+	if ((player->priv->queue_source != NULL) &&
+	    (player->priv->current_playing_source == RB_SOURCE (player->priv->queue_source))) {
+		prev_entry = rb_shell_player_get_playing_entry (player);
 		was_from_queue = TRUE;
 	}
 
-	if (playa->priv->queue_source) {
+	if (player->priv->queue_source) {
 		RBEntryView *queue_sidebar;
 
-		g_object_get (playa->priv->queue_source, "sidebar", &queue_sidebar, NULL);
+		g_object_get (player->priv->queue_source, "sidebar", &queue_sidebar, NULL);
 
-		if (view == queue_sidebar || view == rb_source_get_entry_view (RB_SOURCE (playa->priv->queue_source))) {
+		if (view == queue_sidebar || view == rb_source_get_entry_view (RB_SOURCE (player->priv->queue_source))) {
 
 			/* fall back to the current selected source once the queue is empty */
-			if (view == queue_sidebar && playa->priv->source == NULL) {
-				rb_play_order_playing_source_changed (playa->priv->play_order,
-								      playa->priv->selected_source);
-				playa->priv->source = playa->priv->selected_source;
+			if (view == queue_sidebar && player->priv->source == NULL) {
+				rb_play_order_playing_source_changed (player->priv->play_order,
+								      player->priv->selected_source);
+				player->priv->source = player->priv->selected_source;
 			}
 
 			/* queue entry activated: move it to the start of the queue */
-			rb_static_playlist_source_move_entry (RB_STATIC_PLAYLIST_SOURCE (playa->priv->queue_source), entry, 0);
-			rb_shell_player_set_playing_source (playa, RB_SOURCE (playa->priv->queue_source));
+			rb_static_playlist_source_move_entry (RB_STATIC_PLAYLIST_SOURCE (player->priv->queue_source), entry, 0);
+			rb_shell_player_set_playing_source (player, RB_SOURCE (player->priv->queue_source));
 
 			was_from_queue = FALSE;
 			source_set = TRUE;
 			jump_to_entry = TRUE;
 		} else {
-			if (playa->priv->queue_only) {
-				rb_source_add_to_queue (playa->priv->selected_source,
-							RB_SOURCE (playa->priv->queue_source));
-				rb_shell_player_set_playing_source (playa, RB_SOURCE (playa->priv->queue_source));
+			if (player->priv->queue_only) {
+				rb_source_add_to_queue (player->priv->selected_source,
+							RB_SOURCE (player->priv->queue_source));
+				rb_shell_player_set_playing_source (player, RB_SOURCE (player->priv->queue_source));
 				source_set = TRUE;
 			}
 		}
@@ -2405,18 +2392,18 @@
 	}
 
 	/* bail out if queue only */
-	if (playa->priv->queue_only) {
+	if (player->priv->queue_only) {
 		return;
 	}
 
 	if (!source_set) {
-		rb_shell_player_set_playing_source (playa, playa->priv->selected_source);
+		rb_shell_player_set_playing_source (player, player->priv->selected_source);
 		source_set = TRUE;
 	}
 
-	playa->priv->jump_to_playing_entry = jump_to_entry;
-	if (!rb_shell_player_set_playing_entry (playa, entry, TRUE, FALSE, &error)) {
-		rb_shell_player_error (playa, FALSE, error);
+	player->priv->jump_to_playing_entry = jump_to_entry;
+	if (!rb_shell_player_set_playing_entry (player, entry, TRUE, FALSE, &error)) {
+		rb_shell_player_error (player, FALSE, error);
 		g_clear_error (&error);
 	}
 
@@ -2424,7 +2411,7 @@
 	 * so we'll start again from the start.
 	 */
 	if (was_from_queue && prev_entry != NULL) {
-		rb_play_order_set_playing_entry (playa->priv->queue_play_order, NULL);
+		rb_play_order_set_playing_entry (player->priv->queue_play_order, NULL);
 	}
 
 	if (prev_entry != NULL) {
Index: data/rhythmbox.schemas
===================================================================
--- data/rhythmbox.schemas	(revision 5467)
+++ data/rhythmbox.schemas	(working copy)
@@ -136,9 +136,9 @@
 	<long>Main window X position.</long>
         </locale>
       </schema>
- 
 
-<schema>
+
+      <schema>
         <key>/schemas/apps/rhythmbox/state/window_position_y</key>
         <applyto>/apps/rhythmbox/state/window_position_y</applyto>
         <owner>rhythmbox</owner>
@@ -150,10 +150,7 @@
         </locale>
       </schema>
  
-
-
-
-<schema>
+      <schema>
         <key>/schemas/apps/rhythmbox/state/window_height</key>
         <applyto>/apps/rhythmbox/state/window_height</applyto>
         <owner>rhythmbox</owner>
@@ -439,6 +436,106 @@
         </locale>
       </schema>
       <schema>
+        <key>/schemas/desktop/gnome/url-handlers/itpc/command</key>
+	<applyto>/desktop/gnome/url-handlers/itpc/command</applyto>
+        <owner>rhythmbox</owner>
+        <type>string</type>
+        <default>rhythmbox "%s"</default>
+        <locale name="C">
+	  <short>The command to handle ITPC scheme URLs</short>
+	  <long>The command to handle ITPC scheme URLs.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/itpc/needs_terminal</key>
+	<applyto>/desktop/gnome/url-handlers/itpc/needs_terminal</applyto>
+        <owner>rhythmbox</owner>
+        <type>bool</type>
+        <default>false</default>
+        <locale name="C">
+	  <short>Whether command to handle ITPC scheme URLs needs a terminal</short>
+	  <long>Whether command to handle ITPC scheme URLs needs a terminal.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/itpc/enabled</key>
+	<applyto>/desktop/gnome/url-handlers/itpc/enabled</applyto>
+        <owner>rhythmbox</owner>
+        <type>bool</type>
+        <default>true</default>
+        <locale name="C">
+	  <short>Whether command to handle ITPC scheme URLs is enabled</short>
+	  <long>Whether command to handle ITPC scheme URLs is enabled.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/itms/command</key>
+	<applyto>/desktop/gnome/url-handlers/itms/command</applyto>
+        <owner>rhythmbox</owner>
+        <type>string</type>
+        <default>rhythmbox "%s"</default>
+        <locale name="C">
+	  <short>The command to handle ITMS scheme URLs</short>
+	  <long>The command to handle ITMS scheme URLs.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/itms/needs_terminal</key>
+	<applyto>/desktop/gnome/url-handlers/itms/needs_terminal</applyto>
+        <owner>rhythmbox</owner>
+        <type>bool</type>
+        <default>false</default>
+        <locale name="C">
+	  <short>Whether command to handle ITMS scheme URLs needs a terminal</short>
+	  <long>Whether command to handle ITMS scheme URLs needs a terminal.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/itms/enabled</key>
+	<applyto>/desktop/gnome/url-handlers/itms/enabled</applyto>
+        <owner>rhythmbox</owner>
+        <type>bool</type>
+        <default>true</default>
+        <locale name="C">
+	  <short>Whether command to handle ITMS scheme URLs is enabled</short>
+	  <long>Whether command to handle ITMS scheme URLs is enabled.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/feed/command</key>
+	<applyto>/desktop/gnome/url-handlers/feed/command</applyto>
+        <owner>rhythmbox</owner>
+        <type>string</type>
+        <default>rhythmbox "%s"</default>
+        <locale name="C">
+	  <short>The command to handle FEED scheme URLs</short>
+	  <long>The command to handle FEED scheme URLs.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/feed/needs_terminal</key>
+	<applyto>/desktop/gnome/url-handlers/feed/needs_terminal</applyto>
+        <owner>rhythmbox</owner>
+        <type>bool</type>
+        <default>false</default>
+        <locale name="C">
+	  <short>Whether command to handle FEED scheme URLs needs a terminal</short>
+	  <long>Whether command to handle FEED scheme URLs needs a terminal.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/desktop/gnome/url-handlers/feed/enabled</key>
+	<applyto>/desktop/gnome/url-handlers/feed/enabled</applyto>
+        <owner>rhythmbox</owner>
+        <type>bool</type>
+        <default>true</default>
+        <locale name="C">
+	  <short>Whether command to handle FEED scheme URLs is enabled</short>
+	  <long>Whether command to handle FEED scheme URLs is enabled.</long>
+        </locale>
+      </schema>
+
+      <schema>
         <key>/schemas/apps/rhythmbox/state/podcast/show_browser</key>
         <applyto>/apps/rhythmbox/state/podcast/show_browser</applyto>
         <owner>rhythmbox</owner>
