Fixed compilation issues with --enable-testing
[monky] / src / prss.c
1 /* $Id$
2  *
3  * Copyright (c) 2007 Mikko Sysikaski <mikko.sysikaski@gmail.com>
4  *                                        Toni Spets <toni.spets@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
17
18 #include "conky.h"
19 #include "prss.h"
20 #include <libxml/parser.h>
21 #include <libxml/tree.h>
22
23 #ifndef PARSE_OPTIONS
24 #define PARSE_OPTIONS 0
25 #endif
26
27 PRSS *prss_parse_doc(xmlDocPtr doc);
28
29 PRSS *prss_parse_data(const char *xml_data)
30 {
31         xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL,
32                 PARSE_OPTIONS);
33
34         if (!doc) {
35                 return NULL;
36         }
37
38         return prss_parse_doc(doc);
39 }
40
41 PRSS *prss_parse_file(const char *xml_file)
42 {
43         xmlDocPtr doc = xmlReadFile(xml_file, NULL, PARSE_OPTIONS);
44
45         if (!doc) {
46                 return NULL;
47         }
48
49         return prss_parse_doc(doc);
50 }
51
52 void prss_free(PRSS *data)
53 {
54         if (!data) {
55                 return;
56         }
57         xmlFreeDoc(data->_data);
58         free(data->version);
59         free(data->items);
60         free(data);
61 }
62
63 static inline void prss_null(PRSS *p)
64 {
65         memset(p, 0, sizeof(PRSS));
66 }
67 static inline void prss_null_item(PRSS_Item *i)
68 {
69         memset(i, 0, sizeof(PRSS_Item));
70 }
71
72 static inline void read_item(PRSS_Item *res, xmlNodePtr data)
73 {
74         prss_null_item(res);
75
76         res->title = res->link = res->description = NULL;
77         for (; data; data = data->next) {
78                 xmlNodePtr child;
79                 const char *name;
80
81                 if (data->type != XML_ELEMENT_NODE) {
82                         continue;
83                 }
84                 child = data->children;
85
86                 if (!child) {
87                         continue;
88                 }
89
90                 name = (const char *)data->name;
91                 if (!strcasecmp(name, "title")) {
92                         res->title = (char *) child->content;
93                 } else if (!strcasecmp(name, "link")) {
94                         res->link = (char *) child->content;
95                 } else if (!strcasecmp(name, "description")) {
96                         res->description = (char *) child->content;
97                 } else if (!strcasecmp(name, "category")) {
98                         res->category = (char *) child->content;
99                 } else if (!strcasecmp(name, "pubDate")) {
100                         res->pubdate = (char *) child->content;
101                 } else if (!strcasecmp(name, "guid")) {
102                         res->guid = (char *) child->content;
103                 }
104         }
105 }
106 static inline void read_element(PRSS *res, xmlNodePtr n)
107 {
108         xmlNodePtr child;
109         const char *name;
110
111         if (n->type != XML_ELEMENT_NODE) {
112                 return;
113         }
114         child = n->children;
115
116         if (!child) {
117                 return;
118         }
119
120         name = (const char *)n->name;
121         if (!strcasecmp(name, "title")) {
122                 res->title = (char *) child->content;
123         } else if (!strcasecmp(name, "link")) {
124                 res->link = (char *) child->content;
125         } else if (!strcasecmp(name, "description")) {
126                 res->description = (char *) child->content;
127         } else if (!strcasecmp(name, "language")) {
128                 res->language = (char *) child->content;
129         } else if (!strcasecmp(name, "pubDate")) {
130                 res->pubdate = (char *) child->content;
131         } else if (!strcasecmp(name, "lastBuildDate")) {
132                 res->lastbuilddate = (char *) child->content;
133         } else if (!strcasecmp(name, "generator")) {
134                 res->generator = (char *) child->content;
135         } else if (!strcasecmp(name, "docs")) {
136                 res->docs = (char *) child->content;
137         } else if (!strcasecmp(name, "managingEditor")) {
138                 res->managingeditor = (char *) child->content;
139         } else if (!strcasecmp(name, "webMaster")) {
140                 res->webmaster = (char *) child->content;
141         } else if (!strcasecmp(name, "copyright")) {
142                 res->copyright = (char *) child->content;
143         } else if (!strcasecmp(name, "ttl")) {
144                 res->ttl = (char *) child->content;
145         } else if (!strcasecmp(name, "item")) {
146                 read_item(&res->items[res->item_count++], n->children);
147         }
148 }
149
150 static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root)
151 {
152         xmlNodePtr channel = root->children;
153         xmlNodePtr n;
154         int items = 0;
155
156         while (channel && (channel->type != XML_ELEMENT_NODE
157                         || strcmp((const char *) channel->name, "channel"))) {
158                 channel = channel->next;
159         }
160         if (!channel) {
161                 return 0;
162         }
163
164         for (n = channel->children; n; n = n->next) {
165                 if (n->type == XML_ELEMENT_NODE &&
166                                 !strcmp((const char *) n->name, "item")) {
167                         ++items;
168                 }
169         }
170
171         res->version = strndup("2.0", text_buffer_size);
172         res->items = malloc(items * sizeof(PRSS_Item));
173         res->item_count = 0;
174
175         for (n = channel->children; n; n = n->next) {
176                 read_element(res, n);
177         }
178
179         return 1;
180 }
181 static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root)
182 {
183         int items = 0;
184         xmlNodePtr n;
185
186         for (n = root->children; n; n = n->next) {
187                 if (n->type == XML_ELEMENT_NODE) {
188                         if (!strcmp((const char *) n->name, "item")) {
189                                 ++items;
190                         } else if (!strcmp((const char *) n->name, "channel")) {
191                                 xmlNodePtr i;
192
193                                 for (i = n->children; i; i = i->next) {
194                                         read_element(res, i);
195                                 }
196                         }
197                 }
198         }
199
200         res->version = strndup("1.0", text_buffer_size);
201         res->items = malloc(items * sizeof(PRSS_Item));
202         res->item_count = 0;
203
204         for (n = root->children; n; n = n->next) {
205                 if (n->type == XML_ELEMENT_NODE &&
206                                 !strcmp((const char *) n->name, "item")) {
207                         read_item(&res->items[res->item_count++], n->children);
208                 }
209         }
210
211         return 1;
212 }
213 static inline int parse_rss_0_9x(PRSS *res, xmlNodePtr root)
214 {
215         // almost same...
216         return parse_rss_2_0(res, root);
217 }
218
219 PRSS *prss_parse_doc(xmlDocPtr doc)
220 {
221         /* FIXME: doc shouldn't be freed after failure when called explicitly from
222          * program! */
223
224         xmlNodePtr root = xmlDocGetRootElement(doc);
225         PRSS *result = malloc(sizeof(PRSS));
226
227         prss_null(result);
228         result->_data = doc;
229         do {
230                 if (root->type == XML_ELEMENT_NODE) {
231                         if (!strcmp((const char *) root->name, "RDF")) {
232                                 // RSS 1.0 document
233                                 if (!parse_rss_1_0(result, root)) {
234                                         free(result);
235                                         xmlFreeDoc(doc);
236                                         return NULL;
237                                 }
238                                 return result;
239                         } else if (!strcmp((const char *) root->name, "rss")) {
240                                 // RSS 2.0 or <1.0 document
241                                 if (!parse_rss_2_0(result, root)) {
242                                         free(result);
243                                         xmlFreeDoc(doc);
244                                         return NULL;
245                                 }
246                                 return result;
247                         }
248                 }
249                 root = root->next;
250         } while (root);
251         free(result);
252         return NULL;
253 }