summaryrefslogtreecommitdiff
path: root/trunk/src/plparse/totem-pl-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/plparse/totem-pl-parser.c')
-rw-r--r--trunk/src/plparse/totem-pl-parser.c1206
1 files changed, 1206 insertions, 0 deletions
diff --git a/trunk/src/plparse/totem-pl-parser.c b/trunk/src/plparse/totem-pl-parser.c
new file mode 100644
index 000000000..a0100bf1d
--- /dev/null
+++ b/trunk/src/plparse/totem-pl-parser.c
@@ -0,0 +1,1206 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006 Bastien Nocera
+ Copyright (C) 2003, 2004 Colin Walters <walters@rhythmbox.org>
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+
+#ifndef TOTEM_PL_PARSER_MINI
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <gobject/gvaluecollector.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include "totem-pl-parser.h"
+#include "totemplparser-marshal.h"
+#include "totem-disc.h"
+#endif /* !TOTEM_PL_PARSER_MINI */
+
+#include "totem-pl-parser-mini.h"
+#include "totem-pl-parser-wm.h"
+#include "totem-pl-parser-qt.h"
+#include "totem-pl-parser-pls.h"
+#include "totem-pl-parser-xspf.h"
+#include "totem-pl-parser-media.h"
+#include "totem-pl-parser-smil.h"
+#include "totem-pl-parser-lines.h"
+#include "totem-pl-parser-misc.h"
+#include "totem-pl-parser-private.h"
+
+#define READ_CHUNK_SIZE 8192
+#define RECURSE_LEVEL_MAX 4
+#define DIR_MIME_TYPE "x-directory/normal"
+#define BLOCK_DEVICE_TYPE "x-special/device-block"
+#define EMPTY_FILE_TYPE "application/x-zerosize"
+#define TEXT_URI_TYPE "text/uri-list"
+#define AUDIO_MPEG_TYPE "audio/mpeg"
+
+typedef gboolean (*PlaylistIdenCallback) (const char *data, gsize len);
+
+#ifndef TOTEM_PL_PARSER_MINI
+typedef TotemPlParserResult (*PlaylistCallback) (TotemPlParser *parser, const char *url, const char *base, gpointer data);
+#endif
+
+typedef struct {
+ char *mimetype;
+#ifndef TOTEM_PL_PARSER_MINI
+ PlaylistCallback func;
+#endif
+ PlaylistIdenCallback iden;
+#ifndef TOTEM_PL_PARSER_MINI
+ guint unsafe : 1;
+#endif
+} PlaylistTypes;
+
+#ifndef TOTEM_PL_PARSER_MINI
+
+static void totem_pl_parser_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void totem_pl_parser_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+enum {
+ PROP_NONE,
+ PROP_RECURSE,
+ PROP_DEBUG,
+ PROP_FORCE,
+ PROP_DISABLE_UNSAFE
+};
+
+/* Signals */
+enum {
+ ENTRY,
+ PLAYLIST_START,
+ PLAYLIST_END,
+ LAST_SIGNAL
+};
+
+static int totem_pl_parser_table_signals[LAST_SIGNAL];
+static gboolean i18n_done = FALSE;
+
+static void totem_pl_parser_class_init (TotemPlParserClass *class);
+static void totem_pl_parser_init (TotemPlParser *parser);
+static void totem_pl_parser_finalize (GObject *object);
+
+G_DEFINE_TYPE(TotemPlParser, totem_pl_parser, G_TYPE_OBJECT)
+
+static void
+totem_pl_parser_class_init (TotemPlParserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = totem_pl_parser_finalize;
+ object_class->set_property = totem_pl_parser_set_property;
+ object_class->get_property = totem_pl_parser_get_property;
+
+ /* properties */
+ g_object_class_install_property (object_class,
+ PROP_RECURSE,
+ g_param_spec_boolean ("recurse",
+ "recurse",
+ "Whether or not to process URLs further",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class,
+ PROP_DEBUG,
+ g_param_spec_boolean ("debug",
+ "debug",
+ "Whether or not to enable debugging output",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_FORCE,
+ g_param_spec_boolean ("force",
+ "force",
+ "Whether or not to force parsing the file if the playlist looks unsupported",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_DISABLE_UNSAFE,
+ g_param_spec_boolean ("disable-unsafe",
+ "disable-unsafe",
+ "Whether or not to disable parsing of unsafe locations",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ /* Signals */
+ totem_pl_parser_table_signals[ENTRY] =
+ g_signal_new ("entry",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlParserClass, entry),
+ NULL, NULL,
+ totemplparser_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ totem_pl_parser_table_signals[PLAYLIST_START] =
+ g_signal_new ("playlist-start",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlParserClass, playlist_start),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+ totem_pl_parser_table_signals[PLAYLIST_END] =
+ g_signal_new ("playlist-end",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlParserClass, playlist_end),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+static void
+totem_pl_parser_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TotemPlParser *parser = TOTEM_PL_PARSER (object);
+
+ switch (prop_id)
+ {
+ case PROP_RECURSE:
+ parser->priv->recurse = g_value_get_boolean (value) != FALSE;
+ break;
+ case PROP_DEBUG:
+ parser->priv->debug = g_value_get_boolean (value) != FALSE;
+ break;
+ case PROP_FORCE:
+ parser->priv->force = g_value_get_boolean (value) != FALSE;
+ break;
+ case PROP_DISABLE_UNSAFE:
+ parser->priv->disable_unsafe = g_value_get_boolean (value) != FALSE;
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+totem_pl_parser_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TotemPlParser *parser = TOTEM_PL_PARSER (object);
+
+ switch (prop_id)
+ {
+ case PROP_RECURSE:
+ g_value_set_boolean (value, parser->priv->recurse);
+ break;
+ case PROP_DEBUG:
+ g_value_set_boolean (value, parser->priv->debug);
+ break;
+ case PROP_FORCE:
+ g_value_set_boolean (value, parser->priv->force);
+ break;
+ case PROP_DISABLE_UNSAFE:
+ g_value_set_boolean (value, parser->priv->disable_unsafe);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+GQuark
+totem_pl_parser_error_quark (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("totem_pl_parser_error");
+
+ return quark;
+}
+
+static void
+totem_pl_parser_init_i18n (void)
+{
+ if (i18n_done == FALSE) {
+ /* set up translation catalog */
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ i18n_done = TRUE;
+ }
+}
+
+TotemPlParser *
+totem_pl_parser_new (void)
+{
+ totem_pl_parser_init_i18n ();
+ return TOTEM_PL_PARSER (g_object_new (TOTEM_TYPE_PL_PARSER, NULL));
+}
+
+void
+totem_pl_parser_playlist_start (TotemPlParser *parser, const char *playlist_title)
+{
+ g_signal_emit (G_OBJECT (parser),
+ totem_pl_parser_table_signals[PLAYLIST_START],
+ 0, playlist_title);
+}
+
+void
+totem_pl_parser_playlist_end (TotemPlParser *parser, const char *playlist_title)
+{
+ g_signal_emit (G_OBJECT (parser),
+ totem_pl_parser_table_signals[PLAYLIST_END],
+ 0, playlist_title);
+}
+
+static char *
+my_gnome_vfs_get_mime_type_with_data (const char *uri, gpointer *data, TotemPlParser *parser)
+{
+ GnomeVFSResult result;
+ GnomeVFSHandle *handle;
+ char *buffer;
+ const char *mimetype;
+ GnomeVFSFileSize total_bytes_read;
+ GnomeVFSFileSize bytes_read;
+
+ *data = NULL;
+
+ /* Stat for a block device, we're screwed as far as speed
+ * is concerned now */
+ if (g_str_has_prefix (uri, "file://") != FALSE) {
+ struct stat buf;
+ if (stat (uri + strlen ("file://"), &buf) == 0) {
+ if (S_ISBLK (buf.st_mode))
+ return g_strdup (BLOCK_DEVICE_TYPE);
+ }
+ }
+
+ /* Open the file. */
+ result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
+ if (result != GNOME_VFS_OK) {
+ if (result == GNOME_VFS_ERROR_IS_DIRECTORY)
+ return g_strdup (DIR_MIME_TYPE);
+ DEBUG(g_print ("URL '%s' couldn't be opened in _get_mime_type_with_data: '%s'\n", uri, gnome_vfs_result_to_string (result)));
+ return NULL;
+ }
+ DEBUG(g_print ("URL '%s' was opened successfully in _get_mime_type_with_data:\n", uri));
+
+ /* Read the whole thing, up to MIME_READ_CHUNK_SIZE */
+ buffer = NULL;
+ total_bytes_read = 0;
+ bytes_read = 0;
+ do {
+ buffer = g_realloc (buffer, total_bytes_read
+ + MIME_READ_CHUNK_SIZE);
+ result = gnome_vfs_read (handle,
+ buffer + total_bytes_read,
+ MIME_READ_CHUNK_SIZE,
+ &bytes_read);
+ if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
+ g_free (buffer);
+ gnome_vfs_close (handle);
+ return NULL;
+ }
+
+ /* Check for overflow. */
+ if (total_bytes_read + bytes_read < total_bytes_read) {
+ g_free (buffer);
+ gnome_vfs_close (handle);
+ return NULL;
+ }
+
+ total_bytes_read += bytes_read;
+ } while (result == GNOME_VFS_OK
+ && total_bytes_read < MIME_READ_CHUNK_SIZE);
+
+ /* Close the file but don't overwrite the possible error */
+ if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF)
+ gnome_vfs_close (handle);
+ else
+ result = gnome_vfs_close (handle);
+
+ if (result != GNOME_VFS_OK) {
+ DEBUG(g_print ("URL '%s' couldn't be read or closed in _get_mime_type_with_data: '%s'\n", uri, gnome_vfs_result_to_string (result)));
+ g_free (buffer);
+ return NULL;
+ }
+
+ /* Empty file */
+ if (total_bytes_read == 0) {
+ DEBUG(g_print ("URL '%s' is empty in _get_mime_type_with_data\n", uri));
+ return g_strdup (EMPTY_FILE_TYPE);
+ }
+
+ /* Return the file null-terminated. */
+ buffer = g_realloc (buffer, total_bytes_read + 1);
+ buffer[total_bytes_read] = '\0';
+ *data = buffer;
+
+ mimetype = gnome_vfs_get_mime_type_for_data (*data, total_bytes_read);
+
+ if (mimetype != NULL && strcmp (mimetype, "text/plain") == 0) {
+ if (totem_pl_parser_is_uri_list (*data, total_bytes_read) != FALSE)
+ return g_strdup (TEXT_URI_TYPE);
+ }
+
+ return g_strdup (mimetype);
+}
+
+xmlDocPtr
+totem_pl_parser_parse_xml_file (const char *url)
+{
+ xmlDocPtr doc;
+ char *contents;
+ int size;
+
+ if (gnome_vfs_read_entire_file (url, &size, &contents) != GNOME_VFS_OK)
+ return NULL;
+
+ /* Try to remove HTML style comments */
+ {
+ char *needle;
+
+ while ((needle = strstr (contents, "<!--")) != NULL) {
+ while (strncmp (needle, "-->", 3) != 0) {
+ *needle = ' ';
+ needle++;
+ if (*needle == '\0')
+ break;
+ }
+ }
+ }
+
+ doc = xmlParseMemory (contents, size);
+ if (doc == NULL)
+ doc = xmlRecoverMemory (contents, size);
+ g_free (contents);
+
+ return doc;
+}
+
+char *
+totem_pl_parser_base_url (const char *url)
+{
+ /* Yay, let's reconstruct the base by hand */
+ GnomeVFSURI *uri, *parent;
+ char *base;
+
+ uri = gnome_vfs_uri_new (url);
+ if (uri == NULL)
+ return NULL;
+
+ parent = gnome_vfs_uri_get_parent (uri);
+ if (!parent) {
+ parent = uri;
+ }
+ base = gnome_vfs_uri_to_string (parent, 0);
+
+ gnome_vfs_uri_unref (uri);
+ if (parent != uri) {
+ gnome_vfs_uri_unref (parent);
+ }
+
+ return base;
+}
+
+gboolean
+totem_pl_parser_line_is_empty (const char *line)
+{
+ guint i;
+
+ if (line == NULL)
+ return TRUE;
+
+ for (i = 0; line[i] != '\0'; i++) {
+ if (line[i] != '\t' && line[i] != ' ')
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+totem_pl_parser_write_string (GnomeVFSHandle *handle, const char *buf, GError **error)
+{
+ GnomeVFSResult res;
+ GnomeVFSFileSize written;
+ guint len;
+
+ len = strlen (buf);
+ res = gnome_vfs_write (handle, buf, len, &written);
+ if (res != GNOME_VFS_OK || written < len) {
+ g_set_error (error,
+ TOTEM_PL_PARSER_ERROR,
+ TOTEM_PL_PARSER_ERROR_VFS_WRITE,
+ _("Couldn't write parser: %s"),
+ gnome_vfs_result_to_string (res));
+ gnome_vfs_close (handle);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+totem_pl_parser_num_entries (TotemPlParser *parser, GtkTreeModel *model,
+ TotemPlParserIterFunc func, gpointer user_data)
+{
+ int num_entries, i, ignored;
+
+ num_entries = gtk_tree_model_iter_n_children (model, NULL);
+ ignored = 0;
+
+ for (i = 1; i <= num_entries; i++)
+ {
+ GtkTreeIter iter;
+ char *url, *title;
+ gboolean custom_title;
+
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, i -1) == FALSE)
+ return i - ignored;
+
+ func (model, &iter, &url, &title, &custom_title, user_data);
+ if (totem_pl_parser_scheme_is_ignored (parser, url) != FALSE)
+ ignored++;
+
+ g_free (url);
+ g_free (title);
+ }
+
+ return num_entries - ignored;
+}
+
+char *
+totem_pl_parser_relative (const char *url, const char *output)
+{
+ char *url_base, *output_base;
+ char *base, *needle;
+
+ base = NULL;
+ url_base = totem_pl_parser_base_url (url);
+ if (url_base == NULL)
+ return NULL;
+
+ output_base = totem_pl_parser_base_url (output);
+
+ needle = strstr (url_base, output_base);
+ if (needle != NULL)
+ {
+ GnomeVFSURI *uri;
+ char *newurl;
+
+ uri = gnome_vfs_uri_new (url);
+ newurl = gnome_vfs_uri_to_string (uri, 0);
+ if (newurl[strlen (output_base)] == '/') {
+ base = g_strdup (newurl + strlen (output_base) + 1);
+ } else {
+ base = g_strdup (newurl + strlen (output_base));
+ }
+ gnome_vfs_uri_unref (uri);
+ g_free (newurl);
+
+ /* And finally unescape the string */
+ newurl = gnome_vfs_unescape_string (base, NULL);
+ g_free (base);
+ base = newurl;
+ }
+
+ g_free (url_base);
+ g_free (output_base);
+
+ return base;
+}
+
+#ifndef TOTEM_PL_PARSER_MINI
+gboolean
+totem_pl_parser_write_with_title (TotemPlParser *parser, GtkTreeModel *model,
+ TotemPlParserIterFunc func,
+ const char *output, const char *title,
+ TotemPlParserType type,
+ gpointer user_data, GError **error)
+{
+ switch (type)
+ {
+ case TOTEM_PL_PARSER_PLS:
+ return totem_pl_parser_write_pls (parser, model, func,
+ output, title, user_data, error);
+ case TOTEM_PL_PARSER_M3U:
+ case TOTEM_PL_PARSER_M3U_DOS:
+ return totem_pl_parser_write_m3u (parser, model, func,
+ output, (type == TOTEM_PL_PARSER_M3U_DOS),
+ user_data, error);
+ case TOTEM_PL_PARSER_XSPF:
+ return totem_pl_parser_write_xspf (parser, model, func,
+ output, title, user_data, error);
+ default:
+ g_assert_not_reached ();
+ }
+
+ return FALSE;
+}
+
+gboolean
+totem_pl_parser_write (TotemPlParser *parser, GtkTreeModel *model,
+ TotemPlParserIterFunc func,
+ const char *output, TotemPlParserType type,
+ gpointer user_data,
+ GError **error)
+{
+ return totem_pl_parser_write_with_title (parser, model, func, output,
+ NULL, type, user_data, error);
+}
+
+#endif /* TOTEM_PL_PARSER_MINI */
+
+int
+totem_pl_parser_read_ini_line_int (char **lines, const char *key)
+{
+ int retval = -1;
+ int i;
+
+ if (lines == NULL || key == NULL)
+ return -1;
+
+ for (i = 0; (lines[i] != NULL && retval == -1); i++) {
+ char *line = lines[i];
+
+ while (*line == '\t' || *line == ' ')
+ line++;
+
+ if (g_ascii_strncasecmp (line, key, strlen (key)) == 0) {
+ char **bits;
+
+ bits = g_strsplit (line, "=", 2);
+ if (bits[0] == NULL || bits [1] == NULL) {
+ g_strfreev (bits);
+ return -1;
+ }
+
+ retval = (gint) g_strtod (bits[1], NULL);
+ g_strfreev (bits);
+ }
+ }
+
+ return retval;
+}
+
+char*
+totem_pl_parser_read_ini_line_string_with_sep (char **lines, const char *key,
+ gboolean dos_mode, const char *sep)
+{
+ char *retval = NULL;
+ int i;
+
+ if (lines == NULL || key == NULL)
+ return NULL;
+
+ for (i = 0; (lines[i] != NULL && retval == NULL); i++) {
+ char *line = lines[i];
+
+ while (*line == '\t' || *line == ' ')
+ line++;
+
+ if (g_ascii_strncasecmp (line, key, strlen (key)) == 0) {
+ char **bits;
+ ssize_t len;
+
+ bits = g_strsplit (line, sep, 2);
+ if (bits[0] == NULL || bits [1] == NULL) {
+ g_strfreev (bits);
+ return NULL;
+ }
+
+ retval = g_strdup (bits[1]);
+ len = strlen (retval);
+ if (dos_mode && len >= 2 && retval[len-2] == '\r') {
+ retval[len-2] = '\n';
+ retval[len-1] = '\0';
+ }
+
+ g_strfreev (bits);
+ }
+ }
+
+ return retval;
+}
+
+char*
+totem_pl_parser_read_ini_line_string (char **lines, const char *key, gboolean dos_mode)
+{
+ return totem_pl_parser_read_ini_line_string_with_sep (lines, key, dos_mode, "=");
+}
+
+static void
+totem_pl_parser_init (TotemPlParser *parser)
+{
+ GParamSpec *pspec;
+ parser->priv = g_new0 (TotemPlParserPrivate, 1);
+
+ parser->priv->pspec_pool = g_param_spec_pool_new (FALSE);
+ pspec = g_param_spec_string ("url", "url",
+ "URL to be added", NULL,
+ G_PARAM_READABLE & G_PARAM_WRITABLE);
+ g_param_spec_pool_insert (parser->priv->pspec_pool, pspec, TOTEM_TYPE_PL_PARSER);
+ pspec = g_param_spec_string ("title", "title",
+ "Title of the item to be added", NULL,
+ G_PARAM_READABLE & G_PARAM_WRITABLE);
+ g_param_spec_pool_insert (parser->priv->pspec_pool, pspec, TOTEM_TYPE_PL_PARSER);
+ pspec = g_param_spec_string ("genre", "genre",
+ "Genre of the item to be added", NULL,
+ G_PARAM_READABLE & G_PARAM_WRITABLE);
+ g_param_spec_pool_insert (parser->priv->pspec_pool, pspec, TOTEM_TYPE_PL_PARSER);
+ pspec = g_param_spec_string ("base", "base",
+ "Base URL of the item to be added", NULL,
+ G_PARAM_READABLE & G_PARAM_WRITABLE);
+ g_param_spec_pool_insert (parser->priv->pspec_pool, pspec, TOTEM_TYPE_PL_PARSER);
+}
+
+static void
+totem_pl_parser_finalize (GObject *object)
+{
+ TotemPlParser *parser = TOTEM_PL_PARSER (object);
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (parser->priv != NULL);
+
+ g_list_foreach (parser->priv->ignore_schemes, (GFunc) g_free, NULL);
+ g_list_free (parser->priv->ignore_schemes);
+
+ g_list_foreach (parser->priv->ignore_mimetypes, (GFunc) g_free, NULL);
+ g_list_free (parser->priv->ignore_mimetypes);
+
+ g_free (parser->priv);
+ parser->priv = NULL;
+
+ G_OBJECT_CLASS (totem_pl_parser_parent_class)->finalize (object);
+}
+
+//FIXME remove ?
+static gboolean
+totem_pl_parser_check_utf8 (const char *title)
+{
+ return title ? g_utf8_validate (title, -1, NULL) : FALSE;
+}
+
+static void
+totem_pl_parser_add_url_valist (TotemPlParser *parser,
+ const gchar *first_property_name,
+ va_list var_args)
+{
+ const char *name;
+ char *title, *url, *genre, *base;
+
+ title = url = genre = base = NULL;
+
+ g_object_ref (G_OBJECT (parser));
+
+ name = first_property_name;
+
+ while (name) {
+ GValue value = { 0, };
+ GParamSpec *pspec;
+ char *error = NULL;
+
+ pspec = g_param_spec_pool_lookup (parser->priv->pspec_pool,
+ name,
+ G_OBJECT_TYPE (parser),
+ FALSE);
+
+ if (!pspec) {
+ g_warning ("Unknown property '%s'", name);
+ break;
+ }
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ G_VALUE_COLLECT (&value, var_args, 0, &error);
+ if (error != NULL) {
+ g_warning ("Error getting the value for property '%s'", name);
+ break;
+ }
+
+ if (strcmp (name, "url") == 0) {
+ url = g_value_dup_string (&value);
+ } else if (strcmp (name, "title") == 0) {
+ title = g_value_dup_string (&value);
+ } else if (strcmp (name, "genre") == 0) {
+ genre = g_value_dup_string (&value);
+ } else if (strcmp (name, "base") == 0) {
+ base = g_value_dup_string (&value);
+ }
+
+ g_value_unset (&value);
+ name = va_arg (var_args, char*);
+ }
+
+ g_assert (url != NULL);
+
+ if (parser->priv->disable_unsafe != FALSE) {
+ //FIXME fix this! 396710
+ }
+
+ g_signal_emit (G_OBJECT (parser), totem_pl_parser_table_signals[ENTRY],
+ 0, url, title, genre);
+
+ g_free (url);
+ g_free (title);
+ g_free (genre);
+ g_free (base);
+
+ g_object_unref (G_OBJECT (parser));
+}
+
+void
+totem_pl_parser_add_url (TotemPlParser *parser,
+ const char *first_property_name,
+ ...)
+{
+ va_list var_args;
+ va_start (var_args, first_property_name);
+ totem_pl_parser_add_url_valist (parser, first_property_name, var_args);
+ va_end (var_args);
+}
+
+void
+totem_pl_parser_add_one_url (TotemPlParser *parser, const char *url, const char *title)
+{
+ totem_pl_parser_add_url (parser, "url", url, "title", title, NULL);
+}
+
+char *
+totem_pl_resolve_url (const char *base, const char *url)
+{
+ GnomeVFSURI *base_uri, *new;
+ char *resolved;
+
+ /* If the URI isn't relative, just leave */
+ if (strstr (url, "://") != NULL)
+ return g_strdup (url);
+
+ base_uri = gnome_vfs_uri_new (base);
+ g_return_val_if_fail (base_uri != NULL, g_strdup (url));
+ new = gnome_vfs_uri_resolve_relative (base_uri, url);
+ g_return_val_if_fail (new != NULL, g_strdup (url));
+ gnome_vfs_uri_unref (base_uri);
+ resolved = gnome_vfs_uri_to_string (new, GNOME_VFS_URI_HIDE_NONE);
+ gnome_vfs_uri_unref (new);
+
+ return resolved;
+}
+
+#endif /* !TOTEM_PL_PARSER_MINI */
+
+#ifndef TOTEM_PL_PARSER_MINI
+#define PLAYLIST_TYPE(mime,cb,identcb,unsafe) { mime, cb, identcb, unsafe }
+#define PLAYLIST_TYPE2(mime,cb,identcb) { mime, cb, identcb }
+#define PLAYLIST_TYPE3(mime) { mime, NULL, NULL, FALSE }
+#else
+#define PLAYLIST_TYPE(mime,cb,identcb,unsafe) { mime }
+#define PLAYLIST_TYPE2(mime,cb,identcb) { mime, identcb }
+#define PLAYLIST_TYPE3(mime) { mime }
+#endif
+
+/* These ones need a special treatment, mostly parser formats */
+static PlaylistTypes special_types[] = {
+ PLAYLIST_TYPE ("audio/x-mpegurl", totem_pl_parser_add_m3u, NULL, FALSE),
+ PLAYLIST_TYPE ("audio/playlist", totem_pl_parser_add_m3u, NULL, FALSE),
+ PLAYLIST_TYPE ("audio/x-scpls", totem_pl_parser_add_pls, NULL, FALSE),
+ PLAYLIST_TYPE ("application/x-smil", totem_pl_parser_add_smil, NULL, FALSE),
+ PLAYLIST_TYPE ("application/smil", totem_pl_parser_add_smil, NULL, FALSE),
+ PLAYLIST_TYPE ("video/x-ms-wvx", totem_pl_parser_add_asx, NULL, FALSE),
+ PLAYLIST_TYPE ("audio/x-ms-wax", totem_pl_parser_add_asx, NULL, FALSE),
+ PLAYLIST_TYPE ("application/xspf+xml", totem_pl_parser_add_xspf, NULL, FALSE),
+ PLAYLIST_TYPE ("text/uri-list", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list, FALSE),
+ PLAYLIST_TYPE ("text/x-google-video-pointer", totem_pl_parser_add_gvp, NULL, FALSE),
+ PLAYLIST_TYPE ("text/google-video-pointer", totem_pl_parser_add_gvp, NULL, FALSE),
+#ifndef TOTEM_PL_PARSER_MINI
+ PLAYLIST_TYPE ("application/x-desktop", totem_pl_parser_add_desktop, NULL, TRUE),
+ PLAYLIST_TYPE ("application/x-gnome-app-info", totem_pl_parser_add_desktop, NULL, TRUE),
+ PLAYLIST_TYPE ("application/x-cd-image", totem_pl_parser_add_iso, NULL, TRUE),
+ PLAYLIST_TYPE ("application/x-extension-img", totem_pl_parser_add_iso, NULL, TRUE),
+ PLAYLIST_TYPE ("application/x-cue", totem_pl_parser_add_cue, NULL, TRUE),
+ PLAYLIST_TYPE (DIR_MIME_TYPE, totem_pl_parser_add_directory, NULL, TRUE),
+ PLAYLIST_TYPE (BLOCK_DEVICE_TYPE, totem_pl_parser_add_block, NULL, TRUE),
+#endif
+};
+
+/* These ones are "dual" types, might be a video, might be a parser */
+static PlaylistTypes dual_types[] = {
+ PLAYLIST_TYPE2 ("audio/x-real-audio", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list),
+ PLAYLIST_TYPE2 ("audio/x-pn-realaudio", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list),
+ PLAYLIST_TYPE2 ("application/vnd.rn-realmedia", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list),
+ PLAYLIST_TYPE2 ("audio/x-pn-realaudio-plugin", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list),
+ PLAYLIST_TYPE2 ("audio/vnd.rn-realaudio", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list),
+ PLAYLIST_TYPE2 ("audio/x-realaudio", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list),
+ PLAYLIST_TYPE2 ("text/plain", totem_pl_parser_add_ra, totem_pl_parser_is_uri_list),
+ PLAYLIST_TYPE2 ("audio/x-ms-asx", totem_pl_parser_add_asx, totem_pl_parser_is_asx),
+ PLAYLIST_TYPE2 ("video/x-ms-asf", totem_pl_parser_add_asf, totem_pl_parser_is_asf),
+ PLAYLIST_TYPE2 ("video/x-ms-wmv", totem_pl_parser_add_asf, totem_pl_parser_is_asf),
+ PLAYLIST_TYPE2 ("video/quicktime", totem_pl_parser_add_quicktime, totem_pl_parser_is_quicktime),
+ PLAYLIST_TYPE2 ("application/x-quicktime-media-link", totem_pl_parser_add_quicktime, totem_pl_parser_is_quicktime),
+ PLAYLIST_TYPE2 ("application/x-quicktimeplayer", totem_pl_parser_add_quicktime, totem_pl_parser_is_quicktime),
+};
+
+#ifndef TOTEM_PL_PARSER_MINI
+
+static PlaylistTypes ignore_types[] = {
+ PLAYLIST_TYPE3 ("image/*"),
+ PLAYLIST_TYPE3 ("text/plain"),
+ PLAYLIST_TYPE3 ("application/x-rar"),
+ PLAYLIST_TYPE3 ("application/zip"),
+ PLAYLIST_TYPE3 ("application/x-trash"),
+};
+
+gboolean
+totem_pl_parser_scheme_is_ignored (TotemPlParser *parser, const char *url)
+{
+ GList *l;
+
+ if (parser->priv->ignore_schemes == NULL)
+ return FALSE;
+
+ for (l = parser->priv->ignore_schemes; l != NULL; l = l->next)
+ {
+ const char *scheme = l->data;
+ if (g_str_has_prefix (url, scheme) != FALSE)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+totem_pl_parser_mimetype_is_ignored (TotemPlParser *parser,
+ const char *mimetype)
+{
+ GList *l;
+
+ if (parser->priv->ignore_mimetypes == NULL)
+ return FALSE;
+
+ for (l = parser->priv->ignore_mimetypes; l != NULL; l = l->next)
+ {
+ const char *item = l->data;
+ if (strcmp (mimetype, item) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+gboolean
+totem_pl_parser_ignore (TotemPlParser *parser, const char *url)
+{
+ const char *mimetype;
+ guint i;
+
+ if (totem_pl_parser_scheme_is_ignored (parser, url) != FALSE)
+ return TRUE;
+
+ mimetype = gnome_vfs_get_file_mime_type (url, NULL, TRUE);
+ if (mimetype == NULL || strcmp (mimetype, GNOME_VFS_MIME_TYPE_UNKNOWN) == 0)
+ return FALSE;
+
+ for (i = 0; i < G_N_ELEMENTS (special_types); i++)
+ if (strcmp (special_types[i].mimetype, mimetype) == 0)
+ return FALSE;
+
+ for (i = 0; i < G_N_ELEMENTS (dual_types); i++)
+ if (strcmp (dual_types[i].mimetype, mimetype) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+totem_pl_parser_ignore_from_mimetype (TotemPlParser *parser, const char *mimetype)
+{
+ char *super;
+ guint i;
+
+ super = gnome_vfs_get_supertype_from_mime_type (mimetype);
+ for (i = 0; i < G_N_ELEMENTS (ignore_types) && super != NULL; i++) {
+ if (gnome_vfs_mime_type_is_supertype (ignore_types[i].mimetype) != FALSE) {
+ if (strcmp (super, ignore_types[i].mimetype) == 0) {
+ g_free (super);
+ return TRUE;
+ }
+ } else {
+ GnomeVFSMimeEquivalence eq;
+
+ eq = gnome_vfs_mime_type_get_equivalence (mimetype, ignore_types[i].mimetype);
+ if (eq == GNOME_VFS_MIME_PARENT || eq == GNOME_VFS_MIME_IDENTICAL) {
+ g_free (super);
+ return TRUE;
+ }
+ }
+ }
+ g_free (super);
+
+ return FALSE;
+}
+
+TotemPlParserResult
+totem_pl_parser_parse_internal (TotemPlParser *parser, const char *url,
+ const char *base)
+{
+ char *mimetype;
+ guint i;
+ gpointer data = NULL;
+ TotemPlParserResult ret = TOTEM_PL_PARSER_RESULT_ERROR;
+ gboolean found = FALSE;
+
+ if (parser->priv->recurse_level > RECURSE_LEVEL_MAX)
+ return TOTEM_PL_PARSER_RESULT_ERROR;
+
+ /* Shouldn't gnome-vfs have a list of schemes it supports? */
+ if (g_str_has_prefix (url, "mms") != FALSE
+ || g_str_has_prefix (url, "rtsp") != FALSE
+ || g_str_has_prefix (url, "icy") != FALSE) {
+ DEBUG(g_print ("URL '%s' is MMS, RTSP or ICY, ignoring\n", url));
+ return TOTEM_PL_PARSER_RESULT_UNHANDLED;
+ }
+
+ if (!parser->priv->recurse && parser->priv->recurse_level > 0) {
+ return TOTEM_PL_PARSER_RESULT_UNHANDLED;
+ }
+
+ /* In force mode we want to get the data */
+ if (parser->priv->force != FALSE) {
+ mimetype = my_gnome_vfs_get_mime_type_with_data (url, &data, parser);
+ } else {
+ mimetype = g_strdup (gnome_vfs_get_mime_type_for_name (url));
+ }
+
+ DEBUG(g_print ("_get_mime_type_for_name for '%s' returned '%s'\n", url, mimetype));
+ if (mimetype == NULL || strcmp (GNOME_VFS_MIME_TYPE_UNKNOWN, mimetype) == 0) {
+ mimetype = my_gnome_vfs_get_mime_type_with_data (url, &data, parser);
+ DEBUG(g_print ("_get_mime_type_with_data for '%s' returned '%s'\n", url, mimetype ? mimetype : "NULL"));
+ }
+
+ if (mimetype == NULL) {
+ g_free (data);
+ return TOTEM_PL_PARSER_RESULT_UNHANDLED;
+ }
+
+ if (strcmp (mimetype, EMPTY_FILE_TYPE) == 0) {
+ g_free (data);
+ return TOTEM_PL_PARSER_RESULT_SUCCESS;
+ }
+
+ /* If we're at the top-level of the parsing, try to get more
+ * data from the playlist parser */
+ if (strcmp (mimetype, AUDIO_MPEG_TYPE) == 0 && parser->priv->recurse_level == 0 && data == NULL) {
+ char *tmp;
+ tmp = my_gnome_vfs_get_mime_type_with_data (url, &data, parser);
+ if (tmp != NULL) {
+ g_free (mimetype);
+ mimetype = tmp;
+ }
+ DEBUG(g_print ("_get_mime_type_with_data for '%s' returned '%s' (was %s)\n", url, mimetype, AUDIO_MPEG_TYPE));
+ }
+
+ if (totem_pl_parser_mimetype_is_ignored (parser, mimetype) != FALSE) {
+ g_free (mimetype);
+ g_free (data);
+ return TOTEM_PL_PARSER_RESULT_IGNORED;
+ }
+
+ if (parser->priv->recurse || parser->priv->recurse_level == 0) {
+ parser->priv->recurse_level++;
+
+ for (i = 0; i < G_N_ELEMENTS(special_types); i++) {
+ if (strcmp (special_types[i].mimetype, mimetype) == 0) {
+ DEBUG(g_print ("URL '%s' is special type '%s'\n", url, mimetype));
+ if (parser->priv->disable_unsafe != FALSE && special_types[i].unsafe != FALSE) {
+ g_free (mimetype);
+ g_free (data);
+ return TOTEM_PL_PARSER_RESULT_IGNORED;
+ }
+ ret = (* special_types[i].func) (parser, url, base, data);
+ found = TRUE;
+ break;
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(dual_types) && found == FALSE; i++) {
+ if (strcmp (dual_types[i].mimetype, mimetype) == 0) {
+ DEBUG(g_print ("URL '%s' is dual type '%s'\n", url, mimetype));
+ if (data == NULL) {
+ g_free (mimetype);
+ mimetype = my_gnome_vfs_get_mime_type_with_data (url, &data, parser);
+ }
+ ret = (* dual_types[i].func) (parser, url, base, data);
+ found = TRUE;
+ break;
+ }
+ }
+
+ g_free (data);
+
+ parser->priv->recurse_level--;
+ }
+
+ if (ret == TOTEM_PL_PARSER_RESULT_SUCCESS) {
+ g_free (mimetype);
+ return ret;
+ }
+
+ if (totem_pl_parser_ignore_from_mimetype (parser, mimetype)) {
+ g_free (mimetype);
+ return TOTEM_PL_PARSER_RESULT_IGNORED;
+ }
+ g_free (mimetype);
+
+ if (ret != TOTEM_PL_PARSER_RESULT_SUCCESS && parser->priv->fallback) {
+ totem_pl_parser_add_one_url (parser, url, NULL);
+ return TOTEM_PL_PARSER_RESULT_SUCCESS;
+ }
+
+ return ret;
+}
+
+TotemPlParserResult
+totem_pl_parser_parse_with_base (TotemPlParser *parser, const char *url,
+ const char *base, gboolean fallback)
+{
+ g_return_val_if_fail (TOTEM_IS_PL_PARSER (parser), TOTEM_PL_PARSER_RESULT_UNHANDLED);
+ g_return_val_if_fail (url != NULL, TOTEM_PL_PARSER_RESULT_UNHANDLED);
+
+ if (totem_pl_parser_scheme_is_ignored (parser, url) != FALSE)
+ return TOTEM_PL_PARSER_RESULT_UNHANDLED;
+
+ g_return_val_if_fail (strstr (url, "://") != NULL,
+ TOTEM_PL_PARSER_RESULT_IGNORED);
+
+ parser->priv->recurse_level = 0;
+ parser->priv->fallback = fallback != FALSE;
+ return totem_pl_parser_parse_internal (parser, url, base);
+}
+
+TotemPlParserResult
+totem_pl_parser_parse (TotemPlParser *parser, const char *url,
+ gboolean fallback)
+{
+ return totem_pl_parser_parse_with_base (parser, url, NULL, fallback);
+}
+
+void
+totem_pl_parser_add_ignored_scheme (TotemPlParser *parser,
+ const char *scheme)
+{
+ g_return_if_fail (TOTEM_IS_PL_PARSER (parser));
+
+ parser->priv->ignore_schemes = g_list_prepend
+ (parser->priv->ignore_schemes, g_strdup (scheme));
+}
+
+void
+totem_pl_parser_add_ignored_mimetype (TotemPlParser *parser,
+ const char *mimetype)
+{
+ g_return_if_fail (TOTEM_IS_PL_PARSER (parser));
+
+ parser->priv->ignore_mimetypes = g_list_prepend
+ (parser->priv->ignore_mimetypes, g_strdup (mimetype));
+}
+
+#endif /* !TOTEM_PL_PARSER_MINI */
+
+#define D(x) if (debug) x
+
+gboolean
+totem_pl_parser_can_parse_from_data (const char *data,
+ gsize len,
+ gboolean debug)
+{
+ const char *mimetype;
+ guint i;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ /* Bad cast! */
+ mimetype = gnome_vfs_get_mime_type_for_data ((gpointer) data, (int) len);
+
+ if (mimetype == NULL || strcmp (GNOME_VFS_MIME_TYPE_UNKNOWN, mimetype) == 0) {
+ D(g_message ("totem_pl_parser_can_parse_from_data couldn't get mimetype"));
+ return FALSE;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(special_types); i++) {
+ if (strcmp (special_types[i].mimetype, mimetype) == 0) {
+ D(g_message ("Is special type '%s'", mimetype));
+ return TRUE;
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(dual_types); i++) {
+ if (strcmp (dual_types[i].mimetype, mimetype) == 0) {
+ D(g_message ("Should be dual type '%s', making sure now", mimetype));
+ if (dual_types[i].iden != NULL) {
+ gboolean retval = (* dual_types[i].iden) (data, len);
+ D(g_message ("%s dual type '%s'",
+ retval ? "Is" : "Is not", mimetype));
+ return retval;
+ }
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+totem_pl_parser_can_parse_from_filename (const char *filename, gboolean debug)
+{
+ GMappedFile *map;
+ GError *err = NULL;
+ gboolean retval;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ map = g_mapped_file_new (filename, FALSE, &err);
+ if (map == NULL) {
+ D(g_message ("couldn't mmap %s: %s", filename, err->message));
+ g_error_free (err);
+ return FALSE;
+ }
+
+ retval = totem_pl_parser_can_parse_from_data
+ (g_mapped_file_get_contents (map),
+ g_mapped_file_get_length (map), debug);
+
+ g_mapped_file_free (map);
+
+ return retval;
+}
+