Commit 0ed8c484 authored by Roberto Guido's avatar Roberto Guido Committed by Roberto Guido

New "FeedsGroup" object, to parse and produce groups of feeds

Added OPML files parser
parent 61903cfe
......@@ -14,35 +14,41 @@ sources_private_h = \
utils.h
sources_public_h = \
common.h \
libgrss.h \
feed-atom-handler.h \
feed-channel.h \
feed-enclosure.h \
feed-handler.h \
feed-item.h \
feed-parser.h \
feed-rss-handler.h \
feed-pie-handler.h \
feeds-pool.h \
feeds-store.h \
feeds-subscriber.h \
common.h \
libgrss.h \
feed-atom-handler.h \
feed-channel.h \
feed-enclosure.h \
feed-handler.h \
feed-item.h \
feed-parser.h \
feed-rss-handler.h \
feed-pie-handler.h \
feeds-group.h \
feeds-group-handler.h \
feeds-opml-group-handler.h \
feeds-pool.h \
feeds-store.h \
feeds-subscriber.h \
ns-handler.h
sources_c = \
$(marshal_source) \
feed-atom-handler.c \
feed-channel.c \
feed-enclosure.c \
feed-handler.c \
feed-item.c \
feed-parser.c \
feed-rss-handler.c \
feed-pie-handler.c \
feeds-pool.c \
feeds-store.c \
feeds-subscriber.c \
ns-handler.c \
$(marshal_source) \
feed-atom-handler.c \
feed-channel.c \
feed-enclosure.c \
feed-handler.c \
feed-item.c \
feed-parser.c \
feed-rss-handler.c \
feed-pie-handler.c \
feeds-group.c \
feeds-group-handler.c \
feeds-opml-group-handler.c \
feeds-pool.c \
feeds-store.c \
feeds-subscriber.c \
ns-handler.c \
utils.c
marshal_source = \
......
......@@ -305,13 +305,13 @@ atom10_parse_person_construct (xmlNodePtr cur)
if (xmlStrEqual (cur->name, BAD_CAST"email")) {
if (email)
invalid = TRUE;
g_free(email);
g_free (email);
tmp = (gchar *)xmlNodeListGetString(cur->doc, cur->xmlChildrenNode, 1);
email = g_strdup_printf(" - <a href=\"mailto:%s\">%s</a>", tmp, tmp);
g_free(tmp);
}
if (xmlStrEqual(cur->name, BAD_CAST"uri")) {
if (xmlStrEqual (cur->name, BAD_CAST"uri")) {
if (!uri)
invalid = TRUE;
g_free (uri);
......@@ -599,7 +599,7 @@ atom10_parse_entry (FeedHandler *self, FeedChannel *feed, xmlNodePtr cur)
}
if (xmlStrcmp(cur->ns->href, ATOM10_NS)) {
if (xmlStrcmp (cur->ns->href, ATOM10_NS)) {
cur = cur->next;
continue;
}
......
......@@ -667,7 +667,7 @@ feed_channel_fetch (FeedChannel *channel)
status = soup_session_send_message (session, msg);
if (status >= 200 && status <= 299) {
doc = feed_content_to_xml (msg->response_body->data, msg->response_body->length);
doc = content_to_xml (msg->response_body->data, msg->response_body->length);
if (doc != NULL) {
parser = feed_parser_new ();
......
......@@ -86,7 +86,7 @@ feed_parser_init (FeedParser *object)
object->priv = FEED_PARSER_GET_PRIVATE (object);
}
static GSList *
static GSList*
feed_parsers_get_list (FeedParser *parser)
{
FeedHandler *feed;
......
/*
* Copyright (C) 2010, Roberto Guido <rguido@src.gnome.org>
* Michele Tameni <michele@amdplanet.it>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 "utils.h"
#include "feeds-group-handler.h"
/**
* SECTION: feeds-group-handler
* @short_description: interface for specialized groups parsers
*
* The #FeedsGroupHandler interface defines a unique API for all specialized
* groups parsers implementations
*/
static void
feeds_group_handler_base_init (gpointer g_class)
{
}
GType
feeds_group_handler_get_type ()
{
static GType iface_type = 0;
if (iface_type == 0) {
static const GTypeInfo info = {
sizeof (FeedsGroupHandlerInterface),
feeds_group_handler_base_init,
NULL,
};
iface_type = g_type_register_static (G_TYPE_INTERFACE, "FeedsGroupHandler", &info, 0);
}
return iface_type;
}
/**
* feeds_group_handler_check_format:
* @self: a #FeedsGroupHandler
* @doc: XML document from a parsed feed
* @cur: first valid node into the XML document
*
* Used to check validity of an XML document against the given group parser
*
* Return value: %TRUE if the document can be parsed with the given
* #FeedsGroupHandler, %FALSE otherwise
*/
gboolean
feeds_group_handler_check_format (FeedsGroupHandler *self, xmlDocPtr doc, xmlNodePtr cur)
{
if (IS_FEEDS_GROUP_HANDLER (self) == FALSE)
return FALSE;
return FEEDS_GROUP_HANDLER_GET_INTERFACE (self)->check_format (self, doc, cur);
}
/**
* feeds_group_handler_parse:
* @self: a #FeedsGroupHandler
* @doc: XML document from the feed
* @error: location for eventual errors
*
* Parses the given @doc and extracts a list of #FeedChannels
*
* Return value: a list of #FeedChannels, to be freed when no longer in use,
* or %NULL if an error occours (and @error is set accordly)
*/
GList*
feeds_group_handler_parse (FeedsGroupHandler *self, xmlDocPtr doc, GError *error)
{
if (IS_FEEDS_GROUP_HANDLER (self) == FALSE)
return FALSE;
return FEEDS_GROUP_HANDLER_GET_INTERFACE (self)->parse (self, doc, error);
}
/**
* feeds_group_handler_dump:
* @self: a #FeedsGroupHandler
* @channels: list of #FeedChannels
* @error: location for eventual errors
*
* Builds a rappresentation of the given list of @channels for the managed
* group type
*
* Return value: a text to be dump on a file or transmitted, to be freed when
* no longer in use, or %NULL if an error occours (and @error is set accordly)
*/
gchar*
feeds_group_handler_dump (FeedsGroupHandler *self, GList *channels, GError *error)
{
if (IS_FEEDS_GROUP_HANDLER (self) == FALSE)
return FALSE;
return FEEDS_GROUP_HANDLER_GET_INTERFACE (self)->dump (self, channels, error);
}
/*
* Copyright (C) 2010, Roberto Guido <rguido@src.gnome.org>
* Michele Tameni <michele@amdplanet.it>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*/
#ifndef __FEEDS_GROUP_HANDLER_H__
#define __FEEDS_GROUP_HANDLER_H__
#include "common.h"
#include "feed-channel.h"
#define FEEDS_GROUP_HANDLER_TYPE (feeds_group_handler_get_type ())
#define FEEDS_GROUP_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FEEDS_GROUP_HANDLER_TYPE, FeedsGroupHandler))
#define IS_FEEDS_GROUP_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FEEDS_GROUP_HANDLER_TYPE))
#define FEEDS_GROUP_HANDLER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), FEEDS_GROUP_HANDLER_TYPE, FeedsGroupHandlerInterface))
typedef struct _FeedsGroupHandler FeedsGroupHandler;
typedef struct _FeedsGroupHandlerInterface FeedsGroupHandlerInterface;
struct _FeedsGroupHandlerInterface {
GTypeInterface parent_iface;
gboolean (*check_format) (FeedsGroupHandler *self, xmlDocPtr doc, xmlNodePtr cur);
GList* (*parse) (FeedsGroupHandler *self, xmlDocPtr doc, GError *error);
gchar* (*dump) (FeedsGroupHandler *self, GList *channels, GError *error);
};
GType feeds_group_handler_get_type ();
gboolean feeds_group_handler_check_format (FeedsGroupHandler *self, xmlDocPtr doc, xmlNodePtr cur);
GList* feeds_group_handler_parse (FeedsGroupHandler *self, xmlDocPtr doc, GError *error);
gchar* feeds_group_handler_dump (FeedsGroupHandler *self, GList *channels, GError *error);
#endif /* __FEEDS_GROUP_HANDLER_H__ */
/*
* Copyright (C) 2010, Roberto Guido <rguido@src.gnome.org>
* Michele Tameni <michele@amdplanet.it>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 "utils.h"
#include "feeds-group.h"
#include "feeds-group-handler.h"
#include "feeds-opml-group-handler.h"
#define FEEDS_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), FEEDS_GROUP_TYPE, FeedsGroupPrivate))
/**
* SECTION: feeds-group
* @short_description: import and export group of channels
*
* #FeedsGroup is an utility to import and export list of #FeedChannels in
* different formats, such as OPML
*/
#define FEEDS_GROUP_ERROR feeds_group_error_quark()
struct _FeedsGroupPrivate {
GSList *handlers;
};
enum {
FEEDS_GROUP_PARSE_ERROR,
};
G_DEFINE_TYPE (FeedsGroup, feeds_group, G_TYPE_OBJECT)
static GQuark
feeds_group_error_quark ()
{
return g_quark_from_static_string ("feeds_group_error");
}
static void
feeds_group_finalize (GObject *object)
{
FeedsGroup *group;
group = FEEDS_GROUP (object);
G_OBJECT_CLASS (feeds_group_parent_class)->finalize (object);
}
static void
feeds_group_class_init (FeedsGroupClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (FeedsGroupPrivate));
object_class->finalize = feeds_group_finalize;
}
static void
feeds_group_init (FeedsGroup *object)
{
object->priv = FEEDS_GROUP_GET_PRIVATE (object);
}
static GSList*
feeds_groups_get_list (FeedsGroup *group)
{
FeedsGroupHandler *parser;
if (group->priv->handlers == NULL) {
/*
TODO Parsers may be dinamically loaded and managed as external plugins
*/
parser = FEEDS_GROUP_HANDLER (feeds_opml_group_handler_new ());
group->priv->handlers = g_slist_append (group->priv->handlers, parser);
}
return group->priv->handlers;
}
/**
* feeds_group_new:
*
* Allocates a new #FeedsGroup
*
* Return value: a new #FeedsGroup
*/
FeedsGroup*
feeds_group_new ()
{
FeedsGroup *group;
group = g_object_new (FEEDS_GROUP_TYPE, NULL);
return group;
}
static FeedsGroupHandler*
retrieve_group_handler (FeedsGroup *group, xmlDocPtr doc, xmlNodePtr cur)
{
GSList *iter;
FeedsGroupHandler *handler;
iter = feeds_groups_get_list (group);
while (iter) {
handler = (FeedsGroupHandler*) (iter->data);
if (handler && feeds_group_handler_check_format (handler, doc, cur))
return handler;
iter = g_slist_next (iter);
}
return NULL;
}
GList*
feeds_group_parse_file (FeedsGroup *groups, const gchar *path, GError *error)
{
gchar *contents;
gsize len;
GList *items;
GError *err;
xmlDocPtr doc;
xmlNodePtr cur;
FeedsGroupHandler *handler;
items = NULL;
doc = NULL;
contents = NULL;
do {
err = NULL;
if (g_file_get_contents (path, &contents, &len, &err) == FALSE) {
g_propagate_error (&error, err);
break;
}
doc = content_to_xml (contents, len);
g_set_error (&error, FEEDS_GROUP_ERROR, FEEDS_GROUP_PARSE_ERROR, "Empty document");
if ((cur = xmlDocGetRootElement (doc)) == NULL)
break;
while (cur && xmlIsBlankNode (cur))
cur = cur->next;
if (!cur)
break;
if (!cur->name) {
g_set_error (&error, FEEDS_GROUP_ERROR, FEEDS_GROUP_PARSE_ERROR, "Invalid XML");
break;
}
handler = retrieve_group_handler (groups, doc, cur);
if (handler == NULL)
break;
items = feeds_group_handler_parse (handler, doc, error);
} while (0);
if (doc != NULL)
xmlFreeDoc (doc);
if (contents != NULL)
g_free (contents);
return items;
}
gboolean
feeds_group_export_file (FeedsGroup *groups, GList *channels, const gchar *path, GError *error)
{
return FALSE;
}
/*
* Copyright (C) 2010, Roberto Guido <rguido@src.gnome.org>
* Michele Tameni <michele@amdplanet.it>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*/
#ifndef __FEEDS_GROUP_H__
#define __FEEDS_GROUP_H__
#include "common.h"
#define FEEDS_GROUP_TYPE (feeds_group_get_type())
#define FEEDS_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FEEDS_GROUP_TYPE, FeedsGroup))
#define FEEDS_GROUP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), FEEDS_GROUP_TYPE, FeedsGroupClass))
#define IS_FEEDS_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FEEDS_GROUP_TYPE))
#define IS_FEEDS_GROUP_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), FEEDS_GROUP_TYPE))
#define FEEDS_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), FEEDS_GROUP_TYPE, FeedsGroupClass))
typedef struct _FeedsGroup FeedsGroup;
typedef struct _FeedsGroupPrivate FeedsGroupPrivate;
struct _FeedsGroup {
GObject parent;
FeedsGroupPrivate *priv;
};
typedef struct {
GObjectClass parent;
} FeedsGroupClass;
GType feeds_group_get_type () G_GNUC_CONST;
FeedsGroup* feeds_group_new ();
GList* feeds_group_parse_file (FeedsGroup *groups, const gchar *path, GError *error);
gboolean feeds_group_export_file (FeedsGroup *groups, GList *channels, const gchar *path, GError *error);
#endif /* __FEEDS_GROUP_H__ */
/*
* Copyright (C) 2010, Roberto Guido <rguido@src.gnome.org>
* Michele Tameni <michele@amdplanet.it>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*/
/*
* Original code is from Liferea:
*
* opml_source.c OPML Planet/Blogroll feed list source
*
* Copyright (C) 2006-2010 Lars Lindner <lars.lindner@gmail.com>
*/
#include "feeds-opml-group-handler.h"
#include "utils.h"
#include "feed-channel.h"
#define FEEDS_OPML_GROUP_HANDLER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), FEEDS_OPML_GROUP_HANDLER_TYPE, FeedsOpmlGroupHandlerPrivate))
/**
* SECTION: feeds-opml-group-handler
* @short_description: specialized parser for OPML files
*
* #FeedsOpmlGroupHandler is a #FeedsGroupHandler specialized for OPML contents
*/
struct FeedsOpmlGroupHandlerPrivate {
int rfu;
};
static void feeds_group_handler_interface_init (FeedsGroupHandlerInterface *iface);
G_DEFINE_TYPE_WITH_CODE (FeedsOpmlGroupHandler, feeds_opml_group_handler, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (FEEDS_GROUP_HANDLER_TYPE,
feeds_group_handler_interface_init));
static GQuark
feeds_opml_group_handler_error_quark ()
{
return g_quark_from_static_string ("feeds_opml_group_handler_error");
}
static void
feeds_opml_group_handler_finalize (GObject *object)
{
FeedsOpmlGroupHandler *parser;
parser = FEEDS_OPML_GROUP_HANDLER (object);
G_OBJECT_CLASS (feeds_opml_group_handler_parent_class)->finalize (object);
}
static gboolean
feeds_opml_group_handler_check_format (FeedsGroupHandler *self, xmlDocPtr doc, xmlNodePtr cur)
{
if (!xmlStrcmp (cur->name, BAD_CAST"opml"))
return TRUE;
else
return FALSE;
}
static xmlChar*
get_source_url (xmlNodePtr cur)
{
xmlChar *tmp;
tmp = xmlGetProp (cur, BAD_CAST "xmlUrl");
if (!tmp)
tmp = xmlGetProp (cur, BAD_CAST "xmlurl"); /* e.g. for AmphetaDesk */
if (!tmp)
tmp = xmlGetProp (cur, BAD_CAST"xmlURL"); /* e.g. for LiveJournal */
return tmp;
}
static FeedChannel*
import_parse_outline (xmlNodePtr cur)
{
xmlChar *tmp;
FeedChannel *channel;
channel = feed_channel_new ();
tmp = xmlGetProp (cur, BAD_CAST"title");
if (!tmp || !xmlStrcmp (tmp, BAD_CAST"")) {
if (tmp)
xmlFree (tmp);
tmp = xmlGetProp (cur, BAD_CAST"text");
}
if (tmp) {
feed_channel_set_title (channel, (gchar*) tmp);
xmlFree (tmp);
}
tmp = get_source_url (cur);
if (tmp) {
feed_channel_set_source (channel, (gchar*) tmp);
xmlFree (tmp);
tmp = xmlGetProp (cur, BAD_CAST"htmlUrl");
if (tmp && xmlStrcmp (tmp, BAD_CAST""))
feed_channel_set_homepage (channel, (gchar*) tmp);
xmlFree (tmp);
}
return channel;
}
static GList*
import_parse_body (xmlNodePtr n)
{
xmlChar *type;
xmlChar *tmp;
GList *items;
GList *subitems;
FeedChannel *outline;
xmlNodePtr cur;
cur = n->xmlChildrenNode;
items = NULL;
while (cur) {
if (!xmlStrcmp (cur->name, BAD_CAST"outline")) {
outline = NULL;
subitems = NULL;
type = xmlGetProp (cur, BAD_CAST"type");
if (type) {
if (xmlStrcasecmp (type, BAD_CAST"rss") == 0 || xmlStrcasecmp (type, BAD_CAST"atom") == 0)
outline = import_parse_outline (cur);
else if (xmlStrcasecmp (type, BAD_CAST"folder") == 0)
subitems = import_parse_body (cur);
xmlFree (type);
}
else {
/* if we didn't find a type attribute we use heuristics */
tmp = get_source_url (cur);
if (tmp) {
outline = import_parse_outline (cur);
xmlFree (tmp);
}
else {
subitems = import_parse_body (cur);
}
}
if (outline != NULL)
items = g_list_prepend (items, outline);
else if (subitems != NULL)
items = g_list_concat (items, subitems);
}
cur = cur->next;
}
return items;
}
static GList*
import_parse_OPML (xmlNodePtr n)
{
GList *items;
xmlNodePtr cur;
cur = n->xmlChildrenNode;
items = NULL;
while (cur) {
if (!xmlStrcmp (cur->name, BAD_CAST"body")) {
items = import_parse_body (cur);
break;
}
cur = cur->next;
}
return items;
}
static GList*
feeds_opml_group_handler_parse (FeedsGroupHandler *self, xmlDocPtr doc, GError *error)
{
xmlNodePtr cur;
GList *items;
FeedsOpmlGroupHandler *parser;
items = NULL;
parser = FEEDS_OPML_GROUP_HANDLER (self);
cur = xmlDocGetRootElement (doc);
while (cur) {
if (!xmlIsBlankNode (cur))
if (!xmlStrcmp (cur->name, BAD_CAST"opml")) {
items = import_parse_OPML (cur);
break;
}
cur = cur->next;
}
if (items != NULL)
items = g_list_reverse (items);
return items;
}
static gchar*
feeds_opml_group_handler_dump (FeedsGroupHandler *self, GList *channels, GError *error)
{
return NULL;
}