2020-12-30 22:07:54 -06:00
|
|
|
/*
|
|
|
|
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
|
|
|
* All rights reserved. Distributed under the terms of the MIT license.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Feed.h"
|
|
|
|
|
2020-07-08 03:20:03 -05:00
|
|
|
#include <tinyxml2.h>
|
2020-12-30 22:07:54 -06:00
|
|
|
|
2020-11-16 20:32:58 -06:00
|
|
|
#include "App.h"
|
2020-07-08 03:20:03 -05:00
|
|
|
#include "Entry.h"
|
|
|
|
#include "Config.h"
|
|
|
|
#include "Util.h"
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
Feed::Feed(BString path)
|
2020-07-08 03:20:03 -05:00
|
|
|
{
|
2020-12-30 22:07:54 -06:00
|
|
|
title = BString("Untitled Feed");
|
|
|
|
description = BString("Nondescript, N/A.");
|
2020-07-08 03:20:03 -05:00
|
|
|
homeUrl = BString("");
|
|
|
|
xmlUrl = BString("");
|
2020-08-02 02:05:20 -05:00
|
|
|
updated = true;
|
2020-11-16 21:32:06 -06:00
|
|
|
fetched = false;
|
|
|
|
inputPath = path;
|
2020-12-30 22:07:54 -06:00
|
|
|
SetCachePath(path);
|
2020-07-08 03:20:03 -05:00
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
2021-01-09 16:53:39 -06:00
|
|
|
Feed::Feed(Feed* feed)
|
|
|
|
: Feed()
|
|
|
|
{
|
|
|
|
SetCachePath(feed->GetCachePath());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::Feed()
|
2020-08-11 13:29:49 -05:00
|
|
|
{
|
2020-07-08 04:43:35 -05:00
|
|
|
title = BString("");
|
|
|
|
description = BString("");
|
|
|
|
homeUrl = BString("");
|
|
|
|
xmlUrl = BString("");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-14 14:00:07 -05:00
|
|
|
void
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::Parse()
|
2020-08-14 14:00:07 -05:00
|
|
|
{
|
2020-12-30 22:07:54 -06:00
|
|
|
BFile* feedFile = new BFile(GetCachePath().String(), B_READ_ONLY);
|
2020-08-14 14:00:07 -05:00
|
|
|
BDateTime attrLastDate = BDateTime();
|
2020-12-30 22:07:54 -06:00
|
|
|
time_t tt_lastDate = 0;
|
2020-08-14 14:00:07 -05:00
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
feedFile->ReadAttr("LastDate", B_TIME_TYPE, 0, &tt_lastDate,
|
|
|
|
sizeof(time_t));
|
|
|
|
|
|
|
|
if (tt_lastDate > 0 && ((App*)be_app)->cfg->updateFeeds == true) {
|
|
|
|
attrLastDate.SetTime_t(tt_lastDate);
|
2020-08-14 14:00:07 -05:00
|
|
|
minDate = attrLastDate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-02 02:05:20 -05:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// Download a remote feed's XML to the cache path.
|
2020-08-02 02:05:20 -05:00
|
|
|
BString
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::FetchRemoteFeed()
|
2020-08-02 02:05:20 -05:00
|
|
|
{
|
2020-12-30 22:07:54 -06:00
|
|
|
BUrl givenUrl = BUrl(inputPath);
|
2020-08-14 01:59:15 -05:00
|
|
|
time_t tt_lastDate = 0;
|
|
|
|
BDateTime* lastDate = new BDateTime();
|
2021-01-11 11:41:46 -06:00
|
|
|
BString newHash;
|
|
|
|
BString oldHash;
|
2020-08-02 02:05:20 -05:00
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
BFile* cacheFile = new BFile(GetCachePath(), B_READ_WRITE | B_CREATE_FILE);
|
2020-07-13 12:31:52 -05:00
|
|
|
|
2021-01-11 11:41:46 -06:00
|
|
|
// cacheFile->ReadAttr("LastHash", B_STRING_TYPE, 0, oldHash, 41);
|
|
|
|
cacheFile->ReadAttrString("LastHash", &oldHash);
|
2020-08-02 02:05:20 -05:00
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
if (((App*)be_app)->cfg->verbose)
|
|
|
|
printf("Saving %s...\n", inputPath.String());
|
2020-08-02 02:05:20 -05:00
|
|
|
|
2021-01-11 11:41:46 -06:00
|
|
|
fetch(BUrl(inputPath), cacheFile, &newHash, 30);
|
|
|
|
cacheFile->WriteAttrString("LastHash", &newHash);
|
|
|
|
// cacheFile->WriteAttr("LastHash", B_STRING_TYPE, 0,
|
|
|
|
// newHash.String(), newHash.CountChars());
|
2020-08-02 02:05:20 -05:00
|
|
|
|
2021-01-11 11:41:46 -06:00
|
|
|
if (newHash == oldHash)
|
2020-08-02 02:05:20 -05:00
|
|
|
updated = false;
|
2020-07-13 12:31:52 -05:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
fetched = true;
|
|
|
|
return GetCachePath();
|
2020-07-13 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// return whether or not the feed's given URI/path is remote.
|
|
|
|
bool
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::IsRemote ()
|
2020-11-16 21:32:06 -06:00
|
|
|
{
|
2020-12-30 22:07:54 -06:00
|
|
|
return isRemotePath(inputPath);
|
2020-11-16 21:32:06 -06:00
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// return whether or not the feed seems to have been updated
|
|
|
|
bool
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::IsUpdated ()
|
2020-11-16 21:32:06 -06:00
|
|
|
{
|
|
|
|
return updated;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// return whether or not feed is RSS
|
2020-07-08 04:43:35 -05:00
|
|
|
bool
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::IsRss ()
|
2020-07-08 04:43:35 -05:00
|
|
|
{
|
2020-11-16 21:32:06 -06:00
|
|
|
EnsureCached();
|
2020-07-08 04:43:35 -05:00
|
|
|
tinyxml2::XMLDocument xml;
|
2020-12-30 22:07:54 -06:00
|
|
|
xml.LoadFile(GetCachePath().String());
|
2020-07-08 04:43:35 -05:00
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
if (xml.FirstChildElement("rss"))
|
2020-07-08 04:43:35 -05:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// return whether or not feed is Atom
|
2020-07-08 04:43:35 -05:00
|
|
|
bool
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::IsAtom ()
|
2020-07-08 04:43:35 -05:00
|
|
|
{
|
|
|
|
tinyxml2::XMLDocument xml;
|
2020-12-30 22:07:54 -06:00
|
|
|
xml.LoadFile(GetCachePath().String());
|
2020-07-08 04:43:35 -05:00
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
if (xml.FirstChildElement("feed"))
|
2020-07-08 04:43:35 -05:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// ensure the feed XML is available at the cache path.
|
|
|
|
// if necessary, download it.
|
|
|
|
void
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::EnsureCached ()
|
2020-11-16 21:32:06 -06:00
|
|
|
{
|
2020-12-30 22:07:54 -06:00
|
|
|
if (IsRemote() && fetched == false)
|
2020-11-16 21:32:06 -06:00
|
|
|
FetchRemoteFeed();
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// Return the 'cachePath' (location of XML file locally)
|
|
|
|
BString
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::GetCachePath ()
|
2020-11-16 21:32:06 -06:00
|
|
|
{
|
2020-12-30 22:07:54 -06:00
|
|
|
if (cachePath == NULL)
|
|
|
|
SetCachePath(inputPath);
|
2020-11-16 21:32:06 -06:00
|
|
|
return cachePath;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// Select a 'cachePath' (location of XML file locally)
|
|
|
|
// For remote files, a cache file is created in ~/config/cache/Pogger/ by default
|
|
|
|
// For local files, the same local file is used.
|
|
|
|
BString
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::SetCachePath (BString givenPath)
|
2020-11-16 21:32:06 -06:00
|
|
|
{
|
2020-12-30 22:07:54 -06:00
|
|
|
BUrl givenUrl = BUrl(givenPath);
|
2020-11-16 21:32:06 -06:00
|
|
|
BString protocol = givenUrl.Protocol().String();
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
if (protocol == NULL && givenUrl.UrlString() != NULL) {
|
2020-11-16 21:32:06 -06:00
|
|
|
cachePath = givenPath;
|
|
|
|
return givenPath;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
BString splitName = givenUrl.Host();
|
|
|
|
splitName.Append(givenUrl.Path());
|
2020-11-16 21:32:06 -06:00
|
|
|
splitName.ReplaceAll("/", "_");
|
|
|
|
|
|
|
|
BString filename = ((App*)be_app)->cfg->cacheDir;
|
|
|
|
filename.Append(splitName);
|
|
|
|
|
|
|
|
cachePath = filename;
|
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-09 16:53:39 -06:00
|
|
|
// Count the amount of siblings to an element of given type name
|
2020-07-08 04:43:35 -05:00
|
|
|
int
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::xmlCountSiblings (tinyxml2::XMLElement* xsibling, const char* sibling_name)
|
2020-07-08 03:20:03 -05:00
|
|
|
{
|
2020-07-08 04:43:35 -05:00
|
|
|
int count = 0;
|
2020-12-30 22:07:54 -06:00
|
|
|
while (xsibling) {
|
2020-07-08 04:43:35 -05:00
|
|
|
count++;
|
|
|
|
xsibling = xsibling->NextSiblingElement(sibling_name);
|
|
|
|
}
|
|
|
|
return count;
|
2020-07-08 03:20:03 -05:00
|
|
|
}
|
|
|
|
|
2020-07-08 04:43:35 -05:00
|
|
|
|
2021-01-09 16:53:39 -06:00
|
|
|
// Add the given entry to the feed, if appropriate
|
2020-08-14 01:59:15 -05:00
|
|
|
bool
|
2020-12-30 22:07:54 -06:00
|
|
|
Feed::AddEntry (Entry* newEntry)
|
2020-08-14 01:59:15 -05:00
|
|
|
{
|
2020-11-16 20:32:58 -06:00
|
|
|
Config* cfg = ((App*)be_app)->cfg;
|
2020-12-30 22:07:54 -06:00
|
|
|
if (!withinDateRange(cfg->minDate, newEntry->date, cfg->maxDate) ||
|
|
|
|
!withinDateRange(minDate, newEntry->date, maxDate))
|
2020-08-14 01:59:15 -05:00
|
|
|
return false;
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
if (cfg->verbose == true)
|
|
|
|
printf("\t%s\n", newEntry->title.String());
|
|
|
|
entries.AddItem(newEntry);
|
2020-08-14 01:59:15 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
BList
|
|
|
|
Feed::GetEntries()
|
|
|
|
{
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
Feed::SetTitle(const char* titleStr)
|
|
|
|
{
|
|
|
|
if (titleStr != NULL)
|
|
|
|
title = BString(titleStr);
|
2020-07-08 03:20:03 -05:00
|
|
|
else return false;
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
Feed::SetTitle(tinyxml2::XMLElement* elem)
|
|
|
|
{
|
|
|
|
if (elem != NULL)
|
|
|
|
return SetTitle(elem->GetText());
|
2020-07-08 03:20:03 -05:00
|
|
|
else return false;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
BString
|
|
|
|
Feed::GetTitle()
|
|
|
|
{
|
|
|
|
return title;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
Feed::SetDesc(const char* descStr)
|
|
|
|
{
|
|
|
|
if (descStr != NULL)
|
|
|
|
description = BString(descStr);
|
2020-07-08 03:20:03 -05:00
|
|
|
else return false;
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
Feed::SetDesc(tinyxml2::XMLElement* elem)
|
|
|
|
{
|
|
|
|
if (elem != NULL)
|
|
|
|
return SetDesc(elem->GetText());
|
2020-07-08 03:20:03 -05:00
|
|
|
else return false;
|
|
|
|
}
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
BString
|
|
|
|
Feed::GetDesc()
|
|
|
|
{
|
|
|
|
return description;
|
|
|
|
}
|
|
|
|
|
2020-07-08 03:20:03 -05:00
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// set a feed's «home URL»
|
2020-12-30 22:07:54 -06:00
|
|
|
bool
|
|
|
|
Feed::SetHomeUrl(const char* homepageStr)
|
|
|
|
{
|
|
|
|
if (homepageStr != NULL)
|
|
|
|
homeUrl = BString(homepageStr);
|
2020-07-08 03:20:03 -05:00
|
|
|
else return false;
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
Feed::SetHomeUrl(tinyxml2::XMLElement* elem)
|
|
|
|
{
|
|
|
|
if (elem != NULL)
|
|
|
|
return SetHomeUrl(elem->GetText());
|
2020-07-08 03:20:03 -05:00
|
|
|
else return false;
|
|
|
|
}
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
BString
|
|
|
|
Feed::GetHomeUrl()
|
|
|
|
{
|
|
|
|
return homeUrl;
|
|
|
|
}
|
|
|
|
|
2020-07-08 03:20:03 -05:00
|
|
|
|
2021-01-09 16:53:39 -06:00
|
|
|
BString
|
|
|
|
Feed::GetXmlUrl()
|
|
|
|
{
|
|
|
|
return xmlUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-16 21:32:06 -06:00
|
|
|
// set the update time for a feed
|
2020-12-30 22:07:54 -06:00
|
|
|
bool
|
|
|
|
Feed::SetDate(const char* dateCStr)
|
|
|
|
{
|
|
|
|
if (dateCStr == NULL)
|
2020-07-08 03:20:03 -05:00
|
|
|
return false;
|
2020-12-30 22:07:54 -06:00
|
|
|
BDateTime newDate = feedDateToBDate(dateCStr);
|
|
|
|
if (newDate == NULL)
|
2020-07-08 03:20:03 -05:00
|
|
|
return false;
|
|
|
|
date = newDate;
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
Feed::SetDate(tinyxml2::XMLElement* elem)
|
|
|
|
{
|
|
|
|
if (elem != NULL)
|
|
|
|
return SetDate(elem->GetText());
|
2020-07-08 03:20:03 -05:00
|
|
|
else return false;
|
|
|
|
}
|
2020-12-30 22:07:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
BDateTime
|
|
|
|
Feed::GetDate()
|
|
|
|
{
|
|
|
|
return date;
|
|
|
|
}
|
|
|
|
|
2020-07-08 04:43:35 -05:00
|
|
|
|