]> git.wh0rd.org - tt-rss.git/blobdiff - include/functions.php
force regenerate session id on successful login, remove previous blank SID check
[tt-rss.git] / include / functions.php
index b5e2eb77665a41d4e23cb5e45c40630973eb0972..3cd21969d27f90f3b86d509832b1c8bfb7d2f6fc 100755 (executable)
        // default sleep interval between feed updates (sec)
        define_default('MIN_CACHE_FILE_SIZE', 1024);
        // do not cache files smaller than that (bytes)
+       define_default('MAX_CACHE_FILE_SIZE', 64*1024*1024);
+       // do not cache files larger than that (bytes)
+       define_default('MAX_DOWNLOAD_FILE_SIZE', 16*1024*1024);
+       // do not download general files larger than that (bytes)
        define_default('CACHE_MAX_DAYS', 7);
        // max age in days for various automatically cached (temporary) files
        define_default('MAX_CONDITIONAL_INTERVAL', 3600*12);
                }
        }
 
+       // TODO: max_size currently only works for CURL transfers
        // TODO: multiple-argument way is deprecated, first parameter is a hash now
        function fetch_file_contents($options /* previously: 0: $url , 1: $type = false, 2: $login = false, 3: $pass = false,
                                4: $post_query = false, 5: $timeout = false, 6: $timestamp = 0, 7: $useragent = false*/) {
                $last_modified = isset($options["last_modified"]) ? $options["last_modified"] : "";
                $useragent = isset($options["useragent"]) ? $options["useragent"] : false;
                $followlocation = isset($options["followlocation"]) ? $options["followlocation"] : true;
+               $max_size = isset($options["max_size"]) ? $options["max_size"] : MAX_DOWNLOAD_FILE_SIZE; // in bytes
+               $http_accept = isset($options["http_accept"]) ? $options["http_accept"] : false;
 
                $url = ltrim($url, ' ');
                $url = str_replace(' ', '%20', $url);
 
                        $ch = curl_init($url);
 
-                       if ($last_modified && !$post_query) {
-                               curl_setopt($ch, CURLOPT_HTTPHEADER,
-                                       array("If-Modified-Since: $last_modified"));
-                       }
+                       $curl_http_headers = [];
+
+                       if ($last_modified && !$post_query)
+                               array_push($curl_http_headers, "If-Modified-Since: $last_modified");
+
+                       if ($http_accept)
+                               array_push($curl_http_headers, "Accept: " . $http_accept);
+
+                       if (count($curl_http_headers) > 0)
+                               curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_http_headers);
 
                        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : FILE_FETCH_CONNECT_TIMEOUT);
                        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : FILE_FETCH_TIMEOUT);
                        curl_setopt($ch, CURLOPT_ENCODING, "");
                        //curl_setopt($ch, CURLOPT_REFERER, $url);
 
+                       if ($max_size) {
+                               curl_setopt($ch, CURLOPT_NOPROGRESS, false);
+                               curl_setopt($ch, CURLOPT_BUFFERSIZE, 16384); // needed to get 5 arguments in progress function?
+
+                               // holy shit closures in php
+                               // download & upload are *expected* sizes respectively, could be zero
+                               curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function($curl_handle, $download_size, $downloaded, $upload_size, $uploaded) use( &$max_size) {
+                                       //_debug("[curl progressfunction] $downloaded $max_size");
+
+                                       return ($downloaded > $max_size) ? 1 : 0; // if max size is set, abort when exceeding it
+                               });
+
+                       }
+
                        if (!ini_get("open_basedir")) {
                                curl_setopt($ch, CURLOPT_COOKIEJAR, "/dev/null");
                        }
                                                'protocol_version'=> 1.1)
                                  );
 
