Fix segfault in mail folder (SF: 3007493)
[monky] / src / prss.c
index d3d2932..e562506 100644 (file)
@@ -1,8 +1,8 @@
-/*
- * $Id$
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
+ * vim: ts=4 sw=4 noet ai cindent syntax=c
  *
  * Copyright (c) 2007 Mikko Sysikaski <mikko.sysikaski@gmail.com>
- *                    Toni Spets <toni.spets@gmail.com>
+ *                                       Toni Spets <toni.spets@gmail.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
  */
 
+#include "conky.h"
+#include "prss.h"
+#include "logging.h"
 #include <libxml/parser.h>
 #include <libxml/tree.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "prss.h"
-#include "config.h"
 
 #ifndef PARSE_OPTIONS
 #define PARSE_OPTIONS 0
 #endif
 
-PRSS* prss_parse_doc(xmlDocPtr doc);
+void prss_parse_doc(PRSS *result, xmlDocPtr doc);
 
-PRSS* prss_parse_data(const char* xml_data)
+void prss_parse_data(void *result, const char *xml_data)
 {
-       xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL, PARSE_OPTIONS);
-       if (!doc)
-               return NULL;
-       
-       return prss_parse_doc(doc);
-}
-PRSS* prss_parse_file(const char* xml_file)
-{
-       xmlDocPtr doc = xmlReadFile(xml_file, NULL, PARSE_OPTIONS);
-       if (!doc)
-               return NULL;
-       
-       return prss_parse_doc(doc);
+       PRSS *data = (PRSS*)result;
+
+       xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL,
+               PARSE_OPTIONS);
+
+       if (!doc) {
+               return;
+       }
+
+       prss_parse_doc(data, doc);
+       xmlFreeDoc(doc);
 }
-void prss_free(PRSS* data)
+
+void free_rss_items(PRSS *data)
 {
-       if (!data)
-               return;
-       xmlFreeDoc(data->_data);
-       free(data->version);
+       int i;
+
+       for (i = 0; i < data->item_count; i++) {
+#define CLEAR(a) if (data->items[i].a) { free(data->items[i].a); data->items[i].a = 0; }
+               CLEAR(title);
+               CLEAR(link);
+               CLEAR(description);
+               CLEAR(category);
+               CLEAR(pubDate);
+               CLEAR(guid);
+#undef CLEAR
+       }
        free(data->items);
-       free(data);
+       data->item_count = 0;
+       data->items = 0;
 }
 
-static inline void prss_null(PRSS* p)
+void prss_free(PRSS *data)
 {
-       memset(p, 0, sizeof(PRSS));
+       if (!data) {
+               return;
+       }
+       if (data->version) {
+               free(data->version);
+               data->version = 0;
+       }
+       if (data->items) {
+               free_rss_items(data);
+       }
+       data->version = 0;
+       data->items = 0;
+#define CLEAR(a) if (data->a) { free(data->a); data->a = 0; }
+       CLEAR(title);
+       CLEAR(link);
+       CLEAR(description);
+       CLEAR(language);
+       CLEAR(pubDate);
+       CLEAR(lastBuildDate);
+       CLEAR(generator);
+       CLEAR(docs);
+       CLEAR(managingEditor);
+       CLEAR(webMaster);
+       CLEAR(copyright);
+       CLEAR(ttl);
+#undef CLEAR
 }
-static inline void prss_null_item(PRSS_Item* i)
+
+static inline void prss_null_item(PRSS_Item *i)
 {
        memset(i, 0, sizeof(PRSS_Item));
 }
 
