Implement Atom feed parsing
This commit is contained in:
parent
9fb45e4cdf
commit
de3bd8b281
1
about.c
1
about.c
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
2
dialog.c
2
dialog.c
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
|
17
interface.c
17
interface.c
|
@ -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)
|
||||
|
|
|
@ -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
1
main.c
|
@ -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
2
md5.c
|
@ -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;
|
||||
|
|
|
@ -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
14
netio.c
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue