Skip to content

Commit

Permalink
Add resource limit configuration for digest spam/innocent reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
slusarz committed May 8, 2015
1 parent 9174030 commit 6724968
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 50 deletions.
18 changes: 18 additions & 0 deletions imp/config/backends.php
Expand Up @@ -195,6 +195,24 @@
* messages reported by the user in their marking action and
* sends to the reporting e-mail address in a single
* multipart/digest message.
*
* If this option is selected, two additional, optional
* config options are available:
*
* - digest_limit_msgs: (integer) The maximum number of
* messages that will be sent in a
* multpart/digest message. If the
* number of messages to report exceeds
* this value, multiple digest messages
* will be sent. If 0 (the default),
* there is no limit.
* - digest_limit_size: (integer) The maximum size (in
* bytes) of a single digest message.
* If the size of messages to report
* exceeds this value, multiple digest
* messages will be sent. The default
* is 10 MB. If 0, there is no limit.
*
* - redirect: Redirects the message to the reporting e-mail address
* Note that this alters the original message's headers
* and may break embedded spam reporting signature
Expand Down
3 changes: 3 additions & 0 deletions imp/docs/UPGRADING
Expand Up @@ -105,6 +105,9 @@ Server Options (backends.php)

The 'atc_structure' option has been added.

The 'digest_limit_msgs' and 'digest_limit_size' options have been added for
use with the 'digest' spam/innocent reporting format.


Permissions
-----------
Expand Down
6 changes: 5 additions & 1 deletion imp/lib/Factory/Spam.php
Expand Up @@ -63,7 +63,11 @@ public function create($action)
if (!empty($config['email'])) {
$drivers[] = new IMP_Spam_Email(
$this->_expand($config['email']),
empty($config['email_format']) ? 'digest' : $config['email_format']
$config['email_format'],
array(
'digest_limit_msgs' => $config['digest_limit_msgs'],
'digest_limit_size' => $config['digest_limit_size']
)
);
}

Expand Down
16 changes: 10 additions & 6 deletions imp/lib/Imap/Config.php
Expand Up @@ -288,9 +288,11 @@ public function __get($name)

case 'innocent_params':
$p = $this->spam;
$out = isset($p['innocent'])
? $p['innocent']
: array();
$out = array_merge(array(
'digest_limit_msgs' => 0,
'digest_limit_size' => 10485760, // Default is 10 MB
'email_format' => 'digest'
), isset($p['innocent']) ? $p['innocent'] : array());
break;

case 'maildomain':
Expand Down Expand Up @@ -331,9 +333,11 @@ public function __get($name)

case 'spam_params':
$p = $this->spam;
$out = isset($p['spam'])
? $p['spam']
: array();
$out = array_merge(array(
'digest_limit_msgs' => 0,
'digest_limit_size' => 10485760, // Default is 10 MB
'email_format' => 'digest'
), isset($p['spam']) ? $p['spam'] : array());
break;

case 'thread':
Expand Down
155 changes: 112 additions & 43 deletions imp/lib/Spam/Email.php
Expand Up @@ -36,23 +36,35 @@ class IMP_Spam_Email implements IMP_Spam_Base
*/
protected $_format;

/**
* Additional options.
*
* @var array
*/
protected $_opts;

/**
* Constructor.
*
* @param string $email Reporting e-mail.
* @param string $format E-mail format.
* @param array $opts Additional options:
* - digest_limit_msgs: (integer) Maximum number of messages allowed in
* a digest.
* - digest_limit_size: (integer) Maximum size of a digest.
*/
public function __construct($email, $format)
public function __construct($email, $format, array $opts = array())
{
$this->_email = $email;
$this->_format = $format;
$this->_opts = $opts;
}

/**
*/
public function report(array $msgs, $action)
{
global $injector, $registry;
global $injector;

$ret = 0;

Expand Down Expand Up @@ -80,53 +92,110 @@ public function report(array $msgs, $action)
} catch (Horde_Exception $e) {
$from_line = null;
}
$self = $this;

