[libgrss] Major refactory on FeedsSubscriber



commit 50fc81b8bc1aa45f541b6b1b7f6a34fbb5a58ac2
Author: Roberto Guido <bob4mail gmail com>
Date:   Fri Dec 23 03:48:51 2011 -0800

    Major refactory on FeedsSubscriber

 NEWS                                |    2 +-
 doc/reference/libgrss-sections.txt  |  241 +++++++++---------
 examples/subscriber.c               |    2 +-
 src/Makefile.am                     |   80 ++++---
 src/feed-atom-handler.c             |   19 +--
 src/feed-channel.c                  |  197 +++++++++++----
 src/feed-channel.h                  |    7 +-
 src/feed-handler.c                  |   40 ---
 src/feed-pie-handler.c              |   16 --
 src/feed-rss-handler.c              |   44 +++--
 src/feeds-group-handler.c           |   42 ---
 src/feeds-opml-group-handler.c      |    7 -
 src/feeds-publisher.c               |    6 +-
 src/feeds-pubsubhubbub-subscriber.c |  244 ++++++++++++++++++
 src/feeds-pubsubhubbub-subscriber.h |   49 ++++
 src/feeds-rsscloud-subscriber.c     |  206 +++++++++++++++
 src/feeds-rsscloud-subscriber.h     |   49 ++++
 src/feeds-subscriber-handler.c      |  103 ++++++++
 src/feeds-subscriber-handler.h      |   59 +++++
 src/feeds-subscriber.c              |  468 ++++++++++++-----------------------
 src/feeds-subscriber.h              |    6 +-
 src/feeds-xbel-group-handler.c      |    9 +-
 src/feeds-xoxo-group-handler.c      |    9 +-
 src/ns-handler.c                    |   48 +----
 24 files changed, 1233 insertions(+), 720 deletions(-)
---
diff --git a/NEWS b/NEWS
index 62392a5..6e386a4 100644
--- a/NEWS
+++ b/NEWS
@@ -3,7 +3,7 @@ libgrss 0.5 (UNRELEASED)
 - Complete API break: all object and function names moved to "grss" namespace
 - Added XOXO and XBEL files parsers
 - Added FeedsPublisher to expose feeds contents as files or by local server
-- PubSubHub subscriber
+- PubSubHub and RSSCloud subscribers
 - Added functions feed_channel_fetch_async() and feed_channel_new_from_file()
 
 libgrss 0.4
diff --git a/doc/reference/libgrss-sections.txt b/doc/reference/libgrss-sections.txt
index 5959410..488af51 100644
--- a/doc/reference/libgrss-sections.txt
+++ b/doc/reference/libgrss-sections.txt
@@ -1,152 +1,159 @@
 <SECTION>
 <FILE>feed-enclosure</FILE>
-<TITLE>FeedEnclosure</TITLE>
-FeedEnclosure
-feed_enclosure_new
-feed_enclosure_get_url
-feed_enclosure_set_format
-feed_enclosure_get_format
-feed_enclosure_set_length
-feed_enclosure_get_length
+<TITLE>GrssFeedEnclosure</TITLE>
+GrssFeedEnclosure
+grss_feed_enclosure_new
+grss_feed_enclosure_get_url
+grss_feed_enclosure_set_format
+grss_feed_enclosure_get_format
+grss_feed_enclosure_set_length
+grss_feed_enclosure_get_length
 </SECTION>
 
 <SECTION>
 <FILE>feeds-pool</FILE>
-<TITLE>FeedsPool</TITLE>
-FeedsPool
-feeds_pool_new
-feeds_pool_listen
-feeds_pool_get_listened
-feeds_pool_switch
-feeds_pool_get_session
+<TITLE>GrssFeedsPool</TITLE>
+GrssFeedsPool
+grss_feeds_pool_new
+grss_feeds_pool_listen
+grss_feeds_pool_get_listened
+grss_feeds_pool_switch
+grss_feeds_pool_get_session
 </SECTION>
 
 <SECTION>
 <FILE>feeds-store</FILE>
-<TITLE>FeedsStore</TITLE>
-FeedsStore
-feeds_store_get_channels
-feeds_store_get_items_by_channel
-feeds_store_has_item
-feeds_store_add_item_in_channel
-feeds_store_switch
+<TITLE>GrssFeedsStore</TITLE>
+GrssFeedsStore
+grss_feeds_store_get_channels
+grss_feeds_store_get_items_by_channel
+grss_feeds_store_has_item
+grss_feeds_store_add_item_in_channel
+grss_feeds_store_switch
 </SECTION>
 
 <SECTION>
 <FILE>feeds-subscriber</FILE>
-<TITLE>FeedsSubscriber</TITLE>
-FeedsSubscriber
-feeds_subscriber_new
-feeds_subscriber_listen
-feeds_subscriber_get_listened
-feeds_subscriber_set_port
-feeds_subscriber_set_hub
-feeds_subscriber_switch
+<TITLE>GrssFeedsSubscriber</TITLE>
+GrssFeedsSubscriber
+grss_feeds_subscriber_new
+grss_feeds_subscriber_listen
+grss_feeds_subscriber_get_listened
+grss_feeds_subscriber_set_port
+grss_feeds_subscriber_switch
+grss_feeds_subscriber_get_address
+grss_feeds_subscriber_get_port
+grss_feeds_subscriber_get_session
 </SECTION>
 
 <SECTION>
 <FILE>feeds-publisher</FILE>
-<TITLE>FeedsPublisher</TITLE>
-FeedsPublisher
-feeds_publisher_new
-feeds_publisher_set_port
-feeds_publisher_set_topics
-feeds_publisher_switch
-feeds_publisher_publish
+<TITLE>GrssFeedsPublisher</TITLE>
+GrssFeedsPublisher
+grss_feeds_publisher_new
+grss_feeds_publisher_publish
+grss_feeds_publisher_publish_file
+grss_feeds_publisher_hub_set_port
+grss_feeds_publisher_hub_set_topics
+grss_feeds_publisher_hub_switch
 </SECTION>
 
 <SECTION>
 <FILE>feed-item</FILE>
-<TITLE>FeedItem</TITLE>
-FeedItem
-feed_item_new
-feed_item_get_parent
-feed_item_set_id
-feed_item_get_id
-feed_item_set_title
-feed_item_get_title
-feed_item_set_description
-feed_item_get_description
-feed_item_add_category
-feed_item_get_categories
-feed_item_set_source
-feed_item_get_source
-feed_item_set_real_source
-feed_item_get_real_source
-feed_item_set_related
-feed_item_get_related
-feed_item_set_copyright
-feed_item_get_copyright
-feed_item_set_author
-feed_item_get_author
-feed_item_add_contributor
-feed_item_get_contributors
-feed_item_set_comments_url
-feed_item_get_comments_url
-feed_item_set_geo_point
-feed_item_get_geo_point
-feed_item_set_publish_time
-feed_item_get_publish_time
-feed_item_add_enclosure
-feed_item_get_enclosures
+<TITLE>GrssFeedItem</TITLE>
+GrssFeedItem
+grss_feed_item_new
+grss_feed_item_get_parent
+grss_feed_item_set_id
+grss_feed_item_get_id
+grss_feed_item_set_title
+grss_feed_item_get_title
+grss_feed_item_set_description
+grss_feed_item_get_description
+grss_feed_item_add_category
+grss_feed_item_get_categories
+grss_feed_item_set_source
+grss_feed_item_get_source
+grss_feed_item_set_real_source
+grss_feed_item_get_real_source
+grss_feed_item_set_related
+grss_feed_item_get_related
+grss_feed_item_set_copyright
+grss_feed_item_get_copyright
+grss_feed_item_set_author
+grss_feed_item_get_author
+grss_feed_item_add_contributor
+grss_feed_item_get_contributors
+grss_feed_item_set_comments_url
+grss_feed_item_get_comments_url
+grss_feed_item_set_geo_point
+grss_feed_item_get_geo_point
+grss_feed_item_set_publish_time
+grss_feed_item_get_publish_time
+grss_feed_item_add_enclosure
+grss_feed_item_get_enclosures
 </SECTION>
 
 <SECTION>
 <FILE>feed-parser</FILE>
-<TITLE>FeedParser</TITLE>
-FeedParser
-feed_parser_new
-feed_parser_parse
+<TITLE>GrssFeedParser</TITLE>
+GrssFeedParser
+grss_feed_parser_new
+grss_feed_parser_parse
 </SECTION>
 
 <SECTION>
 <FILE>feed-channel</FILE>
-<TITLE>FeedChannel</TITLE>
-FeedChannel
-feed_channel_new
-feed_channel_set_source
-feed_channel_get_source
-feed_channel_set_title
-feed_channel_get_title
-feed_channel_set_homepage
-feed_channel_get_homepage
-feed_channel_set_description
-feed_channel_get_description
-feed_channel_set_image
-feed_channel_get_image
-feed_channel_set_icon
-feed_channel_get_icon
-feed_channel_set_language
-feed_channel_get_language
-feed_channel_set_category
-feed_channel_get_category
-feed_channel_set_pubsubhub
-feed_channel_get_pubsubhub
-feed_channel_set_copyright
-feed_channel_get_copyright
-feed_channel_set_editor
-feed_channel_get_editor
-feed_channel_add_contributor
-feed_channel_get_contributors
-feed_channel_set_webmaster
-feed_channel_get_webmaster
-feed_channel_set_generator
-feed_channel_get_generator
-feed_channel_set_publish_time
-feed_channel_get_publish_time
-feed_channel_set_update_time
-feed_channel_get_update_time
-feed_channel_set_update_interval
-feed_channel_get_update_interval
-feed_channel_fetch
-feed_channel_fetch_async
+<TITLE>GrssFeedChannel</TITLE>
+GrssFeedChannel
+grss_feed_channel_new
+grss_feed_channel_new_from_source
+grss_feed_channel_new_from_file
+grss_feed_channel_set_source
+grss_feed_channel_get_source
+grss_feed_channel_set_title
+grss_feed_channel_get_title
+grss_feed_channel_set_homepage
+grss_feed_channel_get_homepage
+grss_feed_channel_set_description
+grss_feed_channel_get_description
+grss_feed_channel_set_image
+grss_feed_channel_get_image
+grss_feed_channel_set_icon
+grss_feed_channel_get_icon
+grss_feed_channel_set_language
+grss_feed_channel_get_language
+grss_feed_channel_set_category
+grss_feed_channel_get_category
+grss_feed_channel_set_pubsubhub
+grss_feed_channel_get_pubsubhub
+grss_feed_channel_set_rsscloud
+grss_feed_channel_get_rsscloud
+grss_feed_channel_set_copyright
+grss_feed_channel_get_copyright
+grss_feed_channel_set_editor
+grss_feed_channel_get_editor
+grss_feed_channel_add_contributor
+grss_feed_channel_get_contributors
+grss_feed_channel_set_webmaster
+grss_feed_channel_get_webmaster
+grss_feed_channel_set_generator
+grss_feed_channel_get_generator
+grss_feed_channel_set_publish_time
+grss_feed_channel_get_publish_time
+grss_feed_channel_set_update_time
+grss_feed_channel_get_update_time
+grss_feed_channel_set_update_interval
+grss_feed_channel_get_update_interval
+grss_feed_channel_fetch
+grss_feed_channel_fetch_async
 </SECTION>
 
 <SECTION>
 <FILE>feeds-group</FILE>
-<TITLE>FeedsGroup</TITLE>
-FeedsGroup
-feeds_group_new
-feeds_group_parse_file
-feeds_group_export_file
+<TITLE>GrssFeedsGroup</TITLE>
+GrssFeedsGroup
+grss_feeds_group_new
+grss_feeds_group_parse_file
+grss_feeds_group_export_file
 </SECTION>
diff --git a/examples/subscriber.c b/examples/subscriber.c
index 71f45cf..29789f9 100644
--- a/examples/subscriber.c
+++ b/examples/subscriber.c
@@ -45,7 +45,7 @@ main ()
 	gboolean ok;
 	gchar *example_feeds [] = {
 		"http://rss.slashdot.org/Slashdot/slashdot";,
-		"http://feeds.feedburner.com/Techcrunch";,
+		"http://techcrunch.com/feed/";,
 		NULL
 	};
 	GList *iter;
diff --git a/src/Makefile.am b/src/Makefile.am
index ff2afe2..7643455 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,49 +11,55 @@ INCLUDES = \
 LDADD = $(LIBGRSS_LT_LDFLAGS) -export-dynamic -rpath $(libdir)
 
 sources_private_h = \
