'); // Suffix to encapsulate data in php code.
define('MIN_TIME_UPDATE', 5); // Minimum accepted time for update
define('ERROR_NO_ERROR', 0);
define('ERROR_NO_XML', 1);
define('ERROR_ITEMS_MISSED', 2);
define('ERROR_LAST_UPDATE', 3);
define('ERROR_UNKNOWN', 4);
// fix some warning
date_default_timezone_set('Europe/Paris');
if (!is_dir(DATA_DIR)) {
if (!@mkdir(DATA_DIR, 0755)) {
echo '
';
exit();
}
@chmod(DATA_DIR, 0755);
if (!is_file(DATA_DIR.'/.htaccess')) {
if (!@file_put_contents(
DATA_DIR.'/.htaccess',
"Allow from none\nDeny from all\n"
)) {
echo '
';
exit();
}
}
}
/* function grabFavicon */
function grabFavicon($url, $feedHash){
$url = 'http://getfavicon.appspot.com/'.$url.'?defaulticon=bluepng';
$file = FAVICON_DIR.'/favicon.'.$feedHash.'.ico';
if(!file_exists($file) && in_array('curl', get_loaded_extensions()) && Session::isLogged()){
$ch = curl_init ($url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
$raw = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) {
$fp = fopen($file, 'x');
fwrite($fp, $raw);
fclose($fp);
}
curl_close ($ch);
}
if (file_exists($file)) {
return $file;
} else {
return $url;
}
}
class FeedConf
{
private $_file = '';
public $login = '';
public $hash = '';
public $disableSessionProtection = false;
public $salt = '';
public $title = "Kriss feed";
public $redirector = '';
public $locale = 'en_GB';
public $shaarli = '';
public $maxItems = 100;
public $maxUpdate = 60;
public $order = 'newerFirst';
public $autoreadItem = false;
public $autoreadPage = false;
public $autoUpdate = false;
public $autohide = false;
public $autofocus = true;
public $addFavicon = false;
public $public = false;
public $version;
public $view = 'list';
public $filter = 'unread';
public $listFeeds = 'show';
public $byPage = 10;
public $currentHash = 'all';
public $currentPage = 1;
public $menuView = 1;
public $menuListFeeds = 2;
public $menuFilter = 3;
public $menuOrder = 4;
public $menuUpdate = 5;
public $menuRead = 6;
public $menuUnread = 7;
public $menuEdit = 8;
public $menuAdd = 9;
public $menuHelp = 10;
public $pagingItem = 1;
public $pagingPage = 2;
public $pagingByPage = 3;
public $pagingMarkAs = 4;
public function __construct($configFile, $version)
{
$this->_file = $configFile;
$this->version = $version;
// Loading user config
if (file_exists($this->_file)) {
include_once $this->_file;
} else {
$this->_install();
}
Session::$disableSessionProtection = $this->disableSessionProtection;
if ($this->addFavicon) {
/* favicon dir */
if (!is_dir(INC_DIR)) {
if (!@mkdir(INC_DIR, 0755)) {
die("Can not create inc dir: ".INC_DIR);
}
}
if (!is_dir(FAVICON_DIR)) {
if (!@mkdir(FAVICON_DIR, 0755)) {
die("Can not create inc dir: ".FAVICON_DIR);
}
}
}
if (Session::isLogged()) {
unset($_SESSION['view']);
unset($_SESSION['listFeeds']);
unset($_SESSION['filter']);
unset($_SESSION['order']);
unset($_SESSION['byPage']);
}
$view = $this->getView();
$listFeeds = $this->getListFeeds();
$filter = $this->getFilter();
$order = $this->getOrder();
$byPage = $this->getByPage();
if ($this->view != $view
|| $this->listFeeds != $listFeeds
|| $this->filter != $filter
|| $this->order != $order
|| $this->byPage != $byPage
) {
$this->view = $view;
$this->listFeeds = $listFeeds;
$this->filter = $filter;
$this->order = $order;
$this->byPage = $byPage;
if (Session::isLogged()) {
$this->write();
}
}
if (!Session::isLogged()) {
$_SESSION['view'] = $view;
$_SESSION['listFeeds'] = $listFeeds;
$_SESSION['filter'] = $filter;
$_SESSION['order'] = $order;
$_SESSION['byPage'] = $byPage;
}
}
private function _install()
{
if (!empty($_POST['setlogin']) && !empty($_POST['setpassword'])) {
$this->setSalt(sha1(uniqid('', true).'_'.mt_rand()));
$this->setLogin($_POST['setlogin']);
$this->setHash($_POST['setpassword']);
if ($this->write()) {
echo '
';
exit();
} else {
echo '
';
exit();
}
Session::logout();
} else {
FeedPage::init(
array(
'version' => $this->version,
'pagetitle' => 'KrISS feed installation'
)
);
FeedPage::installTpl();
}
exit();
}
public function hydrate(array $data)
{
foreach ($data as $key => $value) {
// get setter
$method = 'set'.ucfirst($key);
// if setter exists just call it
// (php is not case-sensitive with functions)
if (method_exists($this, $method)) {
$this->$method($value);
}
}
if (!$this->write()) {
die("Can't write to ".CONFIG_FILE);
}
}
public function getView()
{
$view = $this->view;
if (isset($_GET['view'])) {
if ($_GET['view'] == 'expanded') {
$view = 'expanded';
}
if ($_GET['view'] == 'list') {
$view = 'list';
}
} else if (isset($_SESSION['view'])) {
$view = $_SESSION['view'];
}
return $view;
}
public function getFilter()
{
$filter = $this->filter;
if (isset($_GET['filter'])) {
if ($_GET['filter'] == 'unread') {
$filter = 'unread';
}
if ($_GET['filter'] == 'all') {
$filter = 'all';
}
} else if (isset($_SESSION['filter'])) {
$filter = $_SESSION['filter'];
}
return $filter;
}
public function getListFeeds()
{
$listFeeds = $this->listFeeds;
if (isset($_GET['listFeeds'])) {
if ($_GET['listFeeds'] == 'show') {
$listFeeds = 'show';
}
if ($_GET['listFeeds'] == 'hide') {
$listFeeds = 'hide';
}
} else if (isset($_SESSION['listFeeds'])) {
$listFeeds = $_SESSION['listFeeds'];
}
return $listFeeds;
}
public function getByPage()
{
$byPage = $this->byPage;
if (isset($_GET['byPage']) && is_numeric($_GET['byPage']) && $_GET['byPage'] > 0) {
$byPage = $_GET['byPage'];
} else if (isset($_SESSION['byPage'])) {
$byPage = $_SESSION['byPage'];
}
return $byPage;
}
public function getOrder()
{
$order = $this->order;
if (isset($_GET['order'])) {
if ($_GET['order'] === 'newerFirst') {
$order = 'newerFirst';
}
if ($_GET['order'] === 'olderFirst') {
$order = 'olderFirst';
}
} else if (isset($_SESSION['order'])) {
$order = $_SESSION['order'];
}
return $order;
}
public function getCurrentHash()
{
$currentHash = $this->currentHash;
if (isset($_GET['currentHash'])) {
$currentHash = preg_replace('/[^a-zA-Z0-9-_@]/', '', substr(trim($_GET['currentHash'], '/'), 0, 6));
}
if (empty($currentHash)) {
$currentHash = 'all';
}
return $currentHash;
}
public function getCurrentPage()
{
$currentPage = $this->currentPage;
if (isset($_GET['page']) && !empty($_GET['page'])) {
$currentPage = (int)$_GET['page'];
} else if (isset($_GET['previousPage']) && !empty($_GET['previousPage'])) {
$currentPage = (int)$_GET['previousPage'] - 1;
if ($currentPage < 1) {
$currentPage = 1;
}
} else if (isset($_GET['nextPage']) && !empty($_GET['nextPage'])) {
$currentPage = (int)$_GET['nextPage'] + 1;
}
return $currentPage;
}
public function setDisableSessionProtection($disableSessionProtection)
{
$this->disableSessionProtection = $disableSessionProtection;
}
public function setLogin($login)
{
$this->login = $login;
}
public function setPublic($public)
{
$this->public = $public;
}
public function setHash($pass)
{
$this->hash = sha1($pass.$this->login.$this->salt);
}
public function setSalt($salt)
{
$this->salt = $salt;
}
public function setTitle($title)
{
$this->title = $title;
}
public function setLocale($locale)
{
$this->locale = $locale;
}
public function setRedirector($redirector)
{
$this->redirector = $redirector;
}
public function setAutoreadPage($autoreadPage)
{
$this->autoreadPage = $autoreadPage;
}
public function setAutoUpdate($autoUpdate)
{
$this->autoUpdate = $autoUpdate;
}
public function setAutoreadItem($autoreadItem)
{
$this->autoreadItem = $autoreadItem;
}
public function setAutohide($autohide)
{
$this->autohide = $autohide;
}
public function setAutofocus($autofocus)
{
$this->autofocus = $autofocus;
}
public function setAddFavicon($addFavicon)
{
$this->addFavicon = $addFavicon;
}
public function setShaarli($url)
{
$this->shaarli = $url;
}
public function setMaxUpdate($max)
{
$this->maxUpdate = $max;
}
public function setMaxItems($max)
{
$this->maxItems = $max;
}
public function setOrder($order)
{
$this->order = $order;
}
public function getMenu()
{
$menu = array();
if ($this->menuView != 0) {
$menu['menuView'] = $this->menuView;
}
if ($this->menuListFeeds != 0) {
$menu['menuListFeeds'] = $this->menuListFeeds;
}
if ($this->menuFilter != 0) {
$menu['menuFilter'] = $this->menuFilter;
}
if ($this->menuOrder != 0) {
$menu['menuOrder'] = $this->menuOrder;
}
if ($this->menuUpdate != 0) {
$menu['menuUpdate'] = $this->menuUpdate;
}
if ($this->menuRead != 0) {
$menu['menuRead'] = $this->menuRead;
}
if ($this->menuUnread != 0) {
$menu['menuUnread'] = $this->menuUnread;
}
if ($this->menuEdit != 0) {
$menu['menuEdit'] = $this->menuEdit;
}
if ($this->menuAdd != 0) {
$menu['menuAdd'] = $this->menuAdd;
}
if ($this->menuHelp != 0) {
$menu['menuHelp'] = $this->menuHelp;
}
asort($menu);
return $menu;
}
public function getPaging()
{
$paging = array();
if ($this->pagingItem != 0) {
$paging['pagingItem'] = $this->pagingItem;
}
if ($this->pagingPage != 0) {
$paging['pagingPage'] = $this->pagingPage;
}
if ($this->pagingByPage != 0) {
$paging['pagingByPage'] = $this->pagingByPage;
}
if ($this->pagingMarkAs != 0) {
$paging['pagingMarkAs'] = $this->pagingMarkAs;
}
asort($paging);
return $paging;
}
public function setMenuView($menuView)
{
$this->menuView = $menuView;
}
public function setMenuListFeeds($menuListFeeds)
{
$this->menuListFeeds = $menuListFeeds;
}
public function setMenuFilter($menuFilter)
{
$this->menuFilter = $menuFilter;
}
public function setMenuOrder($menuOrder)
{
$this->menuOrder = $menuOrder;
}
public function setMenuUpdate($menuUpdate)
{
$this->menuUpdate = $menuUpdate;
}
public function setMenuRead($menuRead)
{
$this->menuRead = $menuRead;
}
public function setMenuUnread($menuUnread)
{
$this->menuUnread = $menuUnread;
}
public function setMenuEdit($menuEdit)
{
$this->menuEdit = $menuEdit;
}
public function setMenuAdd($menuAdd)
{
$this->menuAdd = $menuAdd;
}
public function setMenuHelp($menuHelp)
{
$this->menuHelp = $menuHelp;
}
public function setPagingItem($pagingItem)
{
$this->pagingItem = $pagingItem;
}
public function setPagingPage($pagingPage)
{
$this->pagingPage = $pagingPage;
}
public function setPagingByPage($pagingByPage)
{
$this->pagingByPage = $pagingByPage;
}
public function setPagingMarkAs($pagingMarkAs)
{
$this->pagingMarkAs = $pagingMarkAs;
}
public function write()
{
$data = array('login', 'hash', 'salt', 'title', 'redirector', 'shaarli',
'byPage', 'order', 'public', 'filter', 'view','locale',
'maxItems', 'autoreadItem', 'autoreadPage', 'maxUpdate',
'autohide', 'autofocus', 'listFeeds', 'autoUpdate', 'menuView',
'menuListFeeds', 'menuFilter', 'menuOrder', 'menuUpdate',
'menuRead', 'menuUnread', 'menuEdit', 'menuAdd', 'menuHelp',
'pagingItem', 'pagingPage', 'pagingByPage', 'addFavicon',
'pagingMarkAs', 'disableSessionProtection');
$out = '$key, array('$' => '\\$', '"' => '\\"'));
$out .= '$this->'.$key.' = "'.$value."\";\n";
}
$out .= '?>';
if (!@file_put_contents($this->_file, $out)) {
return false;
}
return true;
}
}
class FeedPage
{
public static $var = array();
private static $_instance;
public static function init($var)
{
FeedPage::$var = $var;
}
public static function includesTpl()
{
extract(FeedPage::$var);
?>
-
$feed) {
$atitle = trim(htmlspecialchars($feed['description']));
if (empty($atitle) || $atitle == ' ') {
$atitle = trim(htmlspecialchars($feed['title']));
}
if (isset($feed['error'])) {
$atitle = $feed['error'];
}
?>
-
$folder) {
$isOpen = $folder['isOpen'];
?>
-
$feed) {
$atitle = trim(htmlspecialchars($feed['description']));
if (empty($atitle) || $atitle == ' ') {
$atitle = trim(htmlspecialchars($feed['title']));
}
if (isset($feed['error'])) {
$atitle = $feed['error'];
}
?>
-
kfc = $kfc;
$this->dataFile = $dataFile;
$this->cacheDir = $cacheDir;
}
public function getData()
{
return $this->_data;
}
public function setData($data)
{
$this->_data = $data;
}
public function loadData()
{
if (empty($this->_data)) {
if (file_exists($this->dataFile)) {
$this->_data = unserialize(
gzinflate(
base64_decode(
substr(
file_get_contents($this->dataFile),
strlen(PHPPREFIX),
-strlen(PHPSUFFIX)
)
)
)
);
return true;
} else {
$this->_data['feeds'] = array(
MyTool::smallHash('http://tontof.net/?rss') => array(
'title' => 'Tontof',
'foldersHash' => array(),
'timeUpdate' => 'auto',
'lastUpdate' => 0,
'nbUnread' => 0,
'nbAll' => 0,
'htmlUrl' => 'http://tontof.net',
'xmlUrl' => 'http://tontof.net/?rss',
'description' => 'A simple and smart (or stupid) blog'));
$this->_data['folders'] = array();
$this->_data['items'] = array();
$this->_data['newItems'] = array();
return false;
}
}
// data already loaded
return true;
}
public function writeData()
{
if (Session::isLogged() || (isset($_GET['cron']) && $_GET['cron'] === sha1($this->kfc->salt.$this->kfc->hash))) {
$write = @file_put_contents(
$this->dataFile,
PHPPREFIX
. base64_encode(gzdeflate(serialize($this->_data)))
. PHPSUFFIX
);
if (!$write) {
die("Can't write to " . $this->dataFile);
}
}
}
public function getFeeds()
{
return $this->_data['feeds'];
}
public function sortFeeds()
{
uasort(
$this->_data['feeds'],
'Feed::sortByTitle'
);
}
public function getFeedsView()
{
$feedsView = array('all' => array('title' => 'All feeds', 'nbUnread' => 0, 'nbAll' => 0, 'feeds' => array()), 'folders' => array());
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (isset($feed['error'])) {
$feed['error'] = $this->getError($feed['error']);
}
$feedsView['all']['nbUnread'] += $feed['nbUnread'];
$feedsView['all']['nbAll'] += $feed['nbAll'];
if (empty($feed['foldersHash'])) {
$feedsView['all']['feeds'][$feedHash] = $feed;
} else {
foreach ($feed['foldersHash'] as $folderHash) {
$folder = $this->getFolder($folderHash);
if ($folder !== false) {
if (!isset($feedsView['folders'][$folderHash]['title'])) {
$feedsView['folders'][$folderHash]['title'] = $folder['title'];
$feedsView['folders'][$folderHash]['isOpen'] = $folder['isOpen'];
$feedsView['folders'][$folderHash]['nbUnread'] = 0;
$feedsView['folders'][$folderHash]['nbAll'] = 0;
}
$feedsView['folders'][$folderHash]['feeds'][$feedHash] = $feed;
$feedsView['folders'][$folderHash]['nbUnread'] += $feed['nbUnread'];
$feedsView['folders'][$folderHash]['nbAll'] += $feed['nbAll'];
}
}
}
}
return $feedsView;
}
public function getFeed($feedHash)
{
if (isset($this->_data['feeds'][$feedHash])) {
$this->_data['feeds'][$feedHash]['xmlUrl'] = htmlspecialchars($this->_data['feeds'][$feedHash]['xmlUrl']);
$this->_data['feeds'][$feedHash]['htmlUrl'] = htmlspecialchars($this->_data['feeds'][$feedHash]['htmlUrl']);
return $this->_data['feeds'][$feedHash];
}
return false;
}
public function getFeedHtmlUrl($feedHash)
{
if (isset($this->_data['feeds'][$feedHash]['htmlUrl'])) {
return $this->_data['feeds'][$feedHash]['htmlUrl'];
}
return false;
}
public function getFeedTitle($feedHash)
{
if (isset($this->_data['feeds'][$feedHash]['title'])) {
return $this->_data['feeds'][$feedHash]['title'];
}
return false;
}
public function loadFeed($feedHash)
{
if (!isset($this->_data['feeds'][$feedHash]['items'])) {
$this->_data['feeds'][$feedHash]['items'] = array();
if (file_exists($this->cacheDir.'/'.$feedHash.'.php')) {
$items = unserialize(
gzinflate(
base64_decode(
substr(
file_get_contents($this->cacheDir.'/'.$feedHash.'.php'),
strlen(PHPPREFIX),
-strlen(PHPSUFFIX)
)
)
)
);
$this->_data['feeds'][$feedHash]['items'] = $items;
}
}
}
public function editFeed(
$feedHash,
$title,
$description,
$foldersHash,
$timeUpdate)
{
if (isset($this->_data['feeds'][$feedHash])) {
if (!empty($title)) {
$this->_data['feeds'][$feedHash]['title'] = $title;
}
if (!empty($description)) {
$this->_data['feeds'][$feedHash]['description'] = $description;
}
$this->_data['feeds'][$feedHash]['foldersHash'] = $foldersHash;
$this->_data['feeds'][$feedHash]['timeUpdate'] = 'auto';
if (!empty($timeUpdate)) {
if ($timeUpdate == 'max') {
$this->_data['feeds'][$feedHash]['timeUpdate'] = $timeUpdate;
} else {
$this->_data['feeds'][$feedHash]['timeUpdate'] = (int) $timeUpdate;
$maxUpdate = $this->kfc->maxUpdate;
if ($this->_data['feeds'][$feedHash]['timeUpdate'] < MIN_TIME_UPDATE
|| $this->_data['feeds'][$feedHash]['timeUpdate'] > $maxUpdate
) {
$this->_data['feeds'][$feedHash]['timeUpdate'] = 'auto';
}
}
}
}
}
public function removeFeed($feedHash)
{
if (isset($this->_data['feeds'][$feedHash])) {
unset($this->_data['feeds'][$feedHash]);
unlink($this->cacheDir. '/' .$feedHash.'.php' );
foreach (array_keys($this->_data['items']) as $itemHash) {
if (substr($itemHash, 0, 6) === $feedHash) {
unset($this->_data['items'][$itemHash]);
}
}
foreach (array_keys($this->_data['newItems']) as $itemHash) {
if (substr($itemHash, 0, 6) === $feedHash) {
unset($this->_data['newItems'][$itemHash]);
}
}
}
}
public function writeFeed($feedHash, $feed)
{
if (Session::isLogged() || (isset($_GET['cron']) && $_GET['cron'] === sha1($this->kfc->salt.$this->kfc->hash))) {
if (!is_dir($this->cacheDir)) {
if (!@mkdir($this->cacheDir, 0755)) {
die("Can not create cache dir: ".$this->cacheDir);
}
@chmod($this->cacheDir, 0755);
if (!is_file($this->cacheDir.'/.htaccess')) {
if (!@file_put_contents(
$this->cacheDir.'/.htaccess',
"Allow from none\nDeny from all\n"
)) {
die("Can not protect cache dir: ".$this->cacheDir);
}
}
}
$write = @file_put_contents(
$this->cacheDir.'/'.$feedHash.'.php',
PHPPREFIX
. base64_encode(gzdeflate(serialize($feed)))
. PHPSUFFIX
);
if (!$write) {
die("Can't write to " . $this->cacheDir.'/'.$feedHash.'.php');
}
}
}
public function orderFeedsForUpdate($feedsHash)
{
$newFeedsHash = array();
foreach(array_keys($this->_data['items']) as $itemHash) {
$feedHash = substr($itemHash, 0, 6);
if (in_array($feedHash, $feedsHash) and !in_array($feedHash, $newFeedsHash)) {
$newFeedsHash[] = $feedHash;
}
}
foreach($feedsHash as $feedHash) {
if (!in_array($feedHash, $newFeedsHash)) {
$newFeedsHash[] = $feedHash;
}
}
return $newFeedsHash;
}
public function getFeedsHashFromFolderHash($folderHash)
{
$list = array();
$folders = $this->getFolders();
if (isset($folders[$folderHash])) {
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (in_array($folderHash, $feed['foldersHash'])) {
$list[] = $feedHash;
}
}
}
return $list;
}
public function getFolders()
{
return $this->_data['folders'];
}
public function getFolder($folderHash)
{
if (isset($this->_data['folders'][$folderHash])) {
return $this->_data['folders'][$folderHash];
}
return false;
}
public function addFolder($folderTitle, $newFolderHash = '')
{
if (empty($newFolderHash)) {
$newFolderHash = MyTool::smallHash($newFolderTitle);
}
$this->_data['folders'][$newFolderHash] = array(
'title' => $folderTitle,
'isOpen' => 1
);
}
public function renameFolder($oldFolderHash, $newFolderTitle)
{
$newFolderHash = '';
if (!empty($newFolderTitle)) {
$newFolderHash = MyTool::smallHash($newFolderTitle);
$this->addFolder($newFolderTitle, $newFolderHash);
$this->_data['folders'][$newFolderHash]['isOpen'] = $this->_data['folders'][$oldFolderHash]['isOpen'];
}
unset($this->_data['folders'][$oldFolderHash]);
foreach ($this->_data['feeds'] as $feedHash => $feed) {
$i = array_search($oldFolderHash, $feed['foldersHash']);
if ($i !== false) {
unset($this->_data['feeds'][$feedHash]['foldersHash'][$i]);
if (!empty($newFolderTitle)) {
$this->_data['feeds'][$feedHash]['foldersHash'][] = $newFolderHash;
}
}
}
}
public function toggleFolder($hash)
{
if ($this->_data['folders'][$hash]) {
$isOpen = $this->_data['folders'][$hash]['isOpen'];
if ($isOpen) {
$this->_data['folders'][$hash]['isOpen'] = 0;
} else {
$this->_data['folders'][$hash]['isOpen'] = 1;
}
}
return true;
}
public function getFolderTitle($folderHash)
{
if (isset($this->_data['folders'][$folderHash])) {
return $this->_data['folders'][$folderHash]['title'];
}
return false;
}
public function getItems($hash = 'all', $filter = 'all')
{
if (empty($hash) or $hash == 'all' and $filter == 'all') {
return $this->_data['items']+$this->_data['newItems'];
}
if (empty($hash) or $hash == 'all' and $filter == 'old') {
return $this->_data['items'];
}
if (empty($hash) or $hash == 'all' and $filter == 'new') {
return $this->_data['newItems'];
}
$list = array();
$isRead = 1;
if ($filter === 'unread') {
$isRead = 0;
}
if (empty($hash) || $hash == 'all') {
// all items
foreach ($this->_data['items'] as $itemHash => $item) {
if ($item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
foreach ($this->_data['newItems'] as $itemHash => $item) {
if ($item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
} else {
if (strlen($hash) === 12) {
// an item
if (isset($this->_data['items'][$hash])) {
$list[$hash] = $this->_data['items'][$hash];
} else if (isset($this->_data['newItems'][$hash])) {
$list[$hash] = $this->_data['newItems'][$hash];
}
} else {
$feedsHash = array();
if (isset($this->_data['feeds'][$hash])) {
// a feed
$feedsHash[] = $hash;
} else if (isset($this->_data['folders'][$hash])) {
// a folder
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (in_array($hash, $feed['foldersHash'])) {
$feedsHash[] = $feedHash;
}
}
}
// get items from a list of feeds
if (!empty($feedsHash)) {
$flipFeedsHash = array_flip($feedsHash);
foreach ($this->_data['items'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($filter === 'all' or $item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
}
foreach ($this->_data['newItems'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($filter === 'all' or $item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
}
}
}
}
return $list;
}
public function loadItem($itemHash, $keep)
{
$feedHash = substr($itemHash, 0, 6);
$item = array();
if (isset($this->_data['feeds'][$feedHash]['items'])) {
if (isset($this->_data['feeds'][$feedHash]['items'][$itemHash])) {
$item = $this->_data['feeds'][$feedHash]['items'][$itemHash];
}
} else {
$this->loadFeed($feedHash);
return $this->loadItem($itemHash, $keep);
}
if (!$keep) {
unset($this->_data['feeds'][$feedHash]['items']);
}
return $item;
}
public function getItem($itemHash, $keep = true)
{
$item = $this->loadItem($itemHash, $keep);
if (!empty($item)) {
$item['itemHash'] = $itemHash;
$time = $item['time'];
if (strftime('%Y%m%d', $time) == strftime('%Y%m%d', time())) {
// Today
$item['time'] = array('list' => utf8_encode(strftime('%R %p', $time)), 'expanded' => utf8_encode(strftime('%A %d %B %Y - %H:%M', $time)));
} else {
$item['time'] = array('list' => utf8_encode(strftime('%b %e, %Y', $time)), 'expanded' => utf8_encode(strftime('%A %d %B %Y - %H:%M', $time)));
}
if (isset($this->_data['items'][$itemHash])) {
$item['read'] = $this->_data['items'][$itemHash][1];
} else if (isset($this->_data['newItems'][$itemHash])) {
$item['read'] = $this->_data['newItems'][$itemHash][1];
$currentNewItemIndex = array_search($itemHash, array_keys($this->_data['newItems']));
if (isset($_SESSION['lastNewItemsHash'])) {
$lastNewItemIndex = array_search($_SESSION['lastNewItemsHash'], array_keys($this->_data['newItems']));
if ($lastNewItemIndex < $currentNewItemIndex) {
$_SESSION['lastNewItemsHash'] = $itemHash;
}
} else {
$_SESSION['lastNewItemsHash'] = $itemHash;
}
} else {
// FIX: data may be corrupted
return false;
}
$item['author'] = htmlspecialchars(htmlspecialchars_decode(strip_tags($item['author']), ENT_QUOTES), ENT_NOQUOTES);
$item['title'] = htmlspecialchars(htmlspecialchars_decode(strip_tags($item['title']), ENT_QUOTES), ENT_NOQUOTES);
$item['link'] = htmlspecialchars($item['link']);
$item['via'] = htmlspecialchars($item['via']);
return $item;
}
return false;
}
public function updateItems()
{
if (isset($this->_data['needSort']) or (isset($this->_data['order']) and $this->_data['order'] != $this->kfc->order)) {
unset($this->_data['needSort']);
$this->_data['items'] = $this->_data['items']+$this->_data['newItems'];
$this->_data['newItems'] = array();
// sort items
if ($this->kfc->order === 'newerFirst') {
arsort($this->_data['items']);
} else {
asort($this->_data['items']);
}
$this->_data['order'] = $this->kfc->order;
return true;
}
return false;
}
public function formatChannel($channel)
{
$newChannel = array();
// list of format for each info in order of importance
$formats = array('title' => array('title'),
'description' => array('description', 'subtitle'),
'htmlUrl' => array('link', 'id', 'guid'));
foreach ($formats as $format => $list) {
$newChannel[$format] = '';
$len = count($list);
for ($i = 0; $i < $len; $i++) {
if ($channel->hasChildNodes()) {
$child = $channel->childNodes;
for ($j = 0, $lenChannel = $child->length;
$j<$lenChannel;
$j++) {
if (isset($child->item($j)->tagName)
&& $child->item($j)->tagName == $list[$i]
) {
$newChannel[$format]
= $child->item($j)->textContent;
}
}
}
}
}
return $newChannel;
}
public function getChannelFromXml($xml)
{
$channel = array();
// find feed type RSS, Atom
$feed = $xml->getElementsByTagName('channel');
if ($feed->item(0)) {
// RSS/rdf:RDF feed
$channel = $feed->item(0);
} else {
$feed = $xml->getElementsByTagName('feed');
if ($feed->item(0)) {
// Atom feed
$channel = $feed->item(0);
} else {
// unknown feed
}
}
if (!empty($channel)) {
$channel = $this->formatChannel($channel);
}
return $channel;
}
public function formatItems($items, $formats)
{
$newItems = array();
foreach ($items as $item) {
$tmpItem = array();
foreach ($formats as $format => $list) {
$tmpItem[$format] = '';
$len = count($list);
for ($i = 0; $i < $len; $i++) {
if (is_array($list[$i])) {
$tag = $item->getElementsByTagNameNS(
$list[$i][0],
$list[$i][1]
);
} else {
$tag = $item->getElementsByTagName($list[$i]);
// wrong detection : e.g. media:content for content
if ($tag->length != 0) {
for ($j = $tag->length; --$j >= 0;) {
$elt = $tag->item($j);
if ($tag->item($j)->tagName != $list[$i]) {
$elt->parentNode->removeChild($elt);
}
}
}
}
if ($tag->length != 0) {
// we find a correspondence for the current format
// select first item (item(0)), (may not work)
// stop to search for another one
if ($format == 'link') {
$tmpItem[$format] = '';
for ($j = 0; $j < $tag->length; $j++) {
if ($tag->item($j)->hasAttribute('rel') && $tag->item($j)->getAttribute('rel') == 'alternate') {
$tmpItem[$format]
= $tag->item($j)->getAttribute('href');
$j = $tag->length;
}
}
if ($tmpItem[$format] == '') {
$tmpItem[$format]
= $tag->item(0)->getAttribute('href');
}
}
if (empty($tmpItem[$format])) {
$tmpItem[$format] = $tag->item(0)->textContent;
}
$i = $len;
}
}
}
if (!empty($tmpItem['link'])) {
$hashUrl = MyTool::smallHash($tmpItem['link']);
$newItems[$hashUrl] = array();
$newItems[$hashUrl]['title'] = $tmpItem['title'];
$newItems[$hashUrl]['time'] = strtotime($tmpItem['time'])
? strtotime($tmpItem['time'])
: time();
if (MyTool::isUrl($tmpItem['via'])
&& $tmpItem['via'] != $tmpItem['link']) {
$newItems[$hashUrl]['via'] = $tmpItem['via'];
} else {
$newItems[$hashUrl]['via'] = '';
}
$newItems[$hashUrl]['link'] = $tmpItem['link'];
$newItems[$hashUrl]['author'] = $tmpItem['author'];
mb_internal_encoding("UTF-8");
$newItems[$hashUrl]['description'] = mb_substr(
strip_tags($tmpItem['description']), 0, 500
);
$newItems[$hashUrl]['content'] = $tmpItem['content'];
}
}
return $newItems;
}
public function getItemsFromXml ($xml)
{
$items = array();
// find feed type RSS, Atom
$feed = $xml->getElementsByTagName('channel');
if ($feed->item(0)) {
// RSS/rdf:RDF feed
$feed = $xml->getElementsByTagName('item');
$len = $feed->length;
for ($i = 0; $i < $len; $i++) {
$items[$i] = $feed->item($i);
}
$feed = $xml->getElementsByTagName('rss');
if (!$feed->item(0)) {
$feed = $xml->getElementsByTagNameNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
'RDF'
);
}
} else {
$feed = $xml->getElementsByTagName('feed');
if ($feed->item(0)) {
// Atom feed
$feed = $xml->getElementsByTagName('entry');
$len = $feed->length;
for ($i = 0; $i < $len; $i++) {
$items[$i] = $feed->item($i);
}
$feed = $xml->getElementsByTagName('feed');
}
}
// list of format for each info in order of importance
$formats = array(
'author' => array('author', 'creator', 'dc:author',
'dc:creator'),
'content' => array('content:encoded', 'content', 'description',
'summary', 'subtitle'),
'description' => array('description', 'summary', 'subtitle',
'content', 'content:encoded'),
'via' => array('guid', 'id'),
'link' => array('feedburner:origLink', 'link', 'guid', 'id'),
'time' => array('pubDate', 'updated', 'lastBuildDate',
'published', 'dc:date', 'date'),
'title' => array('title'));
if ($feed->item(0)) {
$formats = $this->formatRDF($formats, $feed->item(0));
}
return $this->formatItems($items, $formats);
}
public function getAttributeNS ($feed, $name)
{
$res = '';
if ($feed->nodeName === $name) {
$ns = explode(':', $name);
$res = $feed->getAttribute('xmlns:'.$ns[0]);
} else {
if ($feed->hasChildNodes()) {
foreach ($feed->childNodes as $childNode) {
if ($res === '') {
$res = $this->getAttributeNS($childNode, $name);
} else {
break;
}
}
}
}
return $res;
}
public function formatRDF($formats, $feed)
{
foreach ($formats as $format => $list) {
for ($i = 0, $len = count($list); $i < $len; $i++) {
$name = explode(':', $list[$i]);
if (count($name)>1) {
$res = $feed->getAttribute('xmlns:'.$name[0]);
if (!empty($res)) {
$ns = $res;
} else {
$ns = $this->getAttributeNS($feed, $list[$i]);
}
$formats[$format][$i] = array($ns, $name[1]);
}
}
}
return $formats;
}
public function loadUrl($url, $opts = array()){
$ch = curl_init($url);
if (!empty($opts)) {
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $opts['http']['timeout']);
curl_setopt($ch, CURLOPT_TIMEOUT, $opts['http']['timeout']);
curl_setopt($ch, CURLOPT_USERAGENT, $opts['http']['user_agent']);
}
curl_setopt($ch, CURLOPT_ENCODING, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $url);
$output = $this->curl_exec_follow($ch);
curl_close($ch);
return $output;
}
public function curl_exec_follow(&$ch, $redirects = 20, $curloptHeader = false) {
if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
curl_setopt($ch, CURLOPT_HEADER, $curloptHeader);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0);
curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects);
return curl_exec($ch);
} else {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FORBID_REUSE, false);
do {
$data = curl_exec($ch);
if (curl_errno($ch))
break;
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// 301 Moved Permanently
// 302 Found
// 303 See Other
// 307 Temporary Redirect
if ($code != 301 && $code != 302 && $code!=303 && $code!=307)
break;
$header_start = strpos($data, "\r\n")+2;
$headers = substr($data, $header_start, strpos($data, "\r\n\r\n", $header_start)+2-$header_start);
if (!preg_match("!\r\n(?:Location|location|URI): *(.*?) *\r\n!", $headers, $matches))
break;
curl_setopt($ch, CURLOPT_URL, $matches[1]);
} while (--$redirects);
if (!$redirects)
trigger_error('Too many redirects. When following redirects, libcurl hit the maximum amount.', E_USER_WARNING);
if (!$curloptHeader)
$data = substr($data, strpos($data, "\r\n\r\n")+4);
return $data;
}
}
public function loadXml($xmlUrl)
{
// hide warning/error
set_error_handler(array('MyTool', 'silence_errors'));
// set user agent
// http://php.net/manual/en/function.libxml-set-streams-context.php
$opts = array(
'http' => array(
'timeout' => 4,
'user_agent' => 'KrISS feed agent '.$this->kfc->version.' by Tontof.net http://github.com/tontof/kriss_feed',
)
);
$document = new DOMDocument();
if (in_array('curl', get_loaded_extensions())) {
$output = $this->loadUrl($xmlUrl, $opts);
$document->loadXML($output);
} else {
// try using libxml
$context = stream_context_create($opts);
libxml_set_streams_context($context);
// request a file through HTTP
$document->load($xmlUrl);
}
// show back warning/error
restore_error_handler();
return $document;
}
public function addChannel($xmlUrl)
{
$feedHash = MyTool::smallHash($xmlUrl);
if (!isset($this->_data['feeds'][$feedHash])) {
$xml = $this->loadXml($xmlUrl);
if (!$xml) {
return false;
} else {
$channel = $this->getChannelFromXml($xml);
$items = $this->getItemsFromXml($xml);
if (count($items) == 0) {
return false;
}
foreach (array_keys($items) as $itemHash) {
if (empty($items[$itemHash]['via'])) {
$items[$itemHash]['via'] = $channel['htmlUrl'];
}
if (empty($items[$itemHash]['author'])) {
$items[$itemHash]['author'] = $channel['title'];
} else {
$items[$itemHash]['author']
= $channel['title'] . ' ('
. $items[$itemHash]['author'] . ')';
}
$items[$itemHash]['xmlUrl'] = $xmlUrl;
$this->_data['newItems'][$feedHash . $itemHash] = array(
$items[$itemHash]['time'],
0
);
$items[$feedHash . $itemHash] = $items[$itemHash];
unset($items[$itemHash]);
}
$channel['xmlUrl'] = $xmlUrl;
$channel['foldersHash'] = array();
$channel['nbUnread'] = count($items);
$channel['nbAll'] = count($items);
$channel['timeUpdate'] = 'auto';
$channel['lastUpdate'] = time();
$this->_data['feeds'][$feedHash] = $channel;
$this->_data['needSort'] = true;
$this->writeFeed($feedHash, $items);
return true;
}
}
return false;
}
public function getTimeUpdate($feed)
{
$max = $feed['timeUpdate'];
if ($max == 'auto') {
$max = $this->kfc->maxUpdate;
} elseif ($max == 'max') {
$max = $this->kfc->maxUpdate;
} elseif ((int) $max < MIN_TIME_UPDATE
|| (int) $max > $this->kfc->maxUpdate) {
$max = $this->kfc->maxUpdate;
}
return (int) $max;
}
public function needUpdate($feed)
{
$diff = (int) (time()-$feed['lastUpdate']);
if ($diff > $this->getTimeUpdate($feed) * 60) {
return true;
}
return false;
}
public static function getError($error)
{
switch ($error) {
case ERROR_NO_XML:
return 'Feed is not in XML format';
break;
case ERROR_ITEMS_MISSED:
return 'Items may have been missed since last update';
break;
case ERROR_LAST_UPDATE:
case ERROR_UNKNOWN:
return 'Problem with the last update';
break;
default:
return 'unknown error';
break;
}
}
public function updateChannel($feedHash)
{
$error = '';
$newItems = array();
if (!isset($this->_data['feeds'][$feedHash])) {
return array(
'error' => $error,
'newItems' => $newItems
);
}
unset($this->_data['feeds'][$feedHash]['error']);
$xmlUrl = $this->_data['feeds'][$feedHash]['xmlUrl'];
$xml = $this->loadXml($xmlUrl);
if (!$xml) {
if (file_exists($this->cacheDir.'/'.$feedHash.'.php')) {
$error = ERROR_LAST_UPDATE;
} else {
$error = ERROR_NO_XML;
}
} else {
// if feed description is empty try to update description
// (after opml import, description is often empty)
if (empty($this->_data['feeds'][$feedHash]['description'])) {
$channel = $this->getChannelFromXml($xml);
if (isset($channel['description'])) {
$this->_data['feeds'][$feedHash]['description']
= $channel['description'];
}
// Check description only the first time description is empty
if (empty($this->_data['feeds'][$feedHash]['description'])) {
$this->_data['feeds'][$feedHash]['description'] = ' ';
}
}
$this->loadFeed($feedHash);
$oldItems = $this->_data['feeds'][$feedHash]['items'];
$rssItems = $this->getItemsFromXml($xml);
$rssItems = array_slice($rssItems, 0, $this->kfc->maxItems, true);
$rssItemsHash = array_keys($rssItems);
if (count($rssItemsHash) !== 0) {
// Look for new items
foreach ($rssItemsHash as $itemHash) {
// itemHash is smallHash of link. To compare to item
// hashes into data, we need to concatenate to feedHash.
if (!isset($oldItems[$feedHash.$itemHash])) {
if (empty($rssItems[$itemHash]['via'])) {
$rssItems[$itemHash]['via']
= $this->_data['feeds'][$feedHash]['htmlUrl'];
}
if (empty($rssItems[$itemHash]['author'])) {
$rssItems[$itemHash]['author']
= $this->_data['feeds'][$feedHash]['title'];
} else {
$rssItems[$itemHash]['author']
= $this->_data['feeds'][$feedHash]['title'] . ' ('
. $rssItems[$itemHash]['author'] . ')';
}
$rssItems[$itemHash]['xmlUrl'] = $xmlUrl;
$newItems[$feedHash . $itemHash] = $rssItems[$itemHash];
}
}
$newItemsHash = array_keys($newItems);
$this->_data['feeds'][$feedHash]['items']
= $newItems+$oldItems;
// Check if items may have been missed
if (count($oldItems) !== 0 and count($rssItemsHash) === count($newItemsHash)) {
$error = ERROR_ITEMS_MISSED;
}
// Remove useless items
foreach ($this->getItems($feedHash) as $itemHash => $item) {
$itemRssHash = substr($itemHash, 6, 6);
// Remove from cache already read items not any more in the feed
if (!isset($rssItems[$itemRssHash]) and $item[1] == 1) {
unset($this->_data['feeds'][$feedHash]['items'][$itemHash]);
}
if (!isset($this->_data['feeds'][$feedHash]['items'][$itemHash])) {
// Remove items not any more in the cache
unset($this->_data['items'][$itemHash]);
unset($this->_data['newItems'][$itemHash]);
}
}
// Check if quota exceeded
$nbAll = count($this->_data['feeds'][$feedHash]['items']);
if ($nbAll > $this->kfc->maxItems) {
$this->_data['feeds'][$feedHash]['items']
= array_slice(
$this->_data['feeds'][$feedHash]['items'],
0,
$this->kfc->maxItems, true
);
$nbAll = $this->kfc->maxItems;
}
// Update items list and feed information (nbUnread, nbAll)
$this->_data['feeds'][$feedHash]['nbAll'] = $nbAll;
$nbUnread = 0;
foreach ($this->_data['feeds'][$feedHash]['items'] as $itemHash => $item) {
if (isset($this->_data['items'][$itemHash])) {
if ($this->_data['items'][$itemHash][1] === 0) {
$nbUnread++;
}
} else if (isset($this->_data['newItems'][$itemHash])) {
if ($this->_data['newItems'][$itemHash][1] === 0) {
$nbUnread++;
}
} else {
// TODO: Is appended at the end ??
$this->_data['newItems'][$itemHash] = array(
$item['time'],
0
);
$nbUnread++;
}
}
$this->_data['feeds'][$feedHash]['nbUnread'] = $nbUnread;
} else {
$error = ERROR_UNKNOWN;
}
}
// update feed information
$this->_data['feeds'][$feedHash]['lastUpdate'] = time();
if (!empty($error)) {
$this->_data['feeds'][$feedHash]['error'] = $error;
}
if (empty($newItems)) {
unset($this->_data['feeds'][$feedHash]['items']);
$this->writeData();
} else {
$this->writeFeed($feedHash, $this->_data['feeds'][$feedHash]['items']);
unset($this->_data['feeds'][$feedHash]['items']);
$this->_data['needSort'] = true;
if (isset($_SESSION['lastNewItemsHash'])) {
$lastNewItemIndex = array_search($_SESSION['lastNewItemsHash'], array_keys($this->_data['newItems']));
$this->_data['items'] = $this->_data['items']+array_slice($this->_data['newItems'], 0, $lastNewItemIndex + 1, true);
$this->_data['newItems'] = array_slice($this->_data['newItems'], $lastNewItemIndex + 1, count($this->_data['newItems']) - $lastNewItemIndex, true);
unset($_SESSION['lastNewItemsHash']);
}
if ($this->kfc->order === 'newerFirst') {
arsort($this->_data['newItems']);
} else {
asort($this->_data['newItems']);
}
$this->_data['order'] = $this->kfc->order;
$this->writeData();
}
return array(
'error' => $error,
'newItems' => $newItems
);
}
public function updateFeedsHash($feedsHash, $force, $format = '')
{
$i = 0;
$feedsHash = $this->orderFeedsForUpdate($feedsHash);
ob_end_flush();
if (ob_get_level() == 0) ob_start();
$start = microtime(true);
foreach ($feedsHash as $feedHash) {
$i++;
$feed = $this->getFeed($feedHash);
$str = '
'.number_format(microtime(true)-$start,3).' seconds ('.$i.'/'.count($feedsHash).'): Updating: '.$feed['title'].'';
echo ($format==='html'?$str:strip_tags($str)).str_pad('',4096)."\n";
ob_flush();
flush();
if ($force or $this->needUpdate($feed)) {
$info = $this->updateChannel($feedHash);
$str = '
'.number_format(microtime(true)-$start,3).' seconds: Updated: '.count($info['newItems']).' new item(s)';
if (empty($info['error'])) {
$str .= '';
} else {
$str .= '
('.$this->getError($info['error']).')';
}
} else {
$str = '
'.number_format(microtime(true)-$start,3).' seconds: Already up-to-date: '.$feed['title'].'';
}
echo ($format==='html'?$str:strip_tags($str)).str_pad('',4096)."\n";
ob_flush();
flush();
}
}
public function markAll($read) {
$save = false;
foreach (array_keys($this->_data['items']) as $itemHash) {
if (!$save and $this->_data['items'][$itemHash][1] != $read) {
$save = true;
}
$this->_data['items'][$itemHash][1] = $read;
}
foreach (array_keys($this->_data['newItems']) as $itemHash) {
if (!$save and $this->_data['newItems'][$itemHash][1] != $read) {
$save = true;
}
$this->_data['newItems'][$itemHash][1] = $read;
}
if ($save) {
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if ($read == 1) {
$this->_data['feeds'][$feedHash]['nbUnread'] = 0;
} else {
$this->_data['feeds'][$feedHash]['nbUnread'] = $this->_data['feeds'][$feedHash]['nbAll'];
}
}
}
return $save;
}
public function markItem($itemHash, $read) {
$save = false;
if (isset($this->_data['items'][$itemHash])) {
if ($this->_data['items'][$itemHash][1] != $read) {
$save = true;
$this->_data['items'][$itemHash][1] = $read;
}
} else if (isset($this->_data['newItems'][$itemHash])) {
if ($this->_data['newItems'][$itemHash][1] != $read) {
$save = true;
$this->_data['newItems'][$itemHash][1] = $read;
}
}
if ($save) {
$feedHash = substr($itemHash, 0, 6);
if ($read == 1) {
$this->_data['feeds'][$feedHash]['nbUnread']--;
} else {
$this->_data['feeds'][$feedHash]['nbUnread']++;
}
}
return $save;
}
public function markFeeds($feedsHash, $read) {
$save = false;
// get items from a list of feeds
$flipFeedsHash = array_flip($feedsHash);
foreach ($this->_data['items'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($this->_data['items'][$itemHash][1] != $read) {
$save = true;
$this->_data['items'][$itemHash][1] = $read;
}
}
}
foreach ($this->_data['newItems'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($this->_data['newItems'][$itemHash][1] != $read) {
$save = true;
$this->_data['newItems'][$itemHash][1] = $read;
}
}
}
if ($save) {
foreach (array_values($feedsHash) as $feedHash) {
if ($read == 1) {
$this->_data['feeds'][$feedHash]['nbUnread'] = 0;
} else {
$this->_data['feeds'][$feedHash]['nbUnread'] = $this->_data['feeds']['nbAll'];
}
}
}
return $save;
}
public function mark($hash, $read)
{
if (empty($hash) || $hash == 'all') {
// all items
return $this->markAll($read);
} else {
if (strlen($hash) === 12) {
// an item
return $this->markItem($hash, $read);
} else {
$feedsHash = array();
if (isset($this->_data['feeds'][$hash])) {
// a feed
$feedsHash[] = $hash;
} else if (isset($this->_data['folders'][$hash])) {
// a folder
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (in_array($hash, $feed['foldersHash'])) {
$feedsHash[] = $feedHash;
}
}
}
return $this->markFeeds($feedsHash, $read);
}
}
return false;
}
public function hashType($hash)
{
$type = '';
if (empty($hash) || $hash=='all') {
$type = 'all';
} else {
if (strlen($hash) === 12) {
// should be an item
$type = 'item';
} else {
if (isset($this->_data['folders'][$hash])) {
// a folder
$type = 'folder';
} else {
if (isset($this->_data['feeds'][$hash])) {
// a feed
$type = 'feed';
} else {
$type = 'unknown';
}
}
}
}
return $type;
}
public static function sortByTitle($a, $b) {
return strnatcasecmp($a['title'], $b['title']);
}
}
class MyTool
{
public static function initPhp()
{
define('START_TIME', microtime(true));
if (phpversion() < 5) {
die("Argh you don't have PHP 5 !");
}
error_reporting(E_ALL);
function stripslashesDeep($value) {
return is_array($value)
? array_map('stripslashesDeep', $value)
: stripslashes($value);
}
if (get_magic_quotes_gpc()) {
$_POST = array_map('stripslashesDeep', $_POST);
$_GET = array_map('stripslashesDeep', $_GET);
$_COOKIE = array_map('stripslashesDeep', $_COOKIE);
}
ob_start();
register_shutdown_function('ob_end_flush');
}
public static function isUrl($url)
{
// http://neo22s.com/check-if-url-exists-and-is-online-php/
$pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
return preg_match($pattern, $url);
}
public static function isEmail($email)
{
$pattern = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2, 4}$/i";
return (preg_match($pattern, $email));
}
public static function formatBBCode($text)
{
$replace = array(
'/\[m\](.+?)\[\/m\]/is'
=> '/* moderate */',
'/\[b\](.+?)\[\/b\]/is'
=> '
$1',
'/\[i\](.+?)\[\/i\]/is'
=> '
$1',
'/\[s\](.+?)\[\/s\]/is'
=> '
$1',
'/\[u\](.+?)\[\/u\]/is'
=> '
$1',
'/\[url\](.+?)\[\/url]/is'
=> '
$1',
'/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is'
=> '
$2',
'/\[quote\](.+?)\[\/quote\]/is'
=> '
$1
',
'/\[code\](.+?)\[\/code\]/is'
=> '
$1
',
'/\[([^[]+)\|([^[]+)\]/is'
=> '
$1'
);
$text = preg_replace(
array_keys($replace),
array_values($replace),
$text
);
return $text;
}
public static function formatText($text)
{
$text = preg_replace_callback(
'/
(.*?)<\/code_html>/is',
create_function(
'$matches',
'return htmlspecialchars($matches[1]);'
),
$text
);
$text = preg_replace_callback(
'/(.*?)<\/code_php>/is',
create_function(
'$matches',
'return highlight_string("", true);'
),
$text
);
$text = preg_replace('/
/is', '', $text);
$text = preg_replace(
'#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im',
'\\1\\2\\4',
$text
);
$text = preg_replace(
'#(^|\s)wp:?([a-z]{2}|):([\w]+)#im',
'\\1\\3',
$text
);
$text = str_replace(
'http://.wikipedia.org/wiki/',
'http://www.wikipedia.org/wiki/',
$text
);
$text = str_replace('\wp:', 'wp:', $text);
$text = str_replace('\http:', 'http:', $text);
$text = MyTool::formatBBCode($text);
$text = nl2br($text);
return $text;
}
public static function getUrl()
{
$https = (!empty($_SERVER['HTTPS'])
&& (strtolower($_SERVER['HTTPS']) == 'on'))
|| (isset($_SERVER["SERVER_PORT"])
&& $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
$serverport = (!isset($_SERVER["SERVER_PORT"])
|| $_SERVER["SERVER_PORT"] == '80'
|| ($https && $_SERVER["SERVER_PORT"] == '443')
? ''
: ':' . $_SERVER["SERVER_PORT"]);
$scriptname = ($_SERVER["SCRIPT_NAME"] == 'index.php' ? '' : $_SERVER["SCRIPT_NAME"]);
if (!isset($_SERVER["SERVER_NAME"])) {
return $scriptname;
}
return 'http' . ($https ? 's' : '') . '://'
. $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
}
public static function rrmdir($dir)
{
if (is_dir($dir) && ($d = @opendir($dir))) {
while (($file = @readdir($d)) !== false) {
if ( $file == '.' || $file == '..' ) {
continue;
} else {
unlink($dir . '/' . $file);
}
}
}
}
public static function humanBytes($bytes)
{
$siPrefix = array( 'bytes', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' );
$base = 1024;
$class = min((int) log($bytes, $base), count($siPrefix) - 1);
$val = sprintf('%1.2f', $bytes / pow($base, $class));
return $val . ' ' . $siPrefix[$class];
}
public static function returnBytes($val)
{
$val = trim($val);
$last = strtolower($val[strlen($val)-1]);
switch($last)
{
case 'g': $val *= 1024;
case 'm': $val *= 1024;
case 'k': $val *= 1024;
}
return $val;
}
public static function getMaxFileSize()
{
$sizePostMax = MyTool::returnBytes(ini_get('post_max_size'));
$sizeUploadMax = MyTool::returnBytes(ini_get('upload_max_filesize'));
// Return the smaller of two:
return min($sizePostMax, $sizeUploadMax);
}
public static function smallHash($text)
{
$t = rtrim(base64_encode(hash('crc32', $text, true)), '=');
// Get rid of characters which need encoding in URLs.
$t = str_replace('+', '-', $t);
$t = str_replace('/', '_', $t);
$t = str_replace('=', '@', $t);
return $t;
}
public static function renderJson($data)
{
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
header('Content-type: application/json; charset=UTF-8');
echo json_encode($data);
exit();
}
public static function redirect($rurl = '')
{
if ($rurl === '') {
// if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0)
$rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
if (!empty($_POST)) {
$rurl = $_POST['returnurl'];
}
}
// prevent loop
if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
$rurl = MyTool::getUrl();
}
if (substr($rurl, 0, 1) !== '?') {
$ref = MyTool::getUrl();
if (substr($rurl, 0, strlen($ref)) != $ref) {
$rurl = $ref;
}
}
header('Location: '.$rurl);
exit();
}
public static function silence_errors($num, $str)
{
// No-op
}
}
class Opml
{
public static function importOpml($kfData)
{
$feeds = $kfData['feeds'];
$folders = $kfData['folders'];
$filename = $_FILES['filetoupload']['name'];
$filesize = $_FILES['filetoupload']['size'];
$data = file_get_contents($_FILES['filetoupload']['tmp_name']);
$overwrite = isset($_POST['overwrite']);
$opml = new DOMDocument('1.0', 'UTF-8');
$importCount=0;
if ($opml->loadXML($data)) {
$body = $opml->getElementsByTagName('body');
$xmlArray = Opml::getArrayFromXml($body->item(0));
$array = Opml::convertOpmlArray($xmlArray['outline']);
foreach ($array as $hashUrl => $arrayInfo) {
$title = '';
if (isset($arrayInfo['title'])) {
$title = $arrayInfo['title'];
} else if (isset($arrayInfo['text'])) {
$title = $arrayInfo['text'];
}
$foldersHash = array();
if (isset($arrayInfo['folders'])) {
foreach ($arrayInfo['folders'] as $folder) {
$folderTitle = html_entity_decode(
$folder,
ENT_QUOTES,
'UTF-8'
);
$folderHash = MyTool::smallHash($folderTitle);
if (!isset($folders[$folderHash])) {
$folders[$folderHash] = array('title' => $folderTitle, 'isOpen' => true);
}
$foldersHash[] = $folderHash;
}
}
$timeUpdate = 'auto';
$lastUpdate = 0;
$xmlUrl = '';
if (isset($arrayInfo['xmlUrl'])) {
$xmlUrl = $arrayInfo['xmlUrl'];
}
$htmlUrl = '';
if (isset($arrayInfo['htmlUrl'])) {
$htmlUrl = $arrayInfo['htmlUrl'];
}
$description = '';
if (isset($arrayInfo['description'])) {
$description = $arrayInfo['description'];
}
// create new feed
if (!empty($xmlUrl)) {
$oldFeed = array('nbUnread' => 0, 'nbAll' => 0);
if (isset($feeds[$hashUrl])) {
$oldFeed['nbUnread'] = $feeds[$hashUrl]['nbUnread'];
$oldFeed['nbAll'] = $feeds[$hashUrl]['nbAll'];
}
$currentFeed = array(
'title'
=>
html_entity_decode($title, ENT_QUOTES, 'UTF-8'),
'description'
=>
html_entity_decode($description, ENT_QUOTES, 'UTF-8'),
'htmlUrl'
=>
html_entity_decode($htmlUrl, ENT_QUOTES, 'UTF-8'),
'xmlUrl'
=>
html_entity_decode($xmlUrl, ENT_QUOTES, 'UTF-8'),
'nbUnread' => $oldFeed['nbUnread'],
'nbAll' => $oldFeed['nbAll'],
'foldersHash' => $foldersHash,
'timeUpdate' => $timeUpdate,
'lastUpdate' => $lastUpdate);
if ($overwrite || !isset($feeds[$hashUrl])) {
$feeds[$hashUrl] = $currentFeed;
$importCount++;
}
}
}
echo '';
$kfData['feeds'] = $feeds;
$kfData['folders'] = $folders;
return $kfData;
} else {
echo '';
exit;
}
}
public static function exportOpml($feeds, $folders)
{
$withoutFolder = array();
$withFolder = array();
// get a new representation of data using folders as key
foreach ($feeds as $hashUrl => $arrayInfo) {
if (empty($arrayInfo['foldersHash'])) {
$withoutFolder[] = $hashUrl;
} else {
foreach ($arrayInfo['foldersHash'] as $folderHash) {
$withFolder[$folderHash][] = $hashUrl;
}
}
}
// generate opml file
header('Content-Type: text/xml; charset=utf-8');
header(
'Content-disposition: attachment; filename=kriss_feed_'
. strval(date('Ymd_His')) . '.opml'
);
$opmlData = new DOMDocument('1.0', 'UTF-8');
// we want a nice output
$opmlData->formatOutput = true;
// opml node creation
$opml = $opmlData->createElement('opml');
$opmlVersion = $opmlData->createAttribute('version');
$opmlVersion->value = '1.0';
$opml->appendChild($opmlVersion);
// head node creation
$head = $opmlData->createElement('head');
$title = $opmlData->createElement('title', 'KrISS Feed');
$head->appendChild($title);
$opml->appendChild($head);
// body node creation
$body = $opmlData->createElement('body');
// without folder outline node
foreach ($withoutFolder as $hashUrl) {
$outline = $opmlData->createElement('outline');
$outlineTitle = $opmlData->createAttribute('title');
$outlineTitle->value = htmlspecialchars(
$feeds[$hashUrl]['title']
);
$outline->appendChild($outlineTitle);
$outlineText = $opmlData->createAttribute('text');
$outlineText->value
= htmlspecialchars($feeds[$hashUrl]['title']);
$outline->appendChild($outlineText);
if (!empty($feeds[$hashUrl]['description'])) {
$outlineDescription
= $opmlData->createAttribute('description');
$outlineDescription->value
= htmlspecialchars($feeds[$hashUrl]['description']);
$outline->appendChild($outlineDescription);
}
$outlineXmlUrl = $opmlData->createAttribute('xmlUrl');
$outlineXmlUrl->value
= htmlspecialchars($feeds[$hashUrl]['xmlUrl']);
$outline->appendChild($outlineXmlUrl);
$outlineHtmlUrl = $opmlData->createAttribute('htmlUrl');
$outlineHtmlUrl->value = htmlspecialchars(
$feeds[$hashUrl]['htmlUrl']
);
$outline->appendChild($outlineHtmlUrl);
$body->appendChild($outline);
}
// with folder outline node
foreach ($withFolder as $folderHash => $arrayHashUrl) {
$outline = $opmlData->createElement('outline');
$outlineTitle = $opmlData->createAttribute('title');
$outlineTitle->value = htmlspecialchars($folders[$folderHash]['title']);
$outline->appendChild($outlineTitle);
$outlineText = $opmlData->createAttribute('text');
$outlineText->value = htmlspecialchars($folders[$folderHash]['title']);
$outline->appendChild($outlineText);
foreach ($arrayHashUrl as $hashUrl) {
$outlineKF = $opmlData->createElement('outline');
$outlineTitle = $opmlData->createAttribute('title');
$outlineTitle->value
= htmlspecialchars($feeds[$hashUrl]['title']);
$outlineKF->appendChild($outlineTitle);
$outlineText = $opmlData->createAttribute('text');
$outlineText->value
= htmlspecialchars($feeds[$hashUrl]['title']);
$outlineKF->appendChild($outlineText);
if (!empty($feeds[$hashUrl]['description'])) {
$outlineDescription
= $opmlData->createAttribute('description');
$outlineDescription->value = htmlspecialchars(
$feeds[$hashUrl]['description']
);
$outlineKF->appendChild($outlineDescription);
}
$outlineXmlUrl = $opmlData->createAttribute('xmlUrl');
$outlineXmlUrl->value
= htmlspecialchars($feeds[$hashUrl]['xmlUrl']);
$outlineKF->appendChild($outlineXmlUrl);
$outlineHtmlUrl = $opmlData->createAttribute('htmlUrl');
$outlineHtmlUrl->value
= htmlspecialchars($feeds[$hashUrl]['htmlUrl']);
$outlineKF->appendChild($outlineHtmlUrl);
$outline->appendChild($outlineKF);
}
$body->appendChild($outline);
}
$opml->appendChild($body);
$opmlData->appendChild($opml);
echo $opmlData->saveXML();
exit();
}
public static function getArrayFromXml($node)
{
$array = false;
if ($node->hasAttributes()) {
foreach ($node->attributes as $attr) {
$array[$attr->nodeName] = $attr->nodeValue;
}
}
if ($node->hasChildNodes()) {
if ($node->childNodes->length == 1) {
$array[$node->firstChild->nodeName]
= $node->firstChild->nodeValue;
} else {
foreach ($node->childNodes as $childNode) {
if ($childNode->nodeType != XML_TEXT_NODE) {
$array[$childNode->nodeName][]
= Opml::getArrayFromXml($childNode);
}
}
}
}
return $array;
}
public static function convertOpmlArray($array, $listFolders = array())
{
$newArray = array();
for ($i = 0, $len = count($array); $i < $len; $i++) {
if (isset($array[$i]['outline'])
&& (isset($array[$i]['text'])
|| isset($array[$i]['title']))
) {
// here is a folder
if (isset($array[$i]['text'])) {
$listFolders[] = $array[$i]['text'];
} else {
$listFolders[] = $array[$i]['title'];
}
$newArray = array_merge(
$newArray,
Opml::convertOpmlArray(
$array[$i]['outline'],
$listFolders
)
);
array_pop($listFolders);
} else {
if (isset($array[$i]['xmlUrl'])) {
// here is a feed
$xmlUrl = MyTool::smallHash($array[$i]['xmlUrl']);
if (isset($newArray[$xmlUrl])) {
//feed already exists
foreach ($listFolders as $val) {
// add folder to the feed
if (!in_array(
$val,
$newArray[$xmlUrl]['folders']
)) {
$newArray[$xmlUrl]['folders'][] = $val;
}
}
} else {
// here is a new feed
foreach ($array[$i] as $attr => $val) {
$newArray[$xmlUrl][$attr] = $val;
}
$newArray[$xmlUrl]['folders'] = $listFolders;
}
}
}
}
return $newArray;
}
}
class PageBuilder
{
private $tpl; // For lazy initialization
private $pageClass;
public $var = array();
public function __construct($pageClass)
{
$this->tpl = false;
$this->pageClass = $pageClass;
}
private function initialize()
{
$this->tpl = true;
$ref = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
$this->assign('referer', $ref);
}
//
public function assign($variable, $value = null)
{
if ($this->tpl === false) {
$this->initialize(); // Lazy initialization
}
if (is_array($variable)) {
$this->var += $variable;
} else {
$this->var[$variable] = $value;
}
}
public function renderPage($page)
{
if ($this->tpl===false) {
$this->initialize(); // Lazy initialization
}
$method = $page.'Tpl';
if (method_exists($this->pageClass, $method)) {
$this->assign('template', $page);
$classPage = new $this->pageClass;
$classPage->init($this->var);
ob_start();
$classPage->$method();
ob_end_flush();
} else {
die("renderPage does not exist: ".$page);
}
}
}
class Session
{
private static $_instance;
public static $inactivityTimeout = 3600;
public static $disableSessionProtection = false;
public static $banFile = 'ipbans.php';
public static $banAfter = 4;
public static $banDuration = 1800;
private function __construct($banFile)
{
// Check ban configuration
self::$banFile = $banFile;
if (!is_file(self::$banFile)) {
file_put_contents(self::$banFile, "array(),'BANS'=>array()),true).";\n?>");
}
include self::$banFile;
// Force cookie path (but do not change lifetime)
$cookie=session_get_cookie_params();
// Default cookie expiration and path.
$cookiedir = '';
if(dirname($_SERVER['SCRIPT_NAME'])!='/') {
$cookiedir = dirname($_SERVER["SCRIPT_NAME"]).'/';
}
session_set_cookie_params($cookie['lifetime'], $cookiedir);
// Use cookies to store session.
ini_set('session.use_cookies', 1);
// Force cookies for session (phpsessionID forbidden in URL)
ini_set('session.use_only_cookies', 1);
if (!session_id()) {
// Prevent php to use sessionID in URL if cookies are disabled.
ini_set('session.use_trans_sid', false);
session_name('kriss');
session_start();
}
}
public static function init($banFile)
{
if (!isset(self::$_instance)) {
self::$_instance = new Session($banFile);
}
}
public static function banLoginFailed()
{
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
if (!isset($gb['FAILURES'][$ip])) {
$gb['FAILURES'][$ip] = 0;
}
$gb['FAILURES'][$ip]++;
if ($gb['FAILURES'][$ip] > (self::$banAfter-1)) {
$gb['BANS'][$ip]= time() + self::$banDuration;
}
$GLOBALS['IPBANS'] = $gb;
file_put_contents(self::$banFile, "");
}
function banLoginOk()
{
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
$GLOBALS['IPBANS'] = $gb;
file_put_contents(self::$banFile, "");
}
function banCanLogin()
{
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
if (isset($gb['BANS'][$ip])) {
// User is banned. Check if the ban has expired:
if ($gb['BANS'][$ip] <= time()) {
// Ban expired, user can try to login again.
unset($gb['FAILURES'][$ip]);
unset($gb['BANS'][$ip]);
file_put_contents(self::$banFile, "");
return true; // Ban has expired, user can login.
}
return false; // User is banned.
}
return true; // User is not banned.
}
private static function _allIPs()
{
$ip = $_SERVER["REMOTE_ADDR"];
$ip.= isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? '_'.$_SERVER['HTTP_X_FORWARDED_FOR'] : '';
$ip.= isset($_SERVER['HTTP_CLIENT_IP']) ? '_'.$_SERVER['HTTP_CLIENT_IP'] : '';
return $ip;
}
public static function login (
$login,
$password,
$loginTest,
$passwordTest,
$pValues = array())
{
if (!self::banCanLogin()) {
die('I said: NO. You are banned for the moment. Go away.');
}
if ($login == $loginTest && $password==$passwordTest) {
self::banLoginOk();
// Generate unique random number to sign forms (HMAC)
$_SESSION['uid'] = sha1(uniqid('', true).'_'.mt_rand());
$_SESSION['ip'] = Session::_allIPs();
$_SESSION['username'] = $login;
// Set session expiration.
$_SESSION['expires_on'] = time() + Session::$inactivityTimeout;
foreach ($pValues as $key => $value) {
$_SESSION[$key] = $value;
}
return true;
}
self::banLoginFailed();
Session::logout();
return false;
}
public static function logout()
{
unset($_SESSION['uid'], $_SESSION['ip'], $_SESSION['expires_on']);
}
public static function isLogged()
{
if (!isset ($_SESSION['uid'])
|| (Session::$disableSessionProtection == false
&& $_SESSION['ip']!=Session::_allIPs())
|| time()>=$_SESSION['expires_on']) {
Session::logout();
return false;
}
// User accessed a page : Update his/her session expiration date.
if (time()+Session::$inactivityTimeout > $_SESSION['expires_on']) {
$_SESSION['expires_on'] = time()+Session::$inactivityTimeout;
}
return true;
}
public static function getToken($salt = '')
{
if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens']=array();
}
// We generate a random string and store it on the server side.
$rnd = sha1(uniqid('', true).'_'.mt_rand().$salt);
$_SESSION['tokens'][$rnd]=1;
return $rnd;
}
public static function isToken($token)
{
if (isset($_SESSION['tokens'][$token])) {
unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
return true; // Token is ok.
}
return false; // Wrong token, or already used.
}
}//end class
// Check if php version is correct
MyTool::initPHP();
// Initialize Session
Session::init(BAN_FILE);
// XSRF protection with token
if (!empty($_POST)) {
if (!Session::isToken($_POST['token'])) {
die('Wrong token.');
}
}
unset($_SESSION['tokens']);
$pb = new PageBuilder('FeedPage');
$kfp = new FeedPage(STYLE_FILE);
$kfc = new FeedConf(CONFIG_FILE, FEED_VERSION);
$kf = new Feed(DATA_FILE, CACHE_DIR, $kfc);
// List or Expanded ?
$view = $kfc->view;
// show or hide list of feeds ?
$listFeeds = $kfc->listFeeds;
// All or Unread ?
$filter = $kfc->filter;
// newerFirst or olderFirst
$order = $kfc->order;
// number of item by page
$byPage = $kfc->getByPage();
// Hash : 'all', feed hash or folder hash
$currentHash = $kfc->getCurrentHash();
// Query
$query = '?';
if (!empty($currentHash) and $currentHash !== 'all') {
$query = '?currentHash='.$currentHash.'&';
}
$pb->assign('view', $view);
$pb->assign('listFeeds', $listFeeds);
$pb->assign('filter', $filter);
$pb->assign('order', $order);
$pb->assign('byPage', $byPage);
$pb->assign('currentHash', $currentHash);
$pb->assign('query', $query);
$pb->assign('redirector', $kfc->redirector);
$pb->assign('shaarli', htmlspecialchars($kfc->shaarli));
$pb->assign('autoreadItem', $kfc->autoreadItem);
$pb->assign('autoreadPage', $kfc->autoreadPage);
$pb->assign('autohide', $kfc->autohide);
$pb->assign('autofocus', $kfc->autofocus);
$pb->assign('autoupdate', $kfc->autoUpdate);
$pb->assign('addFavicon', $kfc->addFavicon);
$pb->assign('version', FEED_VERSION);
$pb->assign('kfurl', MyTool::getUrl());
if (isset($_GET['login'])) {
// Login
if (!empty($_POST['login'])
&& !empty($_POST['password'])
) {
if (Session::login(
$kfc->login,
$kfc->hash,
$_POST['login'],
sha1($_POST['password'].$_POST['login'].$kfc->salt)
)) {
if (!empty($_POST['longlastingsession'])) {
// (31536000 seconds = 1 year)
$_SESSION['longlastingsession'] = 31536000;
$_SESSION['expires_on'] =
time() + $_SESSION['longlastingsession'];
session_set_cookie_params($_SESSION['longlastingsession']);
} else {
session_set_cookie_params(0); // when browser closes
}
session_regenerate_id(true);
MyTool::redirect();
}
die("Login failed !");
} else {
$pb->assign('pagetitle', 'Login - '.strip_tags($kfc->title));
$pb->renderPage('login');
}
} elseif (isset($_GET['logout'])) {
//Logout
Session::logout();
MyTool::redirect();
} elseif (isset($_GET['ajax'])) {
$kf->loadData();
$needSave = false;
$result = array();
if (isset($_GET['current'])) {
$result['item'] = $kf->getItem($_GET['current'], false);
$result['item']['itemHash'] = $_GET['current'];
}
if (isset($_GET['read'])) {
$needSave = $kf->mark($_GET['read'], 1);
if ($needSave) {
$result['read'] = $_GET['read'];
}
}
if (isset($_GET['unread'])) {
$needSave = $kf->mark($_GET['unread'], 0);
if ($needSave) {
$result['unread'] = $_GET['unread'];
}
}
if (isset($_GET['toggleFolder'])) {
$needSave = $kf->toggleFolder($_GET['toggleFolder']);
}
if (isset($_GET['page'])) {
$listItems = $kf->getItems($currentHash, $filter);
$currentPage = $_GET['page'];
$index = ($currentPage - 1) * $byPage;
$results = array_slice($listItems, $index, $byPage + 1, true);
$result['page'] = array();
$firstIndex = -1;
if (isset($_GET['last'])) {
$firstIndex = array_search($_GET['last'], array_keys($results));
if ($firstIndex === false) {
$firstIndex = -1;
}
}
$i = 0;
foreach(array_slice($results, $firstIndex + 1, count($results) - $firstIndex - 1, true) as $itemHash => $item) {
$result['page'][$i] = $kf->getItem($itemHash, false);
$result['page'][$i]['read'] = $item[1];
$i++;
}
}
if (isset($_GET['update'])) {
if (Session::isLogged()) {
if (empty($_GET['update'])) {
$result['update']['feeds'] = array();
$feedsHash = $kf->orderFeedsForUpdate(array_keys($kf->getFeeds()));
foreach ($feedsHash as $feedHash) {
$feed = $kf->getFeed($feedHash);
$result['update']['feeds'][] = array($feedHash, $feed['title'], (int) ((time() - $feed['lastUpdate']) / 60), $kf->getTimeUpdate($feed));
}
} else {
$feed = $kf->getFeed($_GET['update']);
$info = $kf->updateChannel($_GET['update']);
if (empty($info['error'])) {
$info['error'] = $feed['description'];
} else {
$info['error'] = $kf->getError($info['error']);
}
$info['newItems'] = array_keys($info['newItems']);
$result['update'] = $info;
}
} else {
$result['update'] = false;
}
}
if ($needSave) {
$kf->writeData();
}
MyTool::renderJson($result);
} elseif (isset($_GET['help'])) {
$pb->assign('pagetitle', 'Help for KrISS feed');
$pb->renderPage('help');
} elseif ((isset($_GET['update'])
&& (Session::isLogged()
|| (isset($_GET['cron'])
&& $_GET['cron'] === sha1($kfc->salt.$kfc->hash))))
|| (isset($argv)
&& count($argv) >= 3
&& $argv[1] == 'update'
&& $argv[2] == sha1($kfc->salt.$kfc->hash))) {
// Update
$kf->loadData();
$forceUpdate = false;
if (isset($_GET['force'])) {
$forceUpdate = true;
}
$feedsHash = array();
$hash = 'all';
if (isset($_GET['update'])) {
$hash = $_GET['update'];
}
// type : 'feed', 'folder', 'all', 'item'
$type = $kf->hashType($hash);
switch($type) {
case 'feed':
$feedsHash[] = $hash;
break;
case 'folder':
$feedsHash = $kf->getFeedsHashFromFolderHash($hash);
break;
case 'all':
case '':
$feedsHash = array_keys($kf->getFeeds());
break;
case 'item':
default:
break;
}
if (isset($_GET['cron']) || isset($argv) && count($argv) >= 3) {
$kf->updateFeedsHash($feedsHash, $forceUpdate);
} else {
$pb->assign('kf', $kf);
$pb->assign('feedsHash', $feedsHash);
$pb->assign('forceUpdate', $forceUpdate);
$pb->assign('pagetitle', 'Update');
$pb->renderPage('update');
}
} elseif (isset($_GET['config']) && Session::isLogged()) {
// Config
if (isset($_POST['save'])) {
if (isset($_POST['disableSessionProtection'])) {
$_POST['disableSessionProtection'] = '1';
} else {
$_POST['disableSessionProtection'] = '0';
}
$kfc->hydrate($_POST);
MyTool::redirect();
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$menu = $kfc->getMenu();
$paging = $kfc->getPaging();
$pb->assign('page', 'config');
$pb->assign('pagetitle', 'Config - '.strip_tags($kfc->title));
$pb->assign('kfctitle', htmlspecialchars($kfc->title));
$pb->assign('kfcredirector', htmlspecialchars($kfc->redirector));
$pb->assign('kfcshaarli', htmlspecialchars($kfc->shaarli));
$pb->assign('kfclocale', htmlspecialchars($kfc->locale));
$pb->assign('kfcmaxitems', htmlspecialchars($kfc->maxItems));
$pb->assign('kfcmaxupdate', htmlspecialchars($kfc->maxUpdate));
$pb->assign('kfcpublic', (int) $kfc->public);
$pb->assign('kfccron', sha1($kfc->salt.$kfc->hash));
$pb->assign('kfcautoreaditem', (int) $kfc->autoreadItem);
$pb->assign('kfcautoreadpage', (int) $kfc->autoreadPage);
$pb->assign('kfcautoupdate', (int) $kfc->autoUpdate);
$pb->assign('kfcautohide', (int) $kfc->autohide);
$pb->assign('kfcautofocus', (int) $kfc->autofocus);
$pb->assign('kfcaddfavicon', (int) $kfc->addFavicon);
$pb->assign('kfcdisablesessionprotection', (int) $kfc->disableSessionProtection);
$pb->assign('kfcmenu', $menu);
$pb->assign('kfcpaging', $paging);
$pb->renderPage('config');
}
} elseif (isset($_GET['import']) && Session::isLogged()) {
// Import
if (isset($_POST['import'])) {
// If file is too big, some form field may be missing.
if ((!isset($_FILES))
|| (isset($_FILES['filetoupload']['size'])
&& $_FILES['filetoupload']['size']==0)
) {
$rurl = empty($_SERVER['HTTP_REFERER'])
? '?'
: $_SERVER['HTTP_REFERER'];
echo '';
exit;
}
$kf->loadData();
$kf->setData(Opml::importOpml($kf->getData()));
$kf->sortFeeds();
$kf->writeData();
exit;
} else if (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$pb->assign('pagetitle', 'Import');
$pb->renderPage('import');
}
} elseif (isset($_GET['export']) && Session::isLogged()) {
// Export
$kf->loadData();
Opml::exportOpml($kf->getFeeds(), $kf->getFolders());
} elseif (isset($_GET['add']) && Session::isLogged()) {
// Add feed
$kf->loadData();
if (isset($_POST['newfeed']) && !empty($_POST['newfeed'])) {
if ($kf->addChannel($_POST['newfeed'])) {
// Add success
$folders = array();
if (!empty($_POST['folders'])) {
foreach ($_POST['folders'] as $hashFolder) {
$folders[] = $hashFolder;
}
}
if (!empty($_POST['newfolder'])) {
$newFolderHash = MyTool::smallHash($_POST['newfolder']);
$kf->addFolder($_POST['newfolder'], $newFolderHash);
$folders[] = $newFolderHash;
}
$hash = MyTool::smallHash($_POST['newfeed']);
$kf->editFeed($hash, '', '', $folders, '');
$kf->sortFeeds();
$kf->writeData();
MyTool::redirect('?currentHash='.$hash);
} else {
// Add fail
$returnurl = empty($_SERVER['HTTP_REFERER'])
? MyTool::getUrl()
: $_SERVER['HTTP_REFERER'];
echo '';
exit;
}
}
$newfeed = '';
if (isset($_GET['newfeed'])) {
$newfeed = htmlspecialchars($_GET['newfeed']);
}
$pb->assign('page', 'add');
$pb->assign('pagetitle', 'Add a new feed');
$pb->assign('newfeed', $newfeed);
$pb->assign('folders', $kf->getFolders());
$pb->renderPage('addFeed');
} elseif (isset($_GET['toggleFolder']) && Session::isLogged()) {
$kf->loadData();
if (isset($_GET['toggleFolder'])) {
$kf->toggleFolder($_GET['toggleFolder']);
}
$kf->writeData();
MyTool::redirect();
} elseif ((isset($_GET['read'])
|| isset($_GET['unread']))
&& Session::isLogged()) {
// mark all as read : item, feed, folder, all
$kf->loadData();
$read = 1;
if (isset($_GET['read'])) {
$hash = $_GET['read'];
$read = 1;
} else {
$hash = $_GET['unread'];
$read = 0;
}
$needSave = $kf->mark($hash, $read);
if ($needSave) {
$kf->writeData();
}
// type : 'feed', 'folder', 'all', 'item'
$type = $kf->hashType($hash);
if ($type === 'item') {
MyTool::redirect($query.'current='.$hash);
} else {
if ($filter === 'unread' && $read === 1) {
MyTool::redirect('?');
} else {
MyTool::redirect($query);
}
}
} elseif (isset($_GET['edit']) && Session::isLogged()) {
// Edit feed, folder, all
$kf->loadData();
$pb->assign('page', 'edit');
$pb->assign('pagetitle', 'edit');
$hash = substr(trim($_GET['edit'], '/'), 0, 6);
// type : 'feed', 'folder', 'all', 'item'
$type = $kf->hashType($currentHash);
$type = $kf->hashType($hash);
switch($type) {
case 'feed':
if (isset($_POST['save'])) {
$title = $_POST['title'];
$description = $_POST['description'];
$folders = array();
if (!empty($_POST['folders'])) {
foreach ($_POST['folders'] as $hashFolder) {
$folders[] = $hashFolder;
}
}
if (!empty($_POST['newfolder'])) {
$newFolderHash = MyTool::smallHash($_POST['newfolder']);
$kf->addFolder($_POST['newfolder'], $newFolderHash);
$folders[] = $newFolderHash;
}
$timeUpdate = $_POST['timeUpdate'];
$kf->editFeed($hash, $title, $description, $folders, $timeUpdate);
$kf->writeData();
MyTool::redirect();
} elseif (isset($_POST['delete'])) {
$kf->removeFeed($hash);
$kf->writeData();
MyTool::redirect('?');
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$feed = $kf->getFeed($hash);
if (!empty($feed)) {
$lastUpdate = 'need update';
if (!$kf->needUpdate($feed)) {
$diff = (int) (time() - $feed['lastUpdate']);
$lastUpdate =
(int) ($diff / 60) . ' m ' . (int) ($diff % 60) . ' s';
}
$pb->assign('feed', $feed);
$pb->assign('folders', $kf->getFolders());
$pb->assign('lastUpdate', $lastUpdate);
$pb->renderPage('editFeed');
} else {
MyTool::redirect();
}
}
break;
case 'folder':
if (isset($_POST['save'])) {
$oldFolderTitle = $kf->getFolderTitle($hash);
$newFolderTitle = $_POST['foldertitle'];
if ($oldFolderTitle !== $newFolderTitle) {
$kf->renameFolder($hash, $newFolderTitle);
$kf->writeData();
}
if (empty($newFolderTitle)) {
MyTool::redirect('?');
} else {
MyTool::redirect('?currentHash='.MyTool::smallHash($newFolderTitle));
}
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$folderTitle = $kf->getFolderTitle($hash);
$pb->assign('foldertitle', htmlspecialchars($folderTitle));
$pb->renderPage('editFolder');
}
break;
case 'all':
if (isset($_POST['save'])) {
$feedsHash = array();
foreach ($_POST['feeds'] as $feedHash) {
$feedsHash[] = $feedHash;
}
foreach ($feedsHash as $feedHash) {
$feed = $kf->getFeed($feedHash);
$addFoldersHash = $feed['foldersHash'];
if (!empty($_POST['addfolders'])) {
foreach ($_POST['addfolders'] as $folderHash) {
if (!in_array($folderHash, $addFoldersHash)) {
$addFoldersHash[] = $folderHash;
}
}
}
if (!empty($_POST['addnewfolder'])) {
$newFolderHash = MyTool::smallHash($_POST['addnewfolder']);
$kf->addFolder($_POST['addnewfolder'], $newFolderHash);
$addFoldersHash[] = $newFolderHash;
}
$removeFoldersHash = array();
if (!empty($_POST['removefolders'])) {
foreach ($_POST['removefolders'] as $folderHash) {
$removeFoldersHash[] = $folderHash;
}
}
$addFoldersHash = array_diff($addFoldersHash, $removeFoldersHash);
$kf->editFeed(
$feedHash,
'',
'',
$addFoldersHash,
''
);
}
$kf->writeData();
MyTool::redirect();
} elseif (isset($_POST['delete'])) {
foreach ($_POST['feeds'] as $feedHash) {
$kf->removeFeed($feedHash);
}
$kf->writeData();
MyTool::redirect();
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$folders = $kf->getFolders();
$listFeeds = $kf->getFeeds();
$pb->assign('folders', $folders);
$pb->assign('listFeeds', $listFeeds);
$pb->renderPage('editAll');
}
break;
case 'item':
default:
MyTool::redirect();
break;
}
} elseif (isset($_GET['shaarli'])) {
$kf->loadData();
$item = $kf->getItem($_GET['shaarli'], false);
$shaarli = $kfc->shaarli;
// remove sel used with javascript
$shaarli = str_replace('${sel}', '', $shaarli);
$url = htmlspecialchars_decode($item['link']);
$via = htmlspecialchars_decode($item['via']);
$title = htmlspecialchars_decode($item['title']);
if (parse_url($url, PHP_URL_HOST) !== parse_url($via, PHP_URL_HOST)) {
$via = 'via '.$via;
} else {
$via = '';
}
$shaarli = str_replace('${url}', urlencode($url), $shaarli);
$shaarli = str_replace('${title}', urlencode($title), $shaarli);
$shaarli = str_replace('${via}', urlencode($via), $shaarli);
header('Location: '.$shaarli);
} else {
if (Session::isLogged() || $kfc->public) {
$kf->loadData();
if ($kf->updateItems()) {
$kf->writeData();
}
$listItems = $kf->getItems($currentHash, $filter);
$listHash = array_keys($listItems);
$currentItemHash = '';
if (isset($_GET['current']) && !empty($_GET['current'])) {
$currentItemHash = $_GET['current'];
}
if (isset($_GET['next']) && !empty($_GET['next'])) {
$currentItemHash = $_GET['next'];
if ($kfc->autoreadItem) {
if ($kf->mark($currentItemHash, 1)) {
if ($filter == 'unread') {
unset($listItems[$currentItemHash]);
}
$kf->writeData();
}
}
}
if (isset($_GET['previous']) && !empty($_GET['previous'])) {
$currentItemHash = $_GET['previous'];
}
if (empty($currentItemHash)) {
$currentPage = $kfc->getCurrentPage();
$index = ($currentPage - 1) * $byPage;
} else {
$index = array_search($currentItemHash, $listHash);
if (isset($_GET['next'])) {
if ($index < count($listHash)-1) {
$index++;
}
}
if (isset($_GET['previous'])) {
if ($index > 0) {
$index--;
}
}
}
if ($index < count($listHash)) {
$currentItemHash = $listHash[$index];
} else {
$index = count($listHash) - 1;
}
$unread = 0;
foreach ($listItems as $itemHash => $item) {
if ($item[1] === 0) {
$unread++;
}
}
// pagination
$currentPage = (int) ($index/$byPage)+1;
if ($currentPage <= 0) {
$currentPage = 1;
}
$begin = ($currentPage - 1) * $byPage;
$maxPage = (count($listItems) <= $byPage) ? '1' : ceil(count($listItems) / $byPage);
$nbItems = count($listItems);
// list items
$listItems = array_slice($listItems, $begin, $byPage, true);
// type : 'feed', 'folder', 'all', 'item'
$currentHashType = $kf->hashType($currentHash);
$hashView = '';
switch($currentHashType){
case 'all':
$hashView = ''.$unread.' unread items';
break;
case 'feed':
$hashView = 'Feed ('.$kf->getFeedTitle($currentHash).'): '.''.$unread.' unread items';
break;
case 'folder':
$hashView = 'Folder ('.$kf->getFolderTitle($currentHash).'): '.$unread.' unread items';
break;
default:
$hashView = ''.$unread.' unread items';
break;
}
$menu = $kfc->getMenu();
$paging = $kfc->getPaging();
$pb->assign('menu', $menu);
$pb->assign('paging', $paging);
$pb->assign('currentHashType', $currentHashType);
$pb->assign('currentHashView', $hashView);
$pb->assign('currentPage', (int) $currentPage);
$pb->assign('maxPage', (int) $maxPage);
$pb->assign('currentItemHash', $currentItemHash);
$pb->assign('nbItems', $nbItems);
$pb->assign('items', $listItems);
if ($listFeeds == 'show') {
$pb->assign('feedsView', $kf->getFeedsView());
}
$pb->assign('kf', $kf);
$pb->assign('pagetitle', strip_tags($kfc->title));
$pb->renderPage('index');
} else {
$pb->assign('pagetitle', 'Login - '.strip_tags($kfc->title));
if (!empty($_SERVER['QUERY_STRING'])) {
$pb->assign('referer', MyTool::getUrl().'?'.$_SERVER['QUERY_STRING']);
}
$pb->renderPage('login');
}
}
//print(number_format(microtime(true)-START_TIME,3).' secondes');