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