136 lines
4.3 KiB
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);
|
|
}
|