/* 
   Copyright (C) 2007 Bastien Nocera <hadess@hadess.net>

   The Gnome 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.

   The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301  USA.

   Author: Bastien Nocera <hadess@hadess.net>
 */

#include "config.h"

#include <string.h>
#include <glib.h>

#ifndef XPLAYER_PL_PARSER_MINI
#include <libsoup/soup.h>
#include "xmlparser.h"
#include "xplayer-pl-parser.h"
#include "xplayer-disc.h"
#endif /* !XPLAYER_PL_PARSER_MINI */

#include "xplayer-pl-parser-mini.h"
#include "xplayer-pl-parser-podcast.h"
#include "xplayer-pl-parser-videosite.h"
#include "xplayer-pl-parser-private.h"

#define RSS_NEEDLE "<rss "
#define RSS_NEEDLE2 "<rss\n"
#define ATOM_NEEDLE "<feed "
#define OPML_NEEDLE "<opml "

const char *
xplayer_pl_parser_is_rss (const char *data, gsize len)
{
	if (len == 0)
		return FALSE;
	if (len > MIME_READ_CHUNK_SIZE)
		len = MIME_READ_CHUNK_SIZE;

	if (g_strstr_len (data, len, RSS_NEEDLE) != NULL)
		return RSS_MIME_TYPE;
	if (g_strstr_len (data, len, RSS_NEEDLE2) != NULL)
		return RSS_MIME_TYPE;

	return NULL;
}

const char *
xplayer_pl_parser_is_atom (const char *data, gsize len)
{
	if (len == 0)
		return FALSE;
	if (len > MIME_READ_CHUNK_SIZE)
		len = MIME_READ_CHUNK_SIZE;

	if (g_strstr_len (data, len, ATOM_NEEDLE) != NULL)
		return ATOM_MIME_TYPE;

	return NULL;
}

const char *
xplayer_pl_parser_is_opml (const char *data, gsize len)
{
	if (len == 0)
		return FALSE;
	if (len > MIME_READ_CHUNK_SIZE)
		len = MIME_READ_CHUNK_SIZE;

	if (g_strstr_len (data, len, OPML_NEEDLE) != NULL)
		return OPML_MIME_TYPE;

	return NULL;
}

const char *
xplayer_pl_parser_is_xml_feed (const char *data, gsize len)
{
	if (xplayer_pl_parser_is_rss (data, len) != NULL)
		return RSS_MIME_TYPE;
	if (xplayer_pl_parser_is_atom (data, len) != NULL)
		return ATOM_MIME_TYPE;
	if (xplayer_pl_parser_is_opml (data, len) != NULL)
		return OPML_MIME_TYPE;
	return NULL;
}

#ifndef XPLAYER_PL_PARSER_MINI

static gboolean
is_image (const char *url)
{
	char *content_type;
	gboolean retval = FALSE;

	content_type = g_content_type_guess (url, NULL, 0, NULL);
	if (content_type == NULL)
		return FALSE;
	if (g_content_type_is_a (content_type, "image/*"))
		retval = TRUE;
	g_free (content_type);
	return retval;
}