-static inline void read_item(PRSS_Item* res, xmlNodePtr data)
+static inline void read_item(PRSS_Item *res, xmlNodePtr data)
 {
        prss_null_item(res);
-       
-       res->title = res->link = res->description = NULL;
-       for(; data; data = data->next) {
-               if (data->type != XML_ELEMENT_NODE)
+
+       for (; data; data = data->next) {
+               xmlNodePtr child;
+
+               if (data->type != XML_ELEMENT_NODE) {
                        continue;
-               xmlNodePtr child = data->children;
-               if (!child)
+               }
+               child = data->children;
+
+               if (!child) {
                        continue;
-       
-               if (!strcasecmp((char*)data->name, "title")) {
-                       res->title = (char*)child->content;
-               } else if (!strcasecmp((char*)data->name, "link")) {
-                       res->link = (char*)child->content;
-               } else if (!strcasecmp((char*)data->name, "description")) {
-                       res->description = (char*)child->content;
-               } else if (!strcasecmp((char*)data->name, "category")) {
-                       res->category = (char*)child->content;
-               } else if (!strcasecmp((char*)data->name, "pubDate")) {
-                       res->pubdate = (char*)child->content;
-               } else if (!strcasecmp((char*)data->name, "guid")) {
-                       res->guid = (char*)child->content;
                }
+
+#define ASSIGN(a) if (strcasecmp((const char*)data->name, #a) == EQUAL) { \
+                       if (res->a) free(res->a); \
+                       res->a = strdup((const char*)child->content); \
+                       continue; \
+               }
+               ASSIGN(title);
+               ASSIGN(link);
+               ASSIGN(description);
+               ASSIGN(category);
+               ASSIGN(pubDate);
+               ASSIGN(guid);
+#undef ASSIGN
        }
 }
-static inline void read_element(PRSS* res, xmlNodePtr n)
+static inline void read_element(PRSS *res, xmlNodePtr n)
 {
-       if (n->type != XML_ELEMENT_NODE)
+       xmlNodePtr child;
+
+       if (n->type != XML_ELEMENT_NODE) {
                return;
-       xmlNodePtr child = n->children;
-       if (!child)
+       }
+       child = n->children;
+
+       if (!child) {
                return;
-               
-       if (!strcasecmp((char*)n->name, "title")) {
-               res->title = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "link")) {
-               res->link = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "description")) {
-               res->description = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "language")) {
-               res->language = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "pubDate")) {
-               res->pubdate = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "lastBuildDate")) {
-               res->lastbuilddate = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "generator")) {
-               res->generator = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "docs")) {
-               res->docs = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "managingEditor")) {
-               res->managingeditor = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "webMaster")) {
-               res->webmaster = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "copyright")) {
-               res->copyright = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "ttl")) {
-               res->ttl = (char*)child->content;
-       } else if (!strcasecmp((char*)n->name, "item")) {
+       }
+
+#define ASSIGN(a) if (strcasecmp((const char*)n->name, #a) == EQUAL) { \
+               if (res->a) free(res->a); \
+               res->a = strdup((const char*)child->content); \
+               return; \
+       }
+       ASSIGN(title);
+       ASSIGN(link);
+       ASSIGN(description);
+       ASSIGN(language);
+       ASSIGN(pubDate);
+       ASSIGN(lastBuildDate);
+       ASSIGN(generator);
+       ASSIGN(docs);
+       ASSIGN(managingEditor);
+       ASSIGN(webMaster);
+       ASSIGN(copyright);
+       ASSIGN(ttl);
+#undef ASSIGN
+       if (!strcasecmp((const char*)n->name, "item")) {
                read_item(&res->items[res->item_count++], n->children);
        }
 }
 
