Skip to content

Commit a84c8a3

Browse files
committed
introduce new configuration flag throttle.blocked to completely block a user without changing his limits and without reporting to admin
getting ready for 1.0.5 release
1 parent 439b9db commit a84c8a3

4 files changed

Lines changed: 42 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# CHANGELOG
22

3-
## 1.0.5 (unreleased)
3+
## 1.0.5 (2019-10-29)
44

55
- Fix for newline after `whoami` shell output, which broke message logging to syslog.
6+
- Introduce new configuration flag `throttle.blocked` to completely block a user without changing his limits and without reporting to admin.
67

78
## 1.0.4 (2019-10-18)
89

app/SendmailThrottle.php

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
*/
1111
class SendmailThrottle extends StdinMailParser
1212
{
13+
const STATUS_OK = 0;
14+
const STATUS_QUOTA_REACHED = 1;
15+
const STATUS_OVERQUOTA = 2;
16+
const STATUS_BLOCKED = 3;
17+
const STATUS_EXCEPTION = 4;
18+
1319
/**
1420
* @var StdClass
1521
*/
@@ -60,7 +66,7 @@ protected function _connect()
6066
*
6167
* status code 0: limit not reached, ok
6268
* status code 1: limit reached, sending notification to admin
63-
* status code 2: limit succeeded, do not warn admin multiple times
69+
* status code 2: over limit, do not warn admin multiple times
6470
*
6571
* @param string $username
6672
* @param int $rcptCount number of recipients
@@ -74,41 +80,35 @@ public function run($username, $rcptCount)
7480
$this->_connect();
7581

7682
// default status code: success
77-
$status = 0;
83+
$status = self::STATUS_OK;
7884

7985
$sql = 'SELECT * FROM throttle WHERE username = :username';
8086
$stmt = $this->_pdo->prepare($sql);
8187
$stmt->bindParam(':username', $username);
8288
$stmt->execute();
83-
$obj = $stmt->fetchObject();
84-
if ($obj) {
89+
$throttle = $stmt->fetchObject();
90+
if ($throttle) {
8591
// reset counters on new day (after midnight)
86-
$dateUpdated = new DateTime($obj->updated_ts);
92+
$dateUpdated = new DateTime($throttle->updated_ts);
8793
$dateCurrent = new DateTime();
8894
$sameDay = ($dateUpdated->format('Y-m-d') == $dateCurrent->format('Y-m-d'));
8995
if (!$sameDay) {
9096
$countCur = 1;
9197
$rcptCur = 1;
9298
} else {
93-
$countCur = ++$obj->count_cur; // raise by 1
94-
$rcptCur = $obj->rcpt_cur + $rcptCount; // raise by number of recipients
99+
$countCur = ++$throttle->count_cur; // raise by 1
100+
$rcptCur = $throttle->rcpt_cur + $rcptCount; // raise by number of recipients
95101
}
96102

97-
$countMax = $obj->count_max;
98-
$countTot = ++$obj->count_tot; // raise by 1
99-
$rcptMax = $obj->rcpt_max;
100-
$rcptTot = $obj->rcpt_tot + $rcptCount; // raise by number of recipients
101-
102-
// check email count
103-
if ($countCur > $countMax) {
104-
// return 1 if previous status was 0 (ok), otherwise 2
105-
$status = ($obj->status == 0) ? 1 : 2;
106-
}
103+
$countMax = $throttle->count_max;
104+
$countTot = ++$throttle->count_tot; // raise by 1
105+
$rcptMax = $throttle->rcpt_max;
106+
$rcptTot = $throttle->rcpt_tot + $rcptCount; // raise by number of recipients
107107

108-
// check recipient count
109-
if ($rcptCur > $obj->rcpt_max) {
108+
// check email or recipient count
109+
if ($countCur > $countMax || $rcptCur > $rcptMax) {
110110
// return 1 if previous status was 0 (ok), otherwise 2
111-
$status = ($obj->status == 0) ? 1 : 2;
111+
$status = ($throttle->status == self::STATUS_OK) ? self::STATUS_QUOTA_REACHED : self::STATUS_OVERQUOTA;
112112
}
113113

114114
$sql = 'UPDATE throttle SET updated_ts = NOW(), count_cur = :countCur, count_tot = :countTot,
@@ -122,7 +122,12 @@ public function run($username, $rcptCount)
122122
$stmt->bindParam(':status' , $status , PDO::PARAM_INT);
123123
$stmt->bindParam(':username', $username);
124124
$stmt->execute();
125-
$id = $obj->id;
125+
$id = $throttle->id;
126+
127+
// if user is blocked, override previous status by return code 3
128+
if ($throttle->blocked) {
129+
$status = self::STATUS_BLOCKED;
130+
}
126131
} else {
127132
$countMax = $this->_conf->throttle->countMax;
128133
$countCur = 1;
@@ -161,12 +166,16 @@ public function run($username, $rcptCount)
161166
$rcptCur,
162167
$rcptTot
163168
);
164-
syslog(LOG_INFO, $syslogMsg);
169+
// Don't write to syslog for blocked useraccounts - we still have all meta information in messages
170+
// table but don't want to fill up syslog.
171+
if ($status != self::STATUS_BLOCKED) {
172+
syslog(LOG_INFO, $syslogMsg);
173+
}
165174

166-
// Report message limit succeeded to administrator
167-
if ($status == 1) {
168-
// Do not report on status code 2, as the admin only wants to get
169-
// notified once!
175+
// Report message limit reached to administrator
176+
if ($status == self::STATUS_QUOTA_REACHED) {
177+
// Do not report on status code 2, as the admin only wants to get notified once!
178+
// Also, he is never interested in blocked accounts (status code 3).
170179
mail(
171180
$this->_conf->global->adminTo,
172181
$this->_conf->throttle->adminSubject,
@@ -175,15 +184,13 @@ public function run($username, $rcptCount)
175184
);
176185
}
177186

178-
// write to db log
187+
// write all meta information to db messages log
179188
$this->_logMessage($id, $username, $rcptCount, $status);
180189

181-
// return status code
182190
return $status;
183-
184191
} catch (PDOException $e) {
185192
syslog(LOG_WARNING, sprintf('%s: PDOException: %s', $this->_conf->throttle->syslogPrefix, $e->getMessage()));
186-
return 3;
193+
return self::STATUS_EXCEPTION;
187194
}
188195
}
189196

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- 2019-10-29
2+
ALTER TABLE `sendmailwrapper`.`throttle` ADD `blocked` TINYINT NOT NULL DEFAULT 0 AFTER `status`;

schema/schema.mysql.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS `sendmailwrapper`.`throttle` (
2222
`rcpt_cur` INT NOT NULL DEFAULT 1 COMMENT 'recipient count of current time period',
2323
`rcpt_tot` INT NOT NULL DEFAULT 1 COMMENT 'total recipient count',
2424
`status` TINYINT NOT NULL DEFAULT 0,
25+
`blocked` TINYINT NOT NULL DEFAULT 0,
2526
PRIMARY KEY (`id`))
2627
ENGINE = InnoDB;
2728

0 commit comments

Comments
 (0)