static XplayerPlParserResult
parse_rss_item (XplayerPlParser *parser, xml_node_t *parent)
{
	const char *title, *uri, *description, *author, *img;
	const char *pub_date, *duration, *filesize, *content_type, *id;
	xml_node_t *node;

	title = uri = description = author = content_type = NULL;
	img = pub_date = duration = filesize = id = NULL;

	for (node = parent->child; node != NULL; node = node->next) {
		if (node->name == NULL)
			continue;

		if (g_ascii_strcasecmp (node->name, "title") == 0) {
			title = node->data;
		} else if (g_ascii_strcasecmp (node->name, "url") == 0) {
			uri = node->data;
		} else if (g_ascii_strcasecmp (node->name, "pubDate") == 0) {
			pub_date = node->data;
		} else if (g_ascii_strcasecmp (node->name, "guid") == 0) {
			id = node->data;
		} else if (g_ascii_strcasecmp (node->name, "description") == 0
			   || g_ascii_strcasecmp (node->name, "itunes:summary") == 0) {
			description = node->data;
		} else if (g_ascii_strcasecmp (node->name, "author") == 0
			   || g_ascii_strcasecmp (node->name, "itunes:author") == 0) {
			author = node->data;
		} else if (g_ascii_strcasecmp (node->name, "itunes:duration") == 0) {
			duration = node->data;
		} else if (g_ascii_strcasecmp (node->name, "length") == 0) {
			filesize = node->data;
		} else if (g_ascii_strcasecmp (node->name, "media:content") == 0) {
			const char *tmp;

			tmp = xml_parser_get_property (node, "type");
			if (tmp != NULL &&
			    g_str_has_prefix (tmp, "audio/") == FALSE) {
				if (g_str_has_prefix (tmp, "image/"))
					img = xml_parser_get_property (node, "url");
				continue;
			}
			content_type = tmp;

			tmp = xml_parser_get_property (node, "url");
			if (tmp != NULL)
				uri = tmp;
			else
				continue;

			tmp = xml_parser_get_property (node, "fileSize");
			if (tmp != NULL)
				filesize = tmp;

			tmp = xml_parser_get_property (node, "duration");
			if (tmp != NULL)
				duration = tmp;
		} else if (g_ascii_strcasecmp (node->name, "enclosure") == 0) {
			const char *tmp;

			tmp = xml_parser_get_property (node, "url");
			if (tmp != NULL && is_image (tmp) == FALSE)
				uri = tmp;
			else
				continue;
			tmp = xml_parser_get_property (node, "length");
			if (tmp != NULL)
				filesize = tmp;
		} else if (g_ascii_strcasecmp (node->name, "link") == 0 &&
			   xplayer_pl_parser_is_videosite (node->data, FALSE) != FALSE) {
			uri = node->data;
		}
	}

	if (id != NULL &&
	    uri == NULL &&
	    xplayer_pl_parser_is_videosite (id, FALSE) != FALSE)
		uri = id;

	if (uri != NULL) {
		xplayer_pl_parser_add_uri (parser,
					 XPLAYER_PL_PARSER_FIELD_URI, uri,
					 XPLAYER_PL_PARSER_FIELD_ID, id,
					 XPLAYER_PL_PARSER_FIELD_TITLE, title,
					 XPLAYER_PL_PARSER_FIELD_PUB_DATE, pub_date,
					 XPLAYER_PL_PARSER_FIELD_DESCRIPTION, description,
					 XPLAYER_PL_PARSER_FIELD_AUTHOR, author,
					 XPLAYER_PL_PARSER_FIELD_DURATION, duration,
					 XPLAYER_PL_PARSER_FIELD_FILESIZE, filesize,
					 XPLAYER_PL_PARSER_FIELD_CONTENT_TYPE, content_type,
					 XPLAYER_PL_PARSER_FIELD_IMAGE_URI, img,
					 NULL);
	}

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
}

static XplayerPlParserResult
parse_rss_items (XplayerPlParser *parser, const char *uri, xml_node_t *parent)
{
	const char *title, *language, *description, *author;
	const char *contact, *img, *pub_date, *copyright;
	xml_node_t *node;

	title = language = description = author = NULL;
	contact = img = pub_date = copyright = NULL;

	/* We need to parse for the feed metadata first, then for the items */
	for (node = parent->child; node != NULL; node = node->next) {
		if (node->name == NULL)
			continue;

		if (g_ascii_strcasecmp (node->name, "title") == 0) {
			title = node->data;
		} else if (g_ascii_strcasecmp (node->name, "language") == 0) {
			language = node->data;
		} else if (g_ascii_strcasecmp (node->name, "description") == 0
			 || g_ascii_strcasecmp (node->name, "itunes:subtitle") == 0) {
		    	description = node->data;
		} else if (g_ascii_strcasecmp (node->name, "author") == 0
			 || g_ascii_strcasecmp (node->name, "itunes:author") == 0
			 || (g_ascii_strcasecmp (node->name, "generator") == 0 && author == NULL)) {
		    	author = node->data;
		} else if (g_ascii_strcasecmp (node->name, "webMaster") == 0) {
			contact = node->data;
		} else if (g_ascii_strcasecmp (node->name, "image") == 0) {
			img = node->data;
		} else if (g_ascii_strcasecmp (node->name, "itunes:image") == 0) {
			const char *href;

			href = xml_parser_get_property (node, "href");
			if (href != NULL)
				img = href;
		} else if (g_ascii_strcasecmp (node->name, "lastBuildDate") == 0
			 || g_ascii_strcasecmp (node->name, "pubDate") == 0) {
		    	pub_date = node->data;
		} else if (g_ascii_strcasecmp (node->name, "copyright") == 0) {
			copyright = node->data;
		}
	}

	/* Send the info we already have about the feed */
	xplayer_pl_parser_add_uri (parser,
				 XPLAYER_PL_PARSER_FIELD_IS_PLAYLIST, TRUE,
				 XPLAYER_PL_PARSER_FIELD_URI, uri,
				 XPLAYER_PL_PARSER_FIELD_TITLE, title,
				 XPLAYER_PL_PARSER_FIELD_LANGUAGE, language,
				 XPLAYER_PL_PARSER_FIELD_DESCRIPTION, description,
				 XPLAYER_PL_PARSER_FIELD_AUTHOR, author,
				 XPLAYER_PL_PARSER_FIELD_PUB_DATE, pub_date,
				 XPLAYER_PL_PARSER_FIELD_COPYRIGHT, copyright,
				 XPLAYER_PL_PARSER_FIELD_IMAGE_URI, img,
				 XPLAYER_PL_PARSER_FIELD_CONTACT, contact,
				 NULL);

	for (node = parent->child; node != NULL; node = node->next) {
		if (node->name == NULL)
			continue;

		if (g_ascii_strcasecmp (node->name, "item") == 0)
			parse_rss_item (parser, node);
	}

	xplayer_pl_parser_playlist_end (parser, uri);

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
}

XplayerPlParserResult
xplayer_pl_parser_add_rss (XplayerPlParser *parser,
			 GFile *file,
			 GFile *base_file,
			 XplayerPlParseData *parse_data,
			 gpointer data)
{
#ifndef HAVE_GMIME
	WARN_NO_GMIME;
#else
	xml_node_t* doc, *channel;
	char *contents;
	gsize size;

	if (g_file_load_contents (file, NULL, &contents, &size, NULL, NULL) == FALSE)
		return XPLAYER_PL_PARSER_RESULT_ERROR;

	doc = xplayer_pl_parser_parse_xml_relaxed (contents, size);
	if (doc == NULL) {
		g_free (contents);
		return XPLAYER_PL_PARSER_RESULT_ERROR;
	}

	/* If the document has no name */
	if (doc->name == NULL
	    || (g_ascii_strcasecmp (doc->name , "rss") != 0
		&& g_ascii_strcasecmp (doc->name , "rss\n") != 0)) {
		g_free (contents);
		xml_parser_free_tree (doc);
		return XPLAYER_PL_PARSER_RESULT_ERROR;
	}

	for (channel = doc->child; channel != NULL; channel = channel->next) {
		if (g_ascii_strcasecmp (channel->name, "channel") == 0) {
			char *uri;

			uri = g_file_get_uri (file);
			parse_rss_items (parser, uri, channel);
			g_free (uri);

			/* One channel per file */
			break;
		}
	}

	g_free (contents);
	xml_parser_free_tree (doc);

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
#endif /* !HAVE_GMIME */
}

/* http://www.apple.com/itunes/store/podcaststechspecs.html */
XplayerPlParserResult
xplayer_pl_parser_add_itpc (XplayerPlParser *parser,
			  GFile *file,
			  GFile *base_file,
			  XplayerPlParseData *parse_data,
			  gpointer data)
{
#ifndef HAVE_GMIME
	WARN_NO_GMIME;
#else
	XplayerPlParserResult ret;
	char *uri, *new_uri, *uri_scheme;
	GFile *new_file;

	uri = g_file_get_uri (file);
	uri_scheme = g_file_get_uri_scheme (file);
	new_uri = g_strdup_printf ("http%s", uri + strlen (uri_scheme));
	g_free (uri);
	g_free (uri_scheme);

	new_file = g_file_new_for_uri (new_uri);
	g_free (new_uri);

	ret = xplayer_pl_parser_add_rss (parser, new_file, base_file, parse_data, data);

	g_object_unref (new_file);

	return ret;
#endif /* !HAVE_GMIME */
}

XplayerPlParserResult
xplayer_pl_parser_add_zune (XplayerPlParser *parser,
			  GFile *file,
			  GFile *base_file,
			  XplayerPlParseData *parse_data,
			  gpointer data)
{
#ifndef HAVE_GMIME
	WARN_NO_GMIME;
#else
	XplayerPlParserResult ret;
	char *uri, *new_uri;
	GFile *new_file;

	uri = g_file_get_uri (file);
	if (g_str_has_prefix (uri, "zune://subscribe/?") == FALSE) {
		g_free (uri);
		return XPLAYER_PL_PARSER_RESULT_UNHANDLED;
	}

	new_uri = strchr (uri + strlen ("zune://subscribe/?"), '=');
	if (new_uri == NULL) {
		g_free (uri);
		return XPLAYER_PL_PARSER_RESULT_UNHANDLED;
	}
	/* Skip over the '=' */
	new_uri++;

	new_file = g_file_new_for_uri (new_uri);
	g_free (uri);

	ret = xplayer_pl_parser_add_rss (parser, new_file, base_file, parse_data, data);

	g_object_unref (new_file);

	return ret;
#endif /* !HAVE_GMIME */
}

/* Atom docs:
 * http://www.atomenabled.org/developers/syndication/atom-format-spec.php#rfc.section.4.1
 * http://tools.ietf.org/html/rfc4287
 * http://tools.ietf.org/html/rfc4946 */
static XplayerPlParserResult
parse_atom_entry (XplayerPlParser *parser, xml_node_t *parent)
{
	const char *title, *author, *uri, *filesize;
	const char *copyright, *pub_date, *description;
	xml_node_t *node;

	title = author = uri = filesize = NULL;
	copyright = pub_date = description = NULL;

	for (node = parent->child; node != NULL; node = node->next) {
		if (node->name == NULL)
			continue;

		if (g_ascii_strcasecmp (node->name, "title") == 0) {
			title = node->data;
		} else if (g_ascii_strcasecmp (node->name, "author") == 0) {
			//FIXME
		} else if (g_ascii_strcasecmp (node->name, "link") == 0) {
			const char *rel;

			//FIXME how do we choose the default enclosure type?
			rel = xml_parser_get_property (node, "rel");
			if (g_ascii_strcasecmp (rel, "enclosure") == 0) {
				const char *href;

				//FIXME what's the difference between url and href there?
				href = xml_parser_get_property (node, "href");
				if (href == NULL)
					continue;
				uri = href;
				filesize = xml_parser_get_property (node, "length");
			} else if (g_ascii_strcasecmp (node->name, "license") == 0) {
				const char *href;

				href = xml_parser_get_property (node, "href");
				if (href == NULL)
					continue;
				/* This isn't really a copyright, but what the hey */
				copyright = href;
			}
		} else if (g_ascii_strcasecmp (node->name, "updated") == 0
			   || (g_ascii_strcasecmp (node->name, "modified") == 0 && pub_date == NULL)) {
			pub_date = node->data;
		} else if (g_ascii_strcasecmp (node->name, "summary") == 0
			   || (g_ascii_strcasecmp (node->name, "content") == 0 && description == NULL)) {
			const char *type;

			type = xml_parser_get_property (node, "content");
			if (type != NULL && g_ascii_strcasecmp (type, "text/plain") == 0)
				description = node->data;
		}
		//FIXME handle category
	}

	if (uri != NULL) {
		xplayer_pl_parser_add_uri (parser,
					 XPLAYER_PL_PARSER_FIELD_TITLE, title,
					 XPLAYER_PL_PARSER_FIELD_AUTHOR, author,
					 XPLAYER_PL_PARSER_FIELD_URI, uri,
					 XPLAYER_PL_PARSER_FIELD_FILESIZE, filesize,
					 XPLAYER_PL_PARSER_FIELD_COPYRIGHT, copyright,
					 XPLAYER_PL_PARSER_FIELD_PUB_DATE, pub_date,
					 XPLAYER_PL_PARSER_FIELD_DESCRIPTION, description,
					 NULL);
	}

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
}

