/**
  * 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);
 }
Beispiel #2
0
 public function deleteCategory($id)
 {
     $sql = 'DELETE FROM `' . $this->prefix . 'category` WHERE id=?';
     $stm = $this->bd->prepare($sql);
     $values = array($id);
     if ($stm && $stm->execute($values)) {
         return $stm->rowCount();
     } else {
         $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
         Minz_Log::error('SQL error deleteCategory: ' . $info[2]);
         return false;
     }
 }
Beispiel #3
0
 public function updateCachedValues()
 {
     //For one single feed, call updateLastUpdate($id)
     $sql = 'UPDATE `' . $this->prefix . 'feed` ' . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0)';
     $stm = $this->bd->prepare($sql);
     if ($stm && $stm->execute()) {
         return $stm->rowCount();
     } else {
         $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
         Minz_Log::error('SQL error updateCachedValues: ' . $info[2]);
         return false;
     }
 }
 /**
  * Démarre l'application (lance le dispatcher et renvoie la réponse)
  */
 public function run()
 {
     try {
         $this->dispatcher->run();
     } catch (Minz_Exception $e) {
         try {
             Minz_Log::error($e->getMessage());
         } catch (Minz_PermissionDeniedException $e) {
             $this->killApp($e->getMessage());
         }
         if ($e instanceof Minz_FileNotExistException || $e instanceof Minz_ControllerNotExistException || $e instanceof Minz_ControllerNotActionControllerException || $e instanceof Minz_ActionException) {
             Minz_Error::error(404, array('error' => array($e->getMessage())), true);
         } else {
             $this->killApp();
         }
     }
 }
Beispiel #5
0
 public function deleteUser($username)
 {
     $db = FreshRSS_Context::$system_conf->db;
     require_once APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php';
     if ($db['type'] === 'sqlite') {
         return unlink(join_path(DATA_PATH, 'users', $username, 'db.sqlite'));
     } else {
         $userPDO = new Minz_ModelPdo($username);
         $sql = sprintf(SQL_DROP_TABLES, $db['prefix'] . $username . '_');
         $stm = $userPDO->bd->prepare($sql);
         if ($stm && $stm->execute()) {
             return true;
         } else {
             $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
             Minz_Log::error('SQL error : ' . $info[2]);
             return false;
         }
     }
 }
 public function checkAction()
 {
     $this->view->change_view('update', 'index');
     if (file_exists(UPDATE_FILENAME)) {
         // There is already an update file to apply: we don't need to check
         // the webserver!
         // Or if already check during the last hour, do nothing.
         Minz_Request::forward(array('c' => 'update'), true);
         return;
     }
     $c = curl_init(FRESHRSS_UPDATE_WEBSITE);
     curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
     curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
     curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
     $result = curl_exec($c);
     $c_status = curl_getinfo($c, CURLINFO_HTTP_CODE);
     $c_error = curl_error($c);
     curl_close($c);
     if ($c_status !== 200) {
         Minz_Log::error('Error during update (HTTP code ' . $c_status . '): ' . $c_error);
         $this->view->message = array('status' => 'bad', 'title' => _t('gen.short.damn'), 'body' => _t('feedback.update.server_not_found', FRESHRSS_UPDATE_WEBSITE));
         return;
     }
     $res_array = explode("\n", $result, 2);
     $status = $res_array[0];
     if (strpos($status, 'UPDATE') !== 0) {
         $this->view->message = array('status' => 'bad', 'title' => _t('gen.short.damn'), 'body' => _t('feedback.update.none'));
         @touch(join_path(DATA_PATH, 'last_update.txt'));
         return;
     }
     $script = $res_array[1];
     if (file_put_contents(UPDATE_FILENAME, $script) !== false) {
         $version = explode(' ', $status, 2);
         $version = $version[1];
         @file_put_contents(join_path(DATA_PATH, 'last_update.txt'), $version);
         Minz_Request::forward(array('c' => 'update'), true);
     } else {
         $this->view->message = array('status' => 'bad', 'title' => _t('gen.short.damn'), 'body' => _t('feedback.update.error', 'Cannot save the update script'));
     }
 }
