mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-08-26 01:34:03 +02:00
Compare commits
30 Commits
2017-08-03
...
2017-08-19
Author | SHA1 | Date | |
---|---|---|---|
|
2595b5d7d8 | ||
|
f858adc884 | ||
|
44e135ce1e | ||
|
9a9ce30b16 | ||
|
0e2b80d5d7 | ||
|
1b1ab6a66e | ||
|
0284e9d488 | ||
|
f91309c7e4 | ||
|
cd012e115b | ||
|
df9e3968dc | ||
|
c237eaa254 | ||
|
f757d7d1a5 | ||
|
4fb1366aaf | ||
|
8166e33e7f | ||
|
ff3b1c9eb2 | ||
|
4924769549 | ||
|
e4fa963bdf | ||
|
54e8bb2228 | ||
|
99e7e7876e | ||
|
62c190d841 | ||
|
84d2c02a09 | ||
|
fc0ae42450 | ||
|
9599f921a5 | ||
|
e125e9aba1 | ||
|
55a77c734d | ||
|
ccd8af09b9 | ||
|
f2d02a4187 | ||
|
f19d34a5a1 | ||
|
f1534c91e2 | ||
|
cbda060b86 |
148
CHANGELOG.md
148
CHANGELOG.md
@@ -1,75 +1,105 @@
|
||||
rss-bridge Changelog
|
||||
===
|
||||
|
||||
RSS-Bridge 2017-08-19
|
||||
==
|
||||
|
||||
## General changes
|
||||
* whitelist: Do case-insensitive whitelist matching
|
||||
* [FeedExpander] Fix Serialization of 'SimpleXMLElement' is not allowed
|
||||
* [FeedExpander] Remove whitespace from source content
|
||||
* [index] Add GET parameter 'q' for search queries
|
||||
- **Example**: You can now add `&q=Twitter` to load into the search field
|
||||
* [index] Check permissions for cache folder and whitelist file
|
||||
* [index] Show bridge options when loading with URL fragment
|
||||
- **Example**: You can now add `#bridge-Twitter` to load the card with all
|
||||
parameters visible
|
||||
* [style] Center search cursor and hide placeholder
|
||||
* [validation] Fix error on undefined optional numeric value
|
||||
|
||||
## Modified bridges
|
||||
* [DanbooruBridge] Allow descendant classes to override tag collection
|
||||
* [DribbbleBridge] Add dribble bridge listing last dribble popular shots (#558)
|
||||
* [FacebookBridge] Fix & in URLs
|
||||
* [GelbooruBridge] Fix bridge not getting tags correctly
|
||||
* [GoComicsBridge] Fix for page structure changes (#568)
|
||||
* [LeBonCoinBridge] Fix bridge is marked executable
|
||||
* [LWNprevBridge] Fix everchanging url
|
||||
* [YoutubeBridge] Fix error on certain keywords
|
||||
* [YoutubeBridge] Fix issues loading playlists
|
||||
|
||||
## Removed bridges
|
||||
* VineBridge
|
||||
|
||||
RSS-Bridge 2017-08-03
|
||||
==
|
||||
|
||||
## Important changes
|
||||
RSS-Bridge now has [contribution guidelines](CONTRIBUTING.md)
|
||||
[phpcs rules](phpcs.xml) follow the [contribution guidelines](CONTRIBUTING.md)
|
||||
* RSS-Bridge now has [contribution guidelines](CONTRIBUTING.md)
|
||||
* [phpcs rules](phpcs.xml) follow the [contribution guidelines](CONTRIBUTING.md)
|
||||
|
||||
## General changes
|
||||
Added a search bar to make searching for bridges easier
|
||||
Added user friendly error page for when a bridge fails
|
||||
Added caching of extraInfos (name, uri)
|
||||
Added an indicator to warn for bridges using HTTP instead of HTTPS
|
||||
Various bug fixes and improvements
|
||||
* Added a search bar to make searching for bridges easier
|
||||
* Added user friendly error page for when a bridge fails
|
||||
* Added caching of extraInfos (name, uri)
|
||||
* Added an indicator to warn for bridges using HTTP instead of HTTPS
|
||||
* Various bug fixes and improvements
|
||||
|
||||
## Modified bridges
|
||||
[AllocineFRBridge] Update Faux Raccord link
|
||||
[DanbooruBridge] Fix broken URI
|
||||
[DuckDuckGoBridge] Disable DuckDuckGo redirects so that the links returned are correct.
|
||||
[FacebookBridge] Add option to hide posts with facebook videos
|
||||
[FacebookBridge] Add requester languages to HTTP header
|
||||
[FacebookBridge] Handle summary posts
|
||||
[FacebookBridge] Replace 'novideo' with 'media_type'
|
||||
[FilterBridge] Initial implementation of basic title permit and block
|
||||
[FlickrTagBridge] Fix and improve bridge by using the FlickrExploreBridge approach
|
||||
[GooglePlusPostBridge] Autofix user names
|
||||
[GooglePlusPostBridge] Fix bridge implementation
|
||||
[GooglePlusPostBridge] Fix content loading
|
||||
[InstagramBridge] Add option to filter for videos and pictures
|
||||
[LWNprevBridge] full rewrite
|
||||
[MangareaderBridge] Fix double forward slashes
|
||||
[NasaApodBridge] Use HTTPS instead of HTTP
|
||||
[PinterestBridge] Fix checkbox not working
|
||||
[PinterestBridge] Fix implementation after DOM changes
|
||||
[RTBFBridge] Update URI
|
||||
[SexactuBridge] Fix URI and timestamp
|
||||
[SexactuBridge] Use most modern version of bridge api and cached pages (#504)
|
||||
[ShanaprojectBridge] Don't throw error if timestamp is missing
|
||||
[TwitterBridge] Add option to hide retweets
|
||||
[TwitterBridge] Avoid empty content caused by new login policy
|
||||
[TwitterBridge] Fix double slashes in URI
|
||||
[TwitterBridge] Fix missing spaces
|
||||
[TwitterBridge] Fix title includes anchors in plaintext format
|
||||
[TwitterBridge] ignore promoted tweets
|
||||
[TwitterBridge] Optimize returned image sizes
|
||||
[TwitterBridge] Show quotes and pictures
|
||||
[WebfailBridge] Properly handle gifs (DOM changed)
|
||||
[YoutubeBridge] Improve readability of feed contents
|
||||
[YoutubeBridge] Improve URL handling in video descriptions
|
||||
* AllocineFRBridge] Update Faux Raccord link
|
||||
* [DanbooruBridge] Fix broken URI
|
||||
* [DuckDuckGoBridge] Disable DuckDuckGo redirects so that the links returned are correct.
|
||||
* [FacebookBridge] Add option to hide posts with facebook videos
|
||||
* [FacebookBridge] Add requester languages to HTTP header
|
||||
* [FacebookBridge] Handle summary posts
|
||||
* [FacebookBridge] Replace 'novideo' with 'media_type'
|
||||
* [FilterBridge] Initial implementation of basic title permit and block
|
||||
* [FlickrTagBridge] Fix and improve bridge by using the FlickrExploreBridge approach
|
||||
* [GooglePlusPostBridge] Autofix user names
|
||||
* [GooglePlusPostBridge] Fix bridge implementation
|
||||
* [GooglePlusPostBridge] Fix content loading
|
||||
* [InstagramBridge] Add option to filter for videos and pictures
|
||||
* [LWNprevBridge] full rewrite
|
||||
* [MangareaderBridge] Fix double forward slashes
|
||||
* [NasaApodBridge] Use HTTPS instead of HTTP
|
||||
* [PinterestBridge] Fix checkbox not working
|
||||
* [PinterestBridge] Fix implementation after DOM changes
|
||||
* [RTBFBridge] Update URI
|
||||
* [SexactuBridge] Fix URI and timestamp
|
||||
* [SexactuBridge] Use most modern version of bridge api and cached pages (#504)
|
||||
* [ShanaprojectBridge] Don't throw error if timestamp is missing
|
||||
* [TwitterBridge] Add option to hide retweets
|
||||
* [TwitterBridge] Avoid empty content caused by new login policy
|
||||
* [TwitterBridge] Fix double slashes in URI
|
||||
* [TwitterBridge] Fix missing spaces
|
||||
* [TwitterBridge] Fix title includes anchors in plaintext format
|
||||
* [TwitterBridge] ignore promoted tweets
|
||||
* [TwitterBridge] Optimize returned image sizes
|
||||
* [TwitterBridge] Show quotes and pictures
|
||||
* [WebfailBridge] Properly handle gifs (DOM changed)
|
||||
* [YoutubeBridge] Improve readability of feed contents
|
||||
* [YoutubeBridge] Improve URL handling in video descriptions
|
||||
|
||||
## New bridges
|
||||
AmazonBridge
|
||||
DiceBridge
|
||||
EtsyBridge
|
||||
FB2Bridge
|
||||
FilterBridge
|
||||
FlickrBridge
|
||||
GithubSearchBridge
|
||||
GoComicsBridge
|
||||
KATBridge
|
||||
KernelBugTrackerBridge
|
||||
MixCloudBridge
|
||||
MoinMoinBridge
|
||||
RainbowSixSiegeBridge
|
||||
SteamBridge
|
||||
TheTVDBBridge
|
||||
Torrent9Bridge
|
||||
UsbekEtRicaBridge
|
||||
WikiLeaksBridge
|
||||
WordPressPluginUpdateBridge
|
||||
* AmazonBridge
|
||||
* DiceBridge
|
||||
* EtsyBridge
|
||||
* FB2Bridge
|
||||
* FilterBridge
|
||||
* FlickrBridge
|
||||
* GithubSearchBridge
|
||||
* GoComicsBridge
|
||||
* KATBridge
|
||||
* KernelBugTrackerBridge
|
||||
* MixCloudBridge
|
||||
* MoinMoinBridge
|
||||
* RainbowSixSiegeBridge
|
||||
* SteamBridge
|
||||
* TheTVDBBridge
|
||||
* Torrent9Bridge
|
||||
* UsbekEtRicaBridge
|
||||
* WikiLeaksBridge
|
||||
* WordPressPluginUpdateBridge
|
||||
|
||||
Alpha 0.2
|
||||
===
|
||||
|
@@ -23,6 +23,7 @@ class DanbooruBridge extends BridgeAbstract {
|
||||
|
||||
const PATHTODATA = 'article';
|
||||
const IDATTRIBUTE = 'data-id';
|
||||
const TAGATTRIBUTE = 'alt';
|
||||
|
||||
protected function getFullURI(){
|
||||
return $this->getURI()
|
||||
@@ -30,6 +31,10 @@ class DanbooruBridge extends BridgeAbstract {
|
||||
. '&tags=' . urlencode($this->getInput('t'));
|
||||
}
|
||||
|
||||
protected function getTags($element){
|
||||
return $element->find('img', 0)->getAttribute(static::TAGATTRIBUTE);
|
||||
}
|
||||
|
||||
protected function getItemFromElement($element){
|
||||
// Fix links
|
||||
defaultLinkTo($element, $this->getURI());
|
||||
@@ -39,7 +44,7 @@ class DanbooruBridge extends BridgeAbstract {
|
||||
$item['postid'] = (int)preg_replace("/[^0-9]/", '', $element->getAttribute(static::IDATTRIBUTE));
|
||||
$item['timestamp'] = time();
|
||||
$thumbnailUri = $element->find('img', 0)->src;
|
||||
$item['tags'] = $element->find('img', 0)->getAttribute('alt');
|
||||
$item['tags'] = $this->getTags($element);
|
||||
$item['title'] = $this->getName() . ' | ' . $item['postid'];
|
||||
$item['content'] = '<a href="'
|
||||
. $item['uri']
|
||||
|
91
bridges/DribbbleBridge.php
Normal file
91
bridges/DribbbleBridge.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
class DribbbleBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'quentinus95';
|
||||
const NAME = 'Dribbble popular shots';
|
||||
const URI = 'https://dribbble.com';
|
||||
const CACHE_TIMEOUT = 1800;
|
||||
const DESCRIPTION = 'Returns the newest popular shots from Dribbble.';
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(self::URI . '/shots')
|
||||
or returnServerError('Error while downloading the website content');
|
||||
|
||||
$json = $this->loadEmbeddedJsonData($html);
|
||||
|
||||
foreach($html->find('li[id^="screenshot-"]') as $shot) {
|
||||
$item = [];
|
||||
|
||||
$additional_data = $this->findJsonForShot($shot, $json);
|
||||
if ($additional_data === null) {
|
||||
$item['uri'] = self::URI . $shot->find('a', 0)->href;
|
||||
$item['title'] = $shot->find('.dribbble-over strong', 0)->plaintext;
|
||||
} else {
|
||||
$item['timestamp'] = strtotime($additional_data['published_at']);
|
||||
$item['uri'] = self::URI . $additional_data['path'];
|
||||
$item['title'] = $additional_data['title'];
|
||||
}
|
||||
|
||||
$item['author'] = trim($shot->find('.attribution-user a', 0)->plaintext);
|
||||
|
||||
$description = $shot->find('.comment', 0);
|
||||
$item['content'] = $description === null ? '' : $description->plaintext;
|
||||
|
||||
$preview_path = $shot->find('picture source', 0)->attr['srcset'];
|
||||
$item['content'] .= $this->getImageTag($preview_path, $item['title']);
|
||||
$item['enclosures'] = [$this->getFullSizeImagePath($preview_path)];
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
private function loadEmbeddedJsonData($html){
|
||||
$json = [];
|
||||
$scripts = $html->find('script');
|
||||
|
||||
foreach($scripts as $script) {
|
||||
if(strpos($script->innertext, 'newestShots') !== false) {
|
||||
// fix single quotes
|
||||
$script->innertext = str_replace('\'', '"', $script->innertext);
|
||||
|
||||
// fix JavaScript JSON (why do they not adhere to the standard?)
|
||||
$script->innertext = preg_replace('/(\w+):/i', '"\1":', $script->innertext);
|
||||
|
||||
// find beginning of JSON array
|
||||
$start = strpos($script->innertext, '[');
|
||||
|
||||
// find end of JSON array, compensate for missing character!
|
||||
$end = strpos($script->innertext, '];') + 1;
|
||||
|
||||
// convert JSON to PHP array
|
||||
$json = json_decode(substr($script->innertext, $start, $end - $start), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
private function findJsonForShot($shot, $json){
|
||||
foreach($json as $element) {
|
||||
if(strpos($shot->getAttribute('id'), (string)$element['id']) !== false) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getImageTag($preview_path, $title){
|
||||
return sprintf(
|
||||
'<br /> <a href="%s"><img src="%s" alt="%s" /></a>',
|
||||
$this->getFullSizeImagePath($preview_path),
|
||||
$preview_path,
|
||||
$title
|
||||
);
|
||||
}
|
||||
|
||||
private function getFullSizeImagePath($preview_path){
|
||||
return str_replace('_1x', '', $preview_path);
|
||||
}
|
||||
}
|
@@ -155,7 +155,7 @@ class FacebookBridge extends BridgeAbstract {
|
||||
|
||||
//Show captcha filling form to the viewer, proxying the captcha image
|
||||
$img = base64_encode(getContents($captcha->find('img', 0)->src));
|
||||
header('HTTP/1.1 500 ' . Http::getMessageForCode(500));
|
||||
http_response_code(500);
|
||||
header('Content-Type: text/html');
|
||||
$message = <<<EOD
|
||||
<form method="post" action="?{$_SERVER['QUERY_STRING']}">
|
||||
@@ -281,9 +281,11 @@ EOD;
|
||||
if(strlen($title) > 64)
|
||||
$title = substr($title, 0, strpos(wordwrap($title, 64), "\n")) . '...';
|
||||
|
||||
$uri = self::URI . $post->find('abbr')[0]->parent()->getAttribute('href');
|
||||
|
||||
//Build and add final item
|
||||
$item['uri'] = self::URI . $post->find('abbr')[0]->parent()->getAttribute('href');
|
||||
$item['content'] = $content;
|
||||
$item['uri'] = htmlspecialchars_decode($uri);
|
||||
$item['content'] = htmlspecialchars_decode($content);
|
||||
$item['title'] = $title;
|
||||
$item['author'] = $author;
|
||||
$item['timestamp'] = $date;
|
||||
|
@@ -10,6 +10,7 @@ class GelbooruBridge extends DanbooruBridge {
|
||||
|
||||
const PATHTODATA = '.thumb';
|
||||
const IDATTRIBUTE = 'id';
|
||||
const TAGATTRIBUTE = 'title';
|
||||
|
||||
const PIDBYPAGE = 63;
|
||||
|
||||
@@ -19,4 +20,16 @@ class GelbooruBridge extends DanbooruBridge {
|
||||
. ($this->getInput('p') ? ($this->getInput('p') - 1) * static::PIDBYPAGE : '')
|
||||
. '&tags=' . urlencode($this->getInput('t'));
|
||||
}
|
||||
|
||||
protected function getTags($element){
|
||||
$tags = parent::getTags($element);
|
||||
$tags = explode(' ', $tags);
|
||||
|
||||
// Remove statistics from the tags list (identified by colon)
|
||||
foreach($tags as $key => $tag) {
|
||||
if(strpos($tag, ':') !== false) unset($tags[$key]);
|
||||
}
|
||||
|
||||
return implode(' ', $tags);
|
||||
}
|
||||
}
|
||||
|
@@ -18,10 +18,10 @@ class GoComicsBridge extends BridgeAbstract {
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Could not request GoComics: ' . $this->getURI());
|
||||
|
||||
foreach($html->find('div.item-comic-container') as $element) {
|
||||
foreach($html->find('div.comic__container') as $element) {
|
||||
|
||||
$img = $element->find('img', 0);
|
||||
$link = $element->find('a.item-comic-link', 0);
|
||||
$link = $element->find('a.js-item-comic-link', 0);
|
||||
$comic = $img->src;
|
||||
$title = $link->title;
|
||||
$url = $html->find('input.js-copy-link', 0)->value;
|
||||
|
@@ -188,9 +188,9 @@ EOD;
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = self::URI.'#'.microtime(true);
|
||||
$item['uri'] = self::URI.'#'.count($items);
|
||||
|
||||
$item['timestamp'] = $this->editionTimeStamp;//+$URICounter;
|
||||
$item['timestamp'] = $this->editionTimeStamp;
|
||||
|
||||
$item['author'] = 'LWN';
|
||||
|
||||
|
0
bridges/LeBonCoinBridge.php
Executable file → Normal file
0
bridges/LeBonCoinBridge.php
Executable file → Normal file
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
class VineBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'ckiw';
|
||||
const NAME = 'Vine bridge';
|
||||
const URI = 'http://vine.co/';
|
||||
const DESCRIPTION = 'Returns the latests vines from vine user page';
|
||||
|
||||
const PARAMETERS = array( array(
|
||||
'u' => array(
|
||||
'name' => 'User id',
|
||||
'required' => true
|
||||
)
|
||||
));
|
||||
|
||||
public function collectData(){
|
||||
$html = '';
|
||||
$uri = self::URI . '/u/' . $this->getInput('u') . '?mode=list';
|
||||
|
||||
$html = getSimpleHTMLDOM($uri)
|
||||
or returnServerError('No results for this query.');
|
||||
|
||||
foreach($html->find('.post') as $element) {
|
||||
$a = $element->find('a', 0);
|
||||
$a->href = str_replace('https://', 'http://', $a->href);
|
||||
$time = strtotime(ltrim($element->find('p', 0)->plaintext, ' Uploaded at '));
|
||||
$video = $element->find('video', 0);
|
||||
$video->controls = 'true';
|
||||
$element->find('h2', 0)->outertext = '';
|
||||
|
||||
$item = array();
|
||||
$item['uri'] = $a->href;
|
||||
$item['timestamp'] = $time;
|
||||
$item['title'] = $a->plaintext;
|
||||
$item['content'] = $element;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
@@ -53,8 +53,12 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
$author = $html->innertext;
|
||||
$author = substr($author, strpos($author, '"author=') + 8);
|
||||
$author = substr($author, 0, strpos($author, '\u0026'));
|
||||
$desc = $html->find('div#watch-description-text', 0)->innertext;
|
||||
$time = strtotime($html->find('meta[itemprop=datePublished]', 0)->getAttribute('content'));
|
||||
|
||||
if(!is_null($html->find('div#watch-description-text', 0)))
|
||||
$desc = $html->find('div#watch-description-text', 0)->innertext;
|
||||
|
||||
if(!is_null($html->find('meta[itemprop=datePublished]', 0)))
|
||||
$time = strtotime($html->find('meta[itemprop=datePublished]', 0)->getAttribute('content'));
|
||||
}
|
||||
|
||||
private function ytBridgeAddItem($vid, $title, $author, $desc, $time){
|
||||
@@ -98,6 +102,7 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
$desc = '';
|
||||
$time = 0;
|
||||
$vid = str_replace('/watch?v=', '', $element->find('a', 0)->href);
|
||||
$vid = substr($vid, 0, strpos($vid, '&') ?: strlen($vid));
|
||||
$title = $this->ytBridgeFixTitle($element->find($title_selector, 0)->plaintext);
|
||||
if($title != '[Private Video]') {
|
||||
$this->ytBridgeQueryVideoInfo($vid, $author, $desc, $time);
|
||||
|
93
index.php
93
index.php
@@ -25,25 +25,23 @@ error_reporting(0);
|
||||
// Specify directory for cached files (using FileCache)
|
||||
define('CACHE_DIR', __DIR__ . '/cache');
|
||||
|
||||
// Specify path for whitelist file
|
||||
define('WHITELIST_FILE', __DIR__ . '/whitelist.txt');
|
||||
|
||||
/*
|
||||
Create a file named 'DEBUG' for enabling debug mode.
|
||||
For further security, you may put whitelisted IP addresses
|
||||
in the 'DEBUG' file, one IP per line. Empty file allows anyone(!).
|
||||
Debugging allows displaying PHP error messages and bypasses the cache: this can allow a malicious
|
||||
client to retrieve data about your server and hammer a provider throught your rss-bridge instance.
|
||||
For further security, you may put whitelisted IP addresses in the file,
|
||||
one IP per line. Empty file allows anyone(!).
|
||||
Debugging allows displaying PHP error messages and bypasses the cache: this
|
||||
can allow a malicious client to retrieve data about your server and hammer
|
||||
a provider throught your rss-bridge instance.
|
||||
*/
|
||||
if(file_exists('DEBUG')) {
|
||||
$debug_enabled = true;
|
||||
$debug_whitelist = trim(file_get_contents('DEBUG'));
|
||||
if(strlen($debug_whitelist) > 0) {
|
||||
$debug_enabled = false;
|
||||
foreach(explode("\n", $debug_whitelist) as $allowed_ip) {
|
||||
if(trim($allowed_ip) === $_SERVER['REMOTE_ADDR']) {
|
||||
$debug_enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$debug_enabled = empty($debug_whitelist)
|
||||
|| in_array($_SERVER['REMOTE_ADDR'], explode("\n", $debug_whitelist));
|
||||
|
||||
if($debug_enabled) {
|
||||
ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
@@ -68,6 +66,14 @@ if(!extension_loaded('libxml'))
|
||||
if(ini_get('allow_url_fopen') !== "1")
|
||||
die('"allow_url_fopen" is not set to "1". Please check "php.ini');
|
||||
|
||||
// Check cache folder permissions (write permissions required)
|
||||
if(!is_writable(CACHE_DIR))
|
||||
die('RSS-Bridge does not have write permissions for ' . CACHE_DIR . '!');
|
||||
|
||||
// Check whitelist file permissions (only in DEBUG mode)
|
||||
if(!file_exists(WHITELIST_FILE) && !is_writable(dirname(WHITELIST_FILE)))
|
||||
die('RSS-Bridge does not have write permissions for ' . WHITELIST_FILE . '!');
|
||||
|
||||
// FIXME : beta test UA spoofing, please report any blacklisting by PHP-fopen-unfriendly websites
|
||||
|
||||
$userAgent = 'Mozilla/5.0(X11; Linux x86_64; rv:30.0)';
|
||||
@@ -77,24 +83,23 @@ $userAgent .= '+https://github.com/RSS-Bridge/rss-bridge)';
|
||||
ini_set('user_agent', $userAgent);
|
||||
|
||||
// default whitelist
|
||||
$whitelist_file = './whitelist.txt';
|
||||
$whitelist_default = array(
|
||||
"BandcampBridge",
|
||||
"CryptomeBridge",
|
||||
"DansTonChatBridge",
|
||||
"DuckDuckGoBridge",
|
||||
"FacebookBridge",
|
||||
"FlickrExploreBridge",
|
||||
"GooglePlusPostBridge",
|
||||
"GoogleSearchBridge",
|
||||
"IdenticaBridge",
|
||||
"InstagramBridge",
|
||||
"OpenClassroomsBridge",
|
||||
"PinterestBridge",
|
||||
"ScmbBridge",
|
||||
"TwitterBridge",
|
||||
"WikipediaBridge",
|
||||
"YoutubeBridge");
|
||||
'BandcampBridge',
|
||||
'CryptomeBridge',
|
||||
'DansTonChatBridge',
|
||||
'DuckDuckGoBridge',
|
||||
'FacebookBridge',
|
||||
'FlickrExploreBridge',
|
||||
'GooglePlusPostBridge',
|
||||
'GoogleSearchBridge',
|
||||
'IdenticaBridge',
|
||||
'InstagramBridge',
|
||||
'OpenClassroomsBridge',
|
||||
'PinterestBridge',
|
||||
'ScmbBridge',
|
||||
'TwitterBridge',
|
||||
'WikipediaBridge',
|
||||
'YoutubeBridge');
|
||||
|
||||
try {
|
||||
|
||||
@@ -102,18 +107,21 @@ try {
|
||||
Format::setDir(__DIR__ . '/formats/');
|
||||
Cache::setDir(__DIR__ . '/caches/');
|
||||
|
||||
if(!file_exists($whitelist_file)) {
|
||||
if(!file_exists(WHITELIST_FILE)) {
|
||||
$whitelist_selection = $whitelist_default;
|
||||
$whitelist_write = implode("\n", $whitelist_default);
|
||||
file_put_contents($whitelist_file, $whitelist_write);
|
||||
file_put_contents(WHITELIST_FILE, $whitelist_write);
|
||||
} else {
|
||||
|
||||
$whitelist_file_content = file_get_contents($whitelist_file);
|
||||
$whitelist_file_content = file_get_contents(WHITELIST_FILE);
|
||||
if($whitelist_file_content != "*\n") {
|
||||
$whitelist_selection = explode("\n", $whitelist_file_content);
|
||||
} else {
|
||||
$whitelist_selection = Bridge::listBridges();
|
||||
}
|
||||
|
||||
// Prepare for case-insensitive match
|
||||
$whitelist_selection = array_map('strtolower', $whitelist_selection);
|
||||
}
|
||||
|
||||
$action = filter_input(INPUT_GET, 'action');
|
||||
@@ -135,7 +143,7 @@ try {
|
||||
}
|
||||
|
||||
// whitelist control
|
||||
if(!Bridge::isWhitelisted($whitelist_selection, $bridge)) {
|
||||
if(!Bridge::isWhitelisted($whitelist_selection, strtolower($bridge))) {
|
||||
throw new \HttpException('This bridge is not whitelisted', 401);
|
||||
die;
|
||||
}
|
||||
@@ -166,7 +174,7 @@ try {
|
||||
$bridge->setCache($cache);
|
||||
$bridge->setDatas($params);
|
||||
} catch(Exception $e) {
|
||||
header('HTTP/1.1 ' . $e->getCode() . ' ' . Http::getMessageForCode($e->getCode()));
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
die(buildBridgeException($e, $bridge));
|
||||
}
|
||||
@@ -178,7 +186,7 @@ try {
|
||||
$format->setExtraInfos($bridge->getExtraInfos());
|
||||
$format->display();
|
||||
} catch(Exception $e) {
|
||||
header('HTTP/1.1 ' . $e->getCode() . ' ' . Http::getMessageForCode($e->getCode()));
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
die(buildTransformException($e, $bridge));
|
||||
}
|
||||
@@ -186,7 +194,7 @@ try {
|
||||
die;
|
||||
}
|
||||
} catch(HttpException $e) {
|
||||
header('HTTP/1.1 ' . $e->getCode() . ' ' . Http::getMessageForCode($e->getCode()));
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/plain');
|
||||
die($e->getMessage());
|
||||
} catch(\Exception $e) {
|
||||
@@ -205,6 +213,7 @@ $formats = Format::searchInformation();
|
||||
<title>RSS-Bridge</title>
|
||||
<link href="static/style.css" rel="stylesheet">
|
||||
<script src="static/search.js"></script>
|
||||
<script src="static/select.js"></script>
|
||||
<noscript>
|
||||
<style>
|
||||
.searchbar {
|
||||
@@ -221,6 +230,8 @@ $formats = Format::searchInformation();
|
||||
$status .= 'debug mode active';
|
||||
}
|
||||
|
||||
$query = filter_input(INPUT_GET, 'q');
|
||||
|
||||
echo <<<EOD
|
||||
<header>
|
||||
<h1>RSS-Bridge</h1>
|
||||
@@ -231,7 +242,7 @@ $formats = Format::searchInformation();
|
||||
<h3>Search</h3>
|
||||
<input type="text" name="searchfield"
|
||||
id="searchfield" placeholder="Enter the bridge you want to search for"
|
||||
onchange="search()" onkeyup="search()">
|
||||
onchange="search()" onkeyup="search()" value="{$query}">
|
||||
</section>
|
||||
|
||||
EOD;
|
||||
@@ -241,7 +252,7 @@ EOD;
|
||||
$inactiveBridges = '';
|
||||
$bridgeList = Bridge::listBridges();
|
||||
foreach($bridgeList as $bridgeName) {
|
||||
if(Bridge::isWhitelisted($whitelist_selection, $bridgeName)) {
|
||||
if(Bridge::isWhitelisted($whitelist_selection, strtolower($bridgeName))) {
|
||||
echo displayBridgeCard($bridgeName, $formats);
|
||||
$activeFoundBridgeCount++;
|
||||
} elseif($showInactive) {
|
||||
@@ -252,7 +263,7 @@ EOD;
|
||||
echo $inactiveBridges;
|
||||
?>
|
||||
<section class="footer">
|
||||
<a href="https://github.com/RSS-Bridge/rss-bridge">RSS-Bridge 2017-08-03 ~ Public Domain</a><br />
|
||||
<a href="https://github.com/RSS-Bridge/rss-bridge">RSS-Bridge 2017-08-19 ~ Public Domain</a><br />
|
||||
<?= $activeFoundBridgeCount; ?>/<?= count($bridgeList) ?> active bridges. <br />
|
||||
<?php
|
||||
if($activeFoundBridgeCount !== count($bridgeList)) {
|
||||
|
@@ -8,16 +8,6 @@ class Bridge {
|
||||
throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a bridge is an instantiable bridge.
|
||||
* @param string $nameBridge name of the bridge that you want to use
|
||||
* @return true if it is an instantiable bridge, false otherwise.
|
||||
*/
|
||||
static public function isInstantiable($nameBridge){
|
||||
$re = new ReflectionClass($nameBridge);
|
||||
return $re->IsInstantiable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new bridge object
|
||||
* @param string $nameBridge Defined bridge name you want use
|
||||
@@ -42,11 +32,11 @@ EOD;
|
||||
|
||||
require_once $pathBridge;
|
||||
|
||||
if(Bridge::isInstantiable($nameBridge)) {
|
||||
if((new ReflectionClass($nameBridge))->isInstantiable()) {
|
||||
return new $nameBridge();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static public function setDir($dirBridge){
|
||||
@@ -62,13 +52,11 @@ EOD;
|
||||
}
|
||||
|
||||
static public function getDir(){
|
||||
$dirBridge = self::$dirBridge;
|
||||
|
||||
if(is_null($dirBridge)) {
|
||||
if(is_null(self::$dirBridge)) {
|
||||
throw new \LogicException(__CLASS__ . ' class need to know bridge path !');
|
||||
}
|
||||
|
||||
return $dirBridge;
|
||||
return self::$dirBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,9 +64,8 @@ EOD;
|
||||
* @return array List of the bridges
|
||||
*/
|
||||
static public function listBridges(){
|
||||
$pathDirBridge = self::getDir();
|
||||
$listBridge = array();
|
||||
$dirFiles = scandir($pathDirBridge);
|
||||
$dirFiles = scandir(self::getDir());
|
||||
|
||||
if($dirFiles !== false) {
|
||||
foreach($dirFiles as $fileName) {
|
||||
@@ -92,14 +79,10 @@ EOD;
|
||||
}
|
||||
|
||||
static public function isWhitelisted($whitelist, $name){
|
||||
if(in_array($name, $whitelist)
|
||||
return in_array($name, $whitelist)
|
||||
|| in_array($name . '.php', $whitelist)
|
||||
|| in_array($name . 'Bridge', $whitelist) // DEPRECATED
|
||||
|| in_array($name . 'Bridge.php', $whitelist) // DEPRECATED
|
||||
|| (count($whitelist) === 1 && trim($whitelist[0]) === '*')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|| in_array($name . 'bridge', $whitelist) // DEPRECATED
|
||||
|| in_array($name . 'bridge.php', $whitelist) // DEPRECATED
|
||||
|| (count($whitelist) === 1 && trim($whitelist[0]) === '*');
|
||||
}
|
||||
}
|
||||
|
@@ -1,64 +1,6 @@
|
||||
<?php
|
||||
class HttpException extends \Exception{}
|
||||
|
||||
/**
|
||||
* Not real http implementation but only utils stuff
|
||||
*/
|
||||
class Http{
|
||||
|
||||
/**
|
||||
* Return message corresponding to Http code
|
||||
*/
|
||||
static public function getMessageForCode($code){
|
||||
$codes = self::getCodes();
|
||||
|
||||
if(isset($codes[$code]))
|
||||
return $codes[$code];
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* List of common Http code
|
||||
*/
|
||||
static public function getCodes(){
|
||||
return array(
|
||||
200 => 'OK',
|
||||
201 => 'Created',
|
||||
202 => 'Accepted',
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Moved Temporarily',
|
||||
307 => 'Temporary Redirect',
|
||||
310 => 'Too many Redirects',
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
402 => 'Payment Required',
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not',
|
||||
406 => 'Not Acceptable',
|
||||
407 => 'Proxy Authentication Required',
|
||||
408 => 'Request Time-out',
|
||||
409 => 'Conflict',
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Request Entity Too Large',
|
||||
414 => 'Request-URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Requested range unsatisfiable',
|
||||
417 => 'Expectation failed',
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Time-out',
|
||||
508 => 'Loop detected',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an URL that automatically populates a new issue on GitHub based
|
||||
* on the information provided
|
||||
|
@@ -18,7 +18,7 @@ abstract class FeedExpander extends BridgeAbstract {
|
||||
*/
|
||||
$content = getContents($url)
|
||||
or returnServerError('Could not request ' . $url);
|
||||
$rssContent = simplexml_load_string($content);
|
||||
$rssContent = simplexml_load_string(trim($content));
|
||||
|
||||
debugMessage('Detecting feed format/version');
|
||||
switch(true) {
|
||||
@@ -102,12 +102,12 @@ abstract class FeedExpander extends BridgeAbstract {
|
||||
if(!isset($content->link)) {
|
||||
$this->uri = '';
|
||||
} elseif (count($content->link) === 1) {
|
||||
$this->uri = $content->link[0]['href'];
|
||||
$this->uri = (string)$content->link[0]['href'];
|
||||
} else {
|
||||
$this->uri = '';
|
||||
foreach($content->link as $link) {
|
||||
if(strtolower($link['rel']) === 'alternate') {
|
||||
$this->uri = $link['href'];
|
||||
$this->uri = (string)$link['href'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -21,19 +21,14 @@ function validateData(&$data, $parameters){
|
||||
$validateNumberValue = function($value){
|
||||
$filteredValue = filter_var($value, FILTER_VALIDATE_INT);
|
||||
|
||||
if($filteredValue === false && !empty($value))
|
||||
if($filteredValue === false)
|
||||
return null;
|
||||
|
||||
return $filteredValue;
|
||||
};
|
||||
|
||||
$validateCheckboxValue = function($value){
|
||||
$filteredValue = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
|
||||
if(is_null($filteredValue))
|
||||
return null;
|
||||
|
||||
return $filteredValue;
|
||||
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
};
|
||||
|
||||
$validateListValue = function($value, $expectedValues){
|
||||
@@ -85,7 +80,7 @@ function validateData(&$data, $parameters){
|
||||
break;
|
||||
}
|
||||
|
||||
if(is_null($data[$name])) {
|
||||
if(is_null($data[$name]) && isset($set[$name]['required']) && $set[$name]['required']) {
|
||||
echo 'Parameter \'' . $name . '\' is invalid!' . PHP_EOL;
|
||||
return false;
|
||||
}
|
||||
|
10
static/select.js
Normal file
10
static/select.js
Normal file
@@ -0,0 +1,10 @@
|
||||
function select(){
|
||||
var fragment = window.location.hash.substr(1);
|
||||
var bridge = document.getElementById(fragment);
|
||||
|
||||
if(bridge !== null) {
|
||||
bridge.getElementsByClassName('showmore-box')[0].checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', select);
|
@@ -52,6 +52,18 @@ header > p.status {
|
||||
color: red;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
|
||||
background-color: white;
|
||||
color: #404552;
|
||||
border: 0px;
|
||||
border-bottom: 2px solid #2196F3;
|
||||
font-size: 1.1em;
|
||||
margin-left: 8px;
|
||||
padding-left: 4px;
|
||||
|
||||
}
|
||||
|
||||
.searchbar {
|
||||
|
||||
width: 50%;
|
||||
@@ -64,6 +76,7 @@ header > p.status {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
font-size: 1.4em;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
@@ -73,6 +86,30 @@ header > p.status {
|
||||
|
||||
}
|
||||
|
||||
.searchbar input[type="text"]:focus::-webkit-input-placeholder {
|
||||
|
||||
opacity: 0;
|
||||
|
||||
}
|
||||
|
||||
.searchbar input[type="text"]:focus::-moz-placeholder {
|
||||
|
||||
opacity: 0;
|
||||
|
||||
}
|
||||
|
||||
.searchbar input[type="text"]:focus:-moz-placeholder {
|
||||
|
||||
opacity: 0;
|
||||
|
||||
}
|
||||
|
||||
.searchbar input[type="text"]:focus:-ms-input-placeholder {
|
||||
|
||||
opacity: 0;
|
||||
|
||||
}
|
||||
|
||||
.searchbar > h3 {
|
||||
|
||||
font-size: 150%;
|
||||
@@ -188,18 +225,6 @@ form {
|
||||
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
|
||||
background-color: white;
|
||||
color: #404552;
|
||||
border: 0px;
|
||||
border-bottom: 2px solid #2196F3;
|
||||
font-size: 1.1em;
|
||||
margin-left: 8px;
|
||||
padding-left: 4px;
|
||||
|
||||
}
|
||||
|
||||
form {
|
||||
|
||||
display: none;
|
||||
|
Reference in New Issue
Block a user