2006-08-19 09:04:45 +02:00
|
|
|
<?php
|
2012-08-23 21:28:32 +02:00
|
|
|
define('EXPECTED_CONFIG_VERSION', 26);
|
2020-09-28 13:14:06 +02:00
|
|
|
define('SCHEMA_VERSION', 140);
|
2012-07-10 13:24:04 +02:00
|
|
|
|
2013-03-27 06:40:07 +01:00
|
|
|
define('LABEL_BASE_INDEX', -1024);
|
2013-03-27 13:14:27 +01:00
|
|
|
define('PLUGIN_FEED_BASE_INDEX', -128);
|
2013-03-27 06:40:07 +01:00
|
|
|
|
2013-04-29 11:03:28 +02:00
|
|
|
define('COOKIE_LIFETIME_LONG', 86400*365);
|
|
|
|
|
2020-11-30 13:53:32 +01:00
|
|
|
// this CSS file is included for everyone (if it exists in themes.local)
|
|
|
|
// on login, registration, and main (index and prefs) pages
|
|
|
|
define('LOCAL_OVERRIDE_STYLESHEET', '.local-overrides.css');
|
|
|
|
|
2012-08-21 12:09:51 +02:00
|
|
|
$fetch_last_error = false;
|
2013-03-30 12:10:53 +01:00
|
|
|
$fetch_last_error_code = false;
|
2013-04-10 00:59:48 +02:00
|
|
|
$fetch_last_content_type = false;
|
2014-08-12 17:36:45 +02:00
|
|
|
$fetch_last_error_content = false; // curl only for the time being
|
2018-02-11 08:56:28 +01:00
|
|
|
$fetch_effective_url = false;
|
2013-04-18 09:35:58 +02:00
|
|
|
$fetch_curl_used = false;
|
2012-08-21 12:09:51 +02:00
|
|
|
|
2020-12-12 16:53:08 +01:00
|
|
|
if (version_compare(PHP_VERSION, '8.0.0', '<')) {
|
|
|
|
libxml_disable_entity_loader(true);
|
|
|
|
}
|
|
|
|
|
2019-06-20 07:40:02 +02:00
|
|
|
libxml_use_internal_errors(true);
|
2014-12-08 12:49:54 +01:00
|
|
|
|
2016-07-07 09:02:55 +02:00
|
|
|
// separate test because this is included before sanity checks
|
|
|
|
if (function_exists("mb_internal_encoding")) mb_internal_encoding("UTF-8");
|
|
|
|
|
2010-11-07 16:14:48 +01:00
|
|
|
date_default_timezone_set('UTC');
|
2010-11-10 21:59:23 +01:00
|
|
|
if (defined('E_DEPRECATED')) {
|
|
|
|
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
|
|
|
|
} else {
|
|
|
|
error_reporting(E_ALL & ~E_NOTICE);
|
|
|
|
}
|
2005-11-23 19:08:01 +01:00
|
|
|
|
2019-12-19 06:37:19 +01:00
|
|
|
ini_set('display_errors', 0);
|
|
|
|
ini_set('display_startup_errors', 0);
|
|
|
|
|
2005-08-21 17:35:22 +02:00
|
|
|
require_once 'config.php';
|
2007-03-05 10:24:13 +01:00
|
|
|
|
2013-04-01 17:32:05 +02:00
|
|
|
/**
|
|
|
|
* Define a constant if not already defined
|
|
|
|
*/
|
|
|
|
function define_default($name, $value) {
|
|
|
|
defined($name) or define($name, $value);
|
|
|
|
}
|
|
|
|
|
2017-05-29 22:14:42 +02:00
|
|
|
/* Some tunables you can override in config.php using define(): */
|
2013-04-02 20:29:10 +02:00
|
|
|
|
|
|
|
define_default('FEED_FETCH_TIMEOUT', 45);
|
|
|
|
// How may seconds to wait for response when requesting feed from a site
|
|
|
|
define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
|
|
|
|
// How may seconds to wait for response when requesting feed from a
|
|
|
|
// site when that feed wasn't cached before
|
|
|
|
define_default('FILE_FETCH_TIMEOUT', 45);
|
|
|
|
// Default timeout when fetching files from remote sites
|
|
|
|
define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
|
|
|
|
// How many seconds to wait for initial response from website when
|
|
|
|
// fetching files from remote sites
|
2017-05-06 09:54:14 +02:00
|
|
|
define_default('DAEMON_UPDATE_LOGIN_LIMIT', 30);
|
2017-05-29 22:14:42 +02:00
|
|
|
// stop updating feeds if users haven't logged in for X days
|
2017-05-06 09:54:14 +02:00
|
|
|
define_default('DAEMON_FEED_LIMIT', 500);
|
2017-05-29 22:14:42 +02:00
|
|
|
// feed limit for one update batch
|
2017-05-06 09:54:14 +02:00
|
|
|
define_default('DAEMON_SLEEP_INTERVAL', 120);
|
2017-05-29 22:14:42 +02:00
|
|
|
// default sleep interval between feed updates (sec)
|
2018-05-20 10:08:33 +02:00
|
|
|
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)
|
2017-05-29 22:14:42 +02:00
|
|
|
define_default('CACHE_MAX_DAYS', 7);
|
|
|
|
// max age in days for various automatically cached (temporary) files
|
2018-01-30 08:44:31 +01:00
|
|
|
define_default('MAX_CONDITIONAL_INTERVAL', 3600*12);
|
|
|
|
// max interval between forced unconditional updates for servers
|
|
|
|
// not complying with http if-modified-since (seconds)
|
2020-01-17 05:26:55 +01:00
|
|
|
// define_default('MAX_FETCH_REQUESTS_PER_HOST', 25);
|
2019-11-14 05:38:49 +01:00
|
|
|
// a maximum amount of allowed HTTP requests per destination host
|
|
|
|
// during a single update (i.e. within PHP process lifetime)
|
|
|
|
// this is used to not cause excessive load on the origin server on
|
|
|
|
// e.g. feed subscription when all articles are being processes
|
2020-01-17 05:26:55 +01:00
|
|
|
// (not implemented)
|
2020-09-30 16:03:16 +02:00
|
|
|
define_default('DAEMON_UNSUCCESSFUL_DAYS_LIMIT', 30);
|
|
|
|
// automatically disable updates for feeds which failed to
|
|
|
|
// update for this amount of days; 0 disables
|
2017-05-29 22:14:42 +02:00
|
|
|
|
|
|
|
/* tunables end here */
|
2017-05-06 09:54:14 +02:00
|
|
|
|
2008-04-21 06:53:19 +02:00
|
|
|
if (DB_TYPE == "pgsql") {
|
|
|
|
define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
|
|
|
|
} else {
|
|
|
|
define('SUBSTRING_FOR_DATE', 'SUBSTRING');
|
|
|
|
}
|
|
|
|
|
2007-08-11 16:25:51 +02:00
|
|
|
function get_translations() {
|
2021-02-12 19:20:04 +01:00
|
|
|
$t = array(
|
2019-01-30 15:57:28 +01:00
|
|
|
"auto" => __("Detect automatically"),
|
2014-08-14 10:09:16 +02:00
|
|
|
"ar_SA" => "العربيّة (Arabic)",
|
2015-03-11 13:36:45 +01:00
|
|
|
"bg_BG" => "Bulgarian",
|
2014-06-03 06:56:40 +02:00
|
|
|
"da_DA" => "Dansk",
|
2009-11-22 21:18:54 +01:00
|
|
|
"ca_CA" => "Català",
|
2013-03-19 14:39:08 +01:00
|
|
|
"cs_CZ" => "Česky",
|
2007-08-11 17:40:27 +02:00
|
|
|
"en_US" => "English",
|
2014-06-03 06:56:40 +02:00
|
|
|
"el_GR" => "Ελληνικά",
|
2014-06-03 06:56:49 +02:00
|
|
|
"es_ES" => "Español (España)",
|
2014-06-03 06:56:40 +02:00
|
|
|
"es_LA" => "Español",
|
2009-04-26 14:42:33 +02:00
|
|
|
"de_DE" => "Deutsch",
|
2020-10-01 09:19:04 +02:00
|
|
|
"fa" => "Persian (Farsi)",
|
2007-08-11 17:40:27 +02:00
|
|
|
"fr_FR" => "Français",
|
2008-02-26 08:57:09 +01:00
|
|
|
"hu_HU" => "Magyar (Hungarian)",
|
2008-11-02 17:42:39 +01:00
|
|
|
"it_IT" => "Italiano",
|
2008-09-25 05:56:59 +02:00
|
|
|
"ja_JP" => "日本語 (Japanese)",
|
2013-03-18 22:13:30 +01:00
|
|
|
"lv_LV" => "Latviešu",
|
2008-04-08 07:13:57 +02:00
|
|
|
"nb_NO" => "Norwegian bokmål",
|
2013-03-23 06:21:57 +01:00
|
|
|
"nl_NL" => "Dutch",
|
2012-10-06 16:27:53 +02:00
|
|
|
"pl_PL" => "Polski",
|
2013-04-01 17:10:26 +02:00
|
|
|
"ru_RU" => "Русский",
|
2007-10-26 09:19:54 +02:00
|
|
|
"pt_BR" => "Portuguese/Brazil",
|
2014-03-21 12:56:26 +01:00
|
|
|
"pt_PT" => "Portuguese/Portugal",
|
2013-04-01 13:51:04 +02:00
|
|
|
"zh_CN" => "Simplified Chinese",
|
2014-02-12 13:30:24 +01:00
|
|
|
"zh_TW" => "Traditional Chinese",
|
2019-01-30 15:14:07 +01:00
|
|
|
"uk_UA" => "Українська",
|
2013-04-01 19:35:00 +02:00
|
|
|
"sv_SE" => "Svenska",
|
2013-12-24 10:27:57 +01:00
|
|
|
"fi_FI" => "Suomi",
|
2013-12-08 19:47:29 +01:00
|
|
|
"tr_TR" => "Türkçe");
|
2007-08-11 16:25:51 +02:00
|
|
|
|
2021-02-12 19:20:04 +01:00
|
|
|
return $t;
|
2007-08-11 16:25:51 +02:00
|
|
|
}
|
|
|
|
|
2020-09-17 17:56:29 +02:00
|
|
|
require_once "lib/gettext/gettext.inc.php";
|
2007-03-06 11:33:06 +01:00
|
|
|
|
2011-03-18 17:25:06 +01:00
|
|
|
function startup_gettext() {
|
2011-03-17 17:05:28 +01:00
|
|
|
|
2021-02-12 19:20:04 +01:00
|
|
|
$selected_locale = "";
|
|
|
|
|
|
|
|
// https://www.codingwithjesse.com/blog/use-accept-language-header/
|
|
|
|
if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
|
|
|
$valid_langs = [];
|
|
|
|
$translations = array_keys(get_translations());
|
|
|
|
|
|
|
|
array_shift($translations); // remove "auto"
|
2007-08-10 18:16:43 +02:00
|
|
|
|
2021-02-12 19:20:04 +01:00
|
|
|
// full locale first
|
|
|
|
foreach ($translations as $t) {
|
|
|
|
$lang = strtolower(str_replace("_", "-", (string)$t));
|
|
|
|
$valid_langs[$lang] = $t;
|
|
|
|
|
|
|
|
$lang = substr($lang, 0, 2);
|
|
|
|
if (!isset($valid_langs[$lang]))
|
|
|
|
$valid_langs[$lang] = $t;
|
|
|
|
}
|
|
|
|
|
|
|
|
// break up string into pieces (languages and q factors)
|
|
|
|
preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i',
|
|
|
|
$_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
|
|
|
|
|
|
|
|
if (count($lang_parse[1])) {
|
|
|
|
// create a list like "en" => 0.8
|
|
|
|
$langs = array_combine($lang_parse[1], $lang_parse[4]);
|
|
|
|
|
|
|
|
if (is_array($langs)) {
|
|
|
|
// set default to 1 for any without q factor
|
|
|
|
foreach ($langs as $lang => $val) {
|
|
|
|
if ($val === '') $langs[$lang] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort list based on value
|
|
|
|
arsort($langs, SORT_NUMERIC);
|
|
|
|
|
|
|
|
foreach (array_keys($langs) as $lang) {
|
|
|
|
$lang = strtolower($lang);
|
|
|
|
|
|
|
|
foreach ($valid_langs as $vlang => $vlocale) {
|
|
|
|
if ($vlang == $lang) {
|
|
|
|
$selected_locale = $vlocale;
|
|
|
|
break 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-03-18 17:25:06 +01:00
|
|
|
}
|
2007-08-10 18:16:43 +02:00
|
|
|
|
2021-02-05 22:12:15 +01:00
|
|
|
if (!empty($_SESSION["uid"]) && get_schema_version() >= 120) {
|
2021-02-12 19:20:04 +01:00
|
|
|
$pref_locale = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
|
2013-04-27 15:12:48 +02:00
|
|
|
|
2021-02-12 19:20:04 +01:00
|
|
|
if (!empty($pref_locale) && $pref_locale != 'auto') {
|
|
|
|
$selected_locale = $pref_locale;
|
2013-04-29 13:54:23 +02:00
|
|
|
}
|
2011-03-18 17:25:06 +01:00
|
|
|
}
|
2008-12-16 08:13:09 +01:00
|
|
|
|
2021-02-12 19:20:04 +01:00
|
|
|
if ($selected_locale) {
|
2011-03-18 17:25:06 +01:00
|
|
|
if (defined('LC_MESSAGES')) {
|
2021-02-12 19:20:04 +01:00
|
|
|
_setlocale(LC_MESSAGES, $selected_locale);
|
2011-03-18 17:25:06 +01:00
|
|
|
} else if (defined('LC_ALL')) {
|
2021-02-12 19:20:04 +01:00
|
|
|
_setlocale(LC_ALL, $selected_locale);
|
2007-05-19 06:46:29 +02:00
|
|
|
}
|
2007-03-06 11:33:06 +01:00
|
|
|
|
2013-03-21 20:47:44 +01:00
|
|
|
_bindtextdomain("messages", "locale");
|
2011-03-18 17:25:06 +01:00
|
|
|
_textdomain("messages");
|
|
|
|
_bind_textdomain_codeset("messages", "UTF-8");
|
2007-05-19 07:13:35 +02:00
|
|
|
}
|
2011-03-18 17:25:06 +01:00
|
|
|
}
|
|
|
|
|
2005-11-16 18:22:13 +01:00
|
|
|
require_once 'db-prefs.php';
|
2017-05-04 13:22:23 +02:00
|
|
|
require_once 'controls.php';
|
2005-08-21 17:35:22 +02:00
|
|
|
|
2019-12-18 13:56:27 +01:00
|
|
|
define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . get_version() . ' (http://tt-rss.org/)');
|
2010-11-17 10:52:17 +01:00
|
|
|
ini_set('user_agent', SELF_USER_AGENT);
|
|
|
|
|
2011-08-04 17:38:25 +02:00
|
|
|
$schema_version = false;
|
|
|
|
|
2020-09-22 08:04:33 +02:00
|
|
|
/* compat shims */
|
|
|
|
|
2018-11-30 06:34:29 +01:00
|
|
|
function _debug($msg) {
|
|
|
|
Debug::log($msg);
|
2013-09-15 20:57:50 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 12:04:26 +02:00
|
|
|
// @deprecated
|
|
|
|
function getFeedUnread($feed, $is_cat = false) {
|
|
|
|
return Feeds::getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// @deprecated
|
2020-09-22 08:04:33 +02:00
|
|
|
function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
|
|
|
|
return Sanitizer::sanitize($str, $force_remove_images, $owner, $site_url, $highlight_words, $article_id);
|
2019-11-17 11:17:21 +01:00
|
|
|
}
|
|
|
|
|
2020-09-23 12:04:26 +02:00
|
|
|
// @deprecated
|
2020-09-22 08:04:33 +02:00
|
|
|
function fetch_file_contents($params) {
|
|
|
|
return UrlHelper::fetch($params);
|
2006-08-28 05:49:08 +02:00
|
|
|
}
|
2005-08-25 08:46:24 +02:00
|
|
|
|
2020-09-23 12:04:26 +02:00
|
|
|
// @deprecated
|
2020-09-22 08:04:33 +02:00
|
|
|
function rewrite_relative_url($url, $rel_url) {
|
|
|
|
return UrlHelper::rewrite_relative($url, $rel_url);
|
|
|
|
}
|
2005-11-18 06:17:17 +01:00
|
|
|
|
2020-09-23 12:04:26 +02:00
|
|
|
// @deprecated
|
2020-09-22 08:04:33 +02:00
|
|
|
function validate_url($url) {
|
|
|
|
return UrlHelper::validate($url);
|
|
|
|
}
|
2005-11-18 06:17:17 +01:00
|
|
|
|
2020-09-23 12:04:26 +02:00
|
|
|
// @deprecated
|
2020-09-22 08:04:33 +02:00
|
|
|
function authenticate_user($login, $password, $check_only = false, $service = false) {
|
|
|
|
return UserHelper::authenticate($login, $password, $check_only, $service);
|
2005-11-18 06:17:17 +01:00
|
|
|
}
|
2006-05-16 09:33:51 +02:00
|
|
|
|
2020-09-23 12:04:26 +02:00
|
|
|
// @deprecated
|
|
|
|
function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false, $eta_min = false) {
|
|
|
|
return TimeHelper::smart_date_time($timestamp, $tz_offset, $owner_uid, $eta_min);
|
|
|
|
}
|
|
|
|
|
|
|
|
// @deprecated
|
|
|
|
function make_local_datetime($timestamp, $long, $owner_uid = false, $no_smart_dt = false, $eta_min = false) {
|
|
|
|
return TimeHelper::make_local_datetime($timestamp, $long, $owner_uid, $no_smart_dt, $eta_min);
|
|
|
|
}
|
|
|
|
|
2020-09-22 08:04:33 +02:00
|
|
|
/* end compat shims */
|
|
|
|
|
2011-03-28 07:45:23 +02:00
|
|
|
function get_ssl_certificate_id() {
|
2021-02-05 21:41:32 +01:00
|
|
|
if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] ?? false) {
|
2011-03-28 07:45:23 +02:00
|
|
|
return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
|
|
|
|
$_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
|
|
|
|
$_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
|
|
|
|
$_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
|
|
|
|
}
|
2021-02-05 21:41:32 +01:00
|
|
|
if ($_SERVER["SSL_CLIENT_M_SERIAL"] ?? false) {
|
2014-01-11 11:33:42 +01:00
|
|
|
return sha1($_SERVER["SSL_CLIENT_M_SERIAL"] .
|
|
|
|
$_SERVER["SSL_CLIENT_V_START"] .
|
|
|
|
$_SERVER["SSL_CLIENT_V_END"] .
|
|
|
|
$_SERVER["SSL_CLIENT_S_DN"]);
|
|
|
|
}
|
2011-03-28 07:45:23 +02:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2017-12-03 21:35:38 +01:00
|
|
|
// this is used for user http parameters unless HTML code is actually needed
|
|
|
|
function clean($param) {
|
|
|
|
if (is_array($param)) {
|
2021-02-05 22:17:41 +01:00
|
|
|
return array_map("trim", array_map("strip_tags", $param));
|
2017-12-03 21:35:38 +01:00
|
|
|
} else if (is_string($param)) {
|
2021-02-05 21:41:32 +01:00
|
|
|
return trim(strip_tags($param));
|
2017-12-03 21:35:38 +01:00
|
|
|
} else {
|
2021-02-05 21:41:32 +01:00
|
|
|
return trim($param);
|
2017-12-03 21:35:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 18:16:50 +01:00
|
|
|
function make_password($length = 12) {
|
2012-01-25 07:47:32 +01:00
|
|
|
$password = "";
|
2019-03-05 18:18:50 +01:00
|
|
|
$possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ*%+^";
|
2012-01-25 07:47:32 +01:00
|
|
|
|
2019-03-05 18:18:50 +01:00
|
|
|
$i = 0;
|
2012-01-25 07:47:32 +01:00
|
|
|
|
|
|
|
while ($i < $length) {
|
2019-03-05 18:18:50 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
$idx = function_exists("random_int") ? random_int(0, strlen($possible) - 1) : mt_rand(0, strlen($possible) - 1);
|
|
|
|
} catch (Exception $e) {
|
|
|
|
$idx = mt_rand(0, strlen($possible) - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
$char = substr($possible, $idx, 1);
|
2012-01-25 07:47:32 +01:00
|
|
|
|
|
|
|
if (!strstr($password, $char)) {
|
|
|
|
$password .= $char;
|
|
|
|
$i++;
|
|
|
|
}
|
|
|
|
}
|
2019-03-05 18:18:50 +01:00
|
|
|
|
2012-01-25 07:47:32 +01:00
|
|
|
return $password;
|
2005-11-18 10:00:18 +01:00
|
|
|
}
|
|
|
|
|
2011-12-26 09:02:52 +01:00
|
|
|
function validate_csrf($csrf_token) {
|
2020-09-17 09:20:55 +02:00
|
|
|
return isset($csrf_token) && hash_equals($_SESSION['csrf_token'], $csrf_token);
|
2011-12-26 09:02:52 +01:00
|
|
|
}
|
|
|
|
|
2010-11-25 10:05:48 +01:00
|
|
|
function truncate_string($str, $max_len, $suffix = '…') {
|
2014-02-02 20:17:13 +01:00
|
|
|
if (mb_strlen($str, "utf-8") > $max_len) {
|
2010-11-25 10:05:48 +01:00
|
|
|
return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
|
2005-11-24 08:25:09 +01:00
|
|
|
} else {
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
}
|
2005-11-26 07:40:47 +01:00
|
|
|
|
2018-12-24 10:44:10 +01:00
|
|
|
function mb_substr_replace($original, $replacement, $position, $length) {
|
|
|
|
$startString = mb_substr($original, 0, $position, "UTF-8");
|
|
|
|
$endString = mb_substr($original, $position + $length, mb_strlen($original), "UTF-8");
|
|
|
|
|
|
|
|
$out = $startString . $replacement . $endString;
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
2016-02-17 14:42:13 +01:00
|
|
|
function truncate_middle($str, $max_len, $suffix = '…') {
|
2018-12-24 10:44:10 +01:00
|
|
|
if (mb_strlen($str) > $max_len) {
|
|
|
|
return mb_substr_replace($str, $suffix, $max_len / 2, mb_strlen($str) - $max_len);
|
2016-02-17 14:42:13 +01:00
|
|
|
} else {
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-30 05:11:48 +01:00
|
|
|
function sql_bool_to_bool($s) {
|
2017-12-02 20:39:34 +01:00
|
|
|
return $s && ($s !== "f" && $s !== "false"); //no-op for PDO, backwards compat for legacy layer
|
2005-12-30 05:11:48 +01:00
|
|
|
}
|
2011-03-17 17:05:28 +01:00
|
|
|
|
2009-02-07 11:40:43 +01:00
|
|
|
function bool_to_sql_bool($s) {
|
2017-12-05 22:12:28 +01:00
|
|
|
return $s ? 1 : 0;
|
2009-02-07 11:40:43 +01:00
|
|
|
}
|
2005-12-30 05:11:48 +01:00
|
|
|
|
2011-04-19 11:20:59 +02:00
|
|
|
// Session caching removed due to causing wrong redirects to upgrade
|
|
|
|
// script when get_schema_version() is called on an obsolete session
|
|
|
|
// created on a previous schema version.
|
2013-04-17 16:34:18 +02:00
|
|
|
function get_schema_version($nocache = false) {
|
2011-08-04 17:38:25 +02:00
|
|
|
global $schema_version;
|
|
|
|
|
2020-09-17 08:18:03 +02:00
|
|
|
$pdo = Db::pdo();
|
2017-11-30 10:28:02 +01:00
|
|
|
|
2013-04-24 12:54:59 +02:00
|
|
|
if (!$schema_version && !$nocache) {
|
2017-11-30 10:28:02 +01:00
|
|
|
$row = $pdo->query("SELECT schema_version FROM ttrss_version")->fetch();
|
|
|
|
$version = $row["schema_version"];
|
2011-08-04 17:38:25 +02:00
|
|
|
$schema_version = $version;
|
2010-01-13 20:03:42 +01:00
|
|
|
return $version;
|
2011-08-04 17:38:25 +02:00
|
|
|
} else {
|
|
|
|
return $schema_version;
|
|
|
|
}
|
2010-01-13 19:25:44 +01:00
|
|
|
}
|
|
|
|
|
2013-04-17 14:23:15 +02:00
|
|
|
function sanity_check() {
|
2011-12-11 20:10:51 +01:00
|
|
|
require_once 'errors.php';
|
2020-12-12 16:50:43 +01:00
|
|
|
$ERRORS = get_error_types();
|
2011-03-18 15:39:23 +01:00
|
|
|
|
2005-12-22 13:51:12 +01:00
|
|
|
$error_code = 0;
|
2013-04-17 16:34:18 +02:00
|
|
|
$schema_version = get_schema_version(true);
|
2005-12-22 13:51:12 +01:00
|
|
|
|
|
|
|
if ($schema_version != SCHEMA_VERSION) {
|
|
|
|
$error_code = 5;
|
|
|
|
}
|
|
|
|
|
2011-03-18 15:39:23 +01:00
|
|
|
return array("code" => $error_code, "message" => $ERRORS[$error_code]);
|
2005-12-22 13:51:12 +01:00
|
|
|
}
|
|
|
|
|
2006-02-13 14:08:23 +01:00
|
|
|
function file_is_locked($filename) {
|
2013-05-29 05:46:14 +02:00
|
|
|
if (file_exists(LOCK_DIRECTORY . "/$filename")) {
|
|
|
|
if (function_exists('flock')) {
|
|
|
|
$fp = @fopen(LOCK_DIRECTORY . "/$filename", "r");
|
|
|
|
if ($fp) {
|
|
|
|
if (flock($fp, LOCK_EX | LOCK_NB)) {
|
|
|
|
flock($fp, LOCK_UN);
|
|
|
|
fclose($fp);
|
|
|
|
return false;
|
|
|
|
}
|
2007-09-25 05:23:29 +02:00
|
|
|
fclose($fp);
|
2013-05-29 05:46:14 +02:00
|
|
|
return true;
|
|
|
|
} else {
|
2007-09-25 05:23:29 +02:00
|
|
|
return false;
|
|
|
|
}
|
2006-02-13 14:08:23 +01:00
|
|
|
}
|
2013-05-29 05:46:14 +02:00
|
|
|
return true; // consider the file always locked and skip the test
|
|
|
|
} else {
|
|
|
|
return false;
|
2006-02-13 14:08:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-12 08:21:52 +01:00
|
|
|
function make_lockfile($filename) {
|
2008-01-17 06:33:52 +01:00
|
|
|
$fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
|
2006-02-12 08:21:52 +01:00
|
|
|
|
2013-02-25 18:28:51 +01:00
|
|
|
if ($fp && flock($fp, LOCK_EX | LOCK_NB)) {
|
2013-06-07 07:39:12 +02:00
|
|
|
$stat_h = fstat($fp);
|
|
|
|
$stat_f = stat(LOCK_DIRECTORY . "/$filename");
|
|
|
|
|
2013-06-11 10:55:47 +02:00
|
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
|
|
|
if ($stat_h["ino"] != $stat_f["ino"] ||
|
|
|
|
$stat_h["dev"] != $stat_f["dev"]) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2013-06-07 07:39:12 +02:00
|
|
|
}
|
|
|
|
|
2010-02-09 15:05:02 +01:00
|
|
|
if (function_exists('posix_getpid')) {
|
|
|
|
fwrite($fp, posix_getpid() . "\n");
|
|
|
|
}
|
2006-02-12 08:21:52 +01:00
|
|
|
return $fp;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-21 12:55:28 +01:00
|
|
|
function checkbox_to_sql_bool($val) {
|
2017-12-03 07:26:11 +01:00
|
|
|
return ($val == "on") ? 1 : 0;
|
2014-03-21 12:55:28 +01:00
|
|
|
}
|
|
|
|
|
2015-08-03 18:21:06 +02:00
|
|
|
function uniqid_short() {
|
|
|
|
return uniqid(base_convert(rand(), 10, 36));
|
|
|
|
}
|
|
|
|
|
2017-05-04 14:13:02 +02:00
|
|
|
function T_sprintf() {
|
|
|
|
$args = func_get_args();
|
|
|
|
return vsprintf(__(array_shift($args)), $args);
|
|
|
|
}
|
|
|
|
|
2021-01-07 16:16:42 +01:00
|
|
|
function T_nsprintf() {
|
|
|
|
$args = func_get_args();
|
|
|
|
return vsprintf(_ngettext(array_shift($args), array_shift($args), array_shift($args)), $args);
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:20:40 +02:00
|
|
|
function is_server_https() {
|
2021-02-05 21:41:32 +01:00
|
|
|
return (!empty($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] != 'off')) ||
|
|
|
|
(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https');
|
2017-07-10 15:20:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function is_prefix_https() {
|
|
|
|
return parse_url(SELF_URL_PATH, PHP_URL_SCHEME) == 'https';
|
|
|
|
}
|
|
|
|
|
2017-07-06 22:01:44 +02:00
|
|
|
// this returns SELF_URL_PATH sans ending slash
|
2017-05-04 14:13:02 +02:00
|
|
|
function get_self_url_prefix() {
|
|
|
|
if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
|
|
|
|
return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
|
|
|
|
} else {
|
|
|
|
return SELF_URL_PATH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function encrypt_password($pass, $salt = '', $mode2 = false) {
|
|
|
|
if ($salt && $mode2) {
|
|
|
|
return "MODE2:" . hash('sha256', $salt . $pass);
|
|
|
|
} else if ($salt) {
|
|
|
|
return "SHA1X:" . sha1("$salt:$pass");
|
|
|
|
} else {
|
|
|
|
return "SHA1:" . sha1($pass);
|
|
|
|
}
|
|
|
|
} // function encrypt_password
|
|
|
|
|
|
|
|
function init_plugins() {
|
|
|
|
PluginHost::getInstance()->load(PLUGINS, PluginHost::KIND_ALL);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!function_exists('gzdecode')) {
|
|
|
|
function gzdecode($string) { // no support for 2nd argument
|
|
|
|
return file_get_contents('compress.zlib://data:who/cares;base64,'.
|
|
|
|
base64_encode($string));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_random_bytes($length) {
|
2020-09-17 07:59:18 +02:00
|
|
|
if (function_exists('random_bytes')) {
|
|
|
|
return random_bytes($length);
|
|
|
|
} else if (function_exists('openssl_random_pseudo_bytes')) {
|
2017-05-04 14:13:02 +02:00
|
|
|
return openssl_random_pseudo_bytes($length);
|
|
|
|
} else {
|
|
|
|
$output = "";
|
|
|
|
|
|
|
|
for ($i = 0; $i < $length; $i++)
|
|
|
|
$output .= chr(mt_rand(0, 255));
|
|
|
|
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function read_stdin() {
|
|
|
|
$fp = fopen("php://stdin", "r");
|
|
|
|
|
|
|
|
if ($fp) {
|
|
|
|
$line = trim(fgets($fp));
|
|
|
|
fclose($fp);
|
|
|
|
return $line;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function implements_interface($class, $interface) {
|
|
|
|
return in_array($interface, class_implements($class));
|
|
|
|
}
|
|
|
|
|
|
|
|
function T_js_decl($s1, $s2) {
|
|
|
|
if ($s1 && $s2) {
|
|
|
|
$s1 = preg_replace("/\n/", "", $s1);
|
|
|
|
$s2 = preg_replace("/\n/", "", $s2);
|
|
|
|
|
|
|
|
$s1 = preg_replace("/\"/", "\\\"", $s1);
|
|
|
|
$s2 = preg_replace("/\"/", "\\\"", $s2);
|
|
|
|
|
|
|
|
return "T_messages[\"$s1\"] = \"$s2\";\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function init_js_translations() {
|
|
|
|
|
|
|
|
print 'var T_messages = new Object();
|
2017-07-06 22:01:44 +02:00
|
|
|
|
2017-05-04 14:13:02 +02:00
|
|
|
function __(msg) {
|
|
|
|
if (T_messages[msg]) {
|
|
|
|
return T_messages[msg];
|
|
|
|
} else {
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
}
|
2017-07-06 22:01:44 +02:00
|
|
|
|
2017-05-04 14:13:02 +02:00
|
|
|
function ngettext(msg1, msg2, n) {
|
|
|
|
return __((parseInt(n) > 1) ? msg2 : msg1);
|
|
|
|
}';
|
|
|
|
|
2019-03-01 12:25:24 +01:00
|
|
|
global $text_domains;
|
2017-05-04 14:13:02 +02:00
|
|
|
|
2019-03-01 12:25:24 +01:00
|
|
|
foreach (array_keys($text_domains) as $domain) {
|
|
|
|
$l10n = _get_reader($domain);
|
|
|
|
|
|
|
|
for ($i = 0; $i < $l10n->total; $i++) {
|
|
|
|
$orig = $l10n->get_original_string($i);
|
2020-09-17 18:02:27 +02:00
|
|
|
if(strpos($orig, "\000") !== false) { // Plural forms
|
2019-03-01 12:25:24 +01:00
|
|
|
$key = explode(chr(0), $orig);
|
|
|
|
print T_js_decl($key[0], _ngettext($key[0], $key[1], 1)); // Singular
|
|
|
|
print T_js_decl($key[1], _ngettext($key[0], $key[1], 2)); // Plural
|
|
|
|
} else {
|
|
|
|
$translation = _dgettext($domain,$orig);
|
|
|
|
print T_js_decl($orig, $translation);
|
|
|
|
}
|
2017-05-04 14:13:02 +02:00
|
|
|
}
|
2019-03-01 12:25:24 +01:00
|
|
|
|
2017-05-04 14:13:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_theme_path($theme) {
|
|
|
|
$check = "themes/$theme";
|
|
|
|
if (file_exists($check)) return $check;
|
|
|
|
|
|
|
|
$check = "themes.local/$theme";
|
|
|
|
if (file_exists($check)) return $check;
|
|
|
|
}
|
|
|
|
|
2018-12-09 09:37:26 +01:00
|
|
|
function theme_exists($theme) {
|
|
|
|
return file_exists("themes/$theme") || file_exists("themes.local/$theme");
|
2017-05-04 14:13:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @SuppressWarnings(unused)
|
|
|
|
*/
|
|
|
|
function error_json($code) {
|
|
|
|
require_once "errors.php";
|
2020-12-12 16:50:43 +01:00
|
|
|
$ERRORS = get_error_types();
|
2017-05-04 14:13:02 +02:00
|
|
|
|
|
|
|
@$message = $ERRORS[$code];
|
|
|
|
|
|
|
|
return json_encode(array("error" =>
|
|
|
|
array("code" => $code, "message" => $message)));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-01-30 08:44:31 +01:00
|
|
|
function arr_qmarks($arr) {
|
|
|
|
return str_repeat('?,', count($arr) - 1) . '?';
|
|
|
|
}
|
2018-12-02 18:07:57 +01:00
|
|
|
|
|
|
|
function get_scripts_timestamp() {
|
|
|
|
$files = glob("js/*.js");
|
|
|
|
$ts = 0;
|
|
|
|
|
|
|
|
foreach ($files as $file) {
|
|
|
|
$file_ts = filemtime($file);
|
|
|
|
if ($file_ts > $ts) $ts = $file_ts;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ts;
|
2018-12-06 17:37:20 +01:00
|
|
|
}
|
2019-12-18 12:27:40 +01:00
|
|
|
|
|
|
|
/* for package maintainers who don't use git: if version_static.txt exists in tt-rss root
|
|
|
|
directory, its contents are displayed instead of git commit-based version, this could be generated
|
|
|
|
based on source git tree commit used when creating the package */
|
|
|
|
|
2020-01-14 18:50:40 +01:00
|
|
|
function get_version(&$git_commit = false, &$git_timestamp = false, &$last_error = false) {
|
2019-12-18 12:27:40 +01:00
|
|
|
global $ttrss_version;
|
|
|
|
|
2019-12-20 16:17:05 +01:00
|
|
|
if (is_array($ttrss_version) && isset($ttrss_version['version'])) {
|
|
|
|
$git_commit = $ttrss_version['commit'];
|
|
|
|
$git_timestamp = $ttrss_version['timestamp'];
|
2021-02-05 21:41:32 +01:00
|
|
|
$last_error = $ttrss_version['last_error'] ?? "";
|
2019-12-18 12:27:40 +01:00
|
|
|
|
2019-12-20 16:17:05 +01:00
|
|
|
return $ttrss_version['version'];
|
|
|
|
} else {
|
|
|
|
$ttrss_version = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
$ttrss_version['version'] = "UNKNOWN (Unsupported)";
|
2019-12-18 12:27:40 +01:00
|
|
|
|
|
|
|
date_default_timezone_set('UTC');
|
|
|
|
$root_dir = dirname(dirname(__FILE__));
|
|
|
|
|
2020-03-02 18:28:21 +01:00
|
|
|
if (PHP_OS === "Darwin") {
|
2019-12-20 16:17:05 +01:00
|
|
|
$ttrss_version['version'] = "UNKNOWN (Unsupported, Darwin)";
|
2019-12-18 17:28:00 +01:00
|
|
|
} else if (file_exists("$root_dir/version_static.txt")) {
|
2019-12-20 16:17:05 +01:00
|
|
|
$ttrss_version['version'] = trim(file_get_contents("$root_dir/version_static.txt")) . " (Unsupported)";
|
2019-12-18 12:27:40 +01:00
|
|
|
} else if (is_dir("$root_dir/.git")) {
|
|
|
|
$rc = 0;
|
|
|
|
$output = [];
|
|
|
|
|
2019-12-18 13:29:12 +01:00
|
|
|
$cwd = getcwd();
|
|
|
|
|
|
|
|
chdir($root_dir);
|
2020-03-02 18:28:21 +01:00
|
|
|
exec('git --no-pager log --pretty="version: %ct %h" -n1 HEAD 2>&1', $output, $rc);
|
2019-12-18 13:29:12 +01:00
|
|
|
chdir($cwd);
|
2019-12-18 12:27:40 +01:00
|
|
|
|
2020-01-14 18:50:40 +01:00
|
|
|
if (is_array($output) && count($output) > 0) {
|
|
|
|
list ($test, $timestamp, $commit) = explode(" ", $output[0], 3);
|
2019-12-18 12:27:40 +01:00
|
|
|
|
2020-01-14 18:50:40 +01:00
|
|
|
if ($test == "version:") {
|
2019-12-18 12:27:40 +01:00
|
|
|
$git_commit = $commit;
|
|
|
|
$git_timestamp = $timestamp;
|
|
|
|
|
2019-12-20 16:17:05 +01:00
|
|
|
$ttrss_version['version'] = strftime("%y.%m", $timestamp) . "-$commit";
|
|
|
|
$ttrss_version['commit'] = $commit;
|
|
|
|
$ttrss_version['timestamp'] = $timestamp;
|
2019-12-18 12:27:40 +01:00
|
|
|
}
|
2020-01-14 18:50:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!isset($ttrss_version['commit'])) {
|
|
|
|
$last_error = "Unable to determine version (using $root_dir): RC=$rc; OUTPUT=" . implode("\n", $output);
|
|
|
|
|
|
|
|
$ttrss_version["last_error"] = $last_error;
|
|
|
|
|
|
|
|
user_error($last_error, E_USER_WARNING);
|
2019-12-18 12:27:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-20 16:17:05 +01:00
|
|
|
return $ttrss_version['version'];
|
2019-12-18 12:27:40 +01:00
|
|
|
}
|