-	feed-atom-handler.h        \
-	feed-handler.h             \
-	feed-rss-handler.h         \
-	feed-pie-handler.h         \
-	feeds-group-handler.h      \
-	feeds-opml-group-handler.h \
-	feeds-xbel-group-handler.h \
-	feeds-xoxo-group-handler.h \
-	ns-handler.h               \
+	feed-atom-handler.h             \
+	feed-handler.h                  \
+	feed-rss-handler.h              \
+	feed-pie-handler.h              \
+	feeds-group-handler.h           \
+	feeds-opml-group-handler.h      \
+	feeds-pubsubhubbub-subscriber.h \
+	feeds-rsscloud-subscriber.h     \
+	feeds-subscriber-handler.h      \
+	feeds-xbel-group-handler.h      \
+	feeds-xoxo-group-handler.h      \
+	ns-handler.h                    \
 	utils.h
 
 sources_public_h = \
-	libgrss.h                  \
-	feed-channel.h             \
-	feed-enclosure.h           \
-	feed-item.h                \
-	feed-parser.h              \
-	feeds-group.h              \
-	feeds-pool.h               \
-	feeds-publisher.h          \
-	feeds-store.h              \
+	libgrss.h                       \
+	feed-channel.h                  \
+	feed-enclosure.h                \
+	feed-item.h                     \
+	feed-parser.h                   \
+	feeds-group.h                   \
+	feeds-pool.h                    \
+	feeds-publisher.h               \
+	feeds-store.h                   \
 	feeds-subscriber.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-group.c              \
-	feeds-group-handler.c      \
-	feeds-opml-group-handler.c \
-	feeds-pool.c               \
-	feeds-publisher.c          \
-	feeds-store.c              \
-	feeds-subscriber.c         \
-	feeds-xbel-group-handler.c \
-	feeds-xoxo-group-handler.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-publisher.c               \
+	feeds-store.c                   \
+	feeds-pubsubhubbub-subscriber.c \
+	feeds-rsscloud-subscriber.c     \
+	feeds-subscriber.c              \
+	feeds-subscriber-handler.c      \
+	feeds-xbel-group-handler.c      \
+	feeds-xoxo-group-handler.c      \
+	ns-handler.c                    \
 	utils.c
 
 marshal_source = \
diff --git a/src/feed-atom-handler.c b/src/feed-atom-handler.c
index e932802..acf05d4 100644
--- a/src/feed-atom-handler.c
+++ b/src/feed-atom-handler.c
@@ -32,14 +32,6 @@
 #include "ns-handler.h"
 
 #define FEED_ATOM_HANDLER_GET_PRIVATE(o)	(G_TYPE_INSTANCE_GET_PRIVATE ((o), FEED_ATOM_HANDLER_TYPE, FeedAtomHandlerPrivate))
-
-/**
- * SECTION: feed-atom-handler
- * @short_description: specialized parser for Atom feeds
- *
- * #FeedAtomHandler is a #FeedHandler specialized for feeds in Atom format
- */
-
 #define FEED_ATOM_HANDLER_ERROR			feed_atom_handler_error_quark()
 #define ATOM10_NS				BAD_CAST"http://www.w3.org/2005/Atom";
 
@@ -406,11 +398,11 @@ atom10_parse_link (xmlNodePtr cur, GrssFeedChannel *feed, GrssFeedItem *item)
 		}
 		else if (g_str_equal (relation, "hub")) {
 			if (feed != NULL)
-				grss_feed_channel_set_pubsubhub (feed, url, NULL);
+				grss_feed_channel_set_pubsubhub (feed, url);
 		}
 		else if (g_str_equal (relation, "self")) {
 			if (feed != NULL)
-				grss_feed_channel_set_pubsubhub (feed, NULL, url);
+				grss_feed_channel_set_source (feed, url);
 		}
 
 		xmlFree (title);
@@ -916,13 +908,6 @@ feed_atom_handler_init (FeedAtomHandler *object)
 	object->priv->entry_elements_hash = hash;
 }
 
-/**
- * feed_atom_handler_new:
- *
- * Allocates a new #FeedAtomHandler
- *
- * Return value: a new #FeedAtomHandler
- */
 FeedAtomHandler*
 feed_atom_handler_new ()
 {
diff --git a/src/feed-channel.c b/src/feed-channel.c
index a06e2b1..9be471f 100644
--- a/src/feed-channel.c
+++ b/src/feed-channel.c
@@ -35,30 +35,35 @@
 
 typedef struct {
 	gchar	*hub;
-	gchar	*self;
 } PubSub;
 
+typedef struct {
+	gchar	*path;
+	gchar	*protocol;
+} RSSCloud;
+
 struct _GrssFeedChannelPrivate {
-	gchar	*source;
-
-	gchar	*title;
-	gchar	*homepage;
-	gchar	*description;
-	gchar	*image;
-	gchar	*icon;
-	gchar	*language;
-	gchar	*category;
-	PubSub	pubsub;
-
-	gchar	*copyright;
-	gchar	*editor;
-	GList	*contributors;
-	gchar	*webmaster;
-	gchar	*generator;
-
-	time_t	pub_time;
-	time_t	update_time;
-	int	update_interval;
+	gchar		*source;
+
+	gchar		*title;
+	gchar		*homepage;
+	gchar		*description;
+	gchar		*image;
+	gchar		*icon;
+	gchar		*language;
+	gchar		*category;
+	PubSub		pubsub;
+	RSSCloud	rsscloud;
+
+	gchar		*copyright;
+	gchar		*editor;
+	GList		*contributors;
+	gchar		*webmaster;
+	gchar		*generator;
+
+	time_t		pub_time;
+	time_t		update_time;
+	int		update_interval;
 };
 
 enum {
@@ -88,7 +93,8 @@ grss_feed_channel_finalize (GObject *obj)
 	FREE_STRING (chan->priv->language);
 	FREE_STRING (chan->priv->category);
 	FREE_STRING (chan->priv->pubsub.hub);
-	FREE_STRING (chan->priv->pubsub.self);
+	FREE_STRING (chan->priv->rsscloud.path);
+	FREE_STRING (chan->priv->rsscloud.protocol);
 	FREE_STRING (chan->priv->copyright);
 	FREE_STRING (chan->priv->editor);
 	FREE_STRING (chan->priv->webmaster);
@@ -133,6 +139,24 @@ grss_feed_channel_new ()
 }
 
 /**
+ * grss_feed_channel_new_from_source:
+ * @source: URL of the feed
+ *
+ * Allocates a new #GrssFeedChannel and assign it the given remote source
+ *
+ * Return value: a #GrssFeedChannel
+ */
+GrssFeedChannel*
+grss_feed_channel_new_from_source (gchar *source)
+{
+	GrssFeedChannel *ret;
+
+	ret = grss_feed_channel_new ();
+	grss_feed_channel_set_source (ret, source);
+	return ret;
+}
+
+/**
  * grss_feed_channel_new_from_file:
  * @path: path of the file to parse
  *
@@ -408,38 +432,23 @@ grss_feed_channel_get_category (GrssFeedChannel *channel)
  * grss_feed_channel_set_pubsubhub:
  * @channel: a #GrssFeedChannel
  * @hub: hub for the feed, or NULL
- * @self: target referencing the feed, or NULL
  *
- * To set information about PubSubHubbub for the channel. Options can be set
- * alternatively, only with hub != %NULL or self != %NULL, and are saved
- * internally to the object: the hub is considered valid
- * (grss_feed_channel_get_pubsubhub() returns %TRUE) only when both parameters has
- * been set. To unset the hub, pass %NULL for both parameters
+ * To set information about PubSubHubbub for the channel. To unset the hub,
+ * pass %NULL as parameter
  */
 void
-grss_feed_channel_set_pubsubhub (GrssFeedChannel *channel, gchar *hub, gchar *self)
+grss_feed_channel_set_pubsubhub (GrssFeedChannel *channel, gchar *hub)
 {
-	if (hub == NULL && self == NULL) {
-		FREE_STRING (channel->priv->pubsub.hub);
-		FREE_STRING (channel->priv->pubsub.self);
-	}
-	else {
-		if (hub != NULL) {
-			FREE_STRING (channel->priv->pubsub.hub);
-			channel->priv->pubsub.hub = g_strdup (hub);
-		}
-		if (self != NULL) {
-			FREE_STRING (channel->priv->pubsub.self);
-			channel->priv->pubsub.self = g_strdup (self);
-		}
-	}
+	FREE_STRING (channel->priv->pubsub.hub);
+
+	if (hub != NULL)
+		channel->priv->pubsub.hub = g_strdup (hub);
 }
 
 /**
  * grss_feed_channel_get_pubsubhub:
  * @channel: a #GrssFeedChannel
  * @hub: location for the hub string, or NULL
- * @self: location for the reference to the feed, or NULL
  *
  * Retrieves information about the PubSubHubbub hub of the channel
  *
@@ -447,14 +456,55 @@ grss_feed_channel_set_pubsubhub (GrssFeedChannel *channel, gchar *hub, gchar *se
  * @channel, %FALSE otherwise
  */
 gboolean
-grss_feed_channel_get_pubsubhub (GrssFeedChannel *channel, gchar **hub, gchar **self)
+grss_feed_channel_get_pubsubhub (GrssFeedChannel *channel, gchar **hub)
 {
 	if (hub != NULL)
 		*hub = channel->priv->pubsub.hub;
-	if (self != NULL)
-		*self = channel->priv->pubsub.self;
 
-	return (channel->priv->pubsub.hub != NULL && channel->priv->pubsub.self != NULL);
+	return (channel->priv->pubsub.hub != NULL);
+}
+
+/**
+ * grss_feed_channel_set_rsscloud:
+ * @channel: a #GrssFeedChannel
+ * @path: complete references of the URL where to register subscription, e.g.
+ * http://example.com/rsscloudNotify
+ * @protocol: type of protocol used for notifications
+ *
+ * To set information about RSSCloud notifications for the channel
+ */
+void
+grss_feed_channel_set_rsscloud (GrssFeedChannel *channel, gchar *path, gchar *protocol)
+{
+	FREE_STRING (channel->priv->rsscloud.path);
+	FREE_STRING (channel->priv->rsscloud.protocol);
+
+	if (path != NULL && protocol != NULL) {
+		channel->priv->rsscloud.path = g_strdup (path);
+		channel->priv->rsscloud.protocol = g_strdup (protocol);
+	}
+}
+
+/**
+ * grss_feed_channel_get_rsscloud:
+ * @channel: a #GrssFeedChannel
+ * @path: location for the path string, or NULL
+ * @protocol: location for the protocol string, or NULL
+ *
+ * Retrieves information about the RSSCloud coordinates of the channel
+ *
+ * Return value: %TRUE if a valid RSSCloud path has been set for the
+ * @channel, %FALSE otherwise
+ */
+gboolean
+grss_feed_channel_get_rsscloud (GrssFeedChannel *channel, gchar **path, gchar **protocol)
+{
+	if (path != NULL)
+		*path = channel->priv->rsscloud.path;
+	if (protocol != NULL)
+		*protocol = channel->priv->rsscloud.protocol;
+
+	return (channel->priv->rsscloud.path != NULL && channel->priv->rsscloud.protocol != NULL);
 }
 
 /**
@@ -690,7 +740,7 @@ grss_feed_channel_get_update_interval (GrssFeedChannel *channel)
 }
 
 static gboolean
-quick_and_dirty_parse (GrssFeedChannel *channel, SoupMessage *msg)
+quick_and_dirty_parse (GrssFeedChannel *channel, SoupMessage *msg, GList **save_items)
 {
 	GList *items;
 	GList *iter;
@@ -699,7 +749,8 @@ quick_and_dirty_parse (GrssFeedChannel *channel, SoupMessage *msg)
 
 	/*
 		TODO	This function is quite inefficent because parses all
-			the feed with a GrssFeedParser and them waste obtained
+			the feed with a GrssFeedParser also when not required
+			(save_items == NULL) and wastes time obtained
 			GrssFeedItems. Perhaps a more aimed function in
 			GrssFeedParser would help...
 	*/
@@ -710,11 +761,14 @@ quick_and_dirty_parse (GrssFeedChannel *channel, SoupMessage *msg)
 		parser = grss_feed_parser_new ();
 		items = grss_feed_parser_parse (parser, channel, doc, NULL);
 
-		if (items != NULL) {
+		if (save_items == NULL && items != NULL) {
 			for (iter = items; iter; iter = g_list_next (iter))
 				g_object_unref (iter->data);
 			g_list_free (items);
 		}
+		else {
+			*save_items = items;
+		}
 
 		g_object_unref (parser);
 		xmlFreeDoc (doc);
@@ -750,7 +804,7 @@ grss_feed_channel_fetch (GrssFeedChannel *channel)
 	status = soup_session_send_message (session, msg);
 
 	if (status >= 200 && status <= 299) {
-		ret = quick_and_dirty_parse (channel, msg);
+		ret = quick_and_dirty_parse (channel, msg, NULL);
 	}
 	else {
 		g_warning ("Unable to fetch feed from %s: %s", grss_feed_channel_get_source (channel), soup_status_get_phrase (status));
@@ -773,7 +827,7 @@ feed_downloaded (SoupSession *session, SoupMessage *msg, gpointer user_data) {
 	g_object_get (msg, "status-code", &status, NULL);
 
 	if (status >= 200 && status <= 299) {
-		quick_and_dirty_parse (channel, msg);
+		quick_and_dirty_parse (channel, msg, NULL);
 	}
 	else {
 		g_simple_async_result_set_error (result, FEEDS_CHANNEL_ERROR, FEEDS_CHANNEL_FETCH_ERROR,
@@ -805,3 +859,40 @@ grss_feed_channel_fetch_async (GrssFeedChannel *channel, GAsyncReadyCallback cal
 	msg = soup_message_new ("GET", grss_feed_channel_get_source (channel));
 	soup_session_queue_message (session, msg, feed_downloaded, result);
 }
+
+/**
+ * grss_feed_channel_fetch_all:
+ * @channel: a #GrssFeedChannel
+ *
+ * Utility to fetch and populate a #GrssFeedChannel, and retrieve all its
+ * items
+ *
+ * Return value: a GList of #GrssFeedItem, to be completely unreferenced and
+ * freed when no longer in use, or %NULL if an error occurs
+ */
+GList*
+grss_feed_channel_fetch_all (GrssFeedChannel *channel)
+{
+	gboolean ret;
+	guint status;
+	GList *items;
+	SoupMessage *msg;
+	SoupSession *session;
+
+	session = soup_session_sync_new ();
+	msg = soup_message_new ("GET", grss_feed_channel_get_source (channel));
+	status = soup_session_send_message (session, msg);
+	items = NULL;
+
+	if (status >= 200 && status <= 299) {
+		ret = quick_and_dirty_parse (channel, msg, &items);
+	}
+	else {
+		g_warning ("Unable to fetch feed from %s: %s", grss_feed_channel_get_source (channel), soup_status_get_phrase (status));
+		ret = FALSE;
+	}
+
+	g_object_unref (session);
+	g_object_unref (msg);
+	return items;
+}
diff --git a/src/feed-channel.h b/src/feed-channel.h
index dd69617..bb04ba7 100644
--- a/src/feed-channel.h
+++ b/src/feed-channel.h
@@ -63,8 +63,10 @@ void			grss_feed_channel_set_language		(GrssFeedChannel *channel, gchar *languag
 const gchar*		grss_feed_channel_get_language		(GrssFeedChannel *channel);
 void			grss_feed_channel_set_category		(GrssFeedChannel *channel, gchar *category);
 const gchar*		grss_feed_channel_get_category		(GrssFeedChannel *channel);
-void			grss_feed_channel_set_pubsubhub		(GrssFeedChannel *channel, gchar *hub, gchar *self);
-gboolean		grss_feed_channel_get_pubsubhub		(GrssFeedChannel *channel, gchar **hub, gchar **self);
+void			grss_feed_channel_set_pubsubhub		(GrssFeedChannel *channel, gchar *hub);
+gboolean		grss_feed_channel_get_pubsubhub		(GrssFeedChannel *channel, gchar **hub);
+void			grss_feed_channel_set_rsscloud		(GrssFeedChannel *channel, gchar *path, gchar *protocol);
+gboolean		grss_feed_channel_get_rsscloud		(GrssFeedChannel *channel, gchar **path, gchar **protocol);
 
 void			grss_feed_channel_set_copyright		(GrssFeedChannel *channel, gchar *copyright);
 const gchar*		grss_feed_channel_get_copyright		(GrssFeedChannel *channel);
@@ -86,5 +88,6 @@ int			grss_feed_channel_get_update_interval	(GrssFeedChannel *channel);
 
 gboolean		grss_feed_channel_fetch			(GrssFeedChannel *channel);
 void			grss_feed_channel_fetch_async		(GrssFeedChannel *channel, GAsyncReadyCallback callback, gpointer user_data);
+GList*			grss_feed_channel_fetch_all		(GrssFeedChannel *channel);
 
 #endif /* __FEED_CHANNEL_H__ */
diff --git a/src/feed-handler.c b/src/feed-handler.c
index 6b8c5a3..dc900b6 100644
--- a/src/feed-handler.c
+++ b/src/feed-handler.c
@@ -21,14 +21,6 @@
 #include "utils.h"
 #include "feed-handler.h"
 
-/**
- * SECTION: feed-handler
- * @short_description: interface for specialized parsers
- *
- * The #FeedHandler interface defines a unique API for all specialized
- * parsers implementations
- */
-
 static void
 feed_handler_base_init (gpointer g_class)
 {
@@ -52,14 +44,6 @@ feed_handler_get_type ()
 	return iface_type;
 }
 
-/**
- * feed_handler_set_ns_handler:
- * @self: a #FeedHandler
- * @handler: instance of #NSHandler
- *
- * Permits to assign a #NSHandler to the specified #FeedHandler, to expand
- * his parsing capabilities to the external managed tags
- */
 void
 feed_handler_set_ns_handler (FeedHandler *self, NSHandler *handler)
 {
@@ -69,17 +53,6 @@ feed_handler_set_ns_handler (FeedHandler *self, NSHandler *handler)
 	return FEED_HANDLER_GET_INTERFACE (self)->set_ns_handler (self, handler);
 }
 
-/**
- * feed_handler_check_format:
- * @self: a #FeedHandler
- * @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 feed parser
- *
- * Return value: %TRUE if the document can be parsed with the given
- * #FeedHandler, %FALSE otherwise
- */
 gboolean
 feed_handler_check_format (FeedHandler *self, xmlDocPtr doc, xmlNodePtr cur)
 {
@@ -89,19 +62,6 @@ feed_handler_check_format (FeedHandler *self, xmlDocPtr doc, xmlNodePtr cur)
 	return FEED_HANDLER_GET_INTERFACE (self)->check_format (self, doc, cur);
 }
 
-/**
- * feed_handler_parse:
- * @self: a #FeedHandler
- * @feed: feed to be parsed
- * @doc: XML document from the feed
- * @error: location for eventual errors
- *
- * Parses the given @doc (obtained fetching @feed) and extracts a list of
- * items
- *
- * Return value: a list of #GrssFeedItem, to be freed when no longer in use, or
- * %NULL if an error occours (and @error is set accordly)
- */
 GList*
 feed_handler_parse (FeedHandler *self, GrssFeedChannel *feed, xmlDocPtr doc, GError **error)
 {
diff --git a/src/feed-pie-handler.c b/src/feed-pie-handler.c
index 824f8f4..056ffa2 100644
--- a/src/feed-pie-handler.c
+++ b/src/feed-pie-handler.c
@@ -34,15 +34,6 @@
 #include "ns-handler.h"
 
 #define FEED_PIE_HANDLER_GET_PRIVATE(o)	(G_TYPE_INSTANCE_GET_PRIVATE ((o), FEED_PIE_HANDLER_TYPE, FeedPieHandlerPrivate))
-
-/**
- * SECTION: feed-pie-handler
- * @short_description: specialized parser for PIE feeds
- *
- * #FeedPieHandler is a #FeedHandler specialized for feeds in PIE format, an
- * older specification of Atom
- */
-
 #define FEED_PIE_HANDLER_ERROR			feed_pie_handler_error_quark()
 
 typedef void 	(*PieChannelParserFunc)		(xmlNodePtr cur, GrssFeedChannel *feed);
@@ -493,13 +484,6 @@ feed_pie_handler_init (FeedPieHandler *object)
 	object->priv = FEED_PIE_HANDLER_GET_PRIVATE (object);
 }
 
-/**
- * feed_pie_handler_new:
- *
- * Allocates a new #FeedPieHandler
- *
- * Return value: a new #FeedPieHandler
- */
 FeedPieHandler*
 feed_pie_handler_new ()
 {
diff --git a/src/feed-rss-handler.c b/src/feed-rss-handler.c
index 7183919..bbcfa05 100644
--- a/src/feed-rss-handler.c
+++ b/src/feed-rss-handler.c
@@ -44,14 +44,6 @@
 
 #define FEED_RSS_HANDLER_GET_PRIVATE(o)	(G_TYPE_INSTANCE_GET_PRIVATE ((o), FEED_RSS_HANDLER_TYPE, FeedRssHandlerPrivate))
 
-/**
- * SECTION: feed-rss-handler
- * @short_description: specialized parser for RSS feeds
- *
- * #FeedRssHandler is a #FeedHandler specialized for feeds in RSS format,
- * both 1.0 and 2.0
- */
-
 #define FEED_RSS_HANDLER_ERROR		feed_rss_handler_error_quark()
 
 /* HTML output strings */
@@ -118,6 +110,32 @@ feed_rss_handler_check_format (FeedHandler *self, xmlDocPtr doc, xmlNodePtr cur)
 }
 
 static void
+parse_rss_cloud (GrssFeedChannel *feed, xmlNodePtr cur) {
+	gchar *domain;
+	gchar *path;
+	gchar *protocol;
+	gchar *completepath;
+
+	domain = (gchar*) xmlGetNsProp (cur, BAD_CAST"domain", NULL);
+	path = (gchar*) xmlGetNsProp (cur, BAD_CAST"path", NULL);
+	protocol = (gchar*) xmlGetNsProp (cur, BAD_CAST"protocol", NULL);
+
+	if (domain != NULL && path != NULL && protocol != NULL) {
+		if (strncmp (domain, "http://";, 7) != 0)
+			completepath = g_strdup_printf ("http://%s%s";, domain, path);
+		else
+			completepath = g_strdup_printf ("%s%s", domain, path);
+
+		grss_feed_channel_set_rsscloud (feed, completepath, protocol);
+
+		g_free (domain);
+		g_free (path);
+		g_free (protocol);
+		g_free (completepath);
+	}
+}
+
+static void
 parse_channel (FeedRssHandler *parser, GrssFeedChannel *feed, xmlDocPtr doc, xmlNodePtr cur) {
 	gchar *tmp;
 	time_t t;
@@ -217,6 +235,9 @@ parse_channel (FeedRssHandler *parser, GrssFeedChannel *feed, xmlDocPtr doc, xml
 				g_free (tmp);
 			}
 		}
+		else if (!xmlStrcmp (cur->name, BAD_CAST"cloud")) {
+ 			parse_rss_cloud (feed, cur);
+		}
 
 		cur = cur->next;
 	}
@@ -538,13 +559,6 @@ feed_rss_handler_init (FeedRssHandler *object)
 	object->priv = FEED_RSS_HANDLER_GET_PRIVATE (object);
 }
 
-/**
- * feed_rss_handler_new:
- *
- * Allocates a new #FeedRssHandler
- *
- * Return value: a new #FeedRssHandler
- */
 FeedRssHandler*
 feed_rss_handler_new ()
 {
diff --git a/src/feeds-group-handler.c b/src/feeds-group-handler.c
index 26c97d2..0699954 100644
--- a/src/feeds-group-handler.c
+++ b/src/feeds-group-handler.c
@@ -21,14 +21,6 @@
 #include "utils.h"
 #include "feeds-group-handler.h"
 
-/**
- * SECTION: feeds-group-handler
- * @short_description: interface for specialized groups parsers
- *
- * The #GrssFeedsGroupHandler interface defines a unique API for all specialized
- * groups parsers implementations
- */
-
 static void
 grss_feeds_group_handler_base_init (gpointer g_class)
 {
@@ -52,17 +44,6 @@ grss_feeds_group_handler_get_type ()
 	return iface_type;
 }
 
-/**
- * grss_feeds_group_handler_check_format:
- * @self: a #GrssFeedsGroupHandler
- * @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
- * #GrssFeedsGroupHandler, %FALSE otherwise
- */
 gboolean
 grss_feeds_group_handler_check_format (GrssFeedsGroupHandler *self, xmlDocPtr doc, xmlNodePtr cur)
 {
@@ -72,17 +53,6 @@ grss_feeds_group_handler_check_format (GrssFeedsGroupHandler *self, xmlDocPtr do
 	return FEEDS_GROUP_HANDLER_GET_INTERFACE (self)->check_format (self, doc, cur);
 }
 
-/**
- * grss_feeds_group_handler_parse:
- * @self: a #GrssFeedsGroupHandler
- * @doc: XML document from the feed
- * @error: location for eventual errors
- *
- * Parses the given @doc and extracts a list of #GrssFeedChannels
- *
- * Return value: a list of #GrssFeedChannels, to be freed when no longer in use,
- * or %NULL if an error occours (and @error is set accordly)
- */
 GList*
 grss_feeds_group_handler_parse (GrssFeedsGroupHandler *self, xmlDocPtr doc, GError **error)
 {
@@ -92,18 +62,6 @@ grss_feeds_group_handler_parse (GrssFeedsGroupHandler *self, xmlDocPtr doc, GErr
 	return FEEDS_GROUP_HANDLER_GET_INTERFACE (self)->parse (self, doc, error);
 }
 
-/**
- * grss_feeds_group_handler_dump:
- * @self: a #GrssFeedsGroupHandler
- * @channels: list of #GrssFeedChannels
- * @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*
 grss_feeds_group_handler_dump (GrssFeedsGroupHandler *self, GList *channels, GError **error)
 {
diff --git a/src/feeds-opml-group-handler.c b/src/feeds-opml-group-handler.c
index 5a833d6..38c21ef 100644
--- a/src/feeds-opml-group-handler.c
+++ b/src/feeds-opml-group-handler.c
@@ -33,13 +33,6 @@
 
 #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 #GrssFeedsGroupHandler specialized for OPML contents
- */
-
 struct FeedsOpmlGroupHandlerPrivate {
 	int	rfu;
 };
diff --git a/src/feeds-publisher.c b/src/feeds-publisher.c
index 35bcd6d..6bc438a 100644
--- a/src/feeds-publisher.c
+++ b/src/feeds-publisher.c
@@ -31,7 +31,7 @@
 
 /**
  * SECTION: feeds-publisher
- * @short_description: feed writer and PubSubHubbub publisher
+ * @short_description: feed writer and PubSubHubBub publisher
  *
  * #GrssFeedsPublisher may be used to expose contents for any given #GrssFeedChannel,
  * both writing a file to be dispatched by the local webserver or providing
@@ -515,7 +515,7 @@ grss_feeds_publisher_publish (GrssFeedsPublisher *pub, GrssFeedChannel *channel,
  * @pub: a #GrssFeedsPublisher
  * @channel: the #GrssFeedChannel to dump in the file
  * @items: list of #GrssFeedItems to be added in the feed
- * @path: path of the file to write
+ * @uri: URI of the file to write
  *
  * Dump the given @channel in an Atom formatted file in @path. If the local
  * PubSubHubbub hub has been activated (with grss_feeds_publisher_hub_switch())
@@ -619,7 +619,7 @@ verification_message_for_client (RemoteSubscriber *client)
 			break;
 	}
 
-	body = g_strdup_printf ("%s?hub.mode=%s&hub.topic=%s&hub.challenge=%s&hub.lease_seconds=%ld",
+	body = g_strdup_printf ("%s?hub.mode=%s&hub.topic=%s&hub.challenge=%s&hub.lease_seconds=%lld",
 				client->callback, mode, client->topic, client->challenge, client->lease_interval);
 
 	ret = soup_message_new ("GET", body);
diff --git a/src/feeds-pubsubhubbub-subscriber.c b/src/feeds-pubsubhubbub-subscriber.c
new file mode 100644
index 0000000..5b38789
--- /dev/null
+++ b/src/feeds-pubsubhubbub-subscriber.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011, 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 "feeds-pubsubhubbub-subscriber.h"
+#include "feeds-subscriber.h"
+#include "feeds-subscriber-handler.h"
+#include "utils.h"
+#include "feed-parser.h"
+
+#define FEEDS_SUBSCRIBER_GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE, GrssFeedsPubsubhubbubSubscriberPrivate))
+
+struct _GrssFeedsPubsubhubbubSubscriberPrivate {
+	GrssFeedsSubscriber	*parent;
+	GrssFeedParser		*parser;
+};
+
+static void feeds_subscriber_handler_interface_init (GrssFeedsSubscriberHandlerInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GrssFeedsPubsubhubbubSubscriber, grss_feeds_pubsubhubbub_subscriber, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GRSS_FEEDS_SUBSCRIBER_HANDLER_TYPE,
+                                                feeds_subscriber_handler_interface_init));
+
+static void
+grss_feeds_pubsubhubbub_subscriber_finalize (GObject *obj)
+{
+	GrssFeedsPubsubhubbubSubscriber *sub;
+
+	sub = GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER (obj);
+	g_object_unref (sub->priv->parser);
+}
+
+static void
+grss_feeds_pubsubhubbub_subscriber_class_init (GrssFeedsPubsubhubbubSubscriberClass *klass)
+{
+	GObjectClass *gobject_class;
+
+	g_type_class_add_private (klass, sizeof (GrssFeedsPubsubhubbubSubscriberPrivate));
+
+	gobject_class = G_OBJECT_CLASS (klass);
+	gobject_class->finalize = grss_feeds_pubsubhubbub_subscriber_finalize;
+}
+
+static void
+grss_feeds_pubsubhubbub_subscriber_init (GrssFeedsPubsubhubbubSubscriber *node)
+{
+	node->priv = FEEDS_SUBSCRIBER_GET_PRIVATE (node);
+	memset (node->priv, 0, sizeof (GrssFeedsPubsubhubbubSubscriberPrivate));
+	node->priv->parser = grss_feed_parser_new ();
+}
+
+GrssFeedsPubsubhubbubSubscriber*
+grss_feeds_pubsubhubbub_subscriber_new ()
+{
+	return g_object_new (GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE, NULL);
+}
+
+static void
+feeds_pubsubhubbub_subscriber_handler_set_parent (GrssFeedsSubscriberHandler *handler,
+                                                  GrssFeedsSubscriber *parent)
+{
+	GrssFeedsPubsubhubbubSubscriber *myself;
+
+	myself = GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER (handler);
+	myself->priv->parent = parent;
+}
+
+static gboolean
+feeds_pubsubhubbub_subscriber_handler_check_format (GrssFeedsSubscriberHandler *handler,
+                                                    GrssFeedChannel *channel)
+{
+	return grss_feed_channel_get_pubsubhub (channel, NULL);
+}
+
+static void
+subscribe_response_cb (SoupSession *session,
+                       SoupMessage *msg,
+                       gpointer user_data)
+{
+	guint status;
+
+	g_object_get (msg, "status-code", &status, NULL);
+	if (status < 200 || status > 299)
+		g_warning ("Unable to subscribe feed: %s", msg->response_body->data);
+}
+
+static void
+feeds_pubsubhubbub_subscriber_handler_subscribe (GrssFeedsSubscriberHandler *handler,
+                                                 GrssFeedChannel *channel,
+                                                 gchar *assigned_url)
+{
+	int local_port;
+	gchar *body;
+	gchar *pubsubhub;
+	const gchar *source;
+	GInetAddress *local_addr;
+	SoupMessage *msg;
+	GrssFeedsPubsubhubbubSubscriber *myself;
+
+	if (grss_feed_channel_get_pubsubhub (channel, &pubsubhub) == FALSE)
+		return;
+
+	myself = GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER (handler);
+
+	local_addr = grss_feeds_subscriber_get_address (myself->priv->parent);
+	local_port = grss_feeds_subscriber_get_port (myself->priv->parent);
+	source = grss_feed_channel_get_source (channel);
+
+	body = g_strdup_printf ("hub.mode=subscribe&hub.callback=http://%s:%d/%s&hub.topic=%s&hub.verify=sync";,
+	                        g_inet_address_to_string (local_addr), local_port, assigned_url, source);
+
+	msg = soup_message_new ("POST", pubsubhub);
+	soup_message_set_request (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, body, strlen (body));
+
+	soup_session_queue_message (grss_feeds_subscriber_get_session (myself->priv->parent), msg, subscribe_response_cb, NULL);
+}
+
+static GList*
+feeds_pubsubhubbub_subscriber_handler_handle_message (GrssFeedsSubscriberHandler *handler,
+                                                      GrssFeedChannel *channel,
+                                                      FEED_SUBSCRIPTION_STATUS *status,
+                                                      SoupServer *server,
+                                                      SoupMessage *msg,
+                                                      const char *path,
+                                                      GHashTable *query,
+                                                      SoupClientContext *client)
+{
+	gchar *mode;
+	gchar *challenge;
+	GList *items;
+	GError *error;
+	xmlDocPtr doc;
+	GrssFeedsPubsubhubbubSubscriber *myself;
+
+	items = NULL;
+
+	if (query != NULL) {
+		mode = (gchar*) g_hash_table_lookup (query, "hub.mode");
+
+		if (*status == FEED_SUBSCRIPTION_SUBSCRIBING && strcmp (mode, "subscribe") == 0) {
+			challenge = g_strdup ((gchar*) g_hash_table_lookup (query, "hub.challenge"));
+			soup_message_set_response (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, challenge, strlen (challenge));
+
+			soup_message_set_status (msg, 200);
+		}
+		else if (*status == FEED_SUBSCRIPTION_UNSUBSCRIBING && strcmp (mode, "unsubscribe") == 0) {
+			*status = FEED_SUBSCRIPTION_IDLE;
+
+			challenge = g_strdup ((gchar*) g_hash_table_lookup (query, "hub.challenge"));
+			soup_message_set_response (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, challenge, strlen (challenge));
+
+			soup_message_set_status (msg, 200);
+		}
+	}
+	else if (*status == FEED_SUBSCRIPTION_SUBSCRIBED) {
+		/*
+			TODO	Parsing and notification has to be moved in a
+				g_idle_add() function, so to reply to the
+				server as soon as possible
+		*/
+
+		myself = GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER (handler);
+		doc = content_to_xml (msg->request_body->data, strlen (msg->request_body->data));
+		error = NULL;
+		items = grss_feed_parser_parse (myself->priv->parser, channel, doc, &error);
+
+		if (items == NULL) {
+			g_warning ("Unable to parse notification from %s: %s", grss_feed_channel_get_source (channel), error->message);
+			g_error_free (error);
+		}
+
+		xmlFreeDoc (doc);
+		soup_message_set_status (msg, 202);
+	}
+	else {
+		soup_message_set_status (msg, 404);
+	}
+
+	return items;
+}
+
+static void
+unsubscribe_response_cb (SoupSession *session,
+                         SoupMessage *msg,
+                         gpointer user_data)
+{
+	/* dummy */
+}
+
+static void
+feeds_pubsubhubbub_subscriber_handler_unsubscribe (GrssFeedsSubscriberHandler *handler,
+                                                   GrssFeedChannel *channel,
+                                                   gchar *assigned_url)
+{
+	int local_port;
+	gchar *body;
+	gchar *pubsubhub;
+	const gchar *source;
+	GInetAddress *local_addr;
+	SoupMessage *msg;
+	GrssFeedsPubsubhubbubSubscriber *myself;
+
+	if (grss_feed_channel_get_pubsubhub (channel, &pubsubhub) == FALSE)
+		return;
+
+	myself = GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER (handler);
+
+	local_addr = grss_feeds_subscriber_get_address (myself->priv->parent);
+	local_port = grss_feeds_subscriber_get_port (myself->priv->parent);
+	source = grss_feed_channel_get_source (channel);
+
+	body = g_strdup_printf ("hub.mode=unsubscribe&hub.callback=http://%s:%d/%s&hub.topic=%s&hub.verify=sync";,
+	                        g_inet_address_to_string (local_addr), local_port, assigned_url, source);
+
+	msg = soup_message_new ("POST", pubsubhub);
+	soup_message_set_request (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, body, strlen (body));
+
+	soup_session_queue_message (grss_feeds_subscriber_get_session (myself->priv->parent), msg, unsubscribe_response_cb, NULL);
+}
+
+static void
+feeds_subscriber_handler_interface_init (GrssFeedsSubscriberHandlerInterface *iface)
+{
+	iface->set_parent = feeds_pubsubhubbub_subscriber_handler_set_parent;
+	iface->check_format = feeds_pubsubhubbub_subscriber_handler_check_format;
+	iface->subscribe = feeds_pubsubhubbub_subscriber_handler_subscribe;
+	iface->handle_message = feeds_pubsubhubbub_subscriber_handler_handle_message;
+	iface->unsubscribe = feeds_pubsubhubbub_subscriber_handler_unsubscribe;
+}
diff --git a/src/feeds-pubsubhubbub-subscriber.h b/src/feeds-pubsubhubbub-subscriber.h
new file mode 100644
index 0000000..5154a86
--- /dev/null
+++ b/src/feeds-pubsubhubbub-subscriber.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011, 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_PUBSUBHUBBUB_SUBSCRIBER_H__
+#define __FEEDS_PUBSUBHUBBUB_SUBSCRIBER_H__
+
+#include "libgrss.h"
+
+#define GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE		(grss_feeds_pubsubhubbub_subscriber_get_type())
+#define GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE, GrssFeedsPubsubhubbubSubscriber))
+#define GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_CLASS(c)	(G_TYPE_CHECK_CLASS_CAST ((c), GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE, GrssFeedsPubsubhubbubSubscriberClass))
+#define GRSS_IS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE))
+#define GRSS_IS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_CLASS(c)	(G_TYPE_CHECK_CLASS_TYPE ((c),  GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE))
+#define GRSS_PUBSUBHUBBUB_SUBSCRIBER_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GRSS_FEEDS_PUBSUBHUBBUB_SUBSCRIBER_TYPE, GrssFeedsPubsubhubbubSubscriberClass))
+
+typedef struct _GrssFeedsPubsubhubbubSubscriber		GrssFeedsPubsubhubbubSubscriber;
+typedef struct _GrssFeedsPubsubhubbubSubscriberPrivate	GrssFeedsPubsubhubbubSubscriberPrivate;
+
+struct _GrssFeedsPubsubhubbubSubscriber {
+	GObject parent;
+	GrssFeedsPubsubhubbubSubscriberPrivate *priv;
+};
+
+typedef struct {
+	GObjectClass parent;
+} GrssFeedsPubsubhubbubSubscriberClass;
+
+GType					grss_feeds_pubsubhubbub_subscriber_get_type	() G_GNUC_CONST;
+
+GrssFeedsPubsubhubbubSubscriber*	grss_feeds_pubsubhubbub_subscriber_new		();
+
+#endif /* __FEEDS_PUBSUBHUBBUB_SUBSCRIBER_H__ */
diff --git a/src/feeds-rsscloud-subscriber.c b/src/feeds-rsscloud-subscriber.c
new file mode 100644
index 0000000..24c3445
--- /dev/null
+++ b/src/feeds-rsscloud-subscriber.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2011, 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 "feeds-rsscloud-subscriber.h"
+#include "feeds-subscriber.h"
+#include "feeds-subscriber-handler.h"
+#include "utils.h"
+#include "feed-parser.h"
+
+#define FEEDS_SUBSCRIBER_GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE, GrssFeedsRsscloudSubscriberPrivate))
+
+struct _GrssFeedsRsscloudSubscriberPrivate {
+	GrssFeedsSubscriber	*parent;
+};
+
+static void feeds_subscriber_handler_interface_init (GrssFeedsSubscriberHandlerInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GrssFeedsRsscloudSubscriber, grss_feeds_rsscloud_subscriber, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GRSS_FEEDS_SUBSCRIBER_HANDLER_TYPE,
+                                                feeds_subscriber_handler_interface_init));
+
+static void
+grss_feeds_rsscloud_subscriber_finalize (GObject *obj)
+{
+	/* dummy */
+}
+
+static void
+grss_feeds_rsscloud_subscriber_class_init (GrssFeedsRsscloudSubscriberClass *klass)
+{
+	GObjectClass *gobject_class;
+
+	g_type_class_add_private (klass, sizeof (GrssFeedsRsscloudSubscriberPrivate));
+
+	gobject_class = G_OBJECT_CLASS (klass);
+	gobject_class->finalize = grss_feeds_rsscloud_subscriber_finalize;
+}
+
+static void
+grss_feeds_rsscloud_subscriber_init (GrssFeedsRsscloudSubscriber *node)
+{
+	node->priv = FEEDS_SUBSCRIBER_GET_PRIVATE (node);
+	memset (node->priv, 0, sizeof (GrssFeedsRsscloudSubscriberPrivate));
+}
+
+GrssFeedsRsscloudSubscriber*
+grss_feeds_rsscloud_subscriber_new ()
+{
+	return g_object_new (GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE, NULL);
+}
+
+static void
+feeds_rsscloud_subscriber_handler_set_parent (GrssFeedsSubscriberHandler *handler,
+                                              GrssFeedsSubscriber *parent)
+{
+	GrssFeedsRsscloudSubscriber *myself;
+
+	myself = GRSS_FEEDS_RSSCLOUD_SUBSCRIBER (handler);
+	myself->priv->parent = parent;
+}
+
+static void
+subscribe_response_cb (SoupSession *session,
+                       SoupMessage *msg,
+                       gpointer user_data)
+{
+	gboolean done;
+	const xmlChar *success;
+	guint status;
+	xmlDocPtr doc;
+	xmlNodePtr cur;
+
+	done = TRUE;
+
+	g_object_get (msg, "status-code", &status, NULL);
+
+	if (status < 200 || status > 299) {
+		done = FALSE;
+	}
+	else {
+		doc = content_to_xml (msg->response_body->data, strlen (msg->response_body->data));
+		success = NULL;
+
+		if ((cur = xmlDocGetRootElement (doc)) == NULL ||
+				xmlStrcmp (cur->name, BAD_CAST "notifyResult") != 0 ||
+				(success = xmlGetProp (cur, BAD_CAST"success")) == NULL ||
+				xmlStrcmp (success, BAD_CAST "true") != 0)
+			done = FALSE;
+
+		xmlFreeDoc (doc);
+
+		if (success != NULL)
+			xmlFree (success);
+	}
+
+	if (done == FALSE) {
+		g_warning ("Unable to subscribe feed: %s", msg->response_body->data);
+	}
+}
+
+gboolean
+feeds_rsscloud_subscriber_handler_check_format (GrssFeedsSubscriberHandler *handler,
+                                                GrssFeedChannel *channel)
+{
+	return grss_feed_channel_get_rsscloud (channel, NULL, NULL);
+}
+
+void
+feeds_rsscloud_subscriber_handler_subscribe (GrssFeedsSubscriberHandler *handler,
+                                             GrssFeedChannel *channel,
+                                             gchar *assigned_url)
+{
+	int local_port;
+	gchar *body;
+	gchar *path;
+	gchar *protocol;
+	const gchar *source;
+	SoupMessage *msg;
+	GInetAddress *local_addr;
+	GrssFeedsRsscloudSubscriber *myself;
+
+	if (grss_feed_channel_get_rsscloud (channel, &path, &protocol) == FALSE)
+		return;
+
+	myself = GRSS_FEEDS_RSSCLOUD_SUBSCRIBER (handler);
+
+	local_addr = grss_feeds_subscriber_get_address (myself->priv->parent);
+	local_port = grss_feeds_subscriber_get_port (myself->priv->parent);
+	source = grss_feed_channel_get_source (channel);
+
+	body = g_strdup_printf ("notifyProcedure=\"\"&protocol=%s&domain=%s&path=%s&port=%d&url1=%s",
+				protocol, g_inet_address_to_string (local_addr), assigned_url, local_port, source);
+
+	msg = soup_message_new ("POST", path);
+	soup_message_set_request (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, body, strlen (body));
+	soup_session_queue_message (grss_feeds_subscriber_get_session (myself->priv->parent), msg, subscribe_response_cb, NULL);
+}
+
+GList*
+feeds_rsscloud_subscriber_handler_handle_message (GrssFeedsSubscriberHandler *handler,
+                                                  GrssFeedChannel *channel,
+                                                  FEED_SUBSCRIPTION_STATUS *status,
+                                                  SoupServer *server,
+                                                  SoupMessage *msg,
+                                                  const char *path,
+                                                  GHashTable *query,
+                                                  SoupClientContext *client)
+{
+	GList *ret;
+	gchar *challenge;
+
+	ret = NULL;
+
+	if (query != NULL) {
+		challenge = (gchar*) g_hash_table_lookup (query, "challenge");
+
+		if (*status == FEED_SUBSCRIPTION_SUBSCRIBING && challenge != NULL) {
+			challenge = g_strdup (challenge);
+			soup_message_set_response (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, challenge, strlen (challenge));
+			soup_message_set_status (msg, 200);
+		}
+	}
+	else if (*status == FEED_SUBSCRIPTION_SUBSCRIBED) {
+		ret = grss_feed_channel_fetch_all (channel);
+		soup_message_set_status (msg, 202);
+	}
+	else {
+		soup_message_set_status (msg, 404);
+	}
+
+	return ret;
+}
+
+void
+feeds_rsscloud_subscriber_handler_unsubscribe (GrssFeedsSubscriberHandler *handler,
+                                               GrssFeedChannel *channel,
+                                               gchar *assigned_url)
+{
+	/* dummy */
+}
+
+static void
+feeds_subscriber_handler_interface_init (GrssFeedsSubscriberHandlerInterface *iface)
+{
+	iface->set_parent = feeds_rsscloud_subscriber_handler_set_parent;
+	iface->check_format = feeds_rsscloud_subscriber_handler_check_format;
+	iface->subscribe = feeds_rsscloud_subscriber_handler_subscribe;
+	iface->handle_message = feeds_rsscloud_subscriber_handler_handle_message;
+	iface->unsubscribe = feeds_rsscloud_subscriber_handler_unsubscribe;
+}
diff --git a/src/feeds-rsscloud-subscriber.h b/src/feeds-rsscloud-subscriber.h
new file mode 100644
index 0000000..07ab3e3
--- /dev/null
+++ b/src/feeds-rsscloud-subscriber.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011, 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_RSSCLOUD_SUBSCRIBER_H__
+#define __FEEDS_RSSCLOUD_SUBSCRIBER_H__
+
+#include "libgrss.h"
+
+#define GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE		(grss_feeds_rsscloud_subscriber_get_type())
+#define GRSS_FEEDS_RSSCLOUD_SUBSCRIBER(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE, GrssFeedsRsscloudSubscriber))
+#define GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_CLASS(c)		(G_TYPE_CHECK_CLASS_CAST ((c), GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE, GrssFeedsRsscloudSubscriberClass))
+#define GRSS_IS_RSSCLOUD_SUBSCRIBER(o)			(G_TYPE_CHECK_INSTANCE_TYPE ((o), GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE))
+#define GRSS_IS_FEEDS_RSSCLOUD_SUBSCRIBER_CLASS(c)	(G_TYPE_CHECK_CLASS_TYPE ((c),  GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE))
+#define GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GRSS_FEEDS_RSSCLOUD_SUBSCRIBER_TYPE, GrssFeedsRsscloudSubscriberClass))
+
+typedef struct _GrssFeedsRsscloudSubscriber		GrssFeedsRsscloudSubscriber;
+typedef struct _GrssFeedsRsscloudSubscriberPrivate	GrssFeedsRsscloudSubscriberPrivate;
+
+struct _GrssFeedsRsscloudSubscriber {
+	GObject parent;
+	GrssFeedsRsscloudSubscriberPrivate *priv;
+};
+
+typedef struct {
+	GObjectClass parent;
+} GrssFeedsRsscloudSubscriberClass;
+
+GType				grss_feeds_rsscloud_subscriber_get_type			() G_GNUC_CONST;
+
+GrssFeedsRsscloudSubscriber*	grss_feeds_rsscloud_subscriber_new			();
+
+#endif /* __FEEDS_RSSCLOUD_SUBSCRIBER_H__ */
diff --git a/src/feeds-subscriber-handler.c b/src/feeds-subscriber-handler.c
new file mode 100644
index 0000000..54213ea
--- /dev/null
+++ b/src/feeds-subscriber-handler.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011, 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-subscriber-handler.h"
+
+static void
+grss_feeds_subscriber_handler_base_init (gpointer g_class)
+{
+}
+
+GType
+grss_feeds_subscriber_handler_get_type ()
+{
+	static GType iface_type = 0;
+
+	if (iface_type == 0) {
+		static const GTypeInfo info = {
+			sizeof (GrssFeedsSubscriberHandlerInterface),
+			grss_feeds_subscriber_handler_base_init,
+			NULL,
+		};
+
+		iface_type = g_type_register_static (G_TYPE_INTERFACE, "GrssFeedsSubscriberHandler", &info, 0);
+	}
+
+	return iface_type;
+}
+
+void
+grss_feeds_subscriber_handler_set_parent (GrssFeedsSubscriberHandler *handler,
+                                          GrssFeedsSubscriber *parent)
+{
+	if (GRSS_IS_FEEDS_SUBSCRIBER_HANDLER (handler) == FALSE)
+		return;
+
+	return GRSS_FEEDS_SUBSCRIBER_HANDLER_GET_INTERFACE (handler)->set_parent (handler, parent);
+}
+
+gboolean
+grss_feeds_subscriber_handler_check_format (GrssFeedsSubscriberHandler *handler,
+                                            GrssFeedChannel *channel)
+{
+	if (GRSS_IS_FEEDS_SUBSCRIBER_HANDLER (handler) == FALSE)
+		return FALSE;
+
+	return GRSS_FEEDS_SUBSCRIBER_HANDLER_GET_INTERFACE (handler)->check_format (handler, channel);
+}
+
+void
+grss_feeds_subscriber_handler_subscribe (GrssFeedsSubscriberHandler *handler,
+                                         GrssFeedChannel *channel,
+                                         gchar *assigned_url)
+{
+	if (GRSS_IS_FEEDS_SUBSCRIBER_HANDLER (handler) == FALSE)
+		return;
+
+	GRSS_FEEDS_SUBSCRIBER_HANDLER_GET_INTERFACE (handler)->subscribe (handler, channel, assigned_url);
+}
+
+GList*
+grss_feeds_subscriber_handler_handle_incoming_message (GrssFeedsSubscriberHandler *handler,
+                                                       GrssFeedChannel *channel,
+                                                       FEED_SUBSCRIPTION_STATUS *status,
+						       SoupServer *server,
+                                                       SoupMessage *msg,
+                                                       const char *path,
+                                                       GHashTable *query,
+                                                       SoupClientContext *client)
+{
+	if (GRSS_IS_FEEDS_SUBSCRIBER_HANDLER (handler) == FALSE)
+		return NULL;
+
+	return GRSS_FEEDS_SUBSCRIBER_HANDLER_GET_INTERFACE (handler)->handle_message (handler, channel, status, server, msg, path, query, client);
+}
+
+void
+grss_feeds_subscriber_handler_unsubscribe (GrssFeedsSubscriberHandler *handler,
+                                           GrssFeedChannel *channel,
+                                           gchar *assigned_url)
+{
+	if (GRSS_IS_FEEDS_SUBSCRIBER_HANDLER (handler) == FALSE)
+		return;
+
+	GRSS_FEEDS_SUBSCRIBER_HANDLER_GET_INTERFACE (handler)->unsubscribe (handler, channel, assigned_url);
+}
diff --git a/src/feeds-subscriber-handler.h b/src/feeds-subscriber-handler.h
new file mode 100644
index 0000000..4479317
--- /dev/null
+++ b/src/feeds-subscriber-handler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011, 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 __GRSS_FEEDS_SUBSCRIBER_HANDLER_H__
+#define __GRSS_FEEDS_SUBSCRIBER_HANDLER_H__
+
+#include "libgrss.h"
+
+#define GRSS_FEEDS_SUBSCRIBER_HANDLER_TYPE			(grss_feeds_subscriber_handler_get_type ())
+#define GRSS_FEEDS_SUBSCRIBER_HANDLER(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GRSS_FEEDS_SUBSCRIBER_HANDLER_TYPE, GrssFeedsSubscriberHandler))
+#define GRSS_IS_FEEDS_SUBSCRIBER_HANDLER(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GRSS_FEEDS_SUBSCRIBER_HANDLER_TYPE))
+#define GRSS_FEEDS_SUBSCRIBER_HANDLER_GET_INTERFACE(inst)	(G_TYPE_INSTANCE_GET_INTERFACE ((inst), GRSS_FEEDS_SUBSCRIBER_HANDLER_TYPE, GrssFeedsSubscriberHandlerInterface))
+
+typedef struct _GrssFeedsSubscriberHandler		GrssFeedsSubscriberHandler;
+typedef struct _GrssFeedsSubscriberHandlerInterface	GrssFeedsSubscriberHandlerInterface;
+
+typedef enum {
+	FEED_SUBSCRIPTION_IDLE,
+	FEED_SUBSCRIPTION_SUBSCRIBING,
+	FEED_SUBSCRIPTION_SUBSCRIBED,
+	FEED_SUBSCRIPTION_UNSUBSCRIBING,
+} FEED_SUBSCRIPTION_STATUS;
+
+struct _GrssFeedsSubscriberHandlerInterface {
+	GTypeInterface parent_iface;
+
+	void (*set_parent) (GrssFeedsSubscriberHandler *handler, GrssFeedsSubscriber *parent);
+	gboolean (*check_format) (GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel);
+	void (*subscribe) (GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel, gchar *assigned_url);
+	GList* (*handle_message) (GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel, FEED_SUBSCRIPTION_STATUS *status, SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client);
+	void (*unsubscribe) (GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel, gchar *assigned_url);
+};
+
+GType			grss_feeds_subscriber_handler_get_type			();
+
+void			grss_feeds_subscriber_handler_set_parent		(GrssFeedsSubscriberHandler *handler, GrssFeedsSubscriber *parent);
+gboolean		grss_feeds_subscriber_handler_check_format		(GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel);
+void			grss_feeds_subscriber_handler_subscribe			(GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel, gchar *assigned_url);
+GList*			grss_feeds_subscriber_handler_handle_incoming_message	(GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel, FEED_SUBSCRIPTION_STATUS *status, SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client);
+void			grss_feeds_subscriber_handler_unsubscribe		(GrssFeedsSubscriberHandler *handler, GrssFeedChannel *channel, gchar *assigned_url);
+
+#endif /* __GRSS_FEEDS_SUBSCRIBER_HANDLER_H__ */
diff --git a/src/feeds-subscriber.c b/src/feeds-subscriber.c
index 3aef7c2..df56694 100644
--- a/src/feeds-subscriber.c
+++ b/src/feeds-subscriber.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Roberto Guido <rguido src gnome org>
+ * Copyright (C) 2011, Roberto Guido <rguido src gnome org>
  *                     Michele Tameni <michele amdplanet it>
  *
  * This library is free software; you can redistribute it and/or
@@ -19,6 +19,9 @@
  */
 
 #include "feeds-subscriber.h"
+#include "feeds-subscriber-handler.h"
+#include "feeds-pubsubhubbub-subscriber.h"
+#include "feeds-rsscloud-subscriber.h"
 #include "utils.h"
 #include "feed-parser.h"
 #include "feed-marshal.h"
@@ -29,16 +32,15 @@
 
 /**
  * SECTION: feeds-subscriber
- * @short_description: PubSubHubbub subscriber
+ * @short_description: feeds subscriber
  *
  * #GrssFeedsSubscriber is an alternative for #GrssFeedsPool, able to receive
- * real-time notifications by feeds managed by a PubSubHubbub hub.
+ * real-time notifications by feeds managed by one of the supported protocols.
  * When the subscriber is executed (with grss_feeds_subscriber_switch()) it opens
  * a server on a local port (configurable with grss_feeds_subscriber_set_port()),
  * engage a subscription for each #GrssFeedChannel passed with
  * grss_feeds_subscriber_listen(), and waits for direct notifications by the
- * remote hub.
- * For more information look at http://code.google.com/p/pubsubhubbub/
+ * remote server.
  */
 
 /*
@@ -49,59 +51,34 @@
 		client side in case of problems by the server side
 */
 
-typedef enum {
-	SUBSCRIBER_IS_IDLE,
-	SUBSCRIBER_TRYING_LOCAL_IP,
-	SUBSCRIBER_CHECKING_PUBLIC_IP
-} SUBSCRIBER_INIT_STATUS;
-
 static void	subscribe_feeds			(GrssFeedsSubscriber *sub);
-static void	try_another_subscription_policy	(GrssFeedsSubscriber *sub);
-
-typedef void (*SubscriberJobCallback) (GrssFeedsSubscriber *subscriber);
 
 struct _GrssFeedsSubscriberPrivate {
 	gboolean		running;
 
-	SUBSCRIBER_INIT_STATUS	initing_status;
-	gboolean		has_errors_in_subscription;
 	int			port;
 	SoupServer		*server;
 	GInetAddress		*local_addr;
-	GInetAddress		*external_addr;
+	GInetAddress		*exposed_addr;
 
-	gchar			*hub;
 	SoupSession		*soupsession;
 
 	GrssFeedParser		*parser;
+	GList			*handlers;
 	GList			*feeds_list;
-
-	guint			refresh_scheduler;
 };
 
-typedef enum {
-	FEED_SUBSCRIPTION_IDLE,
-	FEED_SUBSCRIPTION_SUBSCRIBING,
-	FEED_SUBSCRIPTION_SUBSCRIBED,
-	FEED_SUBSCRIPTION_UNSUBSCRIBING,
-} FEED_SUBSCRIPTION_STATUS;
-
 typedef struct {
 	GrssFeedChannel			*channel;
 
 	FEED_SUBSCRIPTION_STATUS	status;
-	int				identifier;
+	gchar				*identifier;
 	gchar				*path;
 
-	GrssFeedsSubscriber			*sub;
+	GrssFeedsSubscriber		*sub;
+	GrssFeedsSubscriberHandler	*handler;
 } GrssFeedChannelWrap;
 
-typedef struct {
-	int			counter;
-	SubscriberJobCallback	callback;
-	GrssFeedsSubscriber		*subscriber;
-} SubscriberJob;
-
 enum {
 	NOTIFICATION_RECEIVED,
 	LAST_SIGNAL
@@ -143,7 +120,6 @@ grss_feeds_subscriber_finalize (GObject *obj)
 	sub = GRSS_FEEDS_SUBSCRIBER (obj);
 	grss_feeds_subscriber_switch (sub, FALSE);
 	remove_currently_listened (sub);
-	FREE_STRING (sub->priv->hub);
 	g_object_unref (sub->priv->parser);
 }
 
@@ -176,10 +152,22 @@ grss_feeds_subscriber_class_init (GrssFeedsSubscriberClass *klass)
 static void
 grss_feeds_subscriber_init (GrssFeedsSubscriber *node)
 {
+	GrssFeedsSubscriberHandler *handler;
+
 	node->priv = FEEDS_SUBSCRIBER_GET_PRIVATE (node);
 	memset (node->priv, 0, sizeof (GrssFeedsSubscriberPrivate));
 	node->priv->parser = grss_feed_parser_new ();
 	node->priv->port = DEFAULT_SERVER_PORT;
+
+	node->priv->handlers = NULL;
+
+	handler = GRSS_FEEDS_SUBSCRIBER_HANDLER (grss_feeds_pubsubhubbub_subscriber_new ());
+	node->priv->handlers = g_list_prepend (node->priv->handlers, handler);
+	grss_feeds_subscriber_handler_set_parent (handler, node);
+
+	handler = GRSS_FEEDS_SUBSCRIBER_HANDLER (grss_feeds_rsscloud_subscriber_new ());
+	node->priv->handlers = g_list_prepend (node->priv->handlers, handler);
+	grss_feeds_subscriber_handler_set_parent (handler, node);
 }
 
 /**
@@ -195,6 +183,21 @@ grss_feeds_subscriber_new ()
 	return g_object_new (GRSS_FEEDS_SUBSCRIBER_TYPE, NULL);
 }
 
+static GrssFeedsSubscriberHandler*
+retrieve_handler (GrssFeedsSubscriber *sub, GrssFeedChannel *feed)
+{
+	GList *iter;
+	GrssFeedsSubscriberHandler *handler;
+
+	for (iter = sub->priv->handlers; iter; iter = g_list_next (iter)) {
+		handler = (GrssFeedsSubscriberHandler*) iter->data;
+		if (grss_feeds_subscriber_handler_check_format (handler, feed) == TRUE)
+			return handler;
+	}
+
+	return NULL;
+}
+
 static gboolean
 create_listened (GrssFeedsSubscriber *sub, GList *feeds)
 {
@@ -202,14 +205,14 @@ create_listened (GrssFeedsSubscriber *sub, GList *feeds)
 	GList *iter;
 	GrssFeedChannel *feed;
 	GrssFeedChannelWrap *wrap;
+	GrssFeedsSubscriberHandler *handler;
 
 	for (iter = feeds; iter; iter = g_list_next (iter)) {
 		feed = (GrssFeedChannel*) iter->data;
 
-		if (grss_feed_channel_get_pubsubhub (feed, NULL, NULL) == FALSE) {
-			g_warning ("Feed at %s has not PubSubHubbub capability", grss_feed_channel_get_source (feed));
+		handler = retrieve_handler (sub, feed);
+		if (handler == NULL)
 			return FALSE;
-		}
 	}
 
 	list = NULL;
@@ -223,6 +226,7 @@ create_listened (GrssFeedsSubscriber *sub, GList *feeds)
 		wrap->path = NULL;
 		wrap->channel = feed;
 		wrap->sub = sub;
+		wrap->handler = retrieve_handler (sub, feed);
 		list = g_list_prepend (list, wrap);
 	}
 
@@ -242,7 +246,7 @@ create_listened (GrssFeedsSubscriber *sub, GList *feeds)
  * are g_object_ref'd here
  *
  * Return value: %TRUE if all #GrssFeedChannels involved in @feeds are valid
- * (grss_feed_channel_get_pubsubhub() returns %TRUE), %FALSE otherwise
+ * and can be listened with one of the implemented procotols, %FALSE otherwise
  */
 gboolean
 grss_feeds_subscriber_listen (GrssFeedsSubscriber *sub, GList *feeds)
@@ -275,75 +279,19 @@ grss_feeds_subscriber_get_listened (GrssFeedsSubscriber *sub)
 }
 
 static void
-check_complete_job (SubscriberJob *job)
-{
-	job->counter--;
-
-	if (job->counter <= 0) {
-		job->callback (job->subscriber);
-		g_free (job);
-	}
-}
-
-static void
 handle_incoming_notification_cb (SoupServer *server, SoupMessage *msg, const char *path,
                                  GHashTable *query, SoupClientContext *client, gpointer user_data)
 {
-	gchar *mode;
-	gchar *challenge;
 	GList *iter;
 	GList *items;
-	GError *error;
-	xmlDocPtr doc;
 	GrssFeedChannelWrap *feed;
 
 	feed = (GrssFeedChannelWrap*) user_data;
+	items = grss_feeds_subscriber_handler_handle_incoming_message (feed->handler, feed->channel, &(feed->status), server, msg, path, query, client);
 
-	if (query != NULL) {
-		mode = (gchar*) g_hash_table_lookup (query, "hub.mode");
-
-		if (feed->status == FEED_SUBSCRIPTION_SUBSCRIBING && strcmp (mode, "subscribe") == 0) {
-			challenge = g_strdup ((gchar*) g_hash_table_lookup (query, "hub.challenge"));
-			soup_message_set_response (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, challenge, strlen (challenge));
-
-			soup_message_set_status (msg, 200);
-		}
-		else if (feed->status == FEED_SUBSCRIPTION_UNSUBSCRIBING && strcmp (mode, "unsubscribe") == 0) {
-			feed->status = FEED_SUBSCRIPTION_IDLE;
-
-			challenge = g_strdup ((gchar*) g_hash_table_lookup (query, "hub.challenge"));
-			soup_message_set_response (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, challenge, strlen (challenge));
-
-			soup_message_set_status (msg, 200);
-		}
-	}
-	else if (feed->status == FEED_SUBSCRIPTION_SUBSCRIBED) {
-		/*
-			TODO	Parsing and notification has to be moved in a
-				g_idle_add() function, so to reply to the
-				server as soon as possible
-		*/
-
-		doc = content_to_xml (msg->request_body->data, strlen (msg->request_body->data));
-		error = NULL;
-		items = grss_feed_parser_parse (feed->sub->priv->parser, feed->channel, doc, &error);
-
-		if (items == NULL) {
-			g_warning ("Unable to parse notification from %s: %s", grss_feed_channel_get_source (feed->channel), error->message);
-			g_error_free (error);
-		}
-		else {
-			for (iter = items; iter; iter = g_list_next (iter))
-				g_signal_emit (feed->sub, signals [NOTIFICATION_RECEIVED], 0, feed->channel, (GrssFeedItem*) iter->data, NULL);
-			g_list_free (items);
-		}
-
-		xmlFreeDoc (doc);
-		soup_message_set_status (msg, 202);
-	}
-	else {
-		soup_message_set_status (msg, 404);
-	}
+	for (iter = items; iter; iter = g_list_next (iter))
+		g_signal_emit (feed->sub, signals [NOTIFICATION_RECEIVED], 0, feed->channel, (GrssFeedItem*) iter->data, NULL);
+	g_list_free (items);
 }
 
 static void
@@ -367,11 +315,11 @@ register_handlers (GrssFeedsSubscriber *sub)
 
 	for (i = 1, iter = sub->priv->feeds_list; iter; iter = g_list_next (iter), i++) {
 		feed = (GrssFeedChannelWrap*) iter->data;
-		feed->identifier = i;
+		feed->identifier = g_strdup_printf ("%d", i);
 		feed->status = FEED_SUBSCRIPTION_SUBSCRIBING;
 
 		FREE_STRING (feed->path);
-		feed->path = g_strdup_printf ("/%d", feed->identifier);
+		feed->path = g_strdup_printf ("/%s", feed->identifier);
 		soup_server_add_handler (sub->priv->server, feed->path, handle_incoming_notification_cb, feed, NULL);
 	}
 }
@@ -388,72 +336,17 @@ close_server (GrssFeedsSubscriber *sub)
 }
 
 static void
-feeds_subscribed_cb (GrssFeedsSubscriber *sub)
-{
-	if (sub->priv->has_errors_in_subscription == TRUE)
-		try_another_subscription_policy (sub);
-}
-
-static void
-subscribe_response_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
-{
-	guint status;
-	SubscriberJob *job;
-
-	job = (SubscriberJob*) user_data;
-
-	g_object_get (msg, "status-code", &status, NULL);
-	if (status < 200 || status > 299) {
-		g_warning ("Unable to subscribe feed: %s", msg->response_body->data);
-		job->subscriber->priv->has_errors_in_subscription = TRUE;
-	}
-
-	check_complete_job (job);
-}
-
-static void
-subscribe_feed (GrssFeedsSubscriber *sub, GrssFeedChannelWrap *feed, SubscriberJob *job)
-{
-	gchar *body;
-	gchar *pubsubhub;
-	gchar *feed_reference;
-	SoupMessage *msg;
-
-	if (grss_feed_channel_get_pubsubhub (feed->channel, &pubsubhub, &feed_reference) == FALSE)
-		return;
-
-	if (sub->priv->hub != NULL)
-		pubsubhub = sub->priv->hub;
-
-	body = g_strdup_printf ("hub.mode=subscribe&hub.callback=http://%s:%d/%d&hub.topic=%s&hub.verify=sync";,
-	                        g_inet_address_to_string (sub->priv->external_addr), sub->priv->port, feed->identifier, feed_reference);
-
-	msg = soup_message_new ("POST", pubsubhub);
-	soup_message_set_request (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, body, strlen (body));
-
-	soup_session_queue_message (sub->priv->soupsession, msg, subscribe_response_cb, job);
-}
-
-static void
 subscribe_feeds (GrssFeedsSubscriber *sub)
 {
 	GList *iter;
 	GrssFeedChannelWrap *feed;
-	SubscriberJob *job;
 
 	if (sub->priv->feeds_list == NULL)
 		return;
 
-	job = g_new0 (SubscriberJob, 1);
-	job->counter = g_list_length (sub->priv->feeds_list);
-	job->callback = feeds_subscribed_cb;
-	job->subscriber = sub;
-
-	sub->priv->has_errors_in_subscription = FALSE;
-
 	for (iter = sub->priv->feeds_list; iter; iter = g_list_next (iter)) {
 		feed = (GrssFeedChannelWrap*) iter->data;
-		subscribe_feed (sub, feed, job);
+		grss_feeds_subscriber_handler_subscribe (feed->handler, feed->channel, feed->identifier);
 	}
 }
 
@@ -472,20 +365,26 @@ create_and_run_server (GrssFeedsSubscriber *sub)
 	gchar *ip;
 	struct sockaddr_in low_addr;
 	SoupAddress *soup_addr;
-	GInetAddress *my_addr;
-
-	my_addr = my_detect_internet_address (sub);
-	if (my_addr == NULL)
-		return;
 
 	low_addr.sin_family = AF_INET;
 	low_addr.sin_port = htons (sub->priv->port);
-	ip = g_inet_address_to_string (my_addr);
+	ip = g_inet_address_to_string (sub->priv->local_addr);
+
 	inet_pton (AF_INET, ip, &low_addr.sin_addr);
 	g_free (ip);
 
 	soup_addr = soup_address_new_from_sockaddr ((struct sockaddr*) &low_addr, sizeof (low_addr));
-	sub->priv->server = soup_server_new ("port", sub->priv->port, "interface", soup_addr, NULL);
+	if (soup_addr == NULL) {
+		g_warning ("Unable to use detected exposed IP");
+		return;
+	}
+
+	sub->priv->server = soup_server_new (SOUP_SERVER_INTERFACE, soup_addr, NULL);
+	if (sub->priv->server == NULL) {
+		g_warning ("Unable to open server on detected exposed IP");
+		return;
+	}
+
 	g_object_unref (soup_addr);
 
 	register_handlers (sub);
@@ -497,17 +396,43 @@ create_and_run_server (GrssFeedsSubscriber *sub)
 static void
 external_ip_received_cb (SoupSession *session, SoupMessage *msg, gpointer data)
 {
+	int i;
+	int len;
+	gchar *tmp;
 	GrssFeedsSubscriber *sub;
 
-	sub = (GrssFeedsSubscriber*) data;
+	if (msg->status_code == SOUP_STATUS_OK) {
+		sub = (GrssFeedsSubscriber*) data;
 
-	sub->priv->external_addr = g_inet_address_new_from_string (msg->response_body->data);
-	if (sub->priv->external_addr == NULL) {
-		g_warning ("Unable to determine public IP");
-		return;
-	}
+		/*
+			Typical response from checkip.dyndns.org:
+
+			<html><head><title>Current IP Check</title></head><body>Current IP Address: X.X.X.X</body></html>
+			|                                                                          |
+			+----------------------------------- 76 -----------------------------------+
+		*/
+		tmp = g_strdup (msg->response_body->data + 76);
+		len = strlen (tmp);
+		for (i = 0; tmp [i] != '<' && i < len; i++);
+
+		if (i == len) {
+			g_warning ("Unable to determine public IP: %s", msg->response_body->data);
+		}
+		else {
+			tmp [i] = '\0';
 
-	create_and_run_server (sub);
+			sub->priv->exposed_addr = g_inet_address_new_from_string (tmp);
+			if (sub->priv->exposed_addr == NULL)
+				g_warning ("Unable to determine public IP: %s", tmp);
+			else
+				create_and_run_server (sub);
+		}
+
+		g_free (tmp);
+	}
+	else {
+		g_warning ("Unable to determine public IP: %s", soup_status_get_phrase (msg->status_code));
+	}
 }
 
 static void
@@ -515,45 +440,19 @@ subscribe_with_external_ip (GrssFeedsSubscriber *sub)
 {
 	SoupMessage *msg;
 
-	sub->priv->initing_status = SUBSCRIBER_CHECKING_PUBLIC_IP;
-
 	/*
 		This method to determine public IP is quite odd, but no
 		better has been suggested by StackOverflow.com
 	*/
-	msg = soup_message_new ("GET", "http://whatismyip.org";);
+	msg = soup_message_new ("GET", "http://checkip.dyndns.org/";);
 	soup_session_queue_message (sub->priv->soupsession, msg, external_ip_received_cb, sub);
 }
 
 static void
-try_another_subscription_policy (GrssFeedsSubscriber *sub)
-{
-	switch (sub->priv->initing_status) {
-		case SUBSCRIBER_TRYING_LOCAL_IP:
-			close_server (sub);
-			subscribe_with_external_ip (sub);
-			break;
-
-		default:
-			close_server (sub);
-			g_warning ("No way: subscription is failed");
-			break;
-	}
-}
-
-static void
 init_run_server (GrssFeedsSubscriber *sub)
 {
-	gboolean done;
 	GInetAddress *addr;
 
-	done = FALSE;
-
-	if (sub->priv->external_addr != NULL) {
-		g_object_unref (sub->priv->external_addr);
-		sub->priv->external_addr = NULL;
-	}
-
 	if (sub->priv->soupsession == NULL)
 		sub->priv->soupsession = soup_session_async_new ();
 
@@ -562,100 +461,31 @@ init_run_server (GrssFeedsSubscriber *sub)
 
 		        BEGIN
 		          |
-		  +---------------+               +--------------+
-		  | has fixed hub | ---- YES ---> | is hub local | ----- YES ---+
-		  +---------------+               +--------------+              |
-		          |                               |                     |
-		          NO <----------------------------+                     |
-		          |                                                     |
-		+-------------------+           +-----------------+             |
+		          |
+		+-------------------+           +-----------------+
 		| host seems public | -- YES -> | subscribe works | ---- YES ---+
 		+-------------------+           +-----------------+             |
 		          |                               |                     |
 		          NO -----------------------------+                     |
 		          |                                                     |
-		 +-----------------+                                            |
-		 | check public IP |                                            |
-		 +-----------------+                                            |
+		 +-----------------+            +-----------------+             |
+		 | check public IP | --- YES -> | subscribe works | ---- YES ---+
+		 +-----------------+            +-----------------+             |
+		          |                               |                     |
+		          NO -----------------------------+                     |
 		          |                                                     |
-		 +-----------------+                   +------+                 |
-		 | subscribe works | --- YES --------> | DONE | <---------------+
-		 +-----------------+                   +------+
-		          |
-		          NO
-		          |
-		      +--------+
-		      | NO WAY |
-		      +--------+
+		          |                                                     |
+		        NO WAY                                                 DONE
 	*/
 
-	sub->priv->initing_status = SUBSCRIBER_IS_IDLE;
-
-	if (sub->priv->hub != NULL) {
-		addr = g_inet_address_new_from_string (sub->priv->hub);
-		if (g_inet_address_get_is_link_local (addr) == TRUE) {
-			sub->priv->external_addr = my_detect_internet_address (sub);
-			done = TRUE;
-			create_and_run_server (sub);
-		}
+	addr = my_detect_internet_address (sub);
+	if (address_seems_public (addr) == TRUE) {
+		sub->priv->exposed_addr = sub->priv->local_addr;
+		create_and_run_server (sub);
 	}
-
-	if (done == FALSE) {
-		addr = my_detect_internet_address (sub);
-		if (address_seems_public (addr) == TRUE) {
-			sub->priv->external_addr = addr;
-			done = TRUE;
-			sub->priv->initing_status = SUBSCRIBER_TRYING_LOCAL_IP;
-			create_and_run_server (sub);
-		}
-	}
-
-	if (done == FALSE)
+	else {
 		subscribe_with_external_ip (sub);
-}
-
-static void
-feeds_unsubscribed_cb (GrssFeedsSubscriber *sub)
-{
-	close_server (sub);
-	g_object_unref (sub->priv->soupsession);
-	sub->priv->soupsession = NULL;
-}
-
-static void
-unsubscribe_response_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
-{
-	SubscriberJob *job;
-
-	job = (SubscriberJob*) user_data;
-	check_complete_job (job);
-}
-
-static void
-unsubscribe_feed (GrssFeedsSubscriber *sub, GrssFeedChannelWrap *feed, SubscriberJob *job)
-{
-	gchar *body;
-	gchar *pubsubhub;
-	gchar *feed_reference;
-	SoupMessage *msg;
-
-	if (grss_feed_channel_get_pubsubhub (feed->channel, &pubsubhub, &feed_reference) == FALSE) {
-		check_complete_job (job);
-		return;
 	}
-
-	feed->status = FEED_SUBSCRIPTION_UNSUBSCRIBING;
-
-	if (sub->priv->hub != NULL)
-		pubsubhub = sub->priv->hub;
-
-	body = g_strdup_printf ("hub.mode=unsubscribe&hub.callback=http://%s:%d/%d&hub.topic=%s&hub.verify=sync";,
-	                        g_inet_address_to_string (sub->priv->external_addr), sub->priv->port, feed->identifier, feed_reference);
-
-	msg = soup_message_new ("POST", pubsubhub);
-	soup_message_set_request (msg, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, body, strlen (body));
-
-	soup_session_queue_message (sub->priv->soupsession, msg, unsubscribe_response_cb, job);
 }
 
 static void
@@ -663,16 +493,11 @@ unsubscribe_feeds (GrssFeedsSubscriber *sub)
 {
 	GList *iter;
 	GrssFeedChannelWrap *wrap;
-	SubscriberJob *job;
-
-	job = g_new0 (SubscriberJob, 1);
-	job->counter = g_list_length (sub->priv->feeds_list);
-	job->callback = feeds_unsubscribed_cb;
-	job->subscriber = sub;
 
 	for (iter = sub->priv->feeds_list; iter; iter = g_list_next (iter)) {
 		wrap = (GrssFeedChannelWrap*) iter->data;
-		unsubscribe_feed (sub, wrap, job);
+		grss_feeds_subscriber_handler_unsubscribe (wrap->handler, wrap->channel, wrap->identifier);
+		wrap->status = FEED_SUBSCRIPTION_UNSUBSCRIBING;
 	}
 
 	sub->priv->feeds_list = NULL;
@@ -682,6 +507,7 @@ static void
 stop_server (GrssFeedsSubscriber *sub)
 {
 	unsubscribe_feeds (sub);
+	close_server (sub);
 }
 
 /**
@@ -709,22 +535,6 @@ grss_feeds_subscriber_set_port (GrssFeedsSubscriber *sub, int port)
 }
 
 /**
- * grss_feeds_subscriber_set_hub:
- * @sub: a #GrssFeedsSubscriber
- * @hub: URL of the custom hub
- *
- * To customize the default hub to which send subscriptions. If this value is
- * set, hubs from specific feeds are ignored
- */
-void
-grss_feeds_subscriber_set_hub (GrssFeedsSubscriber *sub, gchar *hub)
-{
-	FREE_STRING (sub->priv->hub);
-	if (hub != NULL)
-		sub->priv->hub = g_strdup (hub);
-}
-
-/**
  * grss_feeds_subscriber_switch:
  * @sub: a #GrssFeedsSubscriber
  * @run: TRUE to run the subscriber, FALSE to pause it
@@ -743,3 +553,51 @@ grss_feeds_subscriber_switch (GrssFeedsSubscriber *sub, gboolean run)
 			stop_server (sub);
 	}
 }
+
+/**
+ * grss_feeds_subscriber_get_address:
+ * @sub: a #GrssFeedsSubscriber
+ *
+ * This function returns the Internet address where @sub is listening for
+ * external events. It is often required by #GrssFeedsSubscriberHandlers while
+ * subscribing contents to specify the local endpoint for communications
+ *
+ * Return value: the #GInetAddress used by @sub, or %NULL if the
+ * #GrssFeedsSubscriber is switched off
+ */
+GInetAddress*
+grss_feeds_subscriber_get_address (GrssFeedsSubscriber *sub)
+{
+	return sub->priv->exposed_addr;
+}
+
+/**
+ * grss_feeds_subscriber_get_port:
+ * @sub: a #GrssFeedsSubscriber
+ *
+ * This function returns the Internet port where @sub is listening for
+ * external events. It is often required by #GrssFeedsSubscriberHandlers while
+ * subscribing contents to specify the local endpoint for communications
+ * 
+ * Return value: the port of the socket locally opened by @sub
+ */
+int
+grss_feeds_subscriber_get_port (GrssFeedsSubscriber *sub)
+{
+	return sub->priv->port;
+}
+
+/**
+ * grss_feeds_subscriber_get_session:
+ * @sub: a #GrssFeedsSubscriber
+ *
+ * To obtain the internal #SoupSession of a #GrssFeedsSubscriber, so to re-use
+ * it in #GrssFeedsSubscriberHandlers or similar tasks
+ * 
+ * Return value: the #SoupSession used by the provided #GrssFeedsSubscriber
+ */
+SoupSession*
+grss_feeds_subscriber_get_session (GrssFeedsSubscriber *sub)
+{
+	return sub->priv->soupsession;
+}
diff --git a/src/feeds-subscriber.h b/src/feeds-subscriber.h
index bbb39c1..161ee06 100644
--- a/src/feeds-subscriber.h
+++ b/src/feeds-subscriber.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Roberto Guido <rguido src gnome org>
+ * Copyright (C) 2011, Roberto Guido <rguido src gnome org>
  *                     Michele Tameni <michele amdplanet it>
  *
  * This library is free software; you can redistribute it and/or
@@ -51,7 +51,9 @@ GrssFeedsSubscriber*	grss_feeds_subscriber_new		();
 gboolean		grss_feeds_subscriber_listen		(GrssFeedsSubscriber *sub, GList *feeds);
 GList*			grss_feeds_subscriber_get_listened	(GrssFeedsSubscriber *sub);
 void			grss_feeds_subscriber_set_port		(GrssFeedsSubscriber *sub, int port);
-void			grss_feeds_subscriber_set_hub		(GrssFeedsSubscriber *sub, gchar *hub);
 void			grss_feeds_subscriber_switch		(GrssFeedsSubscriber *sub, gboolean run);
+GInetAddress*		grss_feeds_subscriber_get_address	(GrssFeedsSubscriber *sub);
+int			grss_feeds_subscriber_get_port		(GrssFeedsSubscriber *sub);
+SoupSession*		grss_feeds_subscriber_get_session	(GrssFeedsSubscriber *sub);
 
 #endif /* __FEEDS_SUBSCRIBER_H__ */
diff --git a/src/feeds-xbel-group-handler.c b/src/feeds-xbel-group-handler.c
index ba65d7b..1a57dee 100644
--- a/src/feeds-xbel-group-handler.c
+++ b/src/feeds-xbel-group-handler.c
@@ -25,13 +25,6 @@
 
 #define FEEDS_XBEL_GROUP_HANDLER_GET_PRIVATE(o)	(G_TYPE_INSTANCE_GET_PRIVATE ((o), FEEDS_XBEL_GROUP_HANDLER_TYPE, FeedsXbelGroupHandlerPrivate))
 
-/**
- * SECTION: feeds-xbel-group-handler
- * @short_description: specialized parser for XBEL files
- *
- * #FeedsXbelGroupHandler is a #GrssFeedsGroupHandler specialized for XBEL contents
- */
-
 struct FeedsXbelGroupHandlerPrivate {
 	int	rfu;
 };
@@ -77,7 +70,7 @@ feeds_xbel_group_handler_parse (GrssFeedsGroupHandler *self, xmlDocPtr doc, GErr
 
 	xpathCtx = xmlXPathNewContext (doc);
 
-	/**
+	/*
 		TODO	This XPath query may be improved to check only "bookmark" tags into the main "xbel"
 	*/
 	xpathObj = xmlXPathEvalExpression (BAD_CAST"//bookmark", xpathCtx);
diff --git a/src/feeds-xoxo-group-handler.c b/src/feeds-xoxo-group-handler.c
index c95add0..14fae46 100644
--- a/src/feeds-xoxo-group-handler.c
+++ b/src/feeds-xoxo-group-handler.c
@@ -25,13 +25,6 @@
 
 #define FEEDS_XOXO_GROUP_HANDLER_GET_PRIVATE(o)	(G_TYPE_INSTANCE_GET_PRIVATE ((o), FEEDS_XOXO_GROUP_HANDLER_TYPE, FeedsXoxoGroupHandlerPrivate))
 
-/**
- * SECTION: feeds-xoxo-group-handler
- * @short_description: specialized parser for XOXO files
- *
- * #FeedsXoxoGroupHandler is a #GrssFeedsGroupHandler specialized for XOXO contents
- */
-
 struct FeedsXoxoGroupHandlerPrivate {
 	int	rfu;
 };
@@ -94,7 +87,7 @@ feeds_xoxo_group_handler_parse (GrssFeedsGroupHandler *self, xmlDocPtr doc, GErr
 	xpathCtx = xmlXPathNewContext (doc);
 	xmlXPathRegisterNs (xpathCtx, BAD_CAST"xhtml", BAD_CAST"http://www.w3.org/1999/xhtml";);
 
-	/**
+	/*
 		TODO	This XPath query may be improved to check only "a" tags into the main "ol"
 	*/
 	xpathObj = xmlXPathEvalExpression (BAD_CAST"//xhtml:a[ type='webfeed']", xpathCtx);
diff --git a/src/ns-handler.c b/src/ns-handler.c
index ec9c6d5..74bb9a3 100644
--- a/src/ns-handler.c
+++ b/src/ns-handler.c
@@ -23,17 +23,6 @@
 
 #define NS_HANDLER_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NS_HANDLER_TYPE, NSHandlerPrivate))
 
-/**
- * SECTION: ns-handler
- * @short_description: namespaces handler
- *
- * The #NSHandler object is a special extension used by #FeedHandlers to
- * handle more tags in feeds. When unknow XML elements are found they are
- * filtered by the #NSHandler, which check if that rappresent a valid
- * extension and populates the specified #GrssFeedChannel (or #GrssFeedItem) with
- * more attributes
- */
-
 struct _NSHandlerPrivate {
 	GHashTable	*href_handlers;
 	GHashTable	*prefix_handlers;
@@ -463,10 +452,6 @@ ns_wfw_item (GrssFeedItem *item, xmlNodePtr cur)
 static gboolean
 ns_atom10_channel (GrssFeedChannel *feed, xmlNodePtr cur)
 {
-	/*
-		Used to manage PubSubHubbub information in RSS feeds
-	*/
-
 	gchar *href;
 	gchar *relation;
 
@@ -477,9 +462,9 @@ ns_atom10_channel (GrssFeedChannel *feed, xmlNodePtr cur)
 			href = (gchar*) xmlGetNsProp (cur, BAD_CAST "href", NULL);
 
 			if (strcmp (relation, "self") == 0)
-				grss_feed_channel_set_pubsubhub (feed, NULL, href);
+				grss_feed_channel_set_source (feed, href);
 			else if (strcmp (relation, "hub") == 0)
-				grss_feed_channel_set_pubsubhub (feed, href, NULL);
+				grss_feed_channel_set_pubsubhub (feed, href);
 
 			g_free (relation);
 			g_free (href);
@@ -616,13 +601,6 @@ ns_handler_init (NSHandler *node)
 	g_hash_table_insert (node->priv->href_handlers, "http://www.w3.org/2005/Atom";, nsh);
 }
 
-/**
- * ns_handler_new:
- *
- * Allocates a new #NSHandler
- *
- * Return value: a new #NSHandler
- */
 NSHandler*
 ns_handler_new ()
 {
@@ -645,17 +623,6 @@ retrieve_internal_handler (NSHandler *handler, xmlNodePtr cur)
 	return nsh;
 }
 
-/**
- * ns_handler_channel:
- * @handler: a #NSHandler
- * @feed: channel to which assign eventual values
- * @cur: XML tag to be analyzed
- *
- * Check a given tag for extended namespaces values
- *
- * Return value: %TRUE if a value has been assigned by the @handler, %FALSE
- * otherwise
- */
 gboolean
 ns_handler_channel (NSHandler *handler, GrssFeedChannel *feed, xmlNodePtr cur)
 {
@@ -669,17 +636,6 @@ ns_handler_channel (NSHandler *handler, GrssFeedChannel *feed, xmlNodePtr cur)
 		return FALSE;
 }
 
-/**
- * ns_handler_item:
- * @handler: a #NSHandler
- * @item: item to which assign eventual values
- * @cur: XML tag to be analyzed
- *
- * Check a given tag for extended namespaces values
- *
- * Return value: %TRUE if a value has been assigned by the @handler, %FALSE
- * otherwise
- */
 gboolean
 ns_handler_item (NSHandler *handler, GrssFeedItem *item, xmlNodePtr cur)
 {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]