Skip to content
Permalink
Browse files

added email bounces processing, fixes #44

  • Loading branch information...
following5 committed Jun 3, 2013
1 parent 4aa530b commit a0172b1e10fbaa77b216c6634fa5ceda67c50039
@@ -173,4 +173,12 @@ function dbv_107() // sync of table definitions, developer and production syste
sql("ALTER TABLE `cache_reports` ADD INDEX `userid` (`userid`)");
}
function dbv_108() // automatic email-bounce processiong
{
if (!sql_field_exists('user','last_email_problem'))
sql("ALTER TABLE `user` ADD COLUMN `last_email_problem` datetime default NULL AFTER `email_problems`");
if (!sql_field_exists('user','mailing_problems'))
sql("ALTER TABLE `user` ADD COLUMN `mailing_problems` int(10) unsigned NOT NULL default '0' AFTER `last_email_problem`");
}
?>
@@ -111,6 +111,14 @@
// date format
$opt['db']['dateformat'] = 'Y-m-d H:i:s';
// email delivery processing from syslog-ng eventlog DB
$opt['system']['maillog']['syslog_db_host'] = '';
$opt['system']['maillog']['syslog_db_name'] = '';
$opt['system']['maillog']['syslog_db_user'] = '';
$opt['system']['maillog']['syslog_db_password'] = '';
$opt['system']['maillog']['syslog_oc_host'] = ''; // 'host_name' column in syslog DB
$opt['system']['maillog']['syslog_mta'] = 'postfix/smtp'; // 'program' column in syslog DB
/* cookie or session
*
* SAVE_COOKIE = only use cookies
@@ -2488,6 +2488,7 @@ INSERT INTO `sys_trans` (`id`, `text`, `last_modified`) VALUES ('2066', 'Open ch
INSERT INTO `sys_trans` (`id`, `text`, `last_modified`) VALUES ('2067', 'Status', '2013-05-28 16:51:40');
INSERT INTO `sys_trans` (`id`, `text`, `last_modified`) VALUES ('2068', 'since June 2013', '2013-05-28 16:51:40');
INSERT INTO `sys_trans` (`id`, `text`, `last_modified`) VALUES ('2069', 'since February 2012', '2013-05-28 16:51:40');
INSERT INTO `sys_trans` (`id`, `text`, `last_modified`) VALUES ('2070', 'One ore more emails to this user could not be delivered. It might be a good idea to additionally log comments on the user\'s geocaches, and/or trying to contact him/her through other channels like a message board account or another geocaching platform.', '2013-05-28 16:51:40');

-- Table sys_trans_ref
SET NAMES 'utf8';
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2067', 'DE', 'Status', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2068', 'DE', 'seit Juni 2013', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2069', 'DE', 'seit Februar 2012', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2070', 'DE', 'Eine oder mehrere Emails an diesen Benutzer konnten nicht zugestellt werden. Es könnte ratsam sein, Hinweise zu seinen Caches zusätzlich zu loggen, und ihn ggf. über weitere Adressen wie z.B. Forenaccounts oder andere Geocaching-Plattformen zu kontaktieren.', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('1', 'EN', 'Reorder IDs \r', '2010-09-02 00:15:30');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2', 'EN', 'The database could not be reconnected.', '2010-08-28 11:48:07');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('3', 'EN', 'Testing – please do not login', '2010-08-28 11:48:07');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2067', 'EN', 'Status', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2068', 'EN', 'since June 2013', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2069', 'EN', 'since February 2012', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2070', 'EN', 'One ore more emails to this user could not be delivered. It might be a good idea to additionally log comments on the user\'s geocaches, and/or trying to contact him/her through other channels like a message board account or another geocaching platform.', '2013-04-25 23:00:00');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('1', 'ES', 'Reordenar ID', '2010-12-09 00:17:55');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('2', 'ES', 'La base de datos no se pudo conectar.', '2010-12-09 00:17:55');
INSERT INTO `sys_trans_text` (`trans_id`, `lang`, `text`, `last_modified`) VALUES ('3', 'ES', 'En pruebas - por favor, no entre.', '2010-12-09 00:17:55');
@@ -522,7 +522,13 @@
INSERT IGNORE INTO `notify_waiting` (`id`, `cache_id`, `user_id`, `type`)
SELECT NULL, nCacheId, `user`.`user_id`, 1 /* notify_new_cache */
FROM `user`
WHERE NOT ISNULL(`user`.`latitude`)
/* After reaching the 5-bounces limit, we try to send new cache notifications
in larger intervals for some more time, and at least on per year.
See also runwatch.php. */
WHERE (`email_problems` < 5
OR (`email_problems` < 10 AND NOW() > IFNULL(`last_email_problem`,'2013-03-01') + INTERVAL 30 DAY)
OR NOW() > IFNULL(`last_email_problem`,'2013-03-01') + INTERVAL 365 DAY)
AND NOT ISNULL(`user`.`latitude`)
AND NOT ISNULL(`user`.`longitude`)
AND `user`.`notify_radius`>0
AND (acos(cos((90-nLatitude) * 3.14159 / 180) * cos((90-`user`.`latitude`) * 3.14159 / 180) + sin((90-nLatitude) * 3.14159 / 180) * sin((90-`user`.`latitude`) * 3.14159 / 180) * cos((nLongitude-`user`.`longitude`) * 3.14159 / 180)) * 6370) <= `user`.`notify_radius`;
@@ -11,6 +11,8 @@ CREATE TABLE `user` (
`password` varchar(32) default NULL,
`email` varchar(60) default NULL,
`email_problems` int(10) NOT NULL default '0',
`last_email_problem` datetime default NULL,
`mailing_problems` int(10) unsigned NOT NULL default '0',
`latitude` double NOT NULL,
`longitude` double NOT NULL,
`is_active_flag` tinyint(1) NOT NULL,
@@ -51,6 +51,8 @@ function __construct($nNewUserId=ID_NEW)
$this->reUser->addString('password', null, true);
$this->reUser->addString('email', null, true);
$this->reUser->addString('email_problems', 0, false);
$this->reUser->addDate('last_email_problem', null, true);
$this->reUser->addInt('mailing_problems', 0, false);
$this->reUser->addFloat('latitude', 0, false);
$this->reUser->addFloat('longitude', 0, false);
$this->reUser->addDate('last_modified', time(), true, RE_INSERT_IGNORE);
@@ -1173,16 +1175,20 @@ function delete()
// email bounce processing
function addEmailProblem($licensemail=false)
{
// mailing_problems is a bit-flag field to remember nondelivered, important mailings
if ($licensemail)
return $this->reUser->setValue('email_problems', 1000001) && $this->save();
else
return $this->reUser->setValue('email_problems', $this->getEmailProblems() + 1) && $this->save();
if (!$this->reUser->setValue('mailing_problems', $this->reUser->getValue('mailing_problems') | 1))
return false;
return $this->reUser->setValue('email_problems', $this->getEmailProblems() + 1) &&
$this->reUser->setValue('last_email_problem', date('Y-m-d H:i:s')) &&
$this->save();
}
function getEmailProblems()
{
// see also common.inc.php "SELECT `email_problems`"
return $this->reUser->getValue('email_problems') % 1000000;
return $this->reUser->getValue('email_problems');
}
function getDataLicense()
@@ -1198,7 +1204,7 @@ function getLicenseDeclined()
function missedDataLicenseMail()
{
return $this->reUser->getValue('email_problems') > 1000000;
return $this->reUser->getValue('mailing_problems') & 1;
}
function confirmEmailAddress()
@@ -56,6 +56,7 @@
$tpl->assign('subject', $subject);
$tpl->assign('text', $text);
$tpl->assign('emailaddress', $bEmailaddress);
$tpl->assign('email_problems', $user->getEmailProblems());
$tpl->assign('userid', $user->getUserId());
$tpl->assign('username', $user->getUsername());
@@ -10,7 +10,7 @@
{if $success==true}
<div class="content2-pagetitle">
<img src="resource2/{$opt.template.style}/images/misc/22x22-email.png" style="align: left; margin-right: 10px;" width="32" height="32" alt="" />
<b>{t 1=$smarty.capture.userlink}E-Mail to %1 was sent{/t}</b>
{t 1=$smarty.capture.userlink}E-Mail to %1 was sent{/t}
</div>

<table class="table">
@@ -33,10 +33,18 @@
<input type="hidden" name="userid" value="{$userid}"/>
<div class="content2-pagetitle">
<img src="resource2/{$opt.template.style}/images/misc/22x22-email.png" style="align: left; margin-right: 10px;" width="32" height="32" alt="" />
<b>{t 1=$smarty.capture.userlink}Send E-Mail to %1{/t}</b>
{t 1=$smarty.capture.userlink}Send E-Mail to %1{/t}
</div>

<table class="table">
{if $email_problems > 0}
<tr>
<td colspan="2" class="errormsg">
<p>{t}One ore more emails to this user could not be delivered. It might be a good idea to additionally log comments on the user's geocaches, and/or trying to contact him/her through other channels like a message board account or another geocaching platform.{/t}</p>
</td>
</tr>
{/if}

<tr>
<td colspan="2">{t}Subject:{/t} <input type="text" name="subject" value="{$subject|escape}" class="input400"></td>
</tr>
@@ -60,6 +60,7 @@
// Benachrichtigung speichern
sql("INSERT IGNORE INTO `watches_notified` (`user_id`, `object_id`, `object_type`, `date_created`) VALUES ('&1', '&2', 1, NOW())", $rNewLog['user_id'], $rNewLog['log_id']);
// Owner notifications are always sent, independent of user.email_problems counter.
process_owner_log($rNewLog['user_id'], $rNewLog['log_id']);
}
mysql_free_result($rsNotified);
@@ -77,7 +78,11 @@
{
// Benachrichtigung speichern
sql("INSERT IGNORE INTO `watches_notified` (`user_id`, `object_id`, `object_type`, `date_created`) VALUES ('&1', '&2', 1, NOW())", $rcw['user_id'], $rcw['log_id']);
process_log_watch($rcw['user_id'], $rcw['log_id']);
// Watch notifications are discarded if the user had some undeliverable emails.
// See also stored procedure sp_notify_new_cache().
if (sqlValue("SELECT `email_problems` FROM `user` WHERE `user_id`='" . sql_escape($rcw['user_id']) . "'", 0) < 5)
process_log_watch($rcw['user_id'], $rcw['log_id']);
sql("DELETE FROM `watches_logqueue` WHERE `log_id`='&1' AND `user_id`='&2'", $rcw['log_id'], $rcw['user_id']);
}
@@ -0,0 +1,105 @@
<?php
/***************************************************************************
* For license information see doc/license.txt
*
* Unicode Reminder メモ
*
* Process system maillog to detect email delivery problems
***************************************************************************/
checkJob(new maillog());
class maillog
{
var $name = 'maillog';
var $interval = 600; // every 10 minutes
function run()
{
global $opt;
if ($opt['system']['maillog']['syslog_db_host'] != '')
if ($opt['system']['maillog']['syslog_mta'] != 'postfix/smtp')
{
echo $this->name.": unknown MTA '".$opt['system']['maillog']['syslog_mta']."'\n";
return;
}
else
$this->process_syslog();
}
function process_syslog()
{
global $opt;
$dbc = @mysql_connect($opt['system']['maillog']['syslog_db_host'],
$opt['system']['maillog']['syslog_db_user'],
$opt['system']['maillog']['syslog_db_password']);
if ($dbc === FALSE)
{
echo $this->name.": could not connect to syslog database\n";
return;
}
if (@mysql_query("USE ".$opt['system']['maillog']['syslog_db_name']) === FALSE)
{
echo $this->name.": could not open syslog database: ".mysql_error()."\n";
return;
}
$last_id = sql_value("SELECT `value` FROM `sysconfig` WHERE `name`='syslog_maillog_lastid'", 0);
$last_date = sql_value("SELECT `value` FROM `sysconfig` WHERE `name`='syslog_maillog_lastdate'", "");
// We check for both, new IDs and new creation dates, so that it still works
// if the syslog DB is re-setup and IDs restarted from 1 (dates are not unique).
$rs = @mysql_query(
"SELECT `id`, `message`, `created`
FROM `event`
WHERE (`id`>'" . mysql_real_escape_string($last_id) . "' OR `created`>'" . mysql_real_escape_string($last_date) . "')
AND `host_name`='" . mysql_real_escape_string($opt['system']['maillog']['syslog_oc_host']) . "'
AND `program`='" . mysql_real_escape_string($opt['system']['maillog']['syslog_mta']) . "'
ORDER BY `id`");
if ($rs === FALSE)
{
echo $this->name.": syslog query error (".mysql_errno()."): ".mysql_error()."\n";
return;
}
while ($logentry = mysql_fetch_assoc($rs))
{
$message = $logentry['message']; // latin-1 charset
$delivered = strpos($message, 'status=sent') > 0;
$bounced = strpos($message, 'status=bounced') > 0;
if ($delivered || $bounced)
{
if (preg_match('/ to=<(.+)>,/U',$message,$matches))
{
$emailadr = $matches[1];
if ($delivered)
sql("UPDATE `user` SET `email_problems`=0
WHERE `email`='" . mysql_real_escape_string($emailadr) . "'");
else if ($bounced)
// maximum one bounce per day is counted, to filter out temporary problems
sql("UPDATE `user` SET `email_problems`=`email_problems`+1, `last_email_problem`='" . mysql_real_escape_string($logentry['created']) . "'
WHERE `email`='" . mysql_real_escape_string($emailadr) . "'
AND IFNULL(`last_email_problem`,'') < '" . mysql_real_escape_string(substr($logentry['created'],0,10)) . "'");
}
else
echo $this->name.": no email address found for record ID ".$logentry['id']."\n";
}
$last_id = $logentry['id'];
$last_date = $logentry['created'];
}
mysql_free_result($rs);
sql("INSERT INTO `sysconfig` (`name`, `value`) VALUES ('syslog_maillog_lastid','&1')
ON DUPLICATE KEY UPDATE `value`='&1'",
$last_id);
sql("INSERT INTO `sysconfig` (`name`, `value`) VALUES ('syslog_maillog_lastdate','&1')
ON DUPLICATE KEY UPDATE `value`='&1'",
$last_date);
}
}
?>

0 comments on commit a0172b1

Please sign in to comment.
You can’t perform that action at this time.