static XplayerPlParserResult
parse_atom_entries (XplayerPlParser *parser, const char *uri, xml_node_t *parent)
{
	const char *title, *pub_date, *description;
	const char *author, *img;
	xml_node_t *node;
	gboolean started = FALSE;

	title = pub_date = description = NULL;
	author = img = NULL;

	for (node = parent->child; node != NULL; node = node->next) {
		if (node->name == NULL)
			continue;

		if (g_ascii_strcasecmp (node->name, "title") == 0) {
			title = node->data;
		} else if (g_ascii_strcasecmp (node->name, "tagline") == 0) {
		    	description = node->data;
		} else if (g_ascii_strcasecmp (node->name, "modified") == 0
			   || g_ascii_strcasecmp (node->name, "updated") == 0) {
			pub_date = node->data;
		} else if (g_ascii_strcasecmp (node->name, "author") == 0
			 || (g_ascii_strcasecmp (node->name, "generator") == 0 && author == NULL)) {
		    	author = node->data;
		} else if ((g_ascii_strcasecmp (node->name, "icon") == 0 && img == NULL)
			   || g_ascii_strcasecmp (node->name, "logo") == 0) {
			img = node->data;
		}

		if (g_ascii_strcasecmp (node->name, "entry") == 0) {
			if (started == FALSE) {
				/* Send the info we already have about the feed */
				xplayer_pl_parser_add_uri (parser,
							 XPLAYER_PL_PARSER_FIELD_IS_PLAYLIST, TRUE,
							 XPLAYER_PL_PARSER_FIELD_URI, uri,
							 XPLAYER_PL_PARSER_FIELD_TITLE, title,
							 XPLAYER_PL_PARSER_FIELD_DESCRIPTION, description,
							 XPLAYER_PL_PARSER_FIELD_AUTHOR, author,
							 XPLAYER_PL_PARSER_FIELD_PUB_DATE, pub_date,
							 XPLAYER_PL_PARSER_FIELD_IMAGE_URI, img,
							 NULL);
				started = TRUE;
			}

			parse_atom_entry (parser, node);
		}
	}

	xplayer_pl_parser_playlist_end (parser, uri);

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
}

XplayerPlParserResult
xplayer_pl_parser_add_atom (XplayerPlParser *parser,
			  GFile *file,
			  GFile *base_file,
			  XplayerPlParseData *parse_data,
			  gpointer data)
{
#ifndef HAVE_GMIME
	WARN_NO_GMIME;
#else
	xml_node_t* doc;
	char *contents, *uri;
	gsize size;

	if (g_file_load_contents (file, NULL, &contents, &size, NULL, NULL) == FALSE)
		return XPLAYER_PL_PARSER_RESULT_ERROR;

	doc = xplayer_pl_parser_parse_xml_relaxed (contents, size);
	if (doc == NULL) {
		g_free (contents);
		return XPLAYER_PL_PARSER_RESULT_ERROR;
	}

	/* If the document has no name */
	if (doc->name == NULL
	    || g_ascii_strcasecmp (doc->name , "feed") != 0) {
		g_free (contents);
		xml_parser_free_tree (doc);
		return XPLAYER_PL_PARSER_RESULT_ERROR;
	}

	uri = g_file_get_uri (file);
	parse_atom_entries (parser, uri, doc);
	g_free (uri);

	g_free (contents);
	xml_parser_free_tree (doc);

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
#endif /* !HAVE_GMIME */
}

XplayerPlParserResult
xplayer_pl_parser_add_xml_feed (XplayerPlParser *parser,
			      GFile *file,
			      GFile *base_file,
			      XplayerPlParseData *parse_data,
			      gpointer data)
{
#ifndef HAVE_GMIME
	WARN_NO_GMIME;
#else
	guint len;

	if (data == NULL)
		return XPLAYER_PL_PARSER_RESULT_UNHANDLED;

	len = strlen (data);

	if (xplayer_pl_parser_is_rss (data, len) != FALSE)
		return xplayer_pl_parser_add_rss (parser, file, base_file, parse_data, data);
	if (xplayer_pl_parser_is_atom (data, len) != FALSE)
		return xplayer_pl_parser_add_atom (parser, file, base_file, parse_data, data);
	if (xplayer_pl_parser_is_opml (data, len) != FALSE)
		return xplayer_pl_parser_add_opml (parser, file, base_file, parse_data, data);

	return XPLAYER_PL_PARSER_RESULT_UNHANDLED;
#endif /* !HAVE_GMIME */
}

static GByteArray *
xplayer_pl_parser_load_http_itunes (const char *uri,
				  gboolean    debug)
{
	SoupMessage *msg;
	SoupSession *session;
	GByteArray *data;

	if (debug)
		g_print ("Loading ITMS playlist '%s'\n", uri);

	session = soup_session_sync_new_with_options (
	    SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
	    SOUP_SESSION_USER_AGENT, "iTunes/10.0.0",
	    SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
	    NULL);

	msg = soup_message_new (SOUP_METHOD_GET, uri);
	soup_session_send_message (session, msg);
	if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
		data = g_byte_array_new ();
		g_byte_array_append (data,
				     (guchar *) msg->response_body->data,
				     msg->response_body->length);
	} else {
		return NULL;
	}
	g_object_unref (msg);
	g_object_unref (session);

	return data;
}

