snownews/netio.c

136 lines
4.3 KiB
C

// 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
// as published by the Free Software Foundation.
//
// Snownews is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Snownews. If not, see http://www.gnu.org/licenses/.
#include "netio.h"
#include "uiutil.h"
#include "setup.h"
#include <curl/curl.h>
static size_t FeedReceiver (void* buffer, size_t msz, size_t nm, void* vpfeed)
{
struct feed* fp = vpfeed;
size_t size = msz * nm;
char* t = realloc (fp->xmltext, fp->content_length + size + 1);
if (!t) {
fprintf (stderr, "Error: out of memory\n");
exit (EXIT_FAILURE);
}
fp->xmltext = t;
memcpy (&fp->xmltext[fp->content_length], buffer, size);
fp->content_length += size;
fp->xmltext [fp->content_length] = 0;
return size;
}
// Returns allocated string with body of webserver reply.
// Various status info put into struct feed* fp.
void DownloadFeed (const char* url, struct feed* fp)
{
// Default to error
fp->problem = true;
if (fp->lasterror) {
free (fp->lasterror);
fp->lasterror = NULL;
}
// libcurl global init must be called only once
// snownews is single threaded, so no fancy locks needed
static bool s_curl_initialized = false;
if (!s_curl_initialized) {
if (0 != curl_global_init (CURL_GLOBAL_DEFAULT)) {
UIStatus ("Error: failed to initialize libcurl", 2, 1);
syslog (LOG_ERR, "failed to initialize libcurl");
return;
}
atexit (curl_global_cleanup);
s_curl_initialized = true;
}
// Setup CURL connection
CURL* curl = curl_easy_init();
if (!curl)
return;
curl_easy_setopt (curl, CURLOPT_URL, url);
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, FeedReceiver);
curl_easy_setopt (curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt (curl, CURLOPT_USERAGENT, SNOWNEWS_NAME "/" SNOWNEWS_VERSTRING);
curl_easy_setopt (curl, CURLOPT_BUFFERSIZE, CURL_MAX_READ_SIZE);
curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 8);
curl_easy_setopt (curl, CURLOPT_AUTOREFERER, 1);
// Avoid redownloading if not modified
curl_easy_setopt (curl, CURLOPT_FILETIME, 1);
if (fp->lastmodified > 0) {
curl_easy_setopt (curl, CURLOPT_TIMEVALUE, fp->lastmodified);
curl_easy_setopt (curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
}
// Cookies, if the cookie file is in the config dir
char cookiefile [PATH_MAX];
ConfigFilePath ("cookies", cookiefile, sizeof(cookiefile));
if (access (cookiefile, R_OK) == 0)
curl_easy_setopt (curl, CURLOPT_COOKIEFILE, cookiefile);
// Save old content
char* oldxmltext = fp->xmltext;
unsigned oldcontent_length = fp->content_length;
fp->xmltext = NULL;
fp->content_length = 0;
// Download the new feed
CURLcode rc = curl_easy_perform (curl);
if (rc == CURLE_OK && fp->content_length) {
//
// Transfer successful
//
fp->problem = false;
if (oldxmltext) {
free (oldxmltext);
oldxmltext = NULL;
}
long filetime = 0;
if (CURLE_OK == curl_easy_getinfo (curl, CURLINFO_FILETIME, &filetime) && filetime > 0)
fp->lastmodified = filetime;
} else {
//
// On error, free downloaded text and restore original
//
if (fp->xmltext)
free (fp->xmltext);
fp->xmltext = oldxmltext;
fp->content_length = oldcontent_length;
// Check if failed because already up-to-date
long unmet = 0;
if (CURLE_OK == curl_easy_getinfo (curl, CURLINFO_CONDITION_UNMET, &unmet) && unmet) {
fp->lasterror = strdup (_("already up to date"));
UIStatus (fp->lasterror, 0, 0);
fp->problem = false;
} else {
// The error is stored in fp->lasterror for display
const char* cerrt = curl_easy_strerror (rc);
if (cerrt) {
fp->lasterror = strdup (cerrt);
UIStatus (cerrt, 2, 1);
syslog (LOG_ERR, "%s", cerrt);
}
}
}
curl_easy_cleanup (curl);
}