1 /*
2 * Copyright (C) 2010-2015, Roberto Guido <rguido@src.gnome.org>
3 * Michele Tameni <michele@amdplanet.it>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /*
22 * Original code is from Liferea:
23 *
24 * opml_source.c OPML Planet/Blogroll feed list source
25 *
26 * Copyright (C) 2006-2010 Lars Lindner <lars.lindner@gmail.com>
27 */
28
29 #include "feeds-group-handler.h"
30 #include "feeds-opml-group-handler.h"
31 #include "utils.h"
32 #include "feed-channel.h"
33
34 #define FEEDS_OPML_GROUP_HANDLER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), FEEDS_OPML_GROUP_HANDLER_TYPE, FeedsOpmlGroupHandlerPrivate))
35
36 struct FeedsOpmlGroupHandlerPrivate {
37 int rfu;
38 };
39
40 static void grss_feeds_group_handler_interface_init (GrssFeedsGroupHandlerInterface *iface);
41 G_DEFINE_TYPE_WITH_CODE (FeedsOpmlGroupHandler, feeds_opml_group_handler, G_TYPE_OBJECT,
42 G_IMPLEMENT_INTERFACE (FEEDS_GROUP_HANDLER_TYPE,
43 grss_feeds_group_handler_interface_init));
44
45 static void
feeds_opml_group_handler_finalize(GObject * object)46 feeds_opml_group_handler_finalize (GObject *object)
47 {
48 G_OBJECT_CLASS (feeds_opml_group_handler_parent_class)->finalize (object);
49 }
50
51 static const gchar*
feeds_opml_group_handler_get_name(GrssFeedsGroupHandler * self)52 feeds_opml_group_handler_get_name (GrssFeedsGroupHandler *self)
53 {
54 return "OPML";
55 }
56
57 static gboolean
feeds_opml_group_handler_check_format(GrssFeedsGroupHandler * self,xmlDocPtr doc,xmlNodePtr cur)58 feeds_opml_group_handler_check_format (GrssFeedsGroupHandler *self, xmlDocPtr doc, xmlNodePtr cur)
59 {
60 if (!xmlStrcmp (cur->name, BAD_CAST"opml"))
61 return TRUE;
62 else
63 return FALSE;
64 }
65
66 static xmlChar*
get_source_url(xmlNodePtr cur)67 get_source_url (xmlNodePtr cur)
68 {
69 xmlChar *tmp;
70
71 tmp = xmlGetProp (cur, BAD_CAST "xmlUrl");
72 if (!tmp)
73 tmp = xmlGetProp (cur, BAD_CAST "xmlurl"); /* e.g. for AmphetaDesk */
74 if (!tmp)
75 tmp = xmlGetProp (cur, BAD_CAST"xmlURL"); /* e.g. for LiveJournal */
76
77 return tmp;
78 }
79
80 static GrssFeedChannel*
import_parse_outline(xmlNodePtr cur)81 import_parse_outline (xmlNodePtr cur)
82 {
83 xmlChar *tmp;
84 GrssFeedChannel *channel;
85
86 channel = grss_feed_channel_new ();
87
88 tmp = xmlGetProp (cur, BAD_CAST"title");
89 if (!tmp || !xmlStrcmp (tmp, BAD_CAST"")) {
90 if (tmp)
91 xmlFree (tmp);
92 tmp = xmlGetProp (cur, BAD_CAST"text");
93 }
94
95 if (tmp) {
96 grss_feed_channel_set_title (channel, (gchar*) tmp);
97 xmlFree (tmp);
98 }
99
100 tmp = get_source_url (cur);
101
102 if (tmp) {
103 grss_feed_channel_set_source (channel, (gchar*) tmp);
104 xmlFree (tmp);
105
106 tmp = xmlGetProp (cur, BAD_CAST"htmlUrl");
107 if (tmp && xmlStrcmp (tmp, BAD_CAST""))
108 grss_feed_channel_set_homepage (channel, (gchar*) tmp);
109 xmlFree (tmp);
110 }
111
112 return channel;
113 }
114
115 static GList*
import_parse_body(xmlNodePtr n)116 import_parse_body (xmlNodePtr n)
117 {
118 xmlChar *type;
119 xmlChar *tmp;
120 GList *items;
121 GList *subitems;
122 GrssFeedChannel *outline;
123 xmlNodePtr cur;
124
125 cur = n->xmlChildrenNode;
126 items = NULL;
127
128 while (cur) {
129 if (!xmlStrcmp (cur->name, BAD_CAST"outline")) {
130 outline = NULL;
131 subitems = NULL;
132 type = xmlGetProp (cur, BAD_CAST"type");
133
134 if (type) {
135 if (xmlStrcasecmp (type, BAD_CAST"rss") == 0 || xmlStrcasecmp (type, BAD_CAST"atom") == 0)
136 outline = import_parse_outline (cur);
137 else if (xmlStrcasecmp (type, BAD_CAST"folder") == 0)
138 subitems = import_parse_body (cur);
139
140 xmlFree (type);
141 }
142 else {
143 /* if we didn't find a type attribute we use heuristics */
144
145 tmp = get_source_url (cur);
146
147 if (tmp) {
148 outline = import_parse_outline (cur);
149 xmlFree (tmp);
150 }
151 else {
152 subitems = import_parse_body (cur);
153 }
154 }
155
156 if (outline != NULL)
157 items = g_list_prepend (items, outline);
158 else if (subitems != NULL)
159 items = g_list_concat (items, subitems);
160 }
161
162 cur = cur->next;
163 }
164
165 return items;
166 }
167
168 static GList*
import_parse_OPML(xmlNodePtr n)169 import_parse_OPML (xmlNodePtr n)
170 {
171 GList *items;
172 xmlNodePtr cur;
173
174 cur = n->xmlChildrenNode;
175 items = NULL;
176
177 while (cur) {
178 if (!xmlStrcmp (cur->name, BAD_CAST"body")) {
179 items = import_parse_body (cur);
180 break;
181 }
182
183 cur = cur->next;
184 }
185
186 return items;
187 }
188
189 static GList*
feeds_opml_group_handler_parse(GrssFeedsGroupHandler * self,xmlDocPtr doc,GError ** error)190 feeds_opml_group_handler_parse (GrssFeedsGroupHandler *self, xmlDocPtr doc, GError **error)
191 {
192 xmlNodePtr cur;
193 GList *items;
194
195 items = NULL;
196 cur = xmlDocGetRootElement (doc);
197
198 while (cur) {
199 if (!xmlIsBlankNode (cur))
200 if (!xmlStrcmp (cur->name, BAD_CAST"opml")) {
201 items = import_parse_OPML (cur);
202 break;
203 }
204
205 cur = cur->next;
206 }
207
208 if (items != NULL)
209 items = g_list_reverse (items);
210 return items;
211 }
212
213 static gchar*
feeds_opml_group_handler_dump(GrssFeedsGroupHandler * self,GList * channels,GError ** error)214 feeds_opml_group_handler_dump (GrssFeedsGroupHandler *self, GList *channels, GError **error)
215 {
216 int size;
217 xmlChar *ret;
218 xmlDocPtr doc;
219 xmlNodePtr cur;
220 xmlNodePtr opmlNode;
221 xmlNodePtr childNode;
222 GList *iter;
223 GrssFeedChannel *channel;
224
225 doc = xmlNewDoc (BAD_CAST"1.0");
226
227 opmlNode = xmlNewDocNode (doc, NULL, BAD_CAST"opml", NULL);
228 xmlNewProp (opmlNode, BAD_CAST"version", BAD_CAST"1.0");
229 xmlNewChild (opmlNode, NULL, BAD_CAST"head", NULL);
230
231 cur = xmlNewChild (opmlNode, NULL, BAD_CAST"body", NULL);
232 if (cur) {
233 for (iter = channels; iter; iter = g_list_next (iter)) {
234 channel = (GrssFeedChannel*) iter->data;
235 childNode = xmlNewChild (cur, NULL, BAD_CAST"outline", NULL);
236 xmlNewProp (childNode, BAD_CAST"text", BAD_CAST grss_feed_channel_get_title (channel));
237 xmlNewProp (childNode, BAD_CAST"type", BAD_CAST"rss");
238 xmlNewProp (childNode, BAD_CAST"xmlUrl", BAD_CAST grss_feed_channel_get_source (channel));
239 }
240 }
241
242 xmlDocSetRootElement (doc, opmlNode);
243 xmlDocDumpFormatMemoryEnc (doc, &ret, &size, "utf-8", 1);
244 xmlFreeDoc (doc);
245
246 return (gchar*) ret;
247 }
248
249 static void
grss_feeds_group_handler_interface_init(GrssFeedsGroupHandlerInterface * iface)250 grss_feeds_group_handler_interface_init (GrssFeedsGroupHandlerInterface *iface)
251 {
252 iface->get_name = feeds_opml_group_handler_get_name;
253 iface->check_format = feeds_opml_group_handler_check_format;
254 iface->parse = feeds_opml_group_handler_parse;
255 iface->dump = feeds_opml_group_handler_dump;
256 }
257
258 static void
feeds_opml_group_handler_class_init(FeedsOpmlGroupHandlerClass * klass)259 feeds_opml_group_handler_class_init (FeedsOpmlGroupHandlerClass *klass)
260 {
261 GObjectClass *object_class = G_OBJECT_CLASS (klass);
262
263 g_type_class_add_private (object_class, sizeof (FeedsOpmlGroupHandlerPrivate));
264 object_class->finalize = feeds_opml_group_handler_finalize;
265 }
266
267 static void
feeds_opml_group_handler_init(FeedsOpmlGroupHandler * object)268 feeds_opml_group_handler_init (FeedsOpmlGroupHandler *object)
269 {
270 object->priv = FEEDS_OPML_GROUP_HANDLER_GET_PRIVATE (object);
271 }
272
273 FeedsOpmlGroupHandler*
feeds_opml_group_handler_new()274 feeds_opml_group_handler_new ()
275 {
276 FeedsOpmlGroupHandler *parser;
277
278 parser = g_object_new (FEEDS_OPML_GROUP_HANDLER_TYPE, NULL);
279 return parser;
280 }
281