X-Git-Url: https://git.wh0rd.org/?a=blobdiff_plain;f=include%2Ffunctions.php;h=f5837e51c000285a44072ddf750751391de0c193;hb=77aebd7e4a28ba10b07d8ec697cc84cc4a810777;hp=b5e2eb77665a41d4e23cb5e45c40630973eb0972;hpb=2008ec4ed77b081388a9807c644c8d08209a2af0;p=tt-rss.git diff --git a/include/functions.php b/include/functions.php index b5e2eb77..f5837e51 100755 --- a/include/functions.php +++ b/include/functions.php @@ -56,6 +56,10 @@ // 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); @@ -317,6 +321,7 @@ } } + // 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*/) { @@ -370,6 +375,8 @@ $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); @@ -383,10 +390,16 @@ $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); @@ -401,6 +414,20 @@ 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"); } @@ -499,9 +526,11 @@ '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; @@ -617,7 +646,7 @@ $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]); @@ -683,7 +712,14 @@ } if ($user_id && !$check_only) { - @session_start(); + + if (session_status() != PHP_SESSION_NONE) { + session_destroy(); + session_commit(); + } + + session_regenerate_id(true); + session_start(); $_SESSION["uid"] = $user_id; $_SESSION["version"] = VERSION_STATIC; @@ -782,10 +818,11 @@ } function logout_user() { - session_destroy(); + @session_destroy(); if (isset($_COOKIE[session_name()])) { setcookie(session_name(), '', time()-42000, '/'); } + session_commit(); } function validate_csrf($csrf_token) { @@ -827,8 +864,7 @@ } if (!$_SESSION["uid"]) { - @session_destroy(); - setcookie(session_name(), '', time()-42000, '/'); + logout_user(); render_login_form(); exit; @@ -848,14 +884,14 @@ /* 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"); @@ -1389,7 +1425,7 @@ $search_query_leftover = array(); $pdo = Db::pdo(); - + if ($search_language) $search_language = $pdo->quote(mb_strtolower($search_language)); else @@ -1535,38 +1571,31 @@ 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 = ' '; - $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)) { @@ -1584,14 +1613,58 @@ $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 = ' + + '; + + $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); } @@ -1616,22 +1689,32 @@ } } } + } + + 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); } } @@ -1979,7 +2062,7 @@ } $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']]); @@ -2357,7 +2440,6 @@ } function get_minified_js($files) { - require_once 'lib/jshrink/Minifier.php'; $rv = ''; @@ -2536,6 +2618,9 @@ 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); @@ -2546,6 +2631,13 @@ } $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";