static const char *
xplayer_pl_parser_parse_plist (xml_node_t *item)
{
	for (item = item->child; item != NULL; item = item->next) {
		/* What we're looking for looks like:
		 *  <key>action</key>
		 *  <dict>
		 *    <key>kind</key><string>Goto</string>
		 *    <key>url</key><string>URL</string>
		 */
		if (g_ascii_strcasecmp (item->name, "key") == 0
		    && g_ascii_strcasecmp (item->data, "action") == 0) {
			xml_node_t *node = item->next;

			if (node && g_ascii_strcasecmp (node->name, "[CDATA]") == 0)
				node = node->next;
			if (node && g_ascii_strcasecmp (node->name, "dict") == 0) {
				for (node = node->child; node != NULL; node = node->next) {
					if (g_ascii_strcasecmp (node->name, "key") == 0 &&
					    g_ascii_strcasecmp (node->data, "url") == 0 &&
					    node->next != NULL) {
						node = node->next;
						if (g_ascii_strcasecmp (node->name, "string") == 0)
							return node->data;
					}
				}
			}
		} else {
			const char *ret;

			ret = xplayer_pl_parser_parse_plist (item);
			if (ret != NULL)
				return ret;
		}
	}

	return NULL;
}

static char *
xplayer_pl_parser_parse_html (char *data, gsize len, gboolean debug)
{
	char *s, *end;

	s = g_strstr_len (data, len, "feed-url=\"");
	if (s == NULL)
		return NULL;
	s += strlen ("feed-url=\"");
	if (*s == '\0')
		return NULL;
	end = g_strstr_len (s, len - (s - data), "\"");
	if (end == NULL)
		return NULL;
	return g_strndup (s, end - s);
}

static GFile *
xplayer_pl_parser_get_feed_uri (char *data, gsize len, gboolean debug)
{
	xml_node_t* doc;
	const char *uri;
	GFile *ret = NULL;
	GByteArray *content;

	uri = NULL;

	/* Probably HTML, look for feed-url */
	if (g_strstr_len (data, len, "feed-url") != NULL) {
		char *feed_uri;
		feed_uri = xplayer_pl_parser_parse_html (data, len, debug);
		if (debug)
			g_print ("Found feed-url in HTML: '%s'\n", feed_uri);
		if (feed_uri == NULL)
			return NULL;
		ret = g_file_new_for_uri (feed_uri);
		g_free (feed_uri);
		return ret;
	}

	doc = xplayer_pl_parser_parse_xml_relaxed (data, len);
	if (doc == NULL)
		return NULL;

	/* If the document has no name */
	if (doc->name == NULL || g_ascii_strcasecmp (doc->name, "plist") != 0)
		goto out;

	/* Redirect plist? Find a goto action */
	uri = xplayer_pl_parser_parse_plist (doc);

	if (debug)
		g_print ("Found redirect URL: %s\n", uri);

	if (uri == NULL)
		goto out;

	content = xplayer_pl_parser_load_http_itunes (uri, debug);
	if (!content)
		goto out;
	ret = xplayer_pl_parser_get_feed_uri ((char *) content->data, content->len, debug);
	g_byte_array_free (content, TRUE);

out:
	xml_parser_free_tree (doc);
	return ret;
}

XplayerPlParserResult
xplayer_pl_parser_add_itms (XplayerPlParser *parser,
			  GFile *file,
			  GFile *base_file,
			  XplayerPlParseData *parse_data,
			  gpointer data)
{
#ifndef HAVE_GMIME
	WARN_NO_GMIME;
#else
	GByteArray *content;
	char *itms_uri;
	GFile *feed_file;
	XplayerPlParserResult ret;

	if (g_file_has_uri_scheme (file, "itms") != FALSE ||
	    g_file_has_uri_scheme (file, "itmss") != FALSE) {
		itms_uri= g_file_get_uri (file);
		memcpy (itms_uri, "http", 4);
	} else if (g_file_has_uri_scheme (file, "http") != FALSE) {
		itms_uri = g_file_get_uri (file);
	} else {
		return XPLAYER_PL_PARSER_RESULT_ERROR;
	}

	/* Load the file using iTunes user-agent */
	content = xplayer_pl_parser_load_http_itunes (itms_uri, xplayer_pl_parser_is_debugging_enabled (parser));

	/* And look in the file for the feedURL */
	feed_file = xplayer_pl_parser_get_feed_uri ((char *) content->data, content->len,
						  xplayer_pl_parser_is_debugging_enabled (parser));
	g_byte_array_free (content, TRUE);
	if (feed_file == NULL)
		return XPLAYER_PL_PARSER_RESULT_ERROR;

	DEBUG(feed_file, g_print ("Found feed URI: %s\n", uri));

	ret = xplayer_pl_parser_add_rss (parser, feed_file, NULL, parse_data, NULL);
	g_object_unref (feed_file);

	return ret;
#endif /* !HAVE_GMIME */
}

