/** * This action is the default one for the controller. * * It is called by Minz_Error::error() method. * * Parameters are passed by Minz_Session to have a proper url: * - error_code (default: 404) * - error_logs (default: array()) */ public function indexAction() { $code_int = Minz_Session::param('error_code', 404); $error_logs = Minz_Session::param('error_logs', array()); Minz_Session::_param('error_code'); Minz_Session::_param('error_logs'); switch ($code_int) { case 200: header('HTTP/1.1 200 OK'); break; case 403: header('HTTP/1.1 403 Forbidden'); $this->view->code = 'Error 403 - Forbidden'; $this->view->errorMessage = _t('feedback.access.denied'); break; case 500: header('HTTP/1.1 500 Internal Server Error'); $this->view->code = 'Error 500 - Internal Server Error'; break; case 503: header('HTTP/1.1 503 Service Unavailable'); $this->view->code = 'Error 503 - Service Unavailable'; break; case 404: default: header('HTTP/1.1 404 Not Found'); $this->view->code = 'Error 404 - Not found'; $this->view->errorMessage = _t('feedback.access.not_found'); } $error_message = trim(implode($error_logs)); if ($error_message !== '') { $this->view->errorMessage = $error_message; } Minz_View::prependTitle($this->view->code . ' · '); }
public function nonceAction() { header('Content-Type: application/json; charset=UTF-8'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s \\G\\M\\T')); header('Expires: 0'); header('Cache-Control: private, no-cache, no-store, must-revalidate'); header('Pragma: no-cache'); $user = isset($_GET['user']) ? $_GET['user'] : ''; if (ctype_alnum($user)) { try { $salt = FreshRSS_Context::$system_conf->salt; $conf = get_user_configuration($user); $s = $conf->passwordHash; if (strlen($s) >= 60) { $this->view->salt1 = substr($s, 0, 29); //CRYPT_BLOWFISH Salt: "$2a$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". $this->view->nonce = sha1($salt . uniqid(mt_rand(), true)); Minz_Session::_param('nonce', $this->view->nonce); return; //Success } } catch (Minz_Exception $me) { Minz_Log::warning('Nonce failure: ' . $me->getMessage()); } } else { Minz_Log::notice('Nonce failure due to invalid username!'); } //Failure: Return random data. $this->view->salt1 = sprintf('$2a$%02d$', FreshRSS_user_Controller::BCRYPT_COST); $alphabet = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for ($i = 22; $i > 0; $i--) { $this->view->salt1 .= $alphabet[rand(0, 63)]; } $this->view->nonce = sha1(rand()); }
public function nonceAction() { header('Content-Type: application/json; charset=UTF-8'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s \\G\\M\\T')); header('Expires: 0'); header('Cache-Control: private, no-cache, no-store, must-revalidate'); header('Pragma: no-cache'); $user = isset($_GET['user']) ? $_GET['user'] : ''; if (ctype_alnum($user)) { try { $conf = new FreshRSS_Configuration($user); $s = $conf->passwordHash; if (strlen($s) >= 60) { $this->view->salt1 = substr($s, 0, 29); //CRYPT_BLOWFISH Salt: "$2a$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". $this->view->nonce = sha1(Minz_Configuration::salt() . uniqid(mt_rand(), true)); Minz_Session::_param('nonce', $this->view->nonce); return; //Success } } catch (Minz_Exception $me) { Minz_Log::record('Nonce failure: ' . $me->getMessage(), Minz_Log::WARNING); } } $this->view->nonce = ''; //Failure $this->view->salt1 = ''; }
/** * This action handles the display configuration page. * * It displays the display configuration page. * If this action is reached through a POST request, it stores all new * configuration values then sends a notification to the user. * * The options available on the page are: * - language (default: en) * - theme (default: Origin) * - content width (default: thin) * - display of read action in header * - display of favorite action in header * - display of date in header * - display of open action in header * - display of read action in footer * - display of favorite action in footer * - display of sharing action in footer * - display of tags in footer * - display of date in footer * - display of open action in footer * - html5 notification timeout (default: 0) * Default values are false unless specified. */ public function displayAction() { if (Minz_Request::isPost()) { FreshRSS_Context::$user_conf->language = Minz_Request::param('language', 'en'); FreshRSS_Context::$user_conf->theme = Minz_Request::param('theme', FreshRSS_Themes::$defaultTheme); FreshRSS_Context::$user_conf->content_width = Minz_Request::param('content_width', 'thin'); FreshRSS_Context::$user_conf->topline_read = Minz_Request::param('topline_read', false); FreshRSS_Context::$user_conf->topline_favorite = Minz_Request::param('topline_favorite', false); FreshRSS_Context::$user_conf->topline_date = Minz_Request::param('topline_date', false); FreshRSS_Context::$user_conf->topline_link = Minz_Request::param('topline_link', false); FreshRSS_Context::$user_conf->bottomline_read = Minz_Request::param('bottomline_read', false); FreshRSS_Context::$user_conf->bottomline_favorite = Minz_Request::param('bottomline_favorite', false); FreshRSS_Context::$user_conf->bottomline_sharing = Minz_Request::param('bottomline_sharing', false); FreshRSS_Context::$user_conf->bottomline_tags = Minz_Request::param('bottomline_tags', false); FreshRSS_Context::$user_conf->bottomline_date = Minz_Request::param('bottomline_date', false); FreshRSS_Context::$user_conf->bottomline_link = Minz_Request::param('bottomline_link', false); FreshRSS_Context::$user_conf->html5_notif_timeout = Minz_Request::param('html5_notif_timeout', 0); FreshRSS_Context::$user_conf->save(); Minz_Session::_param('language', FreshRSS_Context::$user_conf->language); Minz_Translate::reset(FreshRSS_Context::$user_conf->language); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), array('c' => 'configure', 'a' => 'display')); } $this->view->themes = FreshRSS_Themes::get(); Minz_View::prependTitle(_t('conf.display.title') . ' · '); }
/** * Permet de lancer une erreur * @param $code le type de l'erreur, par défaut 404 (page not found) * @param $logs logs d'erreurs découpés de la forme * > $logs['error'] * > $logs['warning'] * > $logs['notice'] * @param $redirect indique s'il faut forcer la redirection (les logs ne seront pas transmis) */ public static function error($code = 404, $logs = array(), $redirect = true) { $logs = self::processLogs($logs); $error_filename = APP_PATH . '/Controllers/errorController.php'; if (file_exists($error_filename)) { Minz_Session::_param('error_code', $code); Minz_Session::_param('error_logs', $logs); Minz_Request::forward(array('c' => 'error'), $redirect); } else { echo '<h1>An error occured</h1>' . "\n"; if (!empty($logs)) { echo '<ul>' . "\n"; foreach ($logs as $log) { echo '<li>' . $log . '</li>' . "\n"; } echo '</ul>' . "\n"; } exit; } }
public function archivingAction() { if (Minz_Request::isPost()) { $old = Minz_Request::param('old_entries', 3); $keepHistoryDefault = Minz_Request::param('keep_history_default', 0); $this->view->conf->_old_entries($old); $this->view->conf->_keep_history_default($keepHistoryDefault); $this->view->conf->save(); invalidateHttpCache(); $notif = array('type' => 'good', 'content' => Minz_Translate::t('configuration_updated')); Minz_Session::_param('notification', $notif); Minz_Request::forward(array('c' => 'configure', 'a' => 'archiving'), true); } Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' · '); $entryDAO = new FreshRSS_EntryDAO(); $this->view->nb_total = $entryDAO->count(); $this->view->size_user = $entryDAO->size(); if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { $this->view->size_total = $entryDAO->size(true); } }
/** * This action handles Persona login page. * * If this action is reached through a POST request, assertion from Persona * is verificated and user connected if all is ok. * * Parameter is: * - assertion (default: false) * * @todo: Persona system should be moved to a plugin */ public function personaLoginAction() { $this->view->res = false; if (Minz_Request::isPost()) { $this->view->_useLayout(false); $assert = Minz_Request::param('assertion'); $url = 'https://verifier.login.persona.org/verify'; $params = 'assertion=' . $assert . '&audience=' . urlencode(Minz_Url::display(null, 'php', true)); $ch = curl_init(); $options = array(CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_POST => 2, CURLOPT_POSTFIELDS => $params); curl_setopt_array($ch, $options); $result = curl_exec($ch); curl_close($ch); $res = json_decode($result, true); $login_ok = false; $reason = ''; if ($res['status'] === 'okay') { $email = filter_var($res['email'], FILTER_VALIDATE_EMAIL); if ($email != '') { $persona_file = DATA_PATH . '/persona/' . $email . '.txt'; if (($current_user = @file_get_contents($persona_file)) !== false) { $current_user = trim($current_user); $conf = get_user_configuration($current_user); if (!is_null($conf)) { $login_ok = strcasecmp($email, $conf->mail_login) === 0; } else { $reason = 'Invalid configuration for user ' . '[' . $current_user . ']'; } } } else { $reason = 'Invalid email format [' . $res['email'] . ']'; } } else { $reason = $res['reason']; } if ($login_ok) { Minz_Session::_param('currentUser', $current_user); Minz_Session::_param('mail', $email); FreshRSS_Auth::giveAccess(); invalidateHttpCache(); } else { Minz_Log::warning($reason); $res = array(); $res['status'] = 'failure'; $res['reason'] = _t('feedback.auth.login.invalid'); } header('Content-Type: application/json; charset=UTF-8'); $this->view->res = $res; } }
function invalidateHttpCache() { Minz_Session::_param('touch', uTimeString()); return touch(join_path(DATA_PATH, 'users', Minz_Session::param('currentUser', '_'), 'log.txt')); }
function invalidateHttpCache() { Minz_Session::_param('touch', uTimeString()); return touch(LOG_PATH . '/' . Minz_Session::param('currentUser', '_') . '.log'); }
private function loadNotifications() { $notif = Minz_Session::param('notification'); if ($notif) { Minz_View::_param('notification', $notif); Minz_Session::_param('notification'); } }
public function deleteAction() { if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { require_once APP_PATH . '/sql.php'; $username = Minz_Request::param('username'); $ok = ctype_alnum($username); if ($ok) { $ok &= strcasecmp($username, Minz_Configuration::defaultUser()) !== 0; //It is forbidden to delete the default user } if ($ok) { $configPath = DATA_PATH . '/' . $username . '_user.php'; $ok &= file_exists($configPath); } if ($ok) { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->deleteUser($username); $ok &= unlink($configPath); //TODO: delete Persona file } invalidateHttpCache(); $notif = array('type' => $ok ? 'good' : 'bad', 'content' => Minz_Translate::t($ok ? 'user_deleted' : 'error_occurred', $username)); Minz_Session::_param('notification', $notif); } Minz_Request::forward(array('c' => 'configure', 'a' => 'users'), true); }
public function deleteAction() { if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { $db = FreshRSS_Context::$system_conf->db; require_once APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'; $username = Minz_Request::param('username'); $ok = ctype_alnum($username); $user_data = join_path(DATA_PATH, 'users', $username); if ($ok) { $default_user = FreshRSS_Context::$system_conf->default_user; $ok &= strcasecmp($username, $default_user) !== 0; //It is forbidden to delete the default user } if ($ok) { $ok &= is_dir($user_data); } if ($ok) { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->deleteUser($username); $ok &= recursive_unlink($user_data); //TODO: delete Persona file } invalidateHttpCache(); $notif = array('type' => $ok ? 'good' : 'bad', 'content' => _t('feedback.user.deleted' . (!$ok ? '.error' : ''), $username)); Minz_Session::_param('notification', $notif); } Minz_Request::forward(array('c' => 'user', 'a' => 'manage'), true); }
/** * This action handles the system configuration page. * * It displays the system configuration page. * If this action is reach through a POST request, it stores all new * configuration values then sends a notification to the user. * * The options available on the page are: * - user limit (default: 1) * - user category limit (default: 16384) * - user feed limit (default: 16384) */ public function systemAction() { if (!FreshRSS_Auth::hasAccess('admin')) { Minz_Error::error(403); } if (Minz_Request::isPost()) { $limits = FreshRSS_Context::$system_conf->limits; $limits['max_registrations'] = Minz_Request::param('max-registrations', 1); $limits['max_feeds'] = Minz_Request::param('max-feeds', 16384); $limits['max_categories'] = Minz_Request::param('max-categories', 16384); FreshRSS_Context::$system_conf->limits = $limits; FreshRSS_Context::$system_conf->title = Minz_Request::param('instance-name', 'FreshRSS'); FreshRSS_Context::$system_conf->auto_update_url = Minz_Request::param('auto-update-url', false); FreshRSS_Context::$system_conf->save(); invalidateHttpCache(); Minz_Session::_param('notification', array('type' => 'good', 'content' => _t('feedback.conf.updated'))); } }
/** * This action handles import action. * * It must be reached by a POST request. * * Parameter is: * - file (default: nothing!) * Available file types are: zip, json or xml. */ public function importAction() { if (!Minz_Request::isPost()) { Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); } $file = $_FILES['file']; $status_file = $file['error']; if ($status_file !== 0) { Minz_Log::error('File cannot be uploaded. Error code: ' . $status_file); Minz_Request::bad(_t('feedback.import_export.file_cannot_be_uploaded'), array('c' => 'importExport', 'a' => 'index')); } @set_time_limit(300); $type_file = $this->guessFileType($file['name']); $list_files = array('opml' => array(), 'json_starred' => array(), 'json_feed' => array()); // We try to list all files according to their type $list = array(); if ($type_file === 'zip' && extension_loaded('zip')) { $zip = zip_open($file['tmp_name']); if (!is_resource($zip)) { // zip_open cannot open file: something is wrong Minz_Log::error('Zip archive cannot be imported. Error code: ' . $zip); Minz_Request::bad(_t('feedback.import_export.zip_error'), array('c' => 'importExport', 'a' => 'index')); } while (($zipfile = zip_read($zip)) !== false) { if (!is_resource($zipfile)) { // zip_entry() can also return an error code! Minz_Log::error('Zip file cannot be imported. Error code: ' . $zipfile); } else { $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { $list_files[$type_zipfile][] = zip_entry_read($zipfile, zip_entry_filesize($zipfile)); } } } zip_close($zip); } elseif ($type_file === 'zip') { // Zip extension is not loaded Minz_Request::bad(_t('feedback.import_export.no_zip_extension'), array('c' => 'importExport', 'a' => 'index')); } elseif ($type_file !== 'unknown') { $list_files[$type_file][] = file_get_contents($file['tmp_name']); } // Import file contents. // OPML first(so categories and feeds are imported) // Starred articles then so the "favourite" status is already set // And finally all other files. $error = false; foreach ($list_files['opml'] as $opml_file) { $error = $this->importOpml($opml_file); } foreach ($list_files['json_starred'] as $article_file) { $error = $this->importJson($article_file, true); } foreach ($list_files['json_feed'] as $article_file) { $error = $this->importJson($article_file); } // And finally, we get import status and redirect to the home page Minz_Session::_param('actualize_feeds', true); $content_notif = $error === true ? _t('feedback.import_export.feeds_imported_with_errors') : _t('feedback.import_export.feeds_imported'); Minz_Request::good($content_notif); }
$self = isset($links[0]) ? $links[0] : null; if ($self !== base64url_decode($canonical64)) { //header('HTTP/1.1 422 Unprocessable Entity'); logMe('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . base64url_decode($canonical64)); //die('Self URL does not match registered canonical URL!'); $self = base64url_decode($canonical64); } Minz_Request::_param('url', $self); $nb = 0; foreach ($users as $userFilename) { $username = basename($userFilename, '.txt'); if (!file_exists(USERS_PATH . '/' . $username . '/config.php')) { break; } try { Minz_Session::_param('currentUser', $username); Minz_Configuration::register('user', join_path(USERS_PATH, $username, 'config.php'), join_path(USERS_PATH, '_', 'config.default.php')); FreshRSS_Context::init(); if ($feedController->actualizeAction($simplePie) > 0) { $nb++; } } catch (Exception $e) { logMe('Error: ' . $e->getMessage()); } } $simplePie->__destruct(); unset($simplePie); if ($nb === 0) { header('HTTP/1.1 410 Gone'); logMe('Error: Nobody is subscribed to this feed anymore after all!: ' . $self); die('Nobody is subscribed to this feed anymore after all!');
public function purgeAction() { @set_time_limit(300); $nb_month_old = max($this->view->conf->old_entries, 1); $date_min = time() - 3600 * 24 * 30 * $nb_month_old; $feedDAO = new FreshRSS_FeedDAO(); $feeds = $feedDAO->listFeedsOrderUpdate(); $nbTotal = 0; invalidateHttpCache(); foreach ($feeds as $feed) { $feedHistory = $feed->keepHistory(); if ($feedHistory == -2) { //default $feedHistory = $this->view->conf->keep_history_default; } if ($feedHistory >= 0) { $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, $feedHistory); if ($nb > 0) { $nbTotal += $nb; Minz_Log::record($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG); $feedDAO->updateLastUpdate($feed->id()); } } } invalidateHttpCache(); $notif = array('type' => 'good', 'content' => Minz_Translate::t('purge_completed', $nbTotal)); Minz_Session::_param('notification', $notif); Minz_Request::forward(array('c' => 'configure', 'a' => 'archiving'), true); }
public function formLogoutAction() { $this->view->_useLayout(false); invalidateHttpCache(); Minz_Session::_param('currentUser'); Minz_Session::_param('mail'); Minz_Session::_param('passwordHash'); Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); }
/** * Removes all accesses for the current user. */ public static function removeAccess() { Minz_Session::_param('loginOk'); self::$login_ok = false; $conf = Minz_Configuration::get('system'); Minz_Session::_param('currentUser', $conf->default_user); switch ($conf->auth_type) { case 'form': Minz_Session::_param('passwordHash'); FreshRSS_FormAuth::deleteCookie(); break; case 'persona': Minz_Session::_param('mail'); break; case 'http_auth': case 'none': // Nothing to do... break; default: // TODO: extensions } }
/** * This action actualizes entries from one or several feeds. * * Parameters are: * - id (default: false): Feed ID * - url (default: false): Feed URL * - force (default: false) * If id and url are not specified, all the feeds are actualized. But if force is * false, process stops at 10 feeds to avoid time execution problem. */ public function actualizeAction($simplePiePush = null) { @set_time_limit(300); $feedDAO = FreshRSS_Factory::createFeedDao(); $entryDAO = FreshRSS_Factory::createEntryDao(); Minz_Session::_param('actualize_feeds', false); $id = Minz_Request::param('id'); $url = Minz_Request::param('url'); $force = Minz_Request::param('force'); // Create a list of feeds to actualize. // If id is set and valid, corresponding feed is added to the list but // alone in order to automatize further process. $feeds = array(); if ($id || $url) { $feed = $id ? $feedDAO->searchById($id) : $feedDAO->searchByUrl($url); if ($feed) { $feeds[] = $feed; } } else { $feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$user_conf->ttl_default); } // Calculate date of oldest entries we accept in DB. $nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1); $date_min = time() - 3600 * 24 * 30 * $nb_month_old; // PubSubHubbub support $pubsubhubbubEnabledGeneral = FreshRSS_Context::$system_conf->pubsubhubbub_enabled; $pshbMinAge = time() - 3600 * 24; //TODO: Make a configuration. $updated_feeds = 0; $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; foreach ($feeds as $feed) { $url = $feed->url(); //For detection of HTTP 301 $pubSubHubbubEnabled = $pubsubhubbubEnabledGeneral && $feed->pubSubHubbubEnabled(); if (!$simplePiePush && !$id && $pubSubHubbubEnabled && $feed->lastUpdate() > $pshbMinAge) { //$text = 'Skip pull of feed using PubSubHubbub: ' . $url; //Minz_Log::debug($text); //file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND); continue; //When PubSubHubbub is used, do not pull refresh so often } if (!$feed->lock()) { Minz_Log::notice('Feed already being actualized: ' . $feed->url()); continue; } try { if ($simplePiePush) { $feed->loadEntries($simplePiePush); //Used by PubSubHubbub } else { $feed->load(false); } } catch (FreshRSS_Feed_Exception $e) { Minz_Log::warning($e->getMessage()); $feedDAO->updateLastUpdate($feed->id(), true); $feed->unlock(); continue; } $feed_history = $feed->keepHistory(); if ($feed_history == -2) { // TODO: -2 must be a constant! // -2 means we take the default value from configuration $feed_history = FreshRSS_Context::$user_conf->keep_history_default; } // We want chronological order and SimplePie uses reverse order. $entries = array_reverse($feed->entries()); if (count($entries) > 0) { $newGuids = array(); foreach ($entries as $entry) { $newGuids[] = $entry->guid(); } // For this feed, check existing GUIDs already in database. $existingHashForGuids = $entryDAO->listHashForFeedGuids($feed->id(), $newGuids); unset($newGuids); $oldGuids = array(); // Add entries in database if possible. foreach ($entries as $entry) { $entry_date = $entry->date(true); if (isset($existingHashForGuids[$entry->guid()])) { $existingHash = $existingHashForGuids[$entry->guid()]; if (strcasecmp($existingHash, $entry->hash()) === 0 || $existingHash === '00000000000000000000000000000000') { //This entry already exists and is unchanged. TODO: Remove the test with the zero'ed hash in FreshRSS v1.3 $oldGuids[] = $entry->guid(); } else { //This entry already exists but has been updated Minz_Log::debug('Entry with GUID `' . $entry->guid() . '` updated in feed ' . $feed->id() . ', old hash ' . $existingHash . ', new hash ' . $entry->hash()); //TODO: Make an updated/is_read policy by feed, in addition to the global one. $entry->_isRead(FreshRSS_Context::$user_conf->mark_updated_article_unread ? false : null); //Change is_read according to policy. if (!$entryDAO->hasTransaction()) { $entryDAO->beginTransaction(); } $entryDAO->updateEntry($entry->toArray()); } } elseif ($feed_history == 0 && $entry_date < $date_min) { // This entry should not be added considering configuration and date. $oldGuids[] = $entry->guid(); } else { if ($entry_date < $date_min) { $id = min(time(), $entry_date) . uSecString(); $entry->_isRead(true); //Old article that was not in database. Probably an error, so mark as read } else { $id = uTimeString(); $entry->_isRead($is_read); } $entry->_id($id); $entry = Minz_ExtensionManager::callHook('entry_before_insert', $entry); if ($entry === null) { // An extension has returned a null value, there is nothing to insert. continue; } if ($pubSubHubbubEnabled && !$simplePiePush) { //We use push, but have discovered an article by pull! $text = 'An article was discovered by pull although we use PubSubHubbub!: Feed ' . $url . ' GUID ' . $entry->guid(); file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND); Minz_Log::warning($text); $pubSubHubbubEnabled = false; $feed->pubSubHubbubError(true); } if (!$entryDAO->hasTransaction()) { $entryDAO->beginTransaction(); } $entryDAO->addEntry($entry->toArray()); } } $entryDAO->updateLastSeen($feed->id(), $oldGuids); } if ($feed_history >= 0 && rand(0, 30) === 1) { // TODO: move this function in web cron when available (see entry::purge) // Remove old entries once in 30. if (!$entryDAO->hasTransaction()) { $entryDAO->beginTransaction(); } $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, max($feed_history, count($entries) + 10)); if ($nb > 0) { Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url() . ']'); } } $feedDAO->updateLastUpdate($feed->id(), 0, $entryDAO->hasTransaction()); if ($entryDAO->hasTransaction()) { $entryDAO->commit(); } if ($feed->hubUrl() && $feed->selfUrl()) { //selfUrl has priority for PubSubHubbub if ($feed->selfUrl() !== $url) { //https://code.google.com/p/pubsubhubbub/wiki/MovingFeedsOrChangingHubs $selfUrl = checkUrl($feed->selfUrl()); if ($selfUrl) { Minz_Log::debug('PubSubHubbub unsubscribe ' . $feed->url()); if (!$feed->pubSubHubbubSubscribe(false)) { //Unsubscribe Minz_Log::warning('Error while PubSubHubbub unsubscribing from ' . $feed->url()); } $feed->_url($selfUrl, false); Minz_Log::notice('Feed ' . $url . ' canonical address moved to ' . $feed->url()); $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } } } elseif ($feed->url() !== $url) { // HTTP 301 Moved Permanently Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url()); $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } $feed->faviconPrepare(); if ($pubsubhubbubEnabledGeneral && $feed->pubSubHubbubPrepare()) { Minz_Log::notice('PubSubHubbub subscribe ' . $feed->url()); if (!$feed->pubSubHubbubSubscribe(true)) { //Subscribe Minz_Log::warning('Error while PubSubHubbub subscribing to ' . $feed->url()); } } $feed->unlock(); $updated_feeds++; unset($feed); // No more than 10 feeds unless $force is true to avoid overloading // the server. if ($updated_feeds >= 10 && !$force) { break; } } if (Minz_Request::param('ajax')) { // Most of the time, ajax request is for only one feed. But since // there are several parallel requests, we should return that there // are several updated feeds. $notif = array('type' => 'good', 'content' => _t('feedback.sub.feed.actualizeds')); Minz_Session::_param('notification', $notif); // No layout in ajax request. $this->view->_useLayout(false); } else { // Redirect to the main page with correct notification. if ($updated_feeds === 1) { $feed = reset($feeds); Minz_Request::good(_t('feedback.sub.feed.actualized', $feed->name()), array('params' => array('get' => 'f_' . $feed->id()))); } elseif ($updated_feeds > 1) { Minz_Request::good(_t('feedback.sub.feed.n_actualized', $updated_feeds), array()); } else { Minz_Request::good(_t('feedback.sub.feed.no_refresh'), array()); } } return $updated_feeds; }
/** * This action actualizes entries from one or several feeds. * * Parameters are: * - id (default: false) * - force (default: false) * If id is not specified, all the feeds are actualized. But if force is * false, process stops at 10 feeds to avoid time execution problem. */ public function actualizeAction() { @set_time_limit(300); $feedDAO = FreshRSS_Factory::createFeedDao(); $entryDAO = FreshRSS_Factory::createEntryDao(); Minz_Session::_param('actualize_feeds', false); $id = Minz_Request::param('id'); $force = Minz_Request::param('force'); // Create a list of feeds to actualize. // If id is set and valid, corresponding feed is added to the list but // alone in order to automatize further process. $feeds = array(); if ($id) { $feed = $feedDAO->searchById($id); if ($feed) { $feeds[] = $feed; } } else { $feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$user_conf->ttl_default); } // Calculate date of oldest entries we accept in DB. $nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1); $date_min = time() - 3600 * 24 * 30 * $nb_month_old; $updated_feeds = 0; $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; foreach ($feeds as $feed) { if (!$feed->lock()) { Minz_Log::notice('Feed already being actualized: ' . $feed->url()); continue; } try { // Load entries $feed->load(false); } catch (FreshRSS_Feed_Exception $e) { Minz_Log::notice($e->getMessage()); $feedDAO->updateLastUpdate($feed->id(), 1); $feed->unlock(); continue; } $url = $feed->url(); $feed_history = $feed->keepHistory(); if ($feed_history == -2) { // TODO: -2 must be a constant! // -2 means we take the default value from configuration $feed_history = FreshRSS_Context::$user_conf->keep_history_default; } // We want chronological order and SimplePie uses reverse order. $entries = array_reverse($feed->entries()); if (count($entries) > 0) { // For this feed, check last n entry GUIDs already in database. $existing_guids = array_fill_keys($entryDAO->listLastGuidsByFeed($feed->id(), count($entries) + 10), 1); $use_declared_date = empty($existing_guids); // Add entries in database if possible. $prepared_statement = $entryDAO->addEntryPrepare(); $feedDAO->beginTransaction(); foreach ($entries as $entry) { $entry_date = $entry->date(true); if (isset($existing_guids[$entry->guid()]) || $feed_history == 0 && $entry_date < $date_min) { // This entry already exists in DB or should not be added // considering configuration and date. continue; } $id = uTimeString(); if ($use_declared_date || $entry_date < $date_min) { // Use declared date at first import. $id = min(time(), $entry_date) . uSecString(); } $entry->_id($id); $entry->_isRead($is_read); $entry = Minz_ExtensionManager::callHook('entry_before_insert', $entry); if (is_null($entry)) { // An extension has returned a null value, there is nothing to insert. continue; } $values = $entry->toArray(); $entryDAO->addEntry($values, $prepared_statement); } } if ($feed_history >= 0 && rand(0, 30) === 1) { // TODO: move this function in web cron when available (see entry::purge) // Remove old entries once in 30. if (!$feedDAO->hasTransaction()) { $feedDAO->beginTransaction(); } $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, max($feed_history, count($entries) + 10)); if ($nb > 0) { Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url() . ']'); } } $feedDAO->updateLastUpdate($feed->id(), 0, $feedDAO->hasTransaction()); if ($feedDAO->hasTransaction()) { $feedDAO->commit(); } if ($feed->url() !== $url) { // HTTP 301 Moved Permanently Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url()); $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } $feed->faviconPrepare(); $feed->unlock(); $updated_feeds++; unset($feed); // No more than 10 feeds unless $force is true to avoid overloading // the server. if ($updated_feeds >= 10 && !$force) { break; } } if (Minz_Request::param('ajax')) { // Most of the time, ajax request is for only one feed. But since // there are several parallel requests, we should return that there // are several updated feeds. $notif = array('type' => 'good', 'content' => _t('feedback.sub.feed.actualizeds')); Minz_Session::_param('notification', $notif); // No layout in ajax request. $this->view->_useLayout(false); return; } // Redirect to the main page with correct notification. if ($updated_feeds === 1) { $feed = reset($feeds); Minz_Request::good(_t('feedback.sub.feed.actualized', $feed->name()), array('params' => array('get' => 'f_' . $feed->id()))); } elseif ($updated_feeds > 1) { Minz_Request::good(_t('feedback.sub.feed.n_actualized', $updated_feeds), array()); } else { Minz_Request::good(_t('feedback.sub.feed.no_refresh'), array()); } }
public function deleteAction() { if (Minz_Request::isPost()) { $type = Minz_Request::param('type', 'feed'); $id = Minz_Request::param('id'); $feedDAO = new FreshRSS_FeedDAO(); if ($type == 'category') { if ($feedDAO->deleteFeedByCategory($id)) { $notif = array('type' => 'good', 'content' => Minz_Translate::t('category_emptied')); //TODO: Delete old favicons } else { $notif = array('type' => 'bad', 'content' => Minz_Translate::t('error_occured')); } } else { if ($feedDAO->deleteFeed($id)) { $notif = array('type' => 'good', 'content' => Minz_Translate::t('feed_deleted')); //TODO: Delete old favicon } else { $notif = array('type' => 'bad', 'content' => Minz_Translate::t('error_occured')); } } Minz_Session::_param('notification', $notif); if ($type == 'category') { Minz_Request::forward(array('c' => 'configure', 'a' => 'categorize'), true); } else { Minz_Request::forward(array('c' => 'configure', 'a' => 'feed'), true); } } }
public function logout() { Minz_Session::_param('currentUser'); $this->good(array('status' => 'OK')); }
public static function bad($msg, $url = array()) { Minz_Session::_param('notification', array('type' => 'bad', 'content' => $msg)); Minz_Request::forward($url, true); }
/** * This action delete an existing user. * * Request parameter is: * - username * * @todo clean up this method. Idea: create a User->clean() method. */ public function deleteAction() { $username = Minz_Request::param('username'); $redirect_url = urldecode(Minz_Request::param('r', false, true)); if (!$redirect_url) { $redirect_url = array('c' => 'user', 'a' => 'manage'); } $self_deletion = Minz_Session::param('currentUser', '_') === $username; if (Minz_Request::isPost() && (FreshRSS_Auth::hasAccess('admin') || $self_deletion)) { $db = FreshRSS_Context::$system_conf->db; require_once APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'; $ok = ctype_alnum($username); $user_data = join_path(DATA_PATH, 'users', $username); if ($ok) { $default_user = FreshRSS_Context::$system_conf->default_user; $ok &= strcasecmp($username, $default_user) !== 0; //It is forbidden to delete the default user } if ($ok && $self_deletion) { // We check the password if it's a self-destruction $nonce = Minz_Session::param('nonce'); $challenge = Minz_Request::param('challenge', ''); $ok &= FreshRSS_FormAuth::checkCredentials($username, FreshRSS_Context::$user_conf->passwordHash, $nonce, $challenge); } if ($ok) { $ok &= is_dir($user_data); } if ($ok) { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->deleteUser($username); $ok &= recursive_unlink($user_data); //TODO: delete Persona file } if ($ok && $self_deletion) { FreshRSS_Auth::removeAccess(); $redirect_url = array('c' => 'index', 'a' => 'index'); } invalidateHttpCache(); $notif = array('type' => $ok ? 'good' : 'bad', 'content' => _t('feedback.user.deleted' . (!$ok ? '.error' : ''), $username)); Minz_Session::_param('notification', $notif); } Minz_Request::forward($redirect_url, true); }
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # ***** END LICENSE BLOCK ***** require '../../constants.php'; require LIB_PATH . '/lib_rss.php'; //Includes class autoloader if (file_exists(DATA_PATH . '/do-install.txt')) { require APP_PATH . '/install.php'; } else { session_cache_limiter(''); Minz_Session::init('FreshRSS'); Minz_Session::_param('keepAlive', 1); //For Persona if (!file_exists(DATA_PATH . '/no-cache.txt')) { require LIB_PATH . '/http-conditional.php'; $currentUser = Minz_Session::param('currentUser', ''); $dateLastModification = $currentUser === '' ? time() : max(@filemtime(join_path(USERS_PATH, $currentUser, 'log.txt')), @filemtime(join_path(DATA_PATH, 'config.php'))); if (httpConditional($dateLastModification, 0, 0, false, PHP_COMPRESSION, true)) { exit; //No need to send anything } } try { $front_controller = new FreshRSS(); $front_controller->init(); $front_controller->run(); } catch (Exception $e) {
if (defined('STDOUT')) { fwrite(STDOUT, 'Actualize ' . $myUser . "...\n"); //Unbuffered } echo $myUser, ' '; //Buffered $_GET['c'] = 'feed'; $_GET['a'] = 'actualize'; $_GET['ajax'] = 1; $_GET['force'] = true; $_SERVER['HTTP_HOST'] = ''; $freshRSS = new FreshRSS(); $freshRSS->_useOb(false); Minz_Configuration::_authType('none'); Minz_Session::init('FreshRSS'); Minz_Session::_param('currentUser', $myUser); $freshRSS->init(); $freshRSS->run(); if (!invalidateHttpCache()) { syslog(LOG_NOTICE, 'FreshRSS write access problem in ' . LOG_PATH . '/*.log!'); if (defined('STDERR')) { fwrite(STDERR, 'Write access problem in ' . LOG_PATH . '/*.log!' . "\n"); } } Minz_Session::unset_session(true); Minz_ModelPdo::clean(); } syslog(LOG_INFO, 'FreshRSS actualize done.'); if (defined('STDOUT')) { fwrite(STDOUT, 'Done.' . "\n"); }