Beispiel #7
0
 /**
  * Mark all the articles in a category as read.
  * There is a fail safe to prevent to mark as read articles that are
  * loaded during the mark as read action. Then the cache is updated.
  *
  * If $idMax equals 0, a deprecated debug message is logged
  *
  * @param integer $id category ID
  * @param integer $idMax fail safe article ID
  * @return integer affected rows
  */
 public function markReadCat($id, $idMax = 0)
 {
     if ($idMax == 0) {
         $idMax = time() . '000000';
         Minz_Log::debug('Calling markReadCat(0) is deprecated!');
     }
     $sql = 'UPDATE `' . $this->prefix . 'entry` ' . 'SET is_read=1 ' . 'WHERE is_read=0 AND id <= ? AND ' . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)';
     $values = array($idMax, $id);
     $stm = $this->bd->prepare($sql);
     if (!($stm && $stm->execute($values))) {
         $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
         Minz_Log::error('SQL error markReadCat: ' . $info[2]);
         return false;
     }
     $affected = $stm->rowCount();
     if ($affected > 0 && !$this->updateCacheUnreads($id, false)) {
         return false;
     }
     return $affected;
 }
Beispiel #8
0
#
# ***** 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) {
        echo '### Fatal error! ###<br />', "\n";
        Minz_Log::error($e->getMessage());
        echo 'See logs files.';
    }
}
Beispiel #9
0
 /**
  * This action subscribes to a feed.
  *
  * It can be reached by both GET and POST requests.
  *
  * GET request displays a form to add and configure a feed.
  * Request parameter is:
  *   - url_rss (default: false)
  *
  * POST request adds a feed in database.
  * Parameters are:
  *   - url_rss (default: false)
  *   - category (default: false)
  *   - new_category (required if category == 'nc')
  *   - http_user (default: false)
  *   - http_pass (default: false)
  * It tries to get website information from RSS feed.
  * If no category is given, feed is added to the default one.
  *
  * If url_rss is false, nothing happened.
  */
 public function addAction()
 {
     $url = Minz_Request::param('url_rss');
     if ($url === false) {
         // No url, do nothing
         Minz_Request::forward(array('c' => 'subscription', 'a' => 'index'), true);
     }
     $feedDAO = FreshRSS_Factory::createFeedDao();
     $this->catDAO = new FreshRSS_CategoryDAO();
     $url_redirect = array('c' => 'subscription', 'a' => 'index', 'params' => array());
     $limits = FreshRSS_Context::$system_conf->limits;
     $this->view->feeds = $feedDAO->listFeeds();
     if (count($this->view->feeds) >= $limits['max_feeds']) {
         Minz_Request::bad(_t('feedback.sub.feed.over_max', $limits['max_feeds']), $url_redirect);
     }
     if (Minz_Request::isPost()) {
         @set_time_limit(300);
         $cat = Minz_Request::param('category');
         if ($cat === 'nc') {
             // User want to create a new category, new_category parameter
             // must exist
             $new_cat = Minz_Request::param('new_category');
             if (empty($new_cat['name'])) {
                 $cat = false;
             } else {
                 $cat = $this->catDAO->addCategory($new_cat);
             }
         }
         if ($cat === false) {
             // If category was not given or if creating new category failed,
             // get the default category
             $this->catDAO->checkDefault();
             $def_cat = $this->catDAO->getDefault();
             $cat = $def_cat->id();
         }
         // HTTP information are useful if feed is protected behind a
         // HTTP authentication
         $user = trim(Minz_Request::param('http_user', ''));
         $pass = Minz_Request::param('http_pass', '');
         $http_auth = '';
         if ($user != '' && $pass != '') {
             //TODO: Sanitize
             $http_auth = $user . ':' . $pass;
         }
         $transaction_started = false;
         try {
             $feed = new FreshRSS_Feed($url);
         } catch (FreshRSS_BadUrl_Exception $e) {
             // Given url was not a valid url!
             Minz_Log::warning($e->getMessage());
             Minz_Request::bad(_t('feedback.sub.feed.invalid_url', $url), $url_redirect);
         }
         try {
             $feed->load(true);
         } catch (FreshRSS_Feed_Exception $e) {
             // Something went bad (timeout, server not found, etc.)
             Minz_Log::warning($e->getMessage());
             Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
         } catch (Minz_FileNotExistException $e) {
             // Cache directory doesn't exist!
             Minz_Log::error($e->getMessage());
             Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect);
         }
         if ($feedDAO->searchByUrl($feed->url())) {
             Minz_Request::bad(_t('feedback.sub.feed.already_subscribed', $feed->name()), $url_redirect);
         }
         $feed->_category($cat);
         $feed->_httpAuth($http_auth);
         // Call the extension hook
         $name = $feed->name();
         $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed);
         if ($feed === null) {
             Minz_Request::bad(_t('feedback.sub.feed.not_added', $name), $url_redirect);
         }
         $values = array('url' => $feed->url(), 'category' => $feed->category(), 'name' => $feed->name(), 'website' => $feed->website(), 'description' => $feed->description(), 'lastUpdate' => time(), 'httpAuth' => $feed->httpAuth());
         $id = $feedDAO->addFeed($values);
         if (!$id) {
             // There was an error in database... we cannot say what here.
             Minz_Request::bad(_t('feedback.sub.feed.not_added', $feed->name()), $url_redirect);
         }
         // Ok, feed has been added in database. Now we have to refresh entries.
         $feed->_id($id);
         $feed->faviconPrepare();
         //$feed->pubSubHubbubPrepare();	//TODO: prepare PubSubHubbub already when adding the feed
         $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0;
         $entryDAO = FreshRSS_Factory::createEntryDao();
         // We want chronological order and SimplePie uses reverse order.
         $entries = array_reverse($feed->entries());
         // Calculate date of oldest entries we accept in DB.
         $nb_month_old = FreshRSS_Context::$user_conf->old_entries;
         $date_min = time() - 3600 * 24 * 30 * $nb_month_old;
         // Use a shared statement and a transaction to improve a LOT the
         // performances.
         $feedDAO->beginTransaction();
         foreach ($entries as $entry) {
             // Entries are added without any verification.
             $entry->_feed($feed->id());
             $entry->_id(min(time(), $entry->date(true)) . uSecString());
             $entry->_isRead($is_read);
             $entry = Minz_ExtensionManager::callHook('entry_before_insert', $entry);
             if ($entry === null) {
                 // An extension has returned a null value, there is nothing to insert.
                 continue;
             }
             $values = $entry->toArray();
             $entryDAO->addEntry($values);
         }
         $feedDAO->updateLastUpdate($feed->id());
         $feedDAO->commit();
         // Entries are in DB, we redirect to feed configuration page.
         $url_redirect['params']['id'] = $feed->id();
         Minz_Request::good(_t('feedback.sub.feed.added', $feed->name()), $url_redirect);
     } else {
         // GET request: we must ask confirmation to user before adding feed.
         Minz_View::prependTitle(_t('sub.feed.title_add') . ' · ');
         $this->view->categories = $this->catDAO->listCategories(false);
         $this->view->feed = new FreshRSS_Feed($url);
         try {
             // We try to get more information about the feed.
             $this->view->feed->load(true);
             $this->view->load_ok = true;
         } catch (Exception $e) {
             $this->view->load_ok = false;
         }
         $feed = $feedDAO->searchByUrl($this->view->feed->url());
         if ($feed) {
             // Already subscribe so we redirect to the feed configuration page.
             $url_redirect['params']['id'] = $feed->id();
             Minz_Request::good(_t('feedback.sub.feed.already_subscribed', $feed->name()), $url_redirect);
         }
     }
 }