-static inline int parse_rss_2_0(PRSS* res, xmlNodePtr root)
+static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root)
 {
        xmlNodePtr channel = root->children;
-       while(channel
-                       && (channel->type!=XML_ELEMENT_NODE
-                        || strcmp((char*)channel->name, "channel")))
+       xmlNodePtr n;
+       int items = 0;
+
+       DBGP("parsing rss 2.0 or <1 doc");
+
+       while (channel && (channel->type != XML_ELEMENT_NODE
+                               || strcmp((const char *) channel->name, "channel"))) {
                channel = channel->next;
-       if (!channel)
+       }
+       if (!channel) {
                return 0;
+       }
 
-       int items = 0;
-       xmlNodePtr n;
-       for(n = channel->children; n; n = n->next)
-               if (n->type==XML_ELEMENT_NODE && !strcmp((char*)n->name, "item"))
+       for (n = channel->children; n; n = n->next) {
+               if (n->type == XML_ELEMENT_NODE &&
+                               !strcmp((const char *) n->name, "item")) {
                        ++items;
-       
-       res->version = strdup("2.0");
-       res->items = malloc(items*sizeof(PRSS_Item));
+               }
+       }
+
+       if (res->version) free(res->version);
+       res->version = strndup("2.0", text_buffer_size);
+       if (res->items) free_rss_items(res);
+       res->items = malloc(items * sizeof(PRSS_Item));
        res->item_count = 0;
 
-       for(n = channel->children; n; n = n->next) {
+       for (n = channel->children; n; n = n->next) {
                read_element(res, n);
        }
 
        return 1;
 }
-static inline int parse_rss_1_0(PRSS* res, xmlNodePtr root)
+static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root)
 {
        int items = 0;
        xmlNodePtr n;
-       for(n = root->children; n; n = n->next) {
-               if (n->type==XML_ELEMENT_NODE) {
-                       if (!strcmp((char*)n->name, "item"))
+
+       DBGP("parsing rss 1.0 doc");
+
+       for (n = root->children; n; n = n->next) {
+               if (n->type == XML_ELEMENT_NODE) {
+                       if (!strcmp((const char *) n->name, "item")) {
                                ++items;
-                       else if (!strcmp((char*)n->name, "channel")) {
+                       } else if (!strcmp((const char *) n->name, "channel")) {
                                xmlNodePtr i;
-                               for(i = n->children; i; i = i->next) {
+
+                               for (i = n->children; i; i = i->next) {
                                        read_element(res, i);
                                }
                        }
                }
        }
-       
-       res->version = strdup("1.0");
-       res->items = malloc(items*sizeof(PRSS_Item));
+
+       if (res->version) free(res->version);
+       res->version = strndup("1.0", text_buffer_size);
+       if (res->items) free_rss_items(res);
+       res->items = malloc(items * sizeof(PRSS_Item));
        res->item_count = 0;
 
-       for(n = root->children; n; n = n->next) {
-               if (n->type==XML_ELEMENT_NODE && !strcmp((char*)n->name, "item"))
+       for (n = root->children; n; n = n->next) {
+               if (n->type == XML_ELEMENT_NODE &&
+                               !strcmp((const char *) n->name, "item")) {
                        read_item(&res->items[res->item_count++], n->children);
+               }
        }
-       
+
        return 1;
 }
-static inline int parse_rss_0_9x(PRSS* res, xmlNodePtr root)
-{
-       // almost same...
-       return parse_rss_2_0(res, root);
-}
 
-PRSS* prss_parse_doc(xmlDocPtr doc)
+void prss_parse_doc(PRSS *result, xmlDocPtr doc)
 {
-       // FIXME: doc shouldn't be freed after failure when called explicitly from program!
-
        xmlNodePtr root = xmlDocGetRootElement(doc);
-       PRSS* result = malloc(sizeof(PRSS));
-       prss_null(result);
-       result->_data = doc;
+
        do {
                if (root->type == XML_ELEMENT_NODE) {
-                       if (!strcmp((char*)root->name, "RDF")) {
+                       if (!strcmp((const char *) root->name, "RDF")) {
                                // RSS 1.0 document
-                               if (!parse_rss_1_0(result, root)) {
-                                       free(result);
-                                       xmlFreeDoc(doc);
-                                       return NULL;
-                               }
-                               return result;
-                       } else if (!strcmp((char*)root->name, "rss")) {
+                               parse_rss_1_0(result, root);
+                               return;
+                       } else if (!strcmp((const char *) root->name, "rss")) {
                                // RSS 2.0 or <1.0 document
-                               if (!parse_rss_2_0(result, root)) {
-                                       free(result);
-                                       xmlFreeDoc(doc);
-                                       return NULL;
-                               }
-                               return result;
+                               parse_rss_2_0(result, root);
+                               return;
                        }
                }
                root = root->next;
-       } while(root);
-       free(result);
-       return NULL;
+       } while (root);
+       return;
 }