Overhauled RSS and weather, added $curl.
[monky] / src / prss.c
1 /*
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 void prss_parse_doc(PRSS *result, xmlDocPtr doc);
28
29 void prss_parse_data(void *result, const char *xml_data)
30 {
31         PRSS *data = (PRSS*)result;
32         xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL,
33                 PARSE_OPTIONS);
34
35         if (!doc) {
36                 return;
37         }
38
39         prss_parse_doc(data, doc);
40 }
41
42 void prss_free(PRSS *data)
43 {
44         if (!data) {
45                 return;
46         }
47         xmlFreeDoc(data->_data);
48         free(data->version);
49         free(data->items);
50 }
51
52 static inline void prss_null(PRSS *p)
53 {
54         memset(p, 0, sizeof(PRSS));
55 }
56 static inline void prss_null_item(PRSS_Item *i)
57 {
58         memset(i, 0, sizeof(PRSS_Item));
59 }
60
61 static inline void read_item(PRSS_Item *res, xmlNodePtr data)
62 {
63         prss_null_item(res);
64
65         res->title = res->link = res->description = NULL;
66         for (; data; data = data->next) {
67                 xmlNodePtr child;
68                 const char *name;
69
70                 if (data->type != XML_ELEMENT_NODE) {
71                         continue;
72                 }
73                 child = data->children;
74
75                 if (!child) {
76                         continue;
77                 }
78
79                 name = (const char *)data->name;
80                 if (!strcasecmp(name, "title")) {
81                         res->title = (char *) child->content;
82                 } else if (!strcasecmp(name, "link")) {
83                         res->link = (char *) child->content;
84                 } else if (!strcasecmp(name, "description")) {
85                         res->description = (char *) child->content;
86                 } else if (!strcasecmp(name, "category")) {
87                         res->category = (char *) child->content;
88                 } else if (!strcasecmp(name, "pubDate")) {
89                         res->pubdate = (char *) child->content;
90                 } else if (!strcasecmp(name, "guid")) {
91                         res->guid = (char *) child->content;
92                 }
93         }
94 }
95 static inline void read_element(PRSS *res, xmlNodePtr n)
96 {
97         xmlNodePtr child;
98         const char *name;
99
100         if (n->type != XML_ELEMENT_NODE) {
101                 return;
102         }
103         child = n->children;
104
105         if (!child) {
106                 return;
107         }
108
109         name = (const char *)n->name;
110         if (!strcasecmp(name, "title")) {
111                 res->title = (char *) child->content;
112         } else if (!strcasecmp(name, "link")) {
113                 res->link = (char *) child->content;
114         } else if (!strcasecmp(name, "description")) {
115                 res->description = (char *) child->content;
116         } else if (!strcasecmp(name, "language")) {
117                 res->language = (char *) child->content;
118         } else if (!strcasecmp(name, "pubDate")) {
119                 res->pubdate = (char *) child->content;
120         } else if (!strcasecmp(name, "lastBuildDate")) {
121                 res->lastbuilddate = (char *) child->content;
122         } else if (!strcasecmp(name, "generator")) {
123                 res->generator = (char *) child->content;
124         } else if (!strcasecmp(name, "docs")) {
125                 res->docs = (char *) child->content;
126         } else if (!strcasecmp(name, "managingEditor")) {
127                 res->managingeditor = (char *) child->content;
128         } else if (!strcasecmp(name, "webMaster")) {
129                 res->webmaster = (char *) child->content;
130         } else if (!strcasecmp(name, "copyright")) {
131                 res->copyright = (char *) child->content;
132         } else if (!strcasecmp(name, "ttl")) {
133                 res->ttl = (char *) child->content;
134         } else if (!strcasecmp(name, "item")) {
135                 read_item(&res->items[res->item_count++], n->children);
136         }
137 }
138
139 static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root)
140 {
141         xmlNodePtr channel = root->children;
142         xmlNodePtr n;
143         int items = 0;
144
145         while (channel && (channel->type != XML_ELEMENT_NODE
146                         || strcmp((const char *) channel->name, "channel"))) {
147                 channel = channel->next;
148         }
149         if (!channel) {
150                 return 0;
151         }
152
153         for (n = channel->children; n; n = n->next) {
154                 if (n->type == XML_ELEMENT_NODE &&
155                                 !strcmp((const char *) n->name, "item")) {
156                         ++items;
157                 }
158         }
159
160         res->version = strndup("2.0", text_buffer_size);
161         if (res->items) free(res->items);
162         res->items = malloc(items * sizeof(PRSS_Item));
163         res->item_count = 0;
164
165         for (n = channel->children; n; n = n->next) {
166                 read_element(res, n);
167         }
168
169         return 1;
170 }
171 static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root)
172 {
173         int items = 0;
174         xmlNodePtr n;
175
176         for (n = root->children; n; n = n->next) {
177                 if (n->type == XML_ELEMENT_NODE) {
178                         if (!strcmp((const char *) n->name, "item")) {
179                                 ++items;
180                         } else if (!strcmp((const char *) n->name, "channel")) {
181                                 xmlNodePtr i;
182
183                                 for (i = n->children; i; i = i->next) {
184                                         read_element(res, i);
185                                 }
186                         }
187                 }
188         }
189
190         res->version = strndup("1.0", text_buffer_size);
191         if (res->items) free(res->items);
192         res->items = malloc(items * sizeof(PRSS_Item));
193         res->item_count = 0;
194
195         for (n = root->children; n; n = n->next) {
196                 if (n->type == XML_ELEMENT_NODE &&
197                                 !strcmp((const char *) n->name, "item")) {
198                         read_item(&res->items[res->item_count++], n->children);
199                 }
200         }
201
202         return 1;
203 }
204 static inline int parse_rss_0_9x(PRSS *res, xmlNodePtr root)
205 {
206         // almost same...
207         return parse_rss_2_0(res, root);
208 }
209
210 void prss_parse_doc(PRSS *result, xmlDocPtr doc)
211 {
212         /* FIXME: doc shouldn't be freed after failure when called explicitly from
213          * program! */
214
215         xmlNodePtr root = xmlDocGetRootElement(doc);
216
217         prss_null(result);
218         result->_data = doc;
219         do {
220                 if (root->type == XML_ELEMENT_NODE) {
221                         if (!strcmp((const char *) root->name, "RDF")) {
222                                 // RSS 1.0 document
223                                 parse_rss_1_0(result, root);
224                                 return;
225                         } else if (!strcmp((const char *) root->name, "rss")) {
226                                 // RSS 2.0 or <1.0 document
227                                 parse_rss_2_0(result, root);
228                                 return;
229                         }
230                 }
231                 root = root->next;
232         } while (root);
233         return;
234 }