Commit d38fcbe1 authored by lindonb's avatar lindonb

newsletters: implement updated ticket system; includes reusing $_POST ticket...

newsletters: implement updated ticket system; includes reusing $_POST ticket during throttling and removing immediately after; some related feedback added
parent 3317e764
Pipeline #48102950 failed with stages
in 23 minutes and 20 seconds
......@@ -1538,7 +1538,8 @@ class NlLib extends TikiLib
// info: subject, data, datatxt, dataparsed, wysiwyg, sendingUniqId, files, errorEditionId, editionId
// browser: true if on the browser
public function send($nl_info, $info, $browser = true, &$sent, &$errors, &$logFileName)
// $csrfCheck: indicated whether modified csrf check passed
public function send($nl_info, $info, $browser = true, &$sent, &$errors, &$logFileName, $csrfCheck)
{
global $prefs, $section;
$tikilib = TikiLib::lib('tiki');
......@@ -1633,6 +1634,7 @@ class NlLib extends TikiLib
print str_repeat(' ', 4096) . "\n";
}
if ($csrfCheck) {
try {
$zmail = $this->get_edition_mail($info['editionId'], $us, $info['is_html'], $info['replyto'],
$info['sendfrom']);
......@@ -1650,14 +1652,37 @@ class NlLib extends TikiLib
$logStatus = 'OK';
} catch (Zend\Mail\Exception\ExceptionInterface $e) {
if ($browser) {
print '<div class="confirmation">' . ' Total emails sent: ' . count($sent)
. tr(' after failure to send to') . ' <b>' . $email . '</b>: <span class="text-danger">'
. tr('Error - potential cross site request forgery detected') . '</span></div>' . "\n";
print '<div class="confirmation">' . ' Total emails sent: ' . count($sent)
. tr(' after error in sending to') . ' <b>' . $email . '</b>: <span class="text-danger">'
. tr('Error') . ' - ' . $e->getMessage();
print "'red'>" . tr('Error') . " - {$e->getMessage()}" . '</font></div>' . "\n";
}
$errors[] = ["user" => $us['user'], "email" => $email, "msg" => $e->getMessage()];
$this->mark_edition_subscriber($info['editionId'], $us);
$logStatus = 'Error';
}
} else {
if ($browser) {
print '<div class="confirmation">' . ' Total emails sent: ' . count($sent)
. tr(' after failure to send to') . ' <b>' . $email . '</b>: <span class="text-danger">'
. tr('Error - potential cross site request forgery detected') . '</span></div>' . "\n";
}
$errors[] = [
"user" => $us['user'],
"email" => $email,
"msg" => tr('Potential cross site forgery request detected')
];
$this->mark_edition_subscriber($info['editionId'], $us);
$logStatus = 'Error';
}
if (isset($_SESSION['tickets']['newsletter']['iterations'])) {
if ($_SESSION['tickets']['newsletter']['iterations'] > 1) {
--$_SESSION['tickets']['newsletter']['iterations'];
} else {
unset($_SESSION['tickets']['newsletter']);
}
}
if ($logFileHandle) {
@fwrite($logFileHandle, "$email : $logStatus\n");
......
......@@ -70,7 +70,7 @@
{/self_link}
</action>
<action>
{self_link _icon_name='remove' _menu_text='y' _menu_icon='y' remove=$channels[user].nlId}
{self_link _icon_name='remove' _menu_text='y' _menu_icon='y' remove=$channels[user].nlId _onclick="confirmSimple(event, '{tr}Remove newsletter?{/tr}', '{ticket mode=get}')"}
{tr}Remove{/tr}
{/self_link}
</action>
......@@ -94,6 +94,7 @@
{/if}
<form action="tiki-admin_newsletters.php" method="post">
{ticket}
<input type="hidden" name="nlId" value="{$info.nlId|escape}">
<input type="hidden" name="author" value="{$user|escape}">
<div class="form-group row">
......@@ -175,7 +176,13 @@
</div>
</div>
<div class="text-center">
<input type="submit" class="btn btn-primary" name="save" value="{tr}Save{/tr}">
<input
type="submit"
class="btn btn-primary"
name="save"
value="{tr}Save{/tr}"
onclick="checkTimeout()"
>
</div>
</form>
{/tab}
......
......@@ -15,6 +15,7 @@
</h2>
<br>
<form method="post" action="tiki-newsletters.php">
{ticket}
<input type="hidden" name="nlId" value="{$nlId|escape}">
<div class="form-group row">
<label class="col-sm-3 col-form-label">{tr}Name{/tr}</label>
......@@ -45,7 +46,13 @@
<div class="form-group row">
<label class="col-sm-3 col-form-label"></label>
<div class="col-sm-7">
<input type="submit" class="btn btn-primary" name="subscribe" value="{tr}Subscribe to this Newsletter{/tr}">
<input
type="submit"
class="btn btn-primary"
name="subscribe"
value="{tr}Subscribe to this Newsletter{/tr}"
onclick="checkTimeout()"
>
</div>
</div>
{/if}
......
......@@ -65,6 +65,7 @@
{/remarksbox}
<p>
<form method="post" action="tiki-send_newsletters.php" target="resultIframe" id='confirmForm'>
{ticket}
<input type="hidden" name="nlId" value="{$nlId|escape}">
<input type="hidden" name="sendingUniqId" value="{$sendingUniqId|escape}">
<input type="hidden" name="editionId" value="{$info.editionId}">
......@@ -78,7 +79,7 @@
<input type="hidden" name="wysiwyg" value="{$info.wysiwyg|escape}">
<input type="hidden" name="is_html" value="{$info.is_html|escape}">
<input type="submit" class="btn btn-primary" name="send" value="{tr}Send{/tr}" onclick="document.getElementById('confirmArea').style.display = 'none'; document.getElementById('sendingArea').style.display = 'block';">
<input type="submit" class="btn btn-primary" name="cancel" value="{tr}Cancel{/tr}">
<input type="submit" class="btn btn-primary" name="cancel" value="{tr}Cancel{/tr}" onclick="checkTimeout()">
{foreach from=$info.files item=newsletterfile key=fileid}
<input type='hidden' name='newsletterfile[{$fileid}]' value='{$newsletterfile.id}'>
{/foreach}
......@@ -190,6 +191,7 @@
{* --- tab with editor --- *}
<h2>{tr}Prepare a newsletter to be sent{/tr}</h2>
<form class="form-horizontal" action="tiki-send_newsletters.php" method="post" id='editpageform' enctype='multipart/form-data'>
{ticket}
<input type="hidden" name="editionId" value="{$info.editionId}">
<div class="form-group row">
<label class="col-form-label col-sm-2">{tr}Subject:{/tr}</label>
......@@ -315,11 +317,31 @@
<div class="form-group row">
<label class="col-form-label col-sm-2"></label>
<div class="col-sm-10">
<input type="submit" name="preview" value="{tr}Preview{/tr}" class="wikiaction tips btn btn-primary" title="{tr}Send Newsletters{/tr}|{tr}Preview your changes.{/tr}" onclick="needToConfirm=false">
<input
type="submit"
name="preview"
value="{tr}Preview{/tr}"
class="wikiaction tips btn btn-primary"
title="{tr}Send Newsletters{/tr}|{tr}Preview your changes.{/tr}"
>
&nbsp;
<input type="submit" name="save_only" value="{tr}Save as Draft{/tr}" class="wikiaction tips btn btn-primary" title="{tr}Send Newsletters{/tr}|{tr}Save your changes.{/tr}" onclick="needToConfirm=false">
<input
type="submit"
name="save_only"
value="{tr}Save as Draft{/tr}"
class="wikiaction tips btn btn-primary"
title="{tr}Send Newsletters{/tr}|{tr}Save your changes.{/tr}"
onclick="needToConfirm=false; checkTimeout()"
>
&nbsp;
<input type="submit" name="save" value="{tr}Send Newsletter{/tr}" class="wikiaction tips btn btn-secondary" title="{tr}Send Newsletters{/tr}|{tr}Save any changes and send to all subscribers.{/tr}" onclick="needToConfirm=false"></td>
<input
type="submit"
name="save"
value="{tr}Send Newsletter{/tr}"
class="wikiaction tips btn btn-secondary"
title="{tr}Send Newsletters{/tr}|{tr}Save any changes and send to all subscribers.{/tr}"
onclick="needToConfirm=false"
>
</div>
</div>
</form>
......
......@@ -39,8 +39,11 @@ $tikilib->get_perm_object($_REQUEST['nlId'], 'newsletter');
$access->check_permission('tiki_p_admin_newsletters');
if (isset($_REQUEST['delsel_x']) && isset($_REQUEST['checked'])) {
$access->check_authenticity();
if (isset($_REQUEST['action'])
&& $_REQUEST['action'] === 'delsel_x'
&& isset($_REQUEST['checked'])
&& $access->checkCsrfForm(tr('Remove selected subscriptions?')))
{
$i = 0;
foreach ($_REQUEST['checked'] as $check) {
$result = $nllib->remove_newsletter_subscription_code($check);
......@@ -57,8 +60,7 @@ if (isset($_REQUEST['delsel_x']) && isset($_REQUEST['checked'])) {
}
$smarty->assign('nl_info', $info);
if (isset($_REQUEST["remove"])) {
$access->check_authenticity();
if (isset($_REQUEST["remove"]) && $access->checkCsrfForm(tr('Remove subscription?')) ) {
$result = false;
if (isset($_REQUEST["email"])) {
$result = $nllib->remove_newsletter_subscription($_REQUEST["remove"], $_REQUEST["email"], "n");
......@@ -78,8 +80,7 @@ if (isset($_REQUEST["remove"])) {
}
}
if (isset($_REQUEST["valid"])) {
check_ticket('admin-nl-subsriptions');
if (isset($_REQUEST["valid"]) && $access->checkCsrfForm(tr('Mark subscription as valid?'))) {
if (isset($_REQUEST["email"])) {
$result = $nllib->valid_subscription($_REQUEST["valid"], $_REQUEST["email"], "n");
if ($result && $result->numRows()) {
......@@ -113,8 +114,8 @@ if (isset($_REQUEST["addemail"]) && $_REQUEST["addemail"] == "y") {
$successCount = 0;
$errorCount = 0;
if (isset($_REQUEST["add"]) && isset($_REQUEST["email"]) && $_REQUEST["email"] != "") {
check_ticket('admin-nl-subsriptions');
if (isset($_REQUEST["add"]) && $access->checkCsrf()) {
if (isset($_REQUEST["email"]) && $_REQUEST["email"] != "") {
if (strpos($_REQUEST["email"], ',')) {
$emails = explode(',', $_REQUEST["email"]);
foreach ($emails as $e) {
......@@ -132,30 +133,24 @@ if (isset($_REQUEST["add"]) && isset($_REQUEST["email"]) && $_REQUEST["email"] !
} else {
$errorCount++;
}
}
if (isset($_REQUEST["add"]) && isset($_REQUEST['subuser']) && $_REQUEST['subuser'] != "") {
check_ticket('admin-nl-subsriptions');
$sid = $nllib->newsletter_subscribe($_REQUEST["nlId"], $_REQUEST["subuser"], "y", $confirmEmail, $addEmail);
}
if (isset($_REQUEST['subuser']) && $_REQUEST['subuser'] != "") {
$sid = $nllib->newsletter_subscribe($_REQUEST["nlId"], $_REQUEST["subuser"], "y", $confirmEmail, $addEmail);
if ($sid) {
$successCount++;
} else {
$errorCount++;
}
}
if (isset($_REQUEST["add"]) && isset($_REQUEST["addall"]) && $_REQUEST["addall"] == "on") {
check_ticket('admin-nl-subsriptions');
}
if (isset($_REQUEST["addall"]) && $_REQUEST["addall"] == "on") {
$result = $nllib->add_all_users($_REQUEST["nlId"], $confirmEmail, $addEmail);
if ($result) {
$successCount++;
} else {
$errorCount++;
}
}
if (isset($_REQUEST["add"]) && isset($_REQUEST['group']) && $_REQUEST['group'] != "") {
check_ticket('admin-nl-subsriptions');
}
if (isset($_REQUEST['group']) && $_REQUEST['group'] != "") {
$result = $nllib->add_group_users(
$_REQUEST["nlId"], $_REQUEST['group'], $confirmEmail, $addEmail
);
......@@ -164,6 +159,12 @@ if (isset($_REQUEST["add"]) && isset($_REQUEST['group']) && $_REQUEST['group'] !
} else {
$errorCount++;
}
}
if ($errorCount) {
Feedback::error(tr('Errors encountered when attempting to add subscription'));
} elseif ($successCount) {
Feedback::success(tr('Subscription added'));
}
}
if ($errorCount) {
Feedback::error(tr('Errors encountered when attempting to add subscription'));
......@@ -175,18 +176,17 @@ if (((isset($_REQUEST["addbatch"]) && isset($_FILES['batch_subscription']))
|| (isset($_REQUEST['importPage']) && ! empty($_REQUEST['wikiPageName']))
|| (isset($_REQUEST['tracker']))) && $tiki_p_batch_subscribe_email == 'y' && $tiki_p_subscribe_email == 'y')
{
check_ticket('admin-nl-subscription');
$success = '';
$error = '';
$successCount = 0;
$errorCount = 0;
if (isset($_REQUEST["addbatch"])) {
if (isset($_REQUEST["addbatch"]) && $access->checkCsrf()) {
if (! $emails = file($_FILES['batch_subscription']['tmp_name'])) {
$error = tr('Error opening uploaded file');
} else {
$success = tr('File uploaded');
}
} elseif (isset($_REQUEST["importPage"])) {
} elseif (isset($_REQUEST["importPage"]) && $access->checkCsrf()) {
$emails = $nllib->get_emails_from_page($_REQUEST['wikiPageName']);
if (! $emails) {
......@@ -194,7 +194,7 @@ if (((isset($_REQUEST["addbatch"]) && isset($_FILES['batch_subscription']))
} else {
$success = tr('Wiki page "%0" imported', htmlspecialchars($_REQUEST['wikiPageName']));
}
} elseif (isset($_REQUEST['tracker'])) {
} elseif (isset($_REQUEST['tracker']) && $access->checkCsrf()) {
$emails = $nllib->get_emails_from_tracker($_REQUEST['tracker']);
if (! $emails) {
......@@ -235,8 +235,7 @@ if (((isset($_REQUEST["addbatch"]) && isset($_FILES['batch_subscription']))
}
}
if (isset($_REQUEST["addgroup"]) && isset($_REQUEST['group']) && $_REQUEST['group'] != "") {
check_ticket('admin-nl-subsriptions');
if (isset($_REQUEST["addgroup"]) && isset($_REQUEST['group']) && $_REQUEST['group'] != "" && $access->checkCsrf()) {
$result = $nllib->add_group($_REQUEST["nlId"], $_REQUEST['group'], isset($_REQUEST['include_groups']) ? 'y' : 'n');
if ($result && $result->numRows()) {
Feedback::success(tr('Group "%0" subscribed', htmlspecialchars($_REQUEST['group'])));
......@@ -245,8 +244,9 @@ if (isset($_REQUEST["addgroup"]) && isset($_REQUEST['group']) && $_REQUEST['grou
}
}
if (isset($_REQUEST["addincluded"]) && isset($_REQUEST['included']) && $_REQUEST['included'] != "") {
check_ticket('admin-nl-subsriptions');
if (isset($_REQUEST["addincluded"]) && isset($_REQUEST['included']) && $_REQUEST['included'] != ""
&& $access->checkCsrf())
{
$result = $nllib->add_included($_REQUEST["nlId"], $_REQUEST['included']);
if ($result) {
Feedback::success(tr('Subscribers added'));
......@@ -255,8 +255,7 @@ if (isset($_REQUEST["addincluded"]) && isset($_REQUEST['included']) && $_REQUEST
}
}
if (isset($_REQUEST["addPage"]) && ! empty($_REQUEST['wikiPageName'])) {
check_ticket('admin-nl-subsriptions');
if (isset($_REQUEST["addPage"]) && ! empty($_REQUEST['wikiPageName']) && $access->checkCsrf()) {
$result = $nllib->add_page($_REQUEST["nlId"], $_REQUEST['wikiPageName'], empty($_REQUEST['noConfirmEmail']) ? 'y' : 'n', empty($_REQUEST['noSubscribeEmail']) ? 'y' : 'n');
if ($result && $result->numRows()) {
Feedback::success(tr('Emails from wiki page "%0" subscribed', htmlspecialchars($_REQUEST['wikiPageName'])));
......@@ -271,7 +270,6 @@ if (isset($_REQUEST["addPage"]) || isset($_REQUEST["addPage"]) || isset($_REQUES
}
if (isset($_REQUEST['export'])) {
check_ticket('admin-nl-subsriptions');
$users = $nllib->get_all_subscribers($_REQUEST['nlId'], 'y');
$data = "email\n";
foreach ($users as $u) {
......@@ -366,7 +364,6 @@ if (isset($tiki_p_admin_trackers) && $tiki_p_admin_trackers == 'y') {
}
include_once('tiki-section_options.php');
ask_ticket('admin-nl-subsriptions');
// disallow robots to index page:
$smarty->assign('metatag_robots', 'NOINDEX, NOFOLLOW');
......
......@@ -59,8 +59,7 @@ if ($_REQUEST["nlId"]) {
$update = "y";
}
$smarty->assign('info', $info);
if (isset($_REQUEST["remove"])) {
$access->check_authenticity();
if (isset($_REQUEST["remove"]) && $access->checkCsrfForm(tr('Remove newsletter?')) ) {
$result = $nllib->remove_newsletter($_REQUEST["remove"]);
if ($result && $result->numRows()) {
Feedback::success(tr('Newsletter removed'));
......@@ -68,8 +67,7 @@ if (isset($_REQUEST["remove"])) {
Feedback::error(tr('Newsletter not removed'));
}
}
if (isset($_REQUEST["save"])) {
check_ticket('admin-nl');
if (isset($_REQUEST["save"]) && $access->checkCsrf()) {
if (isset($_REQUEST["allowUserSub"]) && $_REQUEST["allowUserSub"] == 'on') {
$_REQUEST["allowUserSub"] = 'y';
} else {
......@@ -204,7 +202,6 @@ $smarty->assign('articleTypes', $articleTypes);
$smarty->assign_by_ref('cant_pages', $channels["cant"]);
$smarty->assign_by_ref('channels', $channels["data"]);
include_once('tiki-section_options.php');
ask_ticket('admin-nl');
// disallow robots to index page:
$smarty->assign('metatag_robots', 'NOINDEX, NOFOLLOW');
// Display the template
......
......@@ -68,11 +68,10 @@ if ($user) {
}
$smarty->assign('email', $user_email);
if ($tiki_p_subscribe_newsletters == 'y') {
if (isset($_REQUEST["subscribe"])) {
if (isset($_REQUEST["subscribe"]) && $access->checkCsrf() ) {
if (empty($user) && $prefs['feature_antibot'] == 'y' && ! $captchalib->validate()) {
Feedback::errorPage(['mes' => $captchalib->getErrors(), 'errortype' => 'no_redirect_login']);
}
check_ticket('newsletters');
if ($tiki_p_subscribe_email != 'y') {
$_REQUEST["email"] = $userlib->get_user_email($user);
}
......@@ -137,7 +136,6 @@ $smarty->assign_by_ref('sort_mode', $sort_mode);
$channels = $nllib->list_newsletters($offset, $maxRecords, $sort_mode, $find, '', ["tiki_p_subscribe_newsletters", "tiki_p_admin_newsletters", "tiki_p_send_newsletters"]);
$smarty->assign_by_ref('cant', $channels['cant']);
$smarty->assign_by_ref('channels', $channels["data"]);
ask_ticket('newsletters');
$section = 'newsletters';
include_once('tiki-section_options.php');
// Display the template
......
......@@ -314,8 +314,8 @@ if (isset($_REQUEST["preview"])) {
TikiLib::lib('header')->add_cssfile($news_cssfile)->add_cssfile($news_cssfile_option);
}
$smarty->assign('presend', 'n');
//only brings up another page, so no anti-csrf required
if (isset($_REQUEST["save"])) {
check_ticket('send-newsletter');
// Now send the newsletter to all the email addresses and save it in sent_newsletters
$info['datatxt'] = $_REQUEST['datatxt'];
$smarty->assign('presend', 'y');
......@@ -406,8 +406,21 @@ if (! empty($_REQUEST['resendEditionId'])) {
} else {
$resend = 'n';
}
if (isset($_REQUEST["send"]) && ! empty($_REQUEST["sendingUniqId"]) || $resend == 'y') {
check_ticket('send-newsletter');
//store anti-csrf ticket in case of throttling so it can be reused for subsequent iterations since they are get requests
if (isset($_REQUEST["send"]) && $_REQUEST["nlId"] && $prefs['newsletter_throttle'] === 'y' && $_POST['ticket']) {
$throttleLimit = (int) $prefs['newsletter_batch_size'];
$subscribers = count($nllib->get_all_subscribers((int) $_REQUEST["nlId"], ""));
if ($subscribers > $throttleLimit) {
$_SESSION['tickets']['newsletter']['ticket'] = $_POST['ticket'];
$_SESSION['tickets']['newsletter']['iterations'] = ceil((int) $subscribers / (int) $throttleLimit);
$unsetTicket = false;
} else {
$unsetTicket = true;
}
}
if ((isset($_REQUEST["send"]) && ! empty($_REQUEST["sendingUniqId"]) || $resend == 'y')
&& $csrfCheck = $access->checkCsrf(null, $unsetTicket))
{
@set_time_limit(0);
if ($resend != 'y') {
......@@ -425,7 +438,7 @@ if (isset($_REQUEST["send"]) && ! empty($_REQUEST["sendingUniqId"]) || $resend =
}
$_REQUEST['begin'] = true;
$nllib->send($nl_info, $_REQUEST, true, $sent, $errors, $logFileName);
$nllib->send($nl_info, $_REQUEST, true, $sent, $errors, $logFileName, $csrfCheck);
// use lib function to close the frame with the completion info
$nllib->closesendframe($sent, $errors, $logFileName);
......@@ -439,19 +452,26 @@ if (isset($_REQUEST['resume'])) {
// tiki-send_newsletter.php URL in the .tpl
$edition_info = $nllib->get_edition($_REQUEST['resume']);
// if they are set the replyto and sendfrom parameter contents are added to edition_info
if (! empty($_REQUEST['replyto']) && $_REQUEST['replyto'] != "undefined") {
if (! empty($_REQUEST['replyto']) && $_REQUEST['replyto'] != "undefined") {
$edition_info['replyto'] = $_REQUEST['replyto'];
}
if (! empty($_REQUEST['sendfrom']) && $_REQUEST['sendfrom'] != "undefined") {
if (! empty($_REQUEST['sendfrom']) && $_REQUEST['sendfrom'] != "undefined") {
$edition_info['sendfrom'] = $_REQUEST['sendfrom'];
}
$nl_info = $nllib->get_newsletter($edition_info['nlId']);
$nllib->send($nl_info, $edition_info, true, $sent, $errors, $logFileName);
// can't check csrf ticket the usual way since throttle iterations are repeat get requests via javascript
// instead check origin, ticket and remaining iterations separately
$unsetTicket = ! empty($_SESSION['tickets']['newsletter']['iterations'])
&& $_SESSION['tickets']['newsletter']['iterations'] == 1;
$csrfCheck = ! empty($_SESSION['tickets']['newsletter']['iterations'])
&& $_SESSION['tickets']['newsletter']['iterations'] > 0
&& $access->checkOrigin()
&& $access->checkTicket(null, $unsetTicket, $_SESSION['tickets']['newsletter']['ticket']);
$nllib->send($nl_info, $edition_info, true, $sent, $errors, $logFileName, $csrfCheck);
// use lib function to close the frame with the completion info
$nllib->closesendframe($sent, $errors, $logFileName);
exit; // Stop here since we are in an iframe and don't want to use smarty display
exit;// Stop here since we are in an iframe and don't want to use smarty display
}
// Article Clipping
......@@ -475,7 +495,7 @@ if (isset($nl_info) && $nl_info["allowArticleClip"] == 'y' && empty($articleClip
}
$smarty->assign('articleClip', $articleClip);
if (isset($_REQUEST["save_only"])) {
if (isset($_REQUEST["save_only"]) && $access->checkCsrf()) {
if (! isset($txt) || empty($_REQUEST['datatxt'])) {
$txt = "";
}
......@@ -559,7 +579,6 @@ if (count($tpls) > 0) {
}
include_once('tiki-section_options.php');
ask_ticket('send-newsletter');
$wikilib = TikiLib::lib('wiki');
$plugins = $wikilib->list_plugins(true, 'editwiki');
$smarty->assign_by_ref('plugins', $plugins);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment