diff options
Diffstat (limited to 'classes/pref')
| -rwxr-xr-x | classes/pref/feeds.php | 27 | ||||
| -rwxr-xr-x | classes/pref/filters.php | 55 | ||||
| -rw-r--r-- | classes/pref/prefs.php | 122 | ||||
| -rw-r--r-- | classes/pref/users.php | 23 |
4 files changed, 155 insertions, 72 deletions
diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 6d7295beb..4ff6ac9c2 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -449,7 +449,7 @@ class Pref_Feeds extends Handler_Protected { if ($row = $sth->fetch()) { @unlink(ICONS_DIR . "/$feed_id.ico"); - $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = NULL + $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = NULL, favicon_last_checked = '1970-01-01' where id = ?"); $sth->execute([$feed_id]); } @@ -554,7 +554,7 @@ class Pref_Feeds extends Handler_Protected { $last_error = $row["last_error"]; if ($last_error) { - print " <i class=\"material-icons\" + print " <i class=\"material-icons\" title=\"".htmlspecialchars($last_error)."\">error</i>"; } @@ -676,7 +676,7 @@ class Pref_Feeds extends Handler_Protected { $auth_checked = $auth_enabled ? 'checked' : ''; print "<label class='checkbox'> <input type='checkbox' $auth_checked name='need_auth' dojoType='dijit.form.CheckBox' id='feedEditDlg_loginCheck' - onclick='displayIfChecked(this, \"feedEditDlg_loginContainer\")'> + onclick='App.displayIfChecked(this, \"feedEditDlg_loginContainer\")'> ".__('This feed requires authentication.')."</label>"; print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">'; @@ -772,6 +772,7 @@ class Pref_Feeds extends Handler_Protected { <input style='display: none' id='icon_file' size='10' name='icon_file' type='file'> </label> <input type='hidden' name='op' value='pref-feeds'> + <input type='hidden' name='csrf_token' value='".$_SESSION['csrf_token']."'> <input type='hidden' name='feed_id' value='$feed_id'> <input type='hidden' name='method' value='uploadicon'> <button dojoType='dijit.form.Button' onclick=\"return CommonDialogs.uploadFeedIcon();\" @@ -1172,7 +1173,7 @@ class Pref_Feeds extends Handler_Protected { function index() { print "<div dojoType='dijit.layout.AccordionContainer' region='center'>"; - print "<div style='padding : 0px' dojoType='dijit.layout.AccordionPane' + print "<div style='padding : 0px' dojoType='dijit.layout.AccordionPane' title=\"<i class='material-icons'>rss_feed</i> ".__('Feeds')."\">"; $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors @@ -1307,7 +1308,7 @@ class Pref_Feeds extends Handler_Protected { print "</div>"; # feeds pane - print "<div dojoType='dijit.layout.AccordionPane' + print "<div dojoType='dijit.layout.AccordionPane' title='<i class=\"material-icons\">import_export</i> ".__('OPML')."'>"; print "<h3>" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . "</h3>"; @@ -1325,6 +1326,7 @@ class Pref_Feeds extends Handler_Protected { <input style='display : none' id='opml_file' name='opml_file' type='file'> </label> <input type='hidden' name='op' value='dlg'> + <input type='hidden' name='csrf_token' value='".$_SESSION['csrf_token']."'> <input type='hidden' name='method' value='importOpml'> <button dojoType='dijit.form.Button' class='alt-primary' onclick=\"return Helpers.OPML.import();\" type=\"submit\">" . __('Import OPML') . "</button>"; @@ -1360,7 +1362,7 @@ class Pref_Feeds extends Handler_Protected { print "</div>"; # pane - print "<div dojoType=\"dijit.layout.AccordionPane\" + print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"<i class='material-icons'>share</i> ".__('Published & shared articles / Generated feeds')."\">"; print "<h3>" . __('Published articles can be subscribed by anyone who knows the following URL:') . "</h3>"; @@ -1637,6 +1639,8 @@ class Pref_Feeds extends Handler_Protected { } function batchSubscribe() { + print "<form onsubmit='return false'>"; + print_hidden("op", "pref-feeds"); print_hidden("method", "batchaddfeeds"); @@ -1645,7 +1649,7 @@ class Pref_Feeds extends Handler_Protected { print "<textarea style='font-size : 12px; width : 98%; height: 200px;' - dojoType='dijit.form.SimpleTextarea' name='feeds'></textarea>"; + dojoType='fox.form.ValidationTextArea' required='1' name='feeds'></textarea>"; if (get_pref('ENABLE_FEED_CATS')) { print "<fieldset>"; @@ -1670,14 +1674,17 @@ class Pref_Feeds extends Handler_Protected { print "<fieldset class='narrow'> <label class='checkbox'><input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox' - onclick='displayIfChecked(this, \"feedDlg_loginContainer\")'> ". + onclick='App.displayIfChecked(this, \"feedDlg_loginContainer\")'> ". __('Feeds require authentication.')."</label></div>"; print "</fieldset>"; print "<footer> - <button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".__('Subscribe')."</button> + <button dojoType='dijit.form.Button' onclick=\"return dijit.byId('batchSubDlg').execute()\" type='submit' class='alt-primary'>". + __('Subscribe')."</button> <button dojoType='dijit.form.Button' onclick=\"return dijit.byId('batchSubDlg').hide()\">".__('Cancel')."</button> </footer>"; + + print "</form>"; } function batchAddFeeds() { @@ -1696,7 +1703,7 @@ class Pref_Feeds extends Handler_Protected { foreach ($feeds as $feed) { $feed = trim($feed); - if (Feeds::validate_feed_url($feed)) { + if (UrlHelper::validate($feed)) { $this->pdo->beginTransaction(); diff --git a/classes/pref/filters.php b/classes/pref/filters.php index a3a0ce77f..6121e4c14 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -3,7 +3,7 @@ class Pref_Filters extends Handler_Protected { function csrf_ignore($method) { $csrf_ignored = array("index", "getfiltertree", "edit", "newfilter", "newrule", - "newaction", "savefilterorder"); + "newaction", "savefilterorder", "testfilterdlg"); return array_search($method, $csrf_ignored) !== false; } @@ -159,22 +159,19 @@ class Pref_Filters extends Handler_Protected { print json_encode($rv); } - function testFilter() { + function testFilterDlg() { + ?> + <div> + <img id='prefFilterLoadingIndicator' src='images/indicator_tiny.gif'> + <span id='prefFilterProgressMsg'>Looking for articles...</span> + </div> - if (isset($_REQUEST["offset"])) return $this->testFilterDo(); - - //print __("Articles matching this filter:"); - - print "<div><img id='prefFilterLoadingIndicator' src='images/indicator_tiny.gif'> <span id='prefFilterProgressMsg'>Looking for articles...</span></div>"; - - print "<ul class='panel panel-scrollable list list-unstyled' id='prefFilterTestResultList'>"; - print "</ul>"; - - print "<footer class='text-center'>"; - print "<button dojoType='dijit.form.Button' onclick=\"dijit.byId('filterTestDlg').hide()\">". - __('Close this window')."</button>"; - print "</footer>"; + <ul class='panel panel-scrollable list list-unstyled' id='prefFilterTestResultList'></ul> + <footer class='text-center'> + <button dojoType='dijit.form.Button' onclick="dijit.byId('filterTestDlg').hide()"><?php echo __('Close this window') ?></button> + </footer> + <?php } private function getfilterrules_list($filter_id) { @@ -241,6 +238,7 @@ class Pref_Filters extends Handler_Protected { $root = array(); $root['id'] = 'root'; $root['name'] = __('Filters'); + $root['enabled'] = true; $root['items'] = array(); $filter_search = $_SESSION["prefs_filter_search"]; @@ -305,7 +303,7 @@ class Pref_Filters extends Handler_Protected { $filter['param'] = $name[1]; $filter['checkbox'] = false; $filter['last_triggered'] = $line["last_triggered"] ? make_local_datetime($line["last_triggered"], false) : null; - $filter['enabled'] = $line["enabled"]; + $filter['enabled'] = sql_bool_to_bool($line["enabled"]); $filter['rules'] = $this->getfilterrules_list($line['id']); if (!$filter_search || $match_ok) { @@ -600,10 +598,6 @@ class Pref_Filters extends Handler_Protected { } function editSave() { - if (clean($_REQUEST["savemode"] && $_REQUEST["savemode"]) == "test") { - return $this->testFilter(); - } - $filter_id = clean($_REQUEST["id"]); $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"])); $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"])); @@ -714,10 +708,6 @@ class Pref_Filters extends Handler_Protected { } function add() { - if (clean($_REQUEST["savemode"] && $_REQUEST["savemode"]) == "test") { - return $this->testFilter(); - } - $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"])); $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"])); $title = clean($_REQUEST["title"]); @@ -975,19 +965,18 @@ class Pref_Filters extends Handler_Protected { print "<section>"; - print "<input dojoType=\"dijit.form.ValidationTextBox\" - required=\"true\" id=\"filterDlg_regExp\" - onchange='Filters.filterDlgCheckRegExp(this)' - onblur='Filters.filterDlgCheckRegExp(this)' - onfocus='Filters.filterDlgCheckRegExp(this)' - style=\"font-size : 16px; width : 500px\" - name=\"reg_exp\" value=\"$reg_exp\"/>"; + print "<textarea dojoType='fox.form.ValidationTextArea' + required='true' id='filterDlg_regExp' + ValidRegExp='true' + rows='4' + style='font-size : 14px; width : 490px; word-break: break-all' + name='reg_exp'>$reg_exp</textarea>"; print "<div dojoType='dijit.Tooltip' id='filterDlg_regExp_tip' connectId='filterDlg_regExp' position='below'></div>"; print "<fieldset>"; - print "<label class='checkbox'><input id=\"filterDlg_inverse\" dojoType=\"dijit.form.CheckBox\" - name=\"inverse\" $inverse_checked/> ". + print "<label class='checkbox'><input id='filterDlg_inverse' dojoType='dijit.form.CheckBox' + name='inverse' $inverse_checked/> ". __("Inverse regular expression matching")."</label>"; print "</fieldset>"; diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index ac16b5971..f825454dd 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -8,7 +8,7 @@ class Pref_Prefs extends Handler_Protected { private $profile_blacklist = []; function csrf_ignore($method) { - $csrf_ignored = array("index", "updateself", "customizecss", "editprefprofiles"); + $csrf_ignored = array("index", "updateself", "customizecss", "editprefprofiles", "otpqrcode"); return array_search($method, $csrf_ignored) !== false; } @@ -125,8 +125,14 @@ class Pref_Prefs extends Handler_Protected { $old_pw = clean($_POST["old_password"]); $new_pw = clean($_POST["new_password"]); + $new_unclean_pw = $_POST["new_password"]; $con_pw = clean($_POST["confirm_password"]); + if ($new_unclean_pw != $new_pw) { + print "ERROR: ".format_error("New password contains disallowed characters."); + return; + } + if ($old_pw == $new_pw) { print "ERROR: ".format_error("New password must be different from the old one."); return; @@ -213,11 +219,9 @@ class Pref_Prefs extends Handler_Protected { if ($old_email != $email) { $mailer = new Mailer(); - require_once "lib/MiniTemplator.class.php"; + $tpl = new Templator(); - $tpl = new MiniTemplator; - - $tpl->readTemplateFromFile("templates/mail_change_template.txt"); + $tpl->readTemplateFromFile("mail_change_template.txt"); $tpl->setVariable('LOGIN', $row["login"]); $tpl->setVariable('NEWMAIL', $email); @@ -253,7 +257,7 @@ class Pref_Prefs extends Handler_Protected { AND owner_uid = :uid"); $sth->execute([":profile" => $_SESSION['profile'], ":uid" => $_SESSION['uid']]); - initialize_user_prefs($_SESSION["uid"], $_SESSION["profile"]); + $this->initialize_user_prefs($_SESSION["uid"], $_SESSION["profile"]); echo __("Your preferences are now set to default values."); } @@ -382,12 +386,12 @@ class Pref_Prefs extends Handler_Protected { print "<fieldset>"; print "<label>" . __("New password:") . "</label>"; - print "<input dojoType='dijit.form.ValidationTextBox' type='password' required='1' name='new_password'>"; + print "<input dojoType='dijit.form.ValidationTextBox' type='password' regexp='^[^<>]+' required='1' name='new_password'>"; print "</fieldset>"; print "<fieldset>"; print "<label>" . __("Confirm password:") . "</label>"; - print "<input dojoType='dijit.form.ValidationTextBox' type='password' required='1' name='confirm_password'>"; + print "<input dojoType='dijit.form.ValidationTextBox' type='password' regexp='^[^<>]+' required='1' name='confirm_password'>"; print "</fieldset>"; print_hidden("op", "pref-prefs"); @@ -479,8 +483,8 @@ class Pref_Prefs extends Handler_Protected { if (function_exists("imagecreatefromstring")) { print "<h3>" . __("Scan the following code by the Authenticator application or copy the key manually") . "</h3>"; - $csrf_token = $_SESSION["csrf_token"]; - print "<img alt='otp qr-code' src='backend.php?op=pref-prefs&method=otpqrcode&csrf_token=$csrf_token'>"; + $csrf_token_hash = sha1($_SESSION["csrf_token"]); + print "<img alt='otp qr-code' src='backend.php?op=pref-prefs&method=otpqrcode&csrf_token_hash=$csrf_token_hash'>"; } else { print_error("PHP GD functions are required to generate QR codes."); print "<h3>" . __("Use the following OTP key with a compatible Authenticator application") . "</h3>"; @@ -586,9 +590,9 @@ class Pref_Prefs extends Handler_Protected { if ($profile) { print_notice(__("Some preferences are only available in default profile.")); - initialize_user_prefs($_SESSION["uid"], $profile); + $this->initialize_user_prefs($_SESSION["uid"], $profile); } else { - initialize_user_prefs($_SESSION["uid"]); + $this->initialize_user_prefs($_SESSION["uid"]); } $prefs_available = []; @@ -854,6 +858,10 @@ class Pref_Prefs extends Handler_Protected { print_warning("Your PHP configuration has open_basedir restrictions enabled. Some plugins relying on CURL for functionality may not work correctly."); } + if ($_SESSION["safe_mode"]) { + print_error("You have logged in using safe mode, no user plugins will be actually enabled until you login again."); + } + $feed_handler_whitelist = [ "Af_Comics" ]; $feed_handlers = array_merge( @@ -862,7 +870,7 @@ class Pref_Prefs extends Handler_Protected { PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FETCH_FEED)); $feed_handlers = array_filter($feed_handlers, function($plugin) use ($feed_handler_whitelist) { - return in_array(get_class($plugin), $feed_handler_whitelist) === FALSE; }); + return in_array(get_class($plugin), $feed_handler_whitelist) === false; }); if (count($feed_handlers) > 0) { print_error( @@ -1006,21 +1014,28 @@ class Pref_Prefs extends Handler_Protected { } function otpqrcode() { - require_once "lib/phpqrcode/phpqrcode.php"; + $csrf_token_hash = clean($_REQUEST["csrf_token_hash"]); - $sth = $this->pdo->prepare("SELECT login - FROM ttrss_users - WHERE id = ?"); - $sth->execute([$_SESSION['uid']]); + if (sha1($_SESSION["csrf_token"]) === $csrf_token_hash) { + require_once "lib/phpqrcode/phpqrcode.php"; - if ($row = $sth->fetch()) { - $secret = $this->otpsecret(); - $login = $row['login']; + $sth = $this->pdo->prepare("SELECT login + FROM ttrss_users + WHERE id = ?"); + $sth->execute([$_SESSION['uid']]); - if ($secret) { - QRcode::png("otpauth://totp/".urlencode($login). - "?secret=$secret&issuer=".urlencode("Tiny Tiny RSS")); + if ($row = $sth->fetch()) { + $secret = $this->otpsecret(); + $login = $row['login']; + + if ($secret) { + QRcode::png("otpauth://totp/".urlencode($login). + "?secret=$secret&issuer=".urlencode("Tiny Tiny RSS")); + } } + } else { + header("Content-Type: text/json"); + print error_json(6); } } @@ -1087,11 +1102,9 @@ class Pref_Prefs extends Handler_Protected { if ($row = $sth->fetch()) { $mailer = new Mailer(); - require_once "lib/MiniTemplator.class.php"; - - $tpl = new MiniTemplator; + $tpl = new Templator(); - $tpl->readTemplateFromFile("templates/otp_disabled_template.txt"); + $tpl->readTemplateFromFile("otp_disabled_template.txt"); $tpl->setVariable('LOGIN', $row["login"]); $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); @@ -1353,4 +1366,57 @@ class Pref_Prefs extends Handler_Protected { $this->appPasswordList(); } + + static function initialize_user_prefs($uid, $profile = false) { + + if (get_schema_version() < 63) $profile_qpart = ""; + + $pdo = Db::pdo(); + $in_nested_tr = false; + + try { + $pdo->beginTransaction(); + } catch (Exception $e) { + $in_nested_tr = true; + } + + $sth = $pdo->query("SELECT pref_name,def_value FROM ttrss_prefs"); + + if (!is_numeric($profile) || !$profile || get_schema_version() < 63) $profile = null; + + $u_sth = $pdo->prepare("SELECT pref_name + 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]); + + $active_prefs = array(); + + while ($line = $u_sth->fetch()) { + array_push($active_prefs, $line["pref_name"]); + } + + while ($line = $sth->fetch()) { + if (array_search($line["pref_name"], $active_prefs) === false) { +// print "adding " . $line["pref_name"] . "<br>"; + + if (get_schema_version() < 63) { + $i_sth = $pdo->prepare("INSERT INTO ttrss_user_prefs + (owner_uid,pref_name,value) VALUES + (?, ?, ?)"); + $i_sth->execute([$uid, $line["pref_name"], $line["def_value"]]); + + } else { + $i_sth = $pdo->prepare("INSERT INTO ttrss_user_prefs + (owner_uid,pref_name,value, profile) VALUES + (?, ?, ?, ?)"); + $i_sth->execute([$uid, $line["pref_name"], $line["def_value"], $profile]); + } + + } + } + + if (!$in_nested_tr) $pdo->commit(); + + } + } diff --git a/classes/pref/users.php b/classes/pref/users.php index 851d4fa9e..aeabc4502 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -259,7 +259,7 @@ class Pref_Users extends Handler_Protected { print T_sprintf("Added user %s with password %s", $login, $tmp_user_pwd); - initialize_user($new_uid); + $this->initialize_user($new_uid); } else { @@ -443,4 +443,25 @@ class Pref_Users extends Handler_Protected { return $default; } + // this is called after user is created to initialize default feeds, labels + // or whatever else + // user preferences are checked on every login, not here + static function initialize_user($uid) { + + $pdo = Db::pdo(); + + $sth = $pdo->prepare("insert into ttrss_feeds (owner_uid,title,feed_url) + values (?, 'Tiny Tiny RSS: Forum', + 'https://tt-rss.org/forum/rss.php')"); + $sth->execute([$uid]); + } + + static function logout_user() { + @session_destroy(); + if (isset($_COOKIE[session_name()])) { + setcookie(session_name(), '', time()-42000, '/'); + } + session_commit(); + } + } |