aboutsummaryrefslogtreecommitdiff
path: root/app/Controllers/indexController.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Controllers/indexController.php')
-rwxr-xr-xapp/Controllers/indexController.php215
1 files changed, 171 insertions, 44 deletions
diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php
index 38f4c0e7c..e8e26b142 100755
--- a/app/Controllers/indexController.php
+++ b/app/Controllers/indexController.php
@@ -25,16 +25,12 @@ class FreshRSS_index_Controller extends Minz_ActionController {
}
}
- // construction of RSS url of this feed
$params = Minz_Request::params ();
- $params['output'] = 'rss';
if (isset ($params['search'])) {
$params['search'] = urlencode ($params['search']);
}
- if (!Minz_Configuration::allowAnonymous()) {
- $params['token'] = $token;
- }
- $this->view->rss_url = array (
+
+ $this->view->url = array (
'c' => 'index',
'a' => 'index',
'params' => $params
@@ -49,7 +45,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
}
$catDAO = new FreshRSS_CategoryDAO();
- $entryDAO = new FreshRSS_EntryDAO();
+ $entryDAO = FreshRSS_Factory::createEntryDao();
$this->view->cat_aside = $catDAO->listCategories ();
$this->view->nb_favorites = $entryDAO->countUnreadReadFavorites ();
@@ -73,31 +69,32 @@ class FreshRSS_index_Controller extends Minz_ActionController {
// mise à jour des titres
$this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title();
- if ($this->view->nb_not_read > 0) {
- Minz_View::appendTitle (' (' . formatNumber($this->view->nb_not_read) . ')');
- }
- Minz_View::prependTitle (
+ Minz_View::prependTitle(
+ ($this->nb_not_read_cat > 0 ? '(' . formatNumber($this->nb_not_read_cat) . ') ' : '') .
$this->view->currentName .
- ($this->nb_not_read_cat > 0 ? ' (' . formatNumber($this->nb_not_read_cat) . ')' : '') .
' · '
);
// On récupère les différents éléments de filtrage
- $this->view->state = $state = Minz_Request::param ('state', $this->view->conf->default_view);
+ $this->view->state = Minz_Request::param('state', $this->view->conf->default_view);
+ $state_param = Minz_Request::param ('state', null);
$filter = Minz_Request::param ('search', '');
- if (!empty($filter)) {
- $state = 'all'; //Search always in read and unread articles
- }
$this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sort_order);
$nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page);
$first = Minz_Request::param ('next', '');
- if ($state === 'not_read') { //Any unread article in this category at all?
+ $ajax_request = Minz_Request::param('ajax', false);
+ if ($output === 'reader') {
+ $nb = max(1, round($nb / 2));
+ }
+
+ if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all?
switch ($getType) {
case 'a':
$hasUnread = $this->view->nb_not_read > 0;
break;
case 's':
+ // This is deprecated. The favorite button does not exist anymore
$hasUnread = $this->view->nb_favorites['unread'] > 0;
break;
case 'c':
@@ -111,8 +108,8 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$hasUnread = true;
break;
}
- if (!$hasUnread) {
- $this->view->state = $state = 'all';
+ if (!$hasUnread && ($state_param === null)) {
+ $this->view->state = FreshRSS_Entry::STATE_ALL;
}
}
@@ -125,15 +122,22 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$keepHistoryDefault = $this->view->conf->keep_history_default;
try {
- $entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault);
+ $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault);
// Si on a récupéré aucun article "non lus"
// on essaye de récupérer tous les articles
- if ($state === 'not_read' && empty($entries)) {
- Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
- $this->view->state = 'all';
- $entries = $entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault);
+ if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) {
+ Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
+ $feedDAO = FreshRSS_Factory::createFeedDao();
+ try {
+ $feedDAO->updateCachedValues();
+ } catch (Exception $ex) {
+ Minz_Log::record('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::NOTICE);
+ }
+ $this->view->state = FreshRSS_Entry::STATE_ALL;
+ $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault);
}
+ Minz_Request::_param('state', $this->view->state);
if (count($entries) <= $nb) {
$this->view->nextId = '';
@@ -186,7 +190,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
case 'f':
$feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId);
if (empty($feed)) {
- $feedDAO = new FreshRSS_FeedDAO();
+ $feedDAO = FreshRSS_Factory::createFeedDao();
$feed = $feedDAO->searchById($getId);
}
if ($feed) {
@@ -203,25 +207,6 @@ class FreshRSS_index_Controller extends Minz_ActionController {
}
}
- public function statsAction () {
- if (!$this->view->loginOk) {
- Minz_Error::error (
- 403,
- array ('error' => array (Minz_Translate::t ('access_denied')))
- );
- }
-
- Minz_View::prependTitle (Minz_Translate::t ('stats') . ' · ');
-
- $statsDAO = new FreshRSS_StatsDAO ();
- Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
- $this->view->repartition = $statsDAO->calculateEntryRepartition();
- $this->view->count = ($statsDAO->calculateEntryCount());
- $this->view->feedByCategory = $statsDAO->calculateFeedByCategory();
- $this->view->entryByCategory = $statsDAO->calculateEntryByCategory();
- $this->view->topFeed = $statsDAO->calculateTopFeed();
- }
-
public function aboutAction () {
Minz_View::prependTitle (Minz_Translate::t ('about') . ' · ');
}
@@ -316,7 +301,46 @@ class FreshRSS_index_Controller extends Minz_ActionController {
Minz_Session::_param('passwordHash');
}
+ private static function makeLongTermCookie($username, $passwordHash) {
+ do {
+ $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true));
+ $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt';
+ } while (file_exists($tokenFile));
+ if (@file_put_contents($tokenFile, $username . "\t" . $passwordHash) === false) {
+ return false;
+ }
+ $expire = time() + 2629744; //1 month //TODO: Use a configuration instead
+ Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire);
+ Minz_Session::_param('token', $token);
+ return $token;
+ }
+
+ private static function deleteLongTermCookie() {
+ Minz_Session::deleteLongTermCookie('FreshRSS_login');
+ $token = Minz_Session::param('token', null);
+ if (ctype_alnum($token)) {
+ @unlink(DATA_PATH . '/tokens/' . $token . '.txt');
+ }
+ Minz_Session::_param('token');
+ if (rand(0, 10) === 1) {
+ self::purgeTokens();
+ }
+ }
+
+ private static function purgeTokens() {
+ $oldest = time() - 2629744; //1 month //TODO: Use a configuration instead
+ foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $fileInfo) {
+ if ($fileInfo->getExtension() === 'txt' && $fileInfo->getMTime() < $oldest) {
+ @unlink($fileInfo->getPathname());
+ }
+ }
+ }
+
public function formLoginAction () {
+ if ($this->view->loginOk) {
+ Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
+ }
+
if (Minz_Request::isPost()) {
$ok = false;
$nonce = Minz_Session::param('nonce');
@@ -333,6 +357,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
if ($ok) {
Minz_Session::_param('currentUser', $username);
Minz_Session::_param('passwordHash', $s);
+ if (Minz_Request::param('keep_logged_in', false)) {
+ self::makeLongTermCookie($username, $s);
+ } else {
+ self::deleteLongTermCookie();
+ }
} else {
Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING);
}
@@ -351,6 +380,32 @@ class FreshRSS_index_Controller extends Minz_ActionController {
}
$this->view->_useLayout(false);
Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
+ } elseif (Minz_Configuration::unsafeAutologinEnabled() && isset($_GET['u']) && isset($_GET['p'])) {
+ Minz_Session::_param('currentUser');
+ Minz_Session::_param('mail');
+ Minz_Session::_param('passwordHash');
+ $username = ctype_alnum($_GET['u']) ? $_GET['u'] : '';
+ $passwordPlain = $_GET['p'];
+ Minz_Request::_param('p'); //Discard plain-text password ASAP
+ $_GET['p'] = '';
+ if (!function_exists('password_verify')) {
+ include_once(LIB_PATH . '/password_compat.php');
+ }
+ try {
+ $conf = new FreshRSS_Configuration($username);
+ $s = $conf->passwordHash;
+ $ok = password_verify($passwordPlain, $s);
+ unset($passwordPlain);
+ if ($ok) {
+ Minz_Session::_param('currentUser', $username);
+ Minz_Session::_param('passwordHash', $s);
+ } else {
+ Minz_Log::record('Unsafe password mismatch for user ' . $username, Minz_Log::WARNING);
+ }
+ } catch (Minz_Exception $me) {
+ Minz_Log::record('Unsafe login failure: ' . $me->getMessage(), Minz_Log::WARNING);
+ }
+ Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
} elseif (!Minz_Configuration::canLogIn()) {
Minz_Error::error (
403,
@@ -366,6 +421,78 @@ class FreshRSS_index_Controller extends Minz_ActionController {
Minz_Session::_param('currentUser');
Minz_Session::_param('mail');
Minz_Session::_param('passwordHash');
+ self::deleteLongTermCookie();
Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
}
+
+ public function resetAuthAction() {
+ Minz_View::prependTitle(_t('auth_reset') . ' · ');
+ Minz_View::appendScript(Minz_Url::display(
+ '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')
+ ));
+
+ $this->view->no_form = false;
+ // Enable changement of auth only if Persona!
+ if (Minz_Configuration::authType() != 'persona') {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('auth_not_persona')
+ );
+ $this->view->no_form = true;
+ return;
+ }
+
+ $conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser());
+ // Admin user must have set its master password.
+ if (!$conf->passwordHash) {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('auth_no_password_set')
+ );
+ $this->view->no_form = true;
+ return;
+ }
+
+ invalidateHttpCache();
+
+ if (Minz_Request::isPost()) {
+ $nonce = Minz_Session::param('nonce');
+ $username = Minz_Request::param('username', '');
+ $c = Minz_Request::param('challenge', '');
+ if (!(ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce))) {
+ Minz_Log::debug('Invalid credential parameters:' .
+ ' user=' . $username .
+ ' challenge=' . $c .
+ ' nonce=' . $nonce);
+ Minz_Request::bad(_t('invalid_login'),
+ array('c' => 'index', 'a' => 'resetAuth'));
+ }
+
+ if (!function_exists('password_verify')) {
+ include_once(LIB_PATH . '/password_compat.php');
+ }
+
+ $s = $conf->passwordHash;
+ $ok = password_verify($nonce . $s, $c);
+ if ($ok) {
+ Minz_Configuration::_authType('form');
+ $ok = Minz_Configuration::writeFile();
+
+ if ($ok) {
+ Minz_Request::good(_t('auth_form_set'));
+ } else {
+ Minz_Request::bad(_t('auth_form_not_set'),
+ array('c' => 'index', 'a' => 'resetAuth'));
+ }
+ } else {
+ Minz_Log::debug('Password mismatch for user ' . $username .
+ ', nonce=' . $nonce . ', c=' . $c);
+
+ Minz_Request::bad(_t('invalid_login'),
+ array('c' => 'index', 'a' => 'resetAuth'));
+ }
+ }
+ }
}