Implement Atom feed parsing

This commit is contained in:
Mike Sharov 2021-04-10 14:59:19 -04:00
parent 9fb45e4cdf
commit de3bd8b281
17 changed files with 242 additions and 224 deletions

View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3

View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3

View File

@ -217,7 +217,7 @@ char* UIDejunk (const char* feed_description)
else
ch = atol (entity + 1);
} else {
const htmlEntityDesc* ep = htmlEntityLookup ((xmlChar *) entity);
const htmlEntityDesc* ep = htmlEntityLookup ((xmlChar*) entity);
if (ep)
ch = ep->value;
}

View File

@ -21,7 +21,7 @@
#include "filters.h"
#include "io-internal.h"
#include "ui-support.h"
#include "xmlparse.h"
#include "parsefeed.h"
#include <ncurses.h>
#include <sys/stat.h>

View File

@ -1,105 +0,0 @@
<?xml version="1.0"?>
<!--
Copyright (c) 2017, René Puls <koz.ross@retro-freedom.nz>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
-->
<!-- Atom to RSS 1.0 Transformation, written by René Puls (rp@kianga.eu) -->
<!-- Includes enhancements for YouTube video feeds -->
<!-- Requires: xsltproc -->
<!-- Instructions:
1) Add the Atom feed as a feed source
2) Use 'xsltproc atom2rss -' as a filter script -->
<!-- Last tested: 21/09/17 -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns="http://purl.org/rss/1.0/">
<xsl:output method="xml" indent="yes" cdata-section-elements="description" />
<xsl:template match="/">
<xsl:apply-templates select="atom:feed" />
</xsl:template>
<xsl:template match="atom:feed">
<rdf:RDF>
<channel>
<xsl:attribute name="rdf:about"><xsl:value-of select="atom:link[@rel='service.feed']/@href" /></xsl:attribute>
<title><xsl:value-of select="normalize-space(atom:title)" /></title>
<link><xsl:value-of select="atom:link/@href" /></link>
<description><xsl:value-of select="normalize-space(atom:info)" /></description>
<items>
<rdf:Seq>
<xsl:apply-templates select="atom:entry" mode="rdfitem"/>
</rdf:Seq>
</items>
</channel>
<xsl:apply-templates select="atom:entry" mode="rssitem" />
</rdf:RDF>
</xsl:template>
<xsl:template match="atom:entry" mode="rdfitem">
<rdf:li>
<xsl:attribute name="rdf:resource">
<xsl:value-of select="normalize-space(atom:id)" />
</xsl:attribute>
</rdf:li>
</xsl:template>
<xsl:template match="atom:entry" mode="rssitem">
<item>
<xsl:attribute name="rdf:about">
<xsl:value-of select="normalize-space(atom:id)" />
</xsl:attribute>
<title><xsl:value-of select="normalize-space(atom:title)" /></title>
<link><xsl:value-of select="atom:link/@href" /></link>
<xsl:if test="atom:issued">
<dc:date><xsl:value-of select="normalize-space(atom:issued)" /></dc:date>
</xsl:if>
<xsl:if test="atom:author">
<dc:creator><xsl:value-of select="normalize-space(atom:author)" /></dc:creator>
</xsl:if>
<xsl:choose>
<xsl:when test="atom:content">
<description><xsl:value-of select="normalize-space(atom:content)" /></description>
</xsl:when>
<xsl:when test="atom:summary">
<description><xsl:value-of select="normalize-space(atom:summary)" /></description>
</xsl:when>
<xsl:when test="media:group/media:description">
<description><xsl:value-of select="media:group/media:description" /></description>
</xsl:when>
</xsl:choose>
</item>
</xsl:template>
</xsl:stylesheet>

View File

@ -80,12 +80,12 @@ int FilterPipeNG (struct feed* cur_ptr)
char* filter = strdup (cur_ptr->perfeedfilter);
char* command = strsep (&filter, " ");
char** options = malloc (sizeof (char *));
char** options = malloc (sizeof (char*));
size_t nopts = 0;
options[nopts++] = command;
for (;;) {
options = realloc (options, sizeof (char *) * (nopts + 1));
options = realloc (options, sizeof (char*) * (nopts + 1));
if (!(options[nopts++] = strsep (&filter, " ")))
break;
}

