53393f53e84aec7cfbb7fc8ca0e7a86afeab9e1c
[monky] / src / prss.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
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  * vim: ts=4 sw=4 noet ai cindent syntax=c
19  *
20  */
21
22 #include "conky.h"
23 #include "prss.h"
24 #include "logging.h"
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27
28 #ifndef PARSE_OPTIONS
29 #define PARSE_OPTIONS 0
30 #endif
31
32 void prss_parse_doc(PRSS *result, xmlDocPtr doc);
33
34 void prss_parse_data(void *result, const char *xml_data)
35 {
36         PRSS *data = (PRSS*)result;
37
38         xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL,
39                 PARSE_OPTIONS);
40
41         if (!doc) {
42                 return;
43         }
44
45         prss_parse_doc(data, doc);
46         xmlFreeDoc(doc);
47 }
48
49 void free_rss_items(PRSS *data)
50 {
51         for (int i = 0; i < data->item_count; i++) {
52 #define CLEAR(a) if (data->items[i].a) { free(data->items[i].a); data->items[i].a = 0; }
53                 CLEAR(title);
54                 CLEAR(link);
55                 CLEAR(description);
56                 CLEAR(category);
57                 CLEAR(pubDate);
58                 CLEAR(guid);
59 #undef CLEAR
60         }
61         free(data->items);
62         data->item_count = 0;
63         data->items = 0;
64 }
65
66 void prss_free(PRSS *data)
67 {
68         if (!data) {
69                 return;
70         }
71         if (data->version) {
72                 free(data->version);
73                 data->version = 0;
74         }
75         if (data->items) {
76                 free_rss_items(data);
77         }
78         data->version = 0;
79         data->items = 0;
80 #define CLEAR(a) if (data->a) { free(data->a); data->a = 0; }
81         CLEAR(title);
82         CLEAR(link);
83         CLEAR(description);
84         CLEAR(language);
85         CLEAR(pubDate);
86         CLEAR(lastBuildDate);
87         CLEAR(generator);
88         CLEAR(docs);
89         CLEAR(managingEditor);
90         CLEAR(webMaster);
91         CLEAR(copyright);
92         CLEAR(ttl);
93 #undef CLEAR
94 }
95
96 static inline void prss_null_item(PRSS_Item *i)
97 {
98         memset(i, 0, sizeof(PRSS_Item));
99 }
100
101 static inline void read_item(PRSS_Item *res, xmlNodePtr data)
102 {
103         prss_null_item(res);
104
105         for (; data; data = data->next) {
106                 xmlNodePtr child;
107
108                 if (data->type != XML_ELEMENT_NODE) {
109                         continue;
110                 }
111                 child = data->children;
112
113                 if (!child) {
114                         continue;
115                 }
116
117 #define ASSIGN(a) if (strcasecmp((const char*)data->name, #a) == EQUAL) { \
118                         if (res->a) free(res->a); \
119                         res->a = strdup((const char*)child->content); \
120                         continue; \
121                 }
122                 ASSIGN(title);
123                 ASSIGN(link);
124                 ASSIGN(description);
125                 ASSIGN(category);
126                 ASSIGN(pubDate);
127                 ASSIGN(guid);
128 #undef ASSIGN
129         }
130 }
131 static inline void read_element(PRSS *res, xmlNodePtr n)
132 {
133         xmlNodePtr child;
134
135         if (n->type != XML_ELEMENT_NODE) {
136                 return;
137         }
138         child = n->children;
139
140         if (!child) {
141                 return;
142         }
143
144 #define ASSIGN(a) if (strcasecmp((const char*)n->name, #a) == EQUAL) { \
145                 if (res->a) free(res->a); \
146                 res->a = strdup((const char*)child->content); \
147                 return; \
148         }
149         ASSIGN(title);
150         ASSIGN(link);
151         ASSIGN(description);
152         ASSIGN(language);
153         ASSIGN(pubDate);
154         ASSIGN(lastBuildDate);
155         ASSIGN(generator);
156         ASSIGN(docs);
157         ASSIGN(managingEditor);
158         ASSIGN(webMaster);
159         ASSIGN(copyright);
160         ASSIGN(ttl);
161 #undef ASSIGN
162         if (!strcasecmp((const char*)n->name, "item")) {
163                 read_item(&res->items[res->item_count++], n->children);
164         }
165 }
166
167 static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root)
168 {
169         xmlNodePtr channel = root->children;
170         xmlNodePtr n;
171         int items = 0;
172
173         DBGP("parsing rss 2.0 or <1 doc");
174
175         while (channel && (channel->type != XML_ELEMENT_NODE
176                                 || strcmp((const char *) channel->name, "channel"))) {
177                 channel = channel->next;
178         }
179         if (!channel) {
180                 return 0;
181         }
182
183         for (n = channel->children; n; n = n->next) {
184                 if (n->type == XML_ELEMENT_NODE &&
185                                 !strcmp((const char *) n->name, "item")) {
186                         ++items;
187                 }
188         }
189
190         if (res->version) free(res->version);
191         res->version = strndup("2.0", text_buffer_size);
192         if (res->items) free_rss_items(res);
193         res->items = malloc(items * sizeof(PRSS_Item));
194         res->item_count = 0;
195
196         for (n = channel->children; n; n = n->next) {
197                 read_element(res, n);
198         }
199
200         return 1;
201 }
202 static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root)
203 {
204         int items = 0;
205         xmlNodePtr n;
206
207         DBGP("parsing rss 1.0 doc");
208
209         for (n = root->children; n; n = n->next) {
210                 if (n->type == XML_ELEMENT_NODE) {
211                         if (!strcmp((const char *) n->name, "item")) {
212                                 ++items;
213                         } else if (!strcmp((const char *) n->name, "channel")) {
214                                 xmlNodePtr i;
215
216                                 for (i = n->children; i; i = i->next) {
217                                         read_element(res, i);
218                                 }
219                         }
220                 }
221         }
222
223         if (res->version) free(res->version);
224         res->version = strndup("1.0", text_buffer_size);
225         if (res->items) free_rss_items(res);
226         res->items = malloc(items * sizeof(PRSS_Item));
227         res->item_count = 0;
228
229         for (n = root->children; n; n = n->next) {
230                 if (n->type == XML_ELEMENT_NODE &&
231                                 !strcmp((const char *) n->name, "item")) {
232                         read_item(&res->items[res->item_count++], n->children);
233                 }
234         }
235
236         return 1;
237 }
238
239 void prss_parse_doc(PRSS *result, xmlDocPtr doc)
240 {
241         xmlNodePtr root = xmlDocGetRootElement(doc);
242
243         do {
244                 if (root->type == XML_ELEMENT_NODE) {
245                         if (!strcmp((const char *) root->name, "RDF")) {
246                                 // RSS 1.0 document
247                                 parse_rss_1_0(result, root);
248                                 return;
249                         } else if (!strcmp((const char *) root->name, "rss")) {
250                                 // RSS 2.0 or <1.0 document
251                                 parse_rss_2_0(result, root);
252                                 return;
253                         }
254                 }
255                 root = root->next;
256         } while (root);
257         return;
258 }