2018-05-12 16:03:00 +00:00
|
|
|
/*
|
2007-08-10 19:53:44 +00:00
|
|
|
*
|
2007-06-01 15:49:49 +00:00
|
|
|
* Copyright (c) 2007 Mikko Sysikaski <mikko.sysikaski@gmail.com>
|
2008-02-20 20:30:45 +00:00
|
|
|
* Toni Spets <toni.spets@gmail.com>
|
2007-06-01 15:49:49 +00:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
2009-07-27 20:47:19 +00:00
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
2007-06-01 15:49:49 +00:00
|
|
|
|
2008-06-19 06:17:53 +00:00
|
|
|
#include "prss.h"
|
2007-06-01 10:42:57 +00:00
|
|
|
#include <libxml/parser.h>
|
|
|
|
#include <libxml/tree.h>
|
2018-05-12 16:03:00 +00:00
|
|
|
#include "conky.h"
|
|
|
|
#include "logging.h"
|
2007-06-01 10:42:57 +00:00
|
|
|
|
|
|
|
#ifndef PARSE_OPTIONS
|
|
|
|
#define PARSE_OPTIONS 0
|
|
|
|
#endif
|
|
|
|
|
2009-07-20 05:43:36 +00:00
|
|
|
void prss_parse_doc(PRSS *result, xmlDocPtr doc);
|
2007-06-03 08:58:05 +00:00
|
|
|
|
2011-02-15 23:33:22 +00:00
|
|
|
PRSS::PRSS(const std::string &xml_data)
|
2018-05-12 23:26:31 +00:00
|
|
|
: version(nullptr),
|
|
|
|
title(nullptr),
|
|
|
|
link(nullptr),
|
|
|
|
description(nullptr),
|
|
|
|
language(nullptr),
|
|
|
|
generator(nullptr),
|
|
|
|
managingEditor(nullptr),
|
|
|
|
webMaster(nullptr),
|
|
|
|
docs(nullptr),
|
|
|
|
lastBuildDate(nullptr),
|
|
|
|
pubDate(nullptr),
|
|
|
|
copyright(nullptr),
|
|
|
|
ttl(nullptr),
|
|
|
|
items(nullptr),
|
2018-05-12 16:03:00 +00:00
|
|
|
item_count(0) {
|
|
|
|
std::unique_ptr<xmlDoc, void (*)(xmlDoc *)> doc(
|
2018-05-12 23:26:31 +00:00
|
|
|
xmlReadMemory(xml_data.c_str(), xml_data.length(), "", nullptr,
|
2018-05-12 16:03:00 +00:00
|
|
|
PARSE_OPTIONS),
|
|
|
|
xmlFreeDoc);
|
|
|
|
|
|
|
|
if (!doc) throw std::runtime_error("Unable to parse rss data");
|
|
|
|
|
|
|
|
prss_parse_doc(this, doc.get());
|
2007-06-01 10:42:57 +00:00
|
|
|
}
|
2008-02-20 20:30:45 +00:00
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
void free_rss_items(PRSS *data) {
|
|
|
|
int i;
|
2009-08-05 15:55:09 +00:00
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
if (data->items) {
|
|
|
|
for (i = 0; i < data->item_count; i++) {
|
2010-02-24 01:46:45 +00:00
|
|
|
#define CLEAR(a) free_and_zero(data->items[i].a);
|
2018-05-12 16:03:00 +00:00
|
|
|
CLEAR(title);
|
|
|
|
CLEAR(link);
|
|
|
|
CLEAR(description);
|
|
|
|
CLEAR(category);
|
|
|
|
CLEAR(pubDate);
|
|
|
|
CLEAR(guid);
|
2009-08-05 04:56:19 +00:00
|
|
|
#undef CLEAR
|
2018-05-12 16:03:00 +00:00
|
|
|
}
|
|
|
|
free_and_zero(data->items);
|
|
|
|
data->item_count = 0;
|
|
|
|
}
|
2007-06-01 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
PRSS::~PRSS() {
|
|
|
|
free_rss_items(this);
|
|
|
|
free(version);
|
|
|
|
free(title);
|
|
|
|
free(link);
|
|
|
|
free(description);
|
|
|
|
free(language);
|
|
|
|
free(pubDate);
|
|
|
|
free(lastBuildDate);
|
|
|
|
free(generator);
|
|
|
|
free(docs);
|
|
|
|
free(managingEditor);
|
|
|
|
free(webMaster);
|
|
|
|
free(copyright);
|
|
|
|
free(ttl);
|
2007-06-01 15:49:49 +00:00
|
|
|
}
|
2009-08-05 04:56:19 +00:00
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
static inline void prss_null_item(PRSS_Item *i) {
|
|
|
|
memset(i, 0, sizeof(PRSS_Item));
|
2007-06-01 10:42:57 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
static inline void read_item(PRSS_Item *res, xmlNodePtr data) {
|
|
|
|
prss_null_item(res);
|
|
|
|
|
|
|
|
for (; data; data = data->next) {
|
|
|
|
xmlNodePtr child;
|
|
|
|
|
|
|
|
if (data->type != XML_ELEMENT_NODE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
child = data->children;
|
|
|
|
|
|
|
|
if (!child) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ASSIGN(a) \
|
|
|
|
if (strcasecmp((const char *)data->name, #a) == EQUAL) { \
|
|
|
|
free_and_zero(res->a); \
|
|
|
|
res->a = strdup((const char *)child->content); \
|
|
|
|
continue; \
|
|
|
|
}
|
|
|
|
ASSIGN(title);
|
|
|
|
ASSIGN(link);
|
|
|
|
ASSIGN(description);
|
|
|
|
ASSIGN(category);
|
|
|
|
ASSIGN(pubDate);
|
|
|
|
ASSIGN(guid);
|
2009-08-05 04:56:19 +00:00
|
|
|
#undef ASSIGN
|
2018-05-12 16:03:00 +00:00
|
|
|
}
|
2007-06-01 10:42:57 +00:00
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
static inline void read_element(PRSS *res, xmlNodePtr n) {
|
|
|
|
xmlNodePtr child;
|
|
|
|
|
|
|
|
if (n->type != XML_ELEMENT_NODE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
child = n->children;
|
|
|
|
|
|
|
|
if (!child) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ASSIGN(a) \
|
|
|
|
if (strcasecmp((const char *)n->name, #a) == EQUAL) { \
|
|
|
|
free_and_zero(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);
|
2009-08-05 04:56:19 +00:00
|
|
|
#undef ASSIGN
|
2018-05-12 16:03:00 +00:00
|
|
|
if (!strcasecmp((const char *)n->name, "item")) {
|
|
|
|
read_item(&res->items[res->item_count++], n->children);
|
|
|
|
}
|
2007-06-01 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root) {
|
|
|
|
xmlNodePtr channel = root->children;
|
|
|
|
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) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (n = channel->children; n; n = n->next) {
|
|
|
|
if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)n->name, "item")) {
|
|
|
|
++items;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free_and_zero(res->version);
|
|
|
|
res->version = strndup("2.0", text_buffer_size.get(*state));
|
|
|
|
free_rss_items(res);
|
|
|
|
res->items = (PRSS_Item *)malloc(items * sizeof(PRSS_Item));
|
|
|
|
res->item_count = 0;
|
|
|
|
|
|
|
|
for (n = channel->children; n; n = n->next) {
|
|
|
|
read_element(res, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2007-06-01 15:49:49 +00:00
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root) {
|
|
|
|
int items = 0;
|
|
|
|
xmlNodePtr n;
|
|
|
|
|
|
|
|
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((const char *)n->name, "channel")) {
|
|
|
|
xmlNodePtr i;
|
|
|
|
|
|
|
|
for (i = n->children; i; i = i->next) {
|
|
|
|
read_element(res, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free_and_zero(res->version);
|
|
|
|
res->version = strndup("1.0", text_buffer_size.get(*state));
|
|
|
|
free_rss_items(res);
|
|
|
|
res->items = (PRSS_Item *)malloc(items * sizeof(PRSS_Item));
|
|
|
|
res->item_count = 0;
|
|
|
|
|
|
|
|
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;
|
2007-06-01 15:49:49 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
void prss_parse_doc(PRSS *result, xmlDocPtr doc) {
|
|
|
|
xmlNodePtr root = xmlDocGetRootElement(doc);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (root->type == XML_ELEMENT_NODE) {
|
|
|
|
if (!strcmp((const char *)root->name, "RDF")) {
|
|
|
|
// RSS 1.0 document
|
|
|
|
parse_rss_1_0(result, root);
|
|
|
|
return;
|
|
|
|
} else if (!strcmp((const char *)root->name, "rss")) {
|
|
|
|
// RSS 2.0 or <1.0 document
|
|
|
|
parse_rss_2_0(result, root);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
root = root->next;
|
|
|
|
} while (root);
|
|
|
|
return;
|
2007-06-01 10:42:57 +00:00
|
|
|
}
|