/* Build the MIME structure. */
$mime = new Horde_Mime_Part();
$mime->setType('multipart/digest');
$reportDigest = function($m) use($action, $from_line, $self) {
global $registry;

foreach ($msgs as $val) {
$rfc822 = new Horde_Mime_Part();
$rfc822->setType('message/rfc822');
$rfc822->setContents($val->fullMessageText(array(
'stream' => true
)));
$mime[] = $rfc822;
}
if (empty($m)) {
return 0;
}

$spam_headers = new Horde_Mime_Headers();
$spam_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create());
$spam_headers->addHeaderOb(Horde_Mime_Headers_Date::create());
$spam_headers->addHeader('To', $this->_email);
if (!is_null($from_line)) {
$spam_headers->addHeader('From', $from_line);
}
$spam_headers->addHeader(
'Subject',
sprintf(
_("%s report from %s"),
($action === IMP_Spam::SPAM) ? 'spam' : 'innocent',
$registry->getAuth()
)
);
/* Build the MIME structure. */
$mime = new Horde_Mime_Part();
$mime->setType('multipart/digest');

/* Send the message. */
try {
$imp_compose = $injector->getInstance('IMP_Factory_Compose')
->create();
$recip_list = $imp_compose->recipientList(array(
'to' => $this->_email
));
$imp_compose->sendMessage(
$recip_list['list'],
$spam_headers,
$mime,
'UTF-8'
foreach ($m as $val) {
$rfc822 = new Horde_Mime_Part();
$rfc822->setType('message/rfc822');
$rfc822->setContents($val->fullMessageText(array(
'stream' => true
)));
$mime[] = $rfc822;
}

$spam_headers = new Horde_Mime_Headers();
$spam_headers->addHeaderOb(
Horde_Mime_Headers_MessageId::create()
);
$spam_headers->addHeaderOb(
Horde_Mime_Headers_Date::create()
);
$ret = count($msgs);
} catch (IMP_Compose_Exception $e) {
$e->log();
$spam_headers->addHeader('To', $this->_email);
if (!is_null($from_line)) {
$spam_headers->addHeader('From', $from_line);
}
$spam_headers->addHeader(
'Subject',
sprintf(
_("%s report from %s"),
($action === IMP_Spam::SPAM) ? 'spam' : 'innocent',
$registry->getAuth()
)
);

/* Send the message. */
try {
$imp_compose = $injector->getInstance('IMP_Factory_Compose')
->create();
$recip_list = $imp_compose->recipientList(array(
'to' => $this->_email
));
$imp_compose->sendMessage(
$recip_list['list'],
$spam_headers,
$mime,
'UTF-8'
);
return count($m);
} catch (IMP_Compose_Exception $e) {
$e->log();
return 0;
}
};

$mlimit = $orig_mlimit = empty($this->_opts['digest_limit_msgs'])
? null
: $this->_opts['digest_limit_msgs'];
$slimit = $orig_slimit = empty($this->_opts['digest_limit_size'])
? null
: $this->_opts['digest_limit_size'];

$todo = array();

foreach ($msgs as $val) {
$process = false;
$todo[] = $val;

if (!is_null($mlimit) && !(--$mlimit)) {
$process = true;
}

if (!is_null($slimit) && (($slimit -= $val->getBytes()) < 0)) {
$process = true;
/* If we have exceeded size limits with this single
* message, it exceeds the maximum limit and we can't
* send it at all. Don't confuse the user and instead
* report is as a "success" for UI purposes. */
if (count($todo) === 1) {
++$ret;
$todo = array();
Horde::log(
'Could not send spam/innocent reporting message because original message was too large.',
'NOTICE'
);
}
}

if ($process) {
$ret += $reportDigest($todo);
$todo = array();
$msgs = $orig_msgs;
$size = $orig_size;
}
}

$ret += $reportDigest($todo);
break;
}

Expand Down

0 comments on commit 6724968

Please sign in to comment.