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