gboolean
xplayer_pl_parser_is_itms_feed (GFile *file)
{
	char *uri;

	g_return_val_if_fail (file != NULL, FALSE);

	uri = g_file_get_uri (file);

	if (g_file_has_uri_scheme (file, "itms") != FALSE ||
	    g_file_has_uri_scheme (file, "itmss") != FALSE ||
	    (g_file_has_uri_scheme (file, "http") != FALSE &&
	     strstr (uri, ".apple.com/") != FALSE)) {
		if (strstr (uri, "/podcast/") != NULL ||
		    strstr (uri, "viewPodcast") != NULL) {
			g_free (uri);
			return TRUE;
		}
	}

	g_free (uri);

	return FALSE;
}

static XplayerPlParserResult
parse_opml_outline (XplayerPlParser *parser, xml_node_t *parent)
{
	xml_node_t* node;

	for (node = parent->child; node != NULL; node = node->next) {
		const char *title, *uri;

		if (node->name == NULL || g_ascii_strcasecmp (node->name, "outline") != 0)
			continue;

		uri = xml_parser_get_property (node, "xmlUrl");
		title = xml_parser_get_property (node, "text");

		if (uri == NULL)
			continue;

		xplayer_pl_parser_add_uri (parser,
					 XPLAYER_PL_PARSER_FIELD_TITLE, title,
					 XPLAYER_PL_PARSER_FIELD_URI, uri,
					 NULL);
	}

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
}

static XplayerPlParserResult
parse_opml_head_body (XplayerPlParser *parser, const char *uri, xml_node_t *parent)
{
	xml_node_t* node;
	gboolean started;

	started = FALSE;

	for (node = parent->child; node != NULL; node = node->next) {
		if (node->name == NULL)
			continue;

		if (g_ascii_strcasecmp (node->name, "body") == 0) {
			if (started == FALSE) {
				/* Send the info we already have about the feed */
				xplayer_pl_parser_add_uri (parser,
							 XPLAYER_PL_PARSER_FIELD_IS_PLAYLIST, TRUE,
							 XPLAYER_PL_PARSER_FIELD_URI, uri,
							 NULL);
				started = TRUE;
			}

			parse_opml_outline (parser, node);
		}
	}

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
}

XplayerPlParserResult
xplayer_pl_parser_add_opml (XplayerPlParser *parser,
			  GFile *file,
			  GFile *base_file,
			  XplayerPlParseData *parse_data,
			  gpointer data)
{
#ifndef HAVE_GMIME
	WARN_NO_GMIME;
#else
	xml_node_t* doc;
	char *contents, *uri;
	gsize size;

	if (g_file_load_contents (file, NULL, &contents, &size, NULL, NULL) == FALSE)
		return XPLAYER_PL_PARSER_RESULT_ERROR;

	doc = xplayer_pl_parser_parse_xml_relaxed (contents, size);
	if (doc == NULL) {
		g_free (contents);
		return XPLAYER_PL_PARSER_RESULT_ERROR;
	}

	/* If the document has no name */
	if (doc->name == NULL
	    || g_ascii_strcasecmp (doc->name , "opml") != 0) {
		g_free (contents);
		xml_parser_free_tree (doc);
		return XPLAYER_PL_PARSER_RESULT_ERROR;
	}

	uri = g_file_get_uri (file);
	parse_opml_head_body (parser, uri, doc);
	g_free (uri);

	g_free (contents);
	xml_parser_free_tree (doc);

	return XPLAYER_PL_PARSER_RESULT_SUCCESS;
#endif /* !HAVE_GMIME */
}

#endif /* !XPLAYER_PL_PARSER_MINI */

