예제 #1
0
 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!');
     }
     $this->view->nonce = '';
     //Failure
     $this->view->salt1 = '';
 }
예제 #2
0
 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());
 }
예제 #3
0
 /**
  * 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;
 }
예제 #4
0
 /**
  * This action displays the RSS feed of FreshRSS.
  */
 public function rssAction()
 {
     $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous;
     $token = FreshRSS_Context::$user_conf->token;
     $token_param = Minz_Request::param('token', '');
     $token_is_ok = $token != '' && $token === $token_param;
     // Check if user has access.
     if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous && !$token_is_ok) {
         Minz_Error::error(403);
     }
     try {
         $this->updateContext();
     } catch (FreshRSS_Context_Exception $e) {
         Minz_Error::error(404);
     }
     try {
         $this->view->entries = $this->listEntriesByContext();
     } catch (FreshRSS_EntriesGetter_Exception $e) {
         Minz_Log::notice($e->getMessage());
         Minz_Error::error(404);
     }
     // No layout for RSS output.
     $this->view->url = empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING'];
     $this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title();
     $this->view->_useLayout(false);
     header('Content-Type: application/rss+xml; charset=utf-8');
 }
예제 #5
0
function get_content_by_parsing($url, $path)
{
    require_once LIB_PATH . '/lib_phpQuery.php';
    Minz_Log::notice('FreshRSS GET ' . url_remove_credentials($url));
    $html = file_get_contents($url);
    if ($html) {
        $doc = phpQuery::newDocument($html);
        $content = $doc->find($path);
        return sanitizeHTML($content->__toString(), $url);
    } else {
        throw new Exception();
    }
}
예제 #6
0
    if (defined('STDOUT')) {
        fwrite(STDOUT, 'Actualize ' . $user . "...\n");
        //Unbuffered
    }
    echo $user, ' ';
    //Buffered
    Minz_Session::_param('currentUser', $user);
    new Minz_ModelPdo($user);
    //TODO: FIXME: Quick-fix while waiting for a better FreshRSS() constructor/init
    FreshRSS_Auth::giveAccess();
    $app->init();
    $app->run();
    if (!invalidateHttpCache()) {
        Minz_Log::notice('FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt'), $log_file);
        if (defined('STDERR')) {
            fwrite(STDERR, 'Write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n");
        }
    }
}
Minz_Log::notice('FreshRSS actualize done.', $log_file);
if (defined('STDOUT')) {
    fwrite(STDOUT, 'Done.' . "\n");
    $end_date = date_create('now');
    $duration = date_diff($end_date, $begin_date);
    fwrite(STDOUT, 'Ending feed actualization at ' . $end_date->format('c') . "\n");
    //Unbuffered
    fwrite(STDOUT, 'Feed actualizations took ' . $duration->format('%a day(s), %h hour(s),  %i minute(s) and %s seconds') . ' for ' . count($users) . " users\n");
    //Unbuffered
}
echo 'End.', "\n";
ob_end_flush();
예제 #7
0
 public function load($loadDetails = false)
 {
     if ($this->url !== null) {
         if (CACHE_PATH === false) {
             throw new Minz_FileNotExistException('CACHE_PATH', Minz_Exception::ERROR);
         } else {
             $url = htmlspecialchars_decode($this->url, ENT_QUOTES);
             if ($this->httpAuth != '') {
                 $url = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url);
             }
             $feed = customSimplePie();
             if (substr($url, -11) === '#force_feed') {
                 $feed->force_feed(true);
                 $url = substr($url, 0, -11);
             }
             $feed->set_feed_url($url);
             if (!$loadDetails) {
                 //Only activates auto-discovery when adding a new feed
                 $feed->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE);
             }
             $mtime = $feed->init();
             if (!$mtime || $feed->error()) {
                 $errorMessage = $feed->error();
                 throw new FreshRSS_Feed_Exception(($errorMessage == '' ? 'Feed error' : $errorMessage) . ' [' . $url . ']');
             }
             if ($loadDetails) {
                 // si on a utilisé l'auto-discover, notre url va avoir changé
                 $subscribe_url = $feed->subscribe_url(false);
                 $title = strtr(html_only_entity_decode($feed->get_title()), array('<' => '&lt;', '>' => '&gt;', '"' => '&quot;'));
                 //HTML to HTML-PRE	//ENT_COMPAT except &
                 $this->_name($title == '' ? $url : $title);
                 $this->_website(html_only_entity_decode($feed->get_link()));
                 $this->_description(html_only_entity_decode($feed->get_description()));
             } else {
                 //The case of HTTP 301 Moved Permanently
                 $subscribe_url = $feed->subscribe_url(true);
             }
             $clean_url = url_remove_credentials($subscribe_url);
             if ($subscribe_url !== null && $subscribe_url !== $url) {
                 $this->_url($clean_url);
             }
             if ($mtime === true || $mtime > $this->lastUpdate) {
                 Minz_Log::notice('FreshRSS no cache ' . $mtime . ' > ' . $this->lastUpdate . ' for ' . $clean_url);
                 $this->loadEntries($feed);
                 // et on charge les articles du flux
             } else {
                 Minz_Log::notice('FreshRSS use cache for ' . $clean_url);
                 $this->entries = array();
             }
             $feed->__destruct();
             //http://simplepie.org/wiki/faq/i_m_getting_memory_leaks
             unset($feed);
         }
     }
 }
예제 #8
0
function get_content_by_parsing($url, $path)
{
    require_once LIB_PATH . '/lib_phpQuery.php';
    Minz_Log::notice('FreshRSS GET ' . SimplePie_Misc::url_remove_credentials($url));
    $html = file_get_contents($url);
    if ($html) {
        $doc = phpQuery::newDocument($html);
        $content = $doc->find($path);
        foreach (pq('img[data-src]') as $img) {
            $imgP = pq($img);
            $dataSrc = $imgP->attr('data-src');
            if (strlen($dataSrc) > 4) {
                $imgP->attr('src', $dataSrc);
                $imgP->removeAttr('data-src');
            }
        }
        return sanitizeHTML($content->__toString(), $url);
    } else {
        throw new Exception();
    }
}
예제 #9
0
 /**
  * Affiche la Vue en elle-même
  */
 public function render()
 {
     if (!$this->includeFile($this->view_filename)) {
         Minz_Log::notice('File not found: `' . $this->view_filename . '`');
     }
 }
예제 #10
0
 /**
  * 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());
     }
 }