View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3
@ -74,7 +75,7 @@ static void UIDisplayItem (const struct newsitem* current_item, const struct fee
int columns = COLS - 10;
mvaddn_utf8 (2, 1, converted, columns);
if (xmlStrlen ((xmlChar *) converted) > columns)
if (xmlStrlen ((xmlChar*) converted) > columns)
mvaddstr (2, columns + 1, "...");
free (converted);
@ -89,7 +90,7 @@ static void UIDisplayItem (const struct newsitem* current_item, const struct fee
int columns = COLS - 10;
mvaddn_utf8 (2, 1, converted, columns);
if (xmlStrlen ((xmlChar *) converted) > columns)
if (xmlStrlen ((xmlChar*) converted) > columns)
mvaddstr (2, columns + 1, "...");
free (converted);
@ -121,12 +122,12 @@ static void UIDisplayItem (const struct newsitem* current_item, const struct fee
free (newtext);
int columns = COLS - 6;
if (xmlStrlen ((xmlChar *) converted) > columns) {
if (xmlStrlen ((xmlChar*) converted) > columns) {
mvaddn_utf8 (4, 1, converted, columns);
if (xmlStrlen ((xmlChar *) converted) > columns)
if (xmlStrlen ((xmlChar*) converted) > columns)
mvaddstr (4, columns + 1, "...");
} else
mvadd_utf8 (4, (COLS / 2) - (xmlStrlen ((xmlChar *) converted) / 2), converted);
mvadd_utf8 (4, (COLS / 2) - (xmlStrlen ((xmlChar*) converted) / 2), converted);
free (converted);
} else
@ -394,7 +395,7 @@ static int UIDisplayFeed (struct feed* current_feed)
int columns = COLS - 10;
mvaddn_utf8 (2, 1, converted, columns);
if (xmlStrlen ((xmlChar *) converted) > columns)
if (xmlStrlen ((xmlChar*) converted) > columns)
mvaddstr (2, columns + 1, "...");
free (converted);
@ -450,7 +451,7 @@ static int UIDisplayFeed (struct feed* current_feed)
int columns = COLS - 6; // Cut max item length.
mvaddn_utf8 (ypos, 1, converted, columns);
if (xmlStrlen ((xmlChar *) converted) > columns)
if (xmlStrlen ((xmlChar*) converted) > columns)
mvaddstr (ypos, columns + 1, "...");
free (converted);
@ -897,7 +898,7 @@ void UIMainInterface (void)
columns = COLS - 9 - strlen (_("new"));
mvaddn_utf8 (ypos, 1, cur_ptr->title, columns);
if (xmlStrlen ((xmlChar *) cur_ptr->title) > columns)
if (xmlStrlen ((xmlChar*) cur_ptr->title) > columns)
mvaddstr (ypos, columns + 1, "...");
if (cur_ptr->problem)

View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3
@ -20,7 +21,7 @@
#include "io-internal.h"
#include "netio.h"
#include "ui-support.h"
#include "xmlparse.h"
#include "parsefeed.h"
#include <errno.h>
#include <ncurses.h>
#include <sys/stat.h>
@ -356,7 +357,7 @@ static void WriteFeedCache (const struct feed* feed)
fprintf (cache, "<![CDATA[%s]]>", feed->title);
fputs ("</title>\n<link>", cache);
if (feed->link) {
encoded = (char *) xmlEncodeEntitiesReentrant (NULL, (xmlChar *) feed->link);
encoded = (char*) xmlEncodeEntitiesReentrant (NULL, (xmlChar*) feed->link);
fputs (encoded, cache);
free (encoded);
}

1
main.c
View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3

2
md5.c
View File

@ -93,7 +93,7 @@ void hash_md5_data (struct HashMD5* h, const void* d, size_t n)
if (copied > n)
copied = n;
memcpy (h->bytes + blockOffset, d, copied);
d = (const char *) d + copied;
d = (const char*) d + copied;
n -= copied;
h->offset += copied;
blockOffset = (blockOffset + copied) % HASH_BLOCK_SIZE_MD5;

View File

@ -188,7 +188,7 @@ int NetSupportAuth (struct feed* cur_ptr, const char* authdata, const char* url,
//
int checkValidHTTPHeader (const unsigned char* header, unsigned size)
{
unsigned len = strlen ((const char *) header);
unsigned len = strlen ((const char*) header);
if (len > size)
return -1;
for (unsigned i = 0; i < len && header[i] != ':'; ++i)
@ -199,9 +199,9 @@ int checkValidHTTPHeader (const unsigned char* header, unsigned size)
int checkValidHTTPURL (const unsigned char* url)
{
if (strncasecmp ((const char *) url, "http://", 7) != 0)
if (strncasecmp ((const char*) url, "http://", 7) != 0)
return -1;
for (unsigned i = 0, len = strlen ((const char *)url); i < len; ++i)
for (unsigned i = 0, len = strlen ((const char*)url); i < len; ++i)
if (url[i] < ' ' || url[i] > '~')
return -1;
return 0;

14
netio.c
View File

@ -104,7 +104,7 @@ static int NetConnect (int* my_socket, const char* host, struct feed* cur_ptr, b
memcpy (&address.sin_addr.s_addr, remotehost->h_addr_list[0], remotehost->h_length);
// Connect socket.
cur_ptr->connectresult = connect (*my_socket, (struct sockaddr *) &address, sizeof (address));
cur_ptr->connectresult = connect (*my_socket, (struct sockaddr*) &address, sizeof (address));
// Check if we're already connected.
// BSDs will return 0 on connect even in nonblock if connect was fast enough.
@ -149,7 +149,7 @@ static int NetConnect (int* my_socket, const char* host, struct feed* cur_ptr, b
memcpy (&address.sin_addr.s_addr, remotehost->h_addr_list[0], remotehost->h_length);
// Connect socket.
cur_ptr->connectresult = connect (*my_socket, (struct sockaddr *) &address, sizeof (address));
cur_ptr->connectresult = connect (*my_socket, (struct sockaddr*) &address, sizeof (address));
// Check if we're already connected.
// BSDs will return 0 on connect even in nonblock if connect was fast enough.
@ -255,7 +255,7 @@ static char* NetIO (int* my_socket, char* host, char* url, struct feed* cur_ptr,
fclose (stream);
return NULL;
}
if (checkValidHTTPHeader ((unsigned char *) servreply, sizeof (servreply)) != 0) {
if (checkValidHTTPHeader ((unsigned char*) servreply, sizeof (servreply)) != 0) {
cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
fclose (stream);
return NULL;
@ -322,7 +322,7 @@ static char* NetIO (int* my_socket, char* host, char* url, struct feed* cur_ptr,
return NULL;
}
if (checkValidHTTPHeader ((unsigned char *) netbuf, sizeof (netbuf)) != 0) {
if (checkValidHTTPHeader ((unsigned char*) netbuf, sizeof (netbuf)) != 0) {
cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
fclose (stream);
return NULL;
@ -377,7 +377,7 @@ static char* NetIO (int* my_socket, char* host, char* url, struct feed* cur_ptr,
// Change cur_ptr->feedurl on 301.
if (cur_ptr->lasthttpstatus == 301) {
// Check for valid redirection URL
if (checkValidHTTPURL ((unsigned char *) newlocation) != 0) {
if (checkValidHTTPURL ((unsigned char*) newlocation) != 0) {
cur_ptr->problem = true;
cur_ptr->netio_error = NET_ERR_REDIRECT_ERR;
fclose (stream);
@ -508,7 +508,7 @@ static char* NetIO (int* my_socket, char* host, char* url, struct feed* cur_ptr,
if ((fgets (netbuf, sizeof (netbuf), stream)) == NULL)
break;
if (checkValidHTTPHeader ((unsigned char *) netbuf, sizeof (netbuf)) != 0) {
if (checkValidHTTPHeader ((unsigned char*) netbuf, sizeof (netbuf)) != 0) {
cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
fclose (stream);
return NULL;
@ -708,7 +708,7 @@ static char* NetIO (int* my_socket, char* host, char* url, struct feed* cur_ptr,
// Set suppressoutput=1 to disable ncurses calls.
char* DownloadFeed (char* url, struct feed* cur_ptr, bool suppressoutput)
{
if (checkValidHTTPURL ((unsigned char *) url) != 0) {
if (checkValidHTTPURL ((unsigned char*) url) != 0) {
cur_ptr->problem = true;
cur_ptr->netio_error = NET_ERR_HTTP_PROTO_ERR;
return NULL;

View File

@ -2,6 +2,7 @@
//
// Copyright (c) 2003-2004 Rene Puls <rpuls@gmx.net>
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3
@ -15,26 +16,21 @@
// You should have received a copy of the GNU General Public License
// along with Snownews. If not, see http://www.gnu.org/licenses/.
#include "xmlparse.h"
#include "parsefeed.h"
#include "conversions.h"
#include <libxml/parser.h>
//----------------------------------------------------------------------
//{{{ Local variables --------------------------------------------------
static bool saverestore = false;
static struct newsitem* copy = NULL;
static struct newsitem* firstcopy = NULL;
static const xmlChar* dcNs = (const xmlChar *) "http://purl.org/dc/elements/1.1/";
static const xmlChar* snowNs = (const xmlChar *) "http://snownews.kcore.de/ns/1.0/";
static const xmlChar* dcNs = (const xmlChar*) "http://purl.org/dc/elements/1.1/";
static const xmlChar* snowNs = (const xmlChar*) "http://snownews.kcore.de/ns/1.0/";
//----------------------------------------------------------------------
static void parse_rdf10_item (struct feed* feed, xmlDocPtr doc, xmlNodePtr node);
static void parse_rdf10_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node);
static void parse_rdf20_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node);
//----------------------------------------------------------------------
//}}}-------------------------------------------------------------------
//{{{ free_feed
static void free_feed (struct feed* feed)
{
@ -64,60 +60,11 @@ static void free_feed (struct feed* feed)
feed->description = NULL;
}
// Called during parsing, if we look for a <channel> element
// The function returns a new struct for the newsfeed.
static void parse_rdf10_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar *) "title") == 0) {
feed->title = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen (feed->title) > 1 && feed->title[strlen (feed->title) - 1] == '\n')
feed->title[strlen (feed->title) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "link") == 0) {
feed->link = (char *) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen (feed->link) > 1 && feed->link[strlen (feed->link) - 1] == '\n')
feed->link[strlen (feed->link) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "description") == 0) {
feed->description = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->description, 0);
}
}
}
static void parse_rdf20_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar *) "title") == 0) {
feed->title = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen (feed->title) > 1 && feed->title[strlen (feed->title) - 1] == '\n')
feed->title[strlen (feed->title) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "link") == 0) {
feed->link = (char *) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen (feed->link) > 1 && feed->link[strlen (feed->link) - 1] == '\n')
feed->link[strlen (feed->link) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar *) "description") == 0) {
feed->description = (char *) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->description, 0);
} else if (xmlStrcmp (cur->name, (const xmlChar *) "item") == 0)
parse_rdf10_item (feed, doc, cur->children);
}
}
//}}}-------------------------------------------------------------------
//{{{ RSS 1 parsing
// This function is called every time we hit an <item>. As parameter it
// needs the current newsfeed (struct newsfeed *), as well as the current
// needs the current newsfeed (struct newsfeed*), as well as the current
// XML Document handle and the current element, both come directly from
// the libxml.
@ -137,8 +84,8 @@ static void parse_rdf10_item (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
continue;
// Basic RSS
// Title
if (xmlStrcmp (cur->name, (const xmlChar *) "title") == 0) {
item->data->title = (char *) xmlNodeListGetString (doc, cur->children, 1);
if (xmlStrcmp (cur->name, (const xmlChar*) "title") == 0) {
item->data->title = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (item->data->title, 1);
// Remove trailing newline
if (item->data->title != NULL) {
@ -148,8 +95,8 @@ static void parse_rdf10_item (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
}
}
// link
} else if (xmlStrcmp (cur->name, (const xmlChar *) "link") == 0) {
item->data->link = (char *) xmlNodeListGetString (doc, cur->children, 1);
} else if (xmlStrcmp (cur->name, (const xmlChar*) "link") == 0) {
item->data->link = (char*) xmlNodeListGetString (doc, cur->children, 1);
// Remove trailing newline
if (item->data->link != NULL) {
if (strlen (item->data->link) > 1) {
@ -158,41 +105,41 @@ static void parse_rdf10_item (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
}
}
// Description
} else if (xmlStrcmp (cur->name, (const xmlChar *) "description") == 0) {
item->data->description = (char *) xmlNodeListGetString (doc, cur->children, 1);
} else if (xmlStrcmp (cur->name, (const xmlChar*) "description") == 0) {
item->data->description = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (item->data->description, 0);
// Userland extensions (No namespace!)
// guid
} else if (xmlStrcmp (cur->name, (const xmlChar *) "guid") == 0) {
guid = (char *) xmlNodeListGetString (doc, cur->children, 1);
} else if (xmlStrcmp (cur->name, (const xmlChar*) "guid") == 0) {
guid = (char*) xmlNodeListGetString (doc, cur->children, 1);
// pubDate
} else if (xmlStrcmp (cur->name, (const xmlChar *) "pubDate") == 0) {
} else if (xmlStrcmp (cur->name, (const xmlChar*) "pubDate") == 0) {
xmlChar* date_str = xmlNodeListGetString (doc, cur->children, 1);
item->data->date = pubDateToUnix ((const char *) date_str);
item->data->date = pubDateToUnix ((const char*) date_str);
xmlFree (date_str);
// Dublin Core
// dc:date
} else if (cur->ns && xmlStrcmp (cur->ns->href, dcNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "date") == 0) {
} else if (cur->ns && xmlStrcmp (cur->ns->href, dcNs) == 0 && xmlStrcmp (cur->name, (const xmlChar*) "date") == 0) {
xmlChar* date_str = xmlNodeListGetString (doc, cur->children, 1);
item->data->date = ISODateToUnix ((const char *) date_str);
item->data->date = ISODateToUnix ((const char*) date_str);
xmlFree (date_str);
// Internal usage
// Obsolete/backware compat/migration code
} else if (xmlStrcmp (cur->name, (const xmlChar *) "readstatus") == 0) {
} else if (xmlStrcmp (cur->name, (const xmlChar*) "readstatus") == 0) {
// Will cause memory leak otherwise, xmlNodeListGetString must be freed.
xmlChar* readstatusstring = xmlNodeListGetString (doc, cur->children, 1);
item->data->readstatus = atoi ((const char *) readstatusstring);
item->data->readstatus = atoi ((const char*) readstatusstring);
xmlFree (readstatusstring);
// Using snow namespace
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "readstatus") == 0) {
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar*) "readstatus") == 0) {
xmlChar* readstatusstring = xmlNodeListGetString (doc, cur->children, 1);
item->data->readstatus = atoi ((const char *) readstatusstring);
item->data->readstatus = atoi ((const char*) readstatusstring);
xmlFree (readstatusstring);
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "hash") == 0) {
item->data->hash = (char *) xmlNodeListGetString (doc, cur->children, 1);
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar *) "date") == 0) {
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar*) "hash") == 0) {
item->data->hash = (char*) xmlNodeListGetString (doc, cur->children, 1);
} else if (cur->ns && xmlStrcmp (cur->ns->href, snowNs) == 0 && xmlStrcmp (cur->name, (const xmlChar*) "date") == 0) {
xmlChar* date_str = xmlNodeListGetString (doc, cur->children, 1);
item->data->date = atol ((const char *) date_str);
item->data->date = atol ((const char*) date_str);
xmlFree (date_str);
}
}
@ -225,7 +172,174 @@ static void parse_rdf10_item (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
}
}
// rrr
// Called during parsing, if we look for a <channel> element
// The function returns a new struct for the newsfeed.
static void parse_rdf10_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar*) "title") == 0) {
feed->title = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen (feed->title) > 1 && feed->title[strlen (feed->title) - 1] == '\n')
feed->title[strlen (feed->title) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*) "link") == 0) {
feed->link = (char*) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen (feed->link) > 1 && feed->link[strlen (feed->link) - 1] == '\n')
feed->link[strlen (feed->link) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*) "description") == 0) {
feed->description = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->description, 0);
}
}
}
//}}}-------------------------------------------------------------------
//{{{ RSS 2 parsing
static void parse_rdf20_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar*) "title") == 0) {
feed->title = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen (feed->title) > 1 && feed->title[strlen (feed->title) - 1] == '\n')
feed->title[strlen (feed->title) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*) "link") == 0) {
feed->link = (char*) xmlNodeListGetString (doc, cur->children, 1);
if (feed->link && strlen (feed->link) > 1 && feed->link[strlen (feed->link) - 1] == '\n')
feed->link[strlen (feed->link) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*) "description") == 0) {
feed->description = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->description, 0);
} else if (xmlStrcmp (cur->name, (const xmlChar*) "item") == 0)
parse_rdf10_item (feed, doc, cur->children);
}
}
//}}}-------------------------------------------------------------------
//{{{ Atom parsing
static void parse_atom_entry (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Reserve memory for a new news item
struct newsitem* item = calloc (1, sizeof (struct newsitem));
item->data = calloc (1, sizeof (struct newsdata));
item->data->parent = feed;
char* guid = NULL;
// Go through all the tags in the <item> tag and extract the information.
// same procedure as in the parse_channel() function
for (xmlNodePtr cur = node; cur != NULL; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
// Basic RSS
// Title
if (xmlStrcmp (cur->name, (const xmlChar*) "title") == 0) {
item->data->title = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (item->data->title, 1);
// Remove trailing newline
if (item->data->title != NULL) {
if (strlen (item->data->title) > 1) {
if (item->data->title[strlen (item->data->title) - 1] == '\n')
item->data->title[strlen (item->data->title) - 1] = '\0';
}
}
// link
} else if (xmlStrcmp (cur->name, (const xmlChar*) "link") == 0) {
item->data->link = (char*) xmlGetProp (cur, (const xmlChar*) "href");
// Remove trailing newline
if (item->data->link != NULL) {
if (strlen (item->data->link) > 1) {
if (item->data->link[strlen (item->data->link) - 1] == '\n')
item->data->link[strlen (item->data->link) - 1] = '\0';
}
}
// Description
} else if (xmlStrcmp (cur->name, (const xmlChar*) "summary") == 0 && !item->data->description) {
item->data->description = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (item->data->description, 0);
// Userland extensions (No namespace!)
// guid
} else if (xmlStrcmp (cur->name, (const xmlChar*) "content") == 0) {
if (item->data->description)
xmlFree (item->data->description);
item->data->description = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (item->data->description, 0);
// Userland extensions (No namespace!)
// guid
} else if (xmlStrcmp (cur->name, (const xmlChar*) "id") == 0) {
guid = (char*) xmlNodeListGetString (doc, cur->children, 1);
// pubDate
} else if (xmlStrcmp (cur->name, (const xmlChar*) "updated") == 0) {
xmlChar* date_str = xmlNodeListGetString (doc, cur->children, 1);
item->data->date = ISODateToUnix ((const char*) date_str);
xmlFree (date_str);
}
}
// If we have loaded the hash from disk cache, don't regenerate it.
// <guid> is not saved in the cache, thus we would generate a different
// hash than the one from the live feed.
if (item->data->hash == NULL) {
const char* hashitems[] = { item->data->title, item->data->link, guid, NULL };
item->data->hash = genItemHash (hashitems, 3);
xmlFree (guid);
}
// If saverestore == true, restore readstatus.
if (saverestore) {
for (const struct newsitem* i = firstcopy; i; i = i->next) {
if (strcmp (item->data->hash, i->data->hash) == 0) {
item->data->readstatus = i->data->readstatus;
break;
}
}
}
if (!feed->items)
feed->items = item;
else {
item->prev = feed->items;
while (item->prev->next)
item->prev = item->prev->next;
item->prev->next = item;
}
}
static void parse_atom_channel (struct feed* feed, xmlDocPtr doc, xmlNodePtr node)
{
// Free everything before we write to it again.
free_feed (feed);
// Go through all the tags in the <channel> tag and extract the information
for (xmlNodePtr cur = node; cur; cur = cur->next) {
if (cur->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (cur->name, (const xmlChar*) "title") == 0) {
feed->title = (char*) xmlNodeListGetString (doc, cur->children, 1);
CleanupString (feed->title, 1);
if (feed->title && strlen (feed->title) > 1 && feed->title[strlen (feed->title) - 1] == '\n')
feed->title[strlen (feed->title) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*) "link") == 0) {
feed->link = (char*) xmlGetProp (cur, (const xmlChar*) "href");
if (feed->link && strlen (feed->link) > 1 && feed->link[strlen (feed->link) - 1] == '\n')
feed->link[strlen (feed->link) - 1] = '\0'; // Remove trailing newline
} else if (xmlStrcmp (cur->name, (const xmlChar*) "entry") == 0)
parse_atom_entry (feed, doc, cur->children);
}
}
//}}}-------------------------------------------------------------------
int DeXML (struct feed* cur_ptr)
{
@ -279,7 +393,7 @@ int DeXML (struct feed* cur_ptr)
return 2;
}
// Check if the element really is called <RDF>
if (xmlStrcmp (cur->name, (const xmlChar *) "RDF") == 0) {
if (xmlStrcmp (cur->name, (const xmlChar*) "RDF") == 0) {
// Now we go through all the elements in the document. This loop however,
// only the highest level elements work (HTML would only be HEAD and
// BODY), so do not wander entire structure down through. The functions
@ -287,21 +401,23 @@ int DeXML (struct feed* cur_ptr)
for (xmlNodePtr c = cur->children; c; c = c->next) {
if (c->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (c->name, (const xmlChar *) "channel") == 0)
if (xmlStrcmp (c->name, (const xmlChar*) "channel") == 0)
parse_rdf10_channel (cur_ptr, doc, c->children);
if (xmlStrcmp (c->name, (const xmlChar *) "item") == 0)
if (xmlStrcmp (c->name, (const xmlChar*) "item") == 0)
parse_rdf10_item (cur_ptr, doc, c->children);
// Last-Modified is only used when reading from internal feeds (disk cache).
if (c->ns && (xmlStrcmp (c->ns->href, snowNs) == 0) && (xmlStrcmp (c->name, (const xmlChar *) "lastmodified") == 0))
cur_ptr->lastmodified = (char *) xmlNodeListGetString (doc, c->children, 1);
if (c->ns && (xmlStrcmp (c->ns->href, snowNs) == 0) && (xmlStrcmp (c->name, (const xmlChar*) "lastmodified") == 0))
cur_ptr->lastmodified = (char*) xmlNodeListGetString (doc, c->children, 1);
}
} else if (xmlStrcmp (cur->name, (const xmlChar *) "rss") == 0) {
} else if (xmlStrcmp (cur->name, (const xmlChar*) "rss") == 0) {
for (xmlNodePtr c = cur->children; c; c = c->next) {
if (c->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp (c->name, (const xmlChar *) "channel") == 0)
if (xmlStrcmp (c->name, (const xmlChar*) "channel") == 0)
parse_rdf20_channel (cur_ptr, doc, c->children);
}
} else if (xmlStrcmp (cur->name, (const xmlChar*) "feed") == 0) {
parse_atom_channel (cur_ptr, doc, cur->children);
} else {
xmlFreeDoc (doc);
return 3;

View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3

View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// Snownews is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3

View File

@ -36,7 +36,7 @@ static int jg_zlib_uncompress (void const* in_buf, unsigned in_size, void** out_
// Prepare the stream structure.
z_stream stream = { };
stream.next_in = (void *) in_buf;
stream.next_in = (void*) in_buf;
stream.avail_in = in_size;
unsigned char tmp_buf[BUFSIZ];
@ -115,7 +115,7 @@ int jg_gzip_uncompress (const void* in_buf, unsigned in_size, void** out_buf_ptr
unsigned offset = sizeof (*header);
if (header->flags & 8) // skip the file name
while (offset < in_size)
if (((char *) in_buf)[offset++] == 0)
if (((char*) in_buf)[offset++] == 0)
break;
return jg_zlib_uncompress ((char *) in_buf + offset, in_size - offset - 8, out_buf_ptr, out_size, 0);
return jg_zlib_uncompress ((char*) in_buf + offset, in_size - offset - 8, out_buf_ptr, out_size, 0);
}