-                       if (!$post_query && $last_modified) {
+                       if (!$post_query && $last_modified)
                                array_push($context_options['http']['header'], "If-Modified-Since: $last_modified");
-                       }
+
+                       if ($http_accept)
+                               array_push($context_options['http']['header'], "Accept: $http_accept");
 
                        if (defined('_HTTP_PROXY')) {
                                $context_options['http']['request_fulluri'] = true;
                $profile = $profile ? $profile : null;
 
                $u_sth = $pdo->prepare("SELECT pref_name
-                       FROM ttrss_user_prefs WHERE owner_uid = :uid AND 
+                       FROM ttrss_user_prefs WHERE owner_uid = :uid AND
                                (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
                $u_sth->execute([':uid' => $uid, ':profile' => $profile]);
 
                        }
 
                        if ($user_id && !$check_only) {
-                               @session_start();
+
+                               if (session_status() != PHP_SESSION_NONE) {
+                                       session_destroy();
+                                       session_commit();
+                               }
+
+                               session_start();
+                               session_regenerate_id(true);
 
                                $_SESSION["uid"] = $user_id;
                                $_SESSION["version"] = VERSION_STATIC;
        }
 
        function logout_user() {
-               session_destroy();
+               @session_destroy();
                if (isset($_COOKIE[session_name()])) {
                   setcookie(session_name(), '', time()-42000, '/');
                }
+               session_commit();
        }
 
        function validate_csrf($csrf_token) {
                                }
 
                                if (!$_SESSION["uid"]) {
-                                       @session_destroy();
-                                       setcookie(session_name(), '', time()-42000, '/');
+                                       logout_user();
 
                                        render_login_form();
                                        exit;
 
                                /* cleanup ccache */
 
-                               $sth = $pdo->prepare("DELETE FROM ttrss_counters_cache WHERE owner_uid = ? 
+                               $sth = $pdo->prepare("DELETE FROM ttrss_counters_cache WHERE owner_uid = ?
                                        AND
                                                (SELECT COUNT(id) FROM ttrss_feeds WHERE
                                                        ttrss_feeds.id = feed_id) = 0");
 
                                $sth->execute([$_SESSION['uid']]);
 
-                               $sth = $pdo->prepare("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ? 
+                               $sth = $pdo->prepare("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ?
                                        AND
                                                (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
                                                        ttrss_feed_categories.id = feed_id) = 0");
                $search_query_leftover = array();
 
                $pdo = Db::pdo();
-               
+
                if ($search_language)
                        $search_language = $pdo->quote(mb_strtolower($search_language));
                else
                return false;
        }
 
-       function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
-               if (!$owner) $owner = $_SESSION["uid"];
-
-               $res = trim($str); if (!$res) return '';
+       // check for locally cached (media) URLs and rewrite to local versions
+       // this is called separately after sanitize() and plugin render article hooks to allow
+       // plugins work on original source URLs used before caching
 
+       function rewrite_cached_urls($str) {
                $charset_hack = '<head>
                                <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
                        </head>';
 
-               $res = trim($res); if (!$res) return '';
-
-               libxml_use_internal_errors(true);
+               $res = trim($str); if (!$res) return '';
 
                $doc = new DOMDocument();
                $doc->loadHTML($charset_hack . $res);
                $xpath = new DOMXPath($doc);
 
-               $rewrite_base_url = $site_url ? $site_url : get_self_url_prefix();
+               $entries = $xpath->query('(//img[@src]|//video[@poster]|//video/source[@src]|//audio/source[@src])');
 
-               $entries = $xpath->query('(//a[@href]|//img[@src]|//video/source[@src]|//audio/source[@src])');
+               $need_saving = false;
 
                foreach ($entries as $entry) {
 
-                       if ($entry->hasAttribute('href')) {
-                               $entry->setAttribute('href',
-                                       rewrite_relative_url($rewrite_base_url, $entry->getAttribute('href')));
+                       if ($entry->hasAttribute('src') || $entry->hasAttribute('poster')) {
 
-                               $entry->setAttribute('rel', 'noopener noreferrer');
-                       }
-
-                       if ($entry->hasAttribute('src')) {
-                               $src = rewrite_relative_url($rewrite_base_url, $entry->getAttribute('src'));
+                               // should be already absolutized because this is called after sanitize()
+                               $src = $entry->hasAttribute('poster') ? $entry->getAttribute('poster') : $entry->getAttribute('src');
                                $cached_filename = CACHE_DIR . '/images/' . sha1($src);
 
                                if (file_exists($cached_filename)) {
 
                                        $src = get_self_url_prefix() . '/public.php?op=cached_url&hash=' . sha1($src) . $suffix;
 
-                                       if ($entry->hasAttribute('srcset')) {
-                                               $entry->removeAttribute('srcset');
-                                       }
+                                       if ($entry->hasAttribute('poster'))
+                                               $entry->setAttribute('poster', $src);
+                                       else
+                                               $entry->setAttribute('src', $src);
 
-                                       if ($entry->hasAttribute('sizes')) {
-                                               $entry->removeAttribute('sizes');
-                                       }
+                                       $need_saving = true;
                                }
+                       }
+               }
+
+               if ($need_saving) {
+                       $doc->removeChild($doc->firstChild); //remove doctype
+                       $res = $doc->saveHTML();
+               }
+
+               return $res;
+       }
+
+       function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
+               if (!$owner) $owner = $_SESSION["uid"];
+
+               $res = trim($str); if (!$res) return '';
+
+               $charset_hack = '<head>
+                               <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+                       </head>';
+
+               $res = trim($res); if (!$res) return '';
+
+               libxml_use_internal_errors(true);
+
+               $doc = new DOMDocument();
+               $doc->loadHTML($charset_hack . $res);
+               $xpath = new DOMXPath($doc);
+
+               $rewrite_base_url = $site_url ? $site_url : get_self_url_prefix();
+
+               $entries = $xpath->query('(//a[@href]|//img[@src]|//video/source[@src]|//audio/source[@src])');
+
+               foreach ($entries as $entry) {
+
+                       if ($entry->hasAttribute('href')) {
+                               $entry->setAttribute('href',
+                                       rewrite_relative_url($rewrite_base_url, $entry->getAttribute('href')));
+
+                               $entry->setAttribute('rel', 'noopener noreferrer');
+                       }
+
+                       if ($entry->hasAttribute('src')) {
+                               $src = rewrite_relative_url($rewrite_base_url, $entry->getAttribute('src'));
+
+                               // cache stuff has gone to rewrite_cached_urls()
 
                                $entry->setAttribute('src', $src);
                        }
                                                }
                                        }
                                }
+                       }
+
+                       if ($entry->hasAttribute('src') &&
+                                       ($owner && get_pref("STRIP_IMAGES", $owner)) || $force_remove_images || $_SESSION["bw_limit"]) {
+
+                               $p = $doc->createElement('p');
+
+                               $a = $doc->createElement('a');
+                               $a->setAttribute('href', $entry->getAttribute('src'));
+
+                               $a->appendChild(new DOMText($entry->getAttribute('src')));
+                               $a->setAttribute('target', '_blank');
+                               $a->setAttribute('rel', 'noopener noreferrer');
 
-                               if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
-                                       $force_remove_images || $_SESSION["bw_limit"]) {
+                               $p->appendChild($a);
 
-                                       $p = $doc->createElement('p');
+                               if ($entry->nodeName == 'source') {
 
-                                       $a = $doc->createElement('a');
-                                       $a->setAttribute('href', $entry->getAttribute('src'));
+                                       if ($entry->parentNode && $entry->parentNode->parentNode)
+                                               $entry->parentNode->parentNode->replaceChild($p, $entry->parentNode);
 
-                                       $a->appendChild(new DOMText($entry->getAttribute('src')));
-                                       $a->setAttribute('target', '_blank');
-                                       $a->setAttribute('rel', 'noopener noreferrer');
+                               } else if ($entry->nodeName == 'img') {
 
-                                       $p->appendChild($a);
+                                       if ($entry->parentNode)
+                                               $entry->parentNode->replaceChild($p, $entry);
 
-                                       $entry->parentNode->replaceChild($p, $entry);
                                }
                        }
 
                }
 
                $sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories
-                               WHERE (parent_cat = :parent OR (:parent IS NULL AND parent_cat IS NULL)) 
+                               WHERE (parent_cat = :parent OR (:parent IS NULL AND parent_cat IS NULL))
                                AND title = :title AND owner_uid = :uid");
                $sth->execute([':parent' => $parent_cat_id, ':title' => $feed_cat, ':uid' => $_SESSION['uid']]);
 
        }
 
        function get_minified_js($files) {
-               require_once 'lib/jshrink/Minifier.php';
 
                $rv = '';
 
                should be loaded systemwide in config.php */
        function send_local_file($filename) {
                if (file_exists($filename)) {
+
+                       if (is_writable($filename)) touch($filename);
+
                        $tmppluginhost = new PluginHost();
 
                        $tmppluginhost->load(PLUGINS, PluginHost::KIND_SYSTEM);
                        }
 
                        $mimetype = mime_content_type($filename);
+
+                       // this is hardly ideal but 1) only media is cached in images/ and 2) seemingly only mp4
+                       // video files are detected as octet-stream by mime_content_type()
+
+                       if ($mimetype == "application/octet-stream")
+                               $mimetype = "video/mp4";
+
                        header("Content-type: $mimetype");
 
                        $stamp = gmdate("D, d M Y H:i:s", filemtime($filename)) . " GMT";