Beispiel #10
0
 public function cleanOldEntries($id, $date_min, $keep = 15)
 {
     //Remember to call updateLastUpdate($id) or updateCachedValues() just after
     $sql = 'DELETE FROM ' . $this->prefix . 'entry ' . 'WHERE id_feed=:id_feed AND id<=:id_max ' . 'AND is_favorite=false ' . 'AND lastSeen < (SELECT maxLastSeen FROM (SELECT (MAX(e3.lastSeen)-99) AS maxLastSeen FROM ' . $this->prefix . 'entry e3 WHERE e3.id_feed=:id_feed) recent) ' . 'AND id NOT IN (SELECT id FROM (SELECT e2.id FROM ' . $this->prefix . 'entry e2 WHERE e2.id_feed=:id_feed ORDER BY id DESC LIMIT :keep) keep)';
     //Double select: MySQL doesn't support 'LIMIT & IN/ALL/ANY/SOME subquery'
     $stm = $this->bd->prepare($sql);
     if ($stm) {
         $id_max = intval($date_min) . '000000';
         $stm->bindParam(':id_feed', $id, PDO::PARAM_INT);
         $stm->bindParam(':id_max', $id_max, PDO::PARAM_STR);
         $stm->bindParam(':keep', $keep, PDO::PARAM_INT);
     }
     if ($stm && $stm->execute()) {
         return $stm->rowCount();
     } else {
         $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
         Minz_Log::error('SQL error cleanOldEntries: ' . $info[2]);
         return false;
     }
 }
Beispiel #11
0
 /**
  * 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::error($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;
     }
 }
Beispiel #12
0
 public function updateLastSeen($id_feed, $guids)
 {
     if (count($guids) < 1) {
         return 0;
     }
     $sql = 'UPDATE `' . $this->prefix . 'entry` SET lastSeen=? WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1) . '?)';
     $stm = $this->bd->prepare($sql);
     $values = array(time(), $id_feed);
     $values = array_merge($values, $guids);
     if ($stm && $stm->execute($values)) {
         return $stm->rowCount();
     } else {
         $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo();
         if ($this->autoAddColumn($info)) {
             return $this->updateLastSeen($id_feed, $guids);
         }
         Minz_Log::error('SQL error updateLastSeen: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] . ' while updating feed ' . $id_feed);
         return false;
     }
 }
Beispiel #13
0
 /**
  * Mark all the articles in a feed as read.
  * There is a fail safe to prevent to mark as read articles that are
  * loaded during the mark as read action. Then the cache is updated.
  *
  * If $idMax equals 0, a deprecated debug message is logged
  *
  * @param integer $id feed ID
  * @param integer $idMax fail safe article ID
  * @return integer affected rows
  */
 public function markReadFeed($id, $idMax = 0)
 {
     if ($idMax == 0) {
         $idMax = time() . '000000';
         Minz_Log::debug('Calling markReadFeed(0) is deprecated!');
     }
     $this->bd->beginTransaction();
     $sql = 'UPDATE `' . $this->prefix . 'entry` ' . 'SET is_read=1 ' . 'WHERE id_feed=? AND is_read=0 AND id <= ?';
     $values = array($id, $idMax);
     $stm = $this->bd->prepare($sql);
     if (!($stm && $stm->execute($values))) {
         $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
         Minz_Log::error('SQL error markReadFeed: ' . $info[2]);
         $this->bd->rollBack();
         return false;
     }
     $affected = $stm->rowCount();
     if ($affected > 0) {
         $sql = 'UPDATE `' . $this->prefix . 'feed` ' . 'SET cache_nbUnreads=cache_nbUnreads-' . $affected . ' WHERE id=?';
         $values = array($id);
         $stm = $this->bd->prepare($sql);
         if (!($stm && $stm->execute($values))) {
             $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
             Minz_Log::error('SQL error markReadFeed: ' . $info[2]);
             $this->bd->rollBack();
             return false;
         }
     }
     $this->bd->commit();
     return $affected;
 }