PageRenderTime 50ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/public_html/lists/admin/processbounces.php

https://github.com/samtuke/phplist
PHP | 708 lines | 605 code | 37 blank | 66 comment | 84 complexity | bc036728f580c6134b94b44c08dc94c0 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. require_once dirname(__FILE__).'/accesscheck.php';
  3. if (!$GLOBALS["commandline"]) {
  4. ob_end_flush();
  5. if (!MANUALLY_PROCESS_BOUNCES) {
  6. print $GLOBALS['I18N']->get("This page can only be called from the commandline");
  7. return;
  8. }
  9. } else {
  10. ob_end_clean();
  11. print ClineSignature();
  12. ob_start();
  13. }
  14. print '<script language="Javascript" src="js/progressbar.js" type="text/javascript"></script>';
  15. flush();
  16. $outputdone =0;
  17. function prepareOutput() {
  18. global $outputdone;
  19. if (!$outputdone) {
  20. $outputdone = 1;
  21. return formStart('name="outputform" class="processbounces" ').'<textarea name="output" rows=10 cols=50></textarea></form>';
  22. }
  23. }
  24. $report = "";
  25. ## some general functions
  26. function finish ($flag,$message) {
  27. if ($flag == "error") {
  28. $subject = $GLOBALS['I18N']->get("Bounce processing error");
  29. } elseif ($flag == "info") {
  30. $subject = $GLOBALS['I18N']->get("Bounce Processing info");
  31. }
  32. if (!TEST && $message) {
  33. sendReport($subject,$message);
  34. }
  35. }
  36. function ProcessError ($message) {
  37. output( "$message");
  38. finish('error',$message);
  39. exit;
  40. }
  41. function processbounces_shutdown() {
  42. global $report,$process_id;
  43. releaseLock($process_id);
  44. # $report .= "Connection status:".connection_status();
  45. finish('info',$report);
  46. }
  47. function output ($message,$reset = 0) {
  48. $infostring = "[". date("D j M Y H:i",time()) . "] [" . getenv("REMOTE_HOST") ."] [" . getenv("REMOTE_ADDR") ."]";
  49. #print "$infostring $message<br/>\n";
  50. $message = preg_replace("/\n/",'',$message);
  51. ## contribution from http://forums.phplist.com/viewtopic.php?p=14648
  52. ## in languages with accented characters replace the HTML back
  53. //Replace the "&rsquo;" which is not replaced by html_decode
  54. $message = preg_replace("/&rsquo;/","'",$message);
  55. //Decode HTML chars
  56. #$message = html_entity_decode($message,ENT_QUOTES,$_SESSION['adminlanguage']['charset']);
  57. $message = html_entity_decode($message,ENT_QUOTES,'UTF-8');
  58. if ($GLOBALS["commandline"]) {
  59. cl_output($message);
  60. } else {
  61. if ($reset)
  62. print '<script language="Javascript" type="text/javascript">
  63. // if (document.forms[0].name == "outputform") {
  64. document.outputform.output.value = "";
  65. document.outputform.output.value += "\n";
  66. // }
  67. </script>'."\n";
  68. print '<script language="Javascript" type="text/javascript">
  69. // if (document.forms[0].name == "outputform") {
  70. document.outputform.output.value += "'.$message.'";
  71. document.outputform.output.value += "\n";
  72. // } else
  73. // document.writeln("'.$message.'");
  74. </script>'."\n";
  75. }
  76. flush();
  77. }
  78. function findMessageId($text) {
  79. $msgid = 0;
  80. if (preg_match ('/(?:X-MessageId|X-Message): (.*)\r\n/iU', $text, $match)) {
  81. $msgid = trim($match[1]);
  82. }
  83. return $msgid;
  84. }
  85. function findUserID($text) {
  86. global $tables;
  87. $userid = 0;
  88. $user = '';
  89. if (preg_match ('/(?:X-ListMember|X-User): (.*)\r\n/iU', $text, $match)) {
  90. $user = trim($match[1]);
  91. }
  92. # some versions used the email to identify the users, some the userid and others the uniqid
  93. # use backward compatible way to find user
  94. if (strpos($user,'@') !== false) {
  95. $userid_req = Sql_Fetch_Row_Query(sprintf('select id from %s where email = "%s"',$tables["user"],sql_escape($user)));
  96. $userid = $userid_req[0];
  97. } elseif (preg_match("/^\d$/",$user)) {
  98. $userid = $user;
  99. } elseif (!empty($user)) {
  100. $userid_req = Sql_Fetch_Row_Query(sprintf('select id from %s where uniqid = "%s"',$tables["user"],sql_escape($user)));
  101. $userid = $userid_req[0];
  102. }
  103. ### if we didn't find any, parse anything looking like an email address and check if it's a subscriber.
  104. ## this is probably fairly time consuming, but as the process is only done once every so often
  105. ## that should not be too bad
  106. if (!$userid) {
  107. preg_match_all('/[\S]+@[\S\.]+/',$text,$regs);
  108. foreach ($regs[0] as $match) {
  109. $email = cleanEmail($match);
  110. $useridQ = Sql_Fetch_Row_Query(sprintf('select id from %s where email = "%s"',$tables['user'],sql_escape($email)));
  111. if (!empty($useridQ[0])) {
  112. $userid = $useridQ[0];
  113. break;
  114. }
  115. }
  116. }
  117. return $userid;
  118. }
  119. function decodeBody($header,$body) {
  120. $transfer_encoding = '';
  121. if (preg_match('/Content-Transfer-Encoding: ([\w-]+)/i',$header,$regs)) {
  122. $transfer_encoding = strtolower($regs[1]);
  123. }
  124. switch ($transfer_encoding) {
  125. case 'quoted-printable':
  126. $decoded_body = @imap_qprint($body);break;
  127. case 'base64':
  128. $decoded_body = @imap_base64($body);break;
  129. case '7bit':
  130. case '8bit':
  131. default:
  132. # $body = $body;
  133. }
  134. if (!empty($decoded_body)) {
  135. return $decoded_body;
  136. } else {
  137. return $body;
  138. }
  139. }
  140. function processImapBounce ($link,$num,$header) {
  141. global $tables;
  142. $headerinfo = imap_headerinfo($link,$num);
  143. $bounceDate = @strtotime($headerinfo->date);
  144. $body = imap_body ($link,$num);
  145. $body = decodeBody($header,$body);
  146. $msgid = findMessageId($body);
  147. $userid = findUserID($body);
  148. if (VERBOSE) {
  149. output("UID".$userid." MSGID".$msgid);
  150. }
  151. ## @TODO add call to plugins to determine what to do.
  152. # for now, quick hack to zap MsExchange Delayed messages
  153. if (preg_match('/Action: delayed\s+Status: 4\.4\.7/im',$body)) {
  154. ## just say we did something, when actually we didn't
  155. return true;
  156. }
  157. Sql_Query(sprintf('insert into %s (date,header,data)
  158. values("%s","%s","%s")',
  159. $tables["bounce"],
  160. date("Y-m-d H:i",$bounceDate),
  161. addslashes($header),
  162. addslashes($body)));
  163. $bounceid = Sql_Insert_Id($tables['bounce'], 'id');
  164. return processBounceData($bounceid,$msgid,$userid);
  165. }
  166. function processBounceData($bounceid,$msgid,$userid) {
  167. global $tables;
  168. $useremailQ = Sql_fetch_row_query(sprintf('select email from %s where id = %d',$tables['user'],$userid));
  169. $useremail = $useremailQ[0];
  170. if ($msgid === "systemmessage" && !empty($userid)) {
  171. Sql_Query(sprintf('update %s
  172. set status = "bounced system message",
  173. comment = "%s marked unconfirmed"
  174. where id = %d',
  175. $tables["bounce"],
  176. $userid,$bounceid));
  177. logEvent("$userid ".$GLOBALS['I18N']->get("system message bounced, user marked unconfirmed"));
  178. addUserHistory($useremail,$GLOBALS['I18N']->get("Bounced system message"),"
  179. <br/>".$GLOBALS['I18N']->get("User marked unconfirmed")."
  180. <br/><a href=\"./?page=bounce&amp;id=$bounceid\">".$GLOBALS['I18N']->get("View Bounce")."</a>
  181. ");
  182. Sql_Query(sprintf('update %s
  183. set confirmed = 0
  184. where id = %d',
  185. $tables["user"],
  186. $userid));
  187. } elseif (!empty($msgid) && !empty($userid)) {
  188. ## check if we already have this um as a bounce
  189. ## so that we don't double count "delayed" like bounces
  190. $exists = Sql_Fetch_Row_Query(sprintf('select count(*) from %s where user = %d and message = %d',
  191. $tables["user_message_bounce"],$userid,$msgid));
  192. if (empty($exists[0])) {
  193. Sql_Query(sprintf('insert into %s
  194. set user = %d, message = %d, bounce = %d',
  195. $tables["user_message_bounce"],
  196. $userid,$msgid,$bounceid));
  197. Sql_Query(sprintf('update %s
  198. set status = "bounced list message %d",
  199. comment = "%s bouncecount increased"
  200. where id = %d',
  201. $tables["bounce"],
  202. $msgid,
  203. $userid,$bounceid));
  204. Sql_Query(sprintf('update %s
  205. set bouncecount = bouncecount + 1
  206. where id = %d',
  207. $tables["message"],
  208. $msgid));
  209. Sql_Query(sprintf('update %s
  210. set bouncecount = bouncecount + 1
  211. where id = %d',
  212. $tables["user"],
  213. $userid));
  214. } else {
  215. ## we create the relationship, but don't increase counters
  216. Sql_Query(sprintf('insert into %s
  217. set user = %d, message = %d, bounce = %d',
  218. $tables["user_message_bounce"],
  219. $userid,$msgid,$bounceid));
  220. Sql_Query(sprintf('update %s
  221. set status = "bounced list message %d",
  222. comment = "duplicate bounce for %d"
  223. where id = %d',
  224. $tables["bounce"],
  225. $msgid,
  226. $userid,$bounceid));
  227. }
  228. } elseif ($userid) {
  229. Sql_Query(sprintf('update %s
  230. set status = "bounced unidentified message",
  231. comment = "%s bouncecount increased"
  232. where id = %d',
  233. $tables["bounce"],
  234. $userid,$bounceid));
  235. Sql_Query(sprintf('update %s
  236. set bouncecount = bouncecount + 1
  237. where id = %d',
  238. $tables["user"],
  239. $userid));
  240. } elseif ($msgid === 'systemmessage') {
  241. Sql_Query(sprintf('update %s
  242. set status = "bounced system message",
  243. comment = "unknown user"
  244. where id = %d',
  245. $tables["bounce"],
  246. $bounceid));
  247. logEvent("$userid ".$GLOBALS['I18N']->get("system message bounced, but unknown user"));
  248. } elseif ($msgid) {
  249. Sql_Query(sprintf('update %s
  250. set status = "bounced list message %d",
  251. comment = "unknown user"
  252. where id = %d',
  253. $tables["bounce"],
  254. $msgid,
  255. $bounceid));
  256. Sql_Query(sprintf('update %s
  257. set bouncecount = bouncecount + 1
  258. where id = %d',
  259. $tables["message"],
  260. $msgid));
  261. } else {
  262. Sql_Query(sprintf('update %s
  263. set status = "unidentified bounce",
  264. comment = "not processed"
  265. where id = %d',
  266. $tables["bounce"],
  267. $bounceid));
  268. return false;
  269. }
  270. return true;
  271. }
  272. function processPop ($server,$user,$password) {
  273. $port = $GLOBALS["bounce_mailbox_port"];
  274. if (!$port) {
  275. $port = '110/pop3/notls';
  276. }
  277. set_time_limit(6000);
  278. if (!TEST) {
  279. $link=imap_open("{".$server.":".$port."}INBOX",$user,$password,CL_EXPUNGE);
  280. } else {
  281. $link=imap_open("{".$server.":".$port."}INBOX",$user,$password);
  282. }
  283. if (!$link) {
  284. output($GLOBALS['I18N']->get("Cannot create POP3 connection to")." $server: ".imap_last_error());
  285. return;
  286. }
  287. return processMessages($link,100000);
  288. }
  289. function processMbox ($file) {
  290. set_time_limit(6000);
  291. if (!TEST) {
  292. $link=imap_open($file,"","",CL_EXPUNGE);
  293. } else {
  294. $link=imap_open($file,"","");
  295. }
  296. if (!$link) {
  297. output($GLOBALS['I18N']->get("Cannot open mailbox file")." ".imap_last_error());
  298. return;
  299. }
  300. return processMessages($link,100000);
  301. }
  302. function processMessages($link,$max = 3000) {
  303. global $bounce_mailbox_purge_unprocessed,$bounce_mailbox_purge;
  304. $num = imap_num_msg($link);
  305. output( $num . " ".$GLOBALS['I18N']->get("bounces to fetch from the mailbox")."\n");
  306. output( $GLOBALS['I18N']->get("Please do not interrupt this process")."\n");
  307. $report = $num . " ".$GLOBALS['I18N']->get("bounces to process")."\n";
  308. if ($num > $max) {
  309. print $GLOBALS['I18N']->get("Processing first")." $max ".$GLOBALS['I18N']->get("bounces")."<br/>";
  310. $report .= $num . " ".$GLOBALS['I18N']->get("processing first")." $max ".$GLOBALS['I18N']->get("bounces")."\n";
  311. $num = $max;
  312. }
  313. if (TEST) {
  314. print $GLOBALS['I18N']->get("Running in test mode, not deleting messages from mailbox")."<br/>";
  315. } else {
  316. print $GLOBALS['I18N']->get("Processed messages will be deleted from mailbox")."<br/>";
  317. }
  318. $nberror = 0;
  319. # for ($x=1;$x<150;$x++) {
  320. for($x=1; $x <= $num; $x++) {
  321. set_time_limit(60);
  322. $header = imap_fetchheader($link,$x);
  323. if ($x % 25 == 0)
  324. # output( $x . " ". nl2br($header));
  325. output($x . " done",1);
  326. print "\n";
  327. flush();
  328. $processed = processImapBounce($link,$x,$header);
  329. if ($processed) {
  330. if (!TEST && $bounce_mailbox_purge) {
  331. if (VERBOSE) output( $GLOBALS['I18N']->get("Deleting message")." $x");
  332. imap_delete($link,$x);
  333. } elseif (VERBOSE) {
  334. output(s("Not deleting processed message")." $x $bounce_mailbox_purge");
  335. }
  336. } else {
  337. if (!TEST && $bounce_mailbox_purge_unprocessed) {
  338. if (VERBOSE) output( $GLOBALS['I18N']->get("Deleting message")." $x");
  339. imap_delete($link,$x);
  340. } elseif (VERBOSE) {
  341. output(s("Not deleting unprocessed message")." $x");
  342. }
  343. }
  344. flush();
  345. }
  346. flush();
  347. output($GLOBALS['I18N']->get("Closing mailbox, and purging messages"));
  348. set_time_limit(60 * $num);
  349. imap_close($link);
  350. # print '<script language="Javascript" type="text/javascript"> finish(); </script>';
  351. if ($num)
  352. return $report;
  353. }
  354. if (!function_exists('imap_open')) {
  355. Error($GLOBALS['I18N']->get('IMAP is not included in your PHP installation, cannot continue').
  356. '<br/>'.$GLOBALS['I18N']->get('Check out').
  357. ' <a href="http://www.php.net/manual/en/ref.imap.php">http://www.php.net/manual/en/ref.imap.php</a>');
  358. return;
  359. }
  360. if (empty($bounce_mailbox) && (empty($bounce_mailbox_host) || empty($bounce_mailbox_user) || empty($bounce_mailbox_password))) {
  361. Error($GLOBALS['I18N']->get("Bounce mechanism not properly configured"));
  362. return;
  363. }
  364. print '<script language="Javascript" type="text/javascript"> yposition = 10;document.write(progressmeter); start();</script>';
  365. print prepareOutput();
  366. flushBrowser();
  367. # lets not do this unless we do some locking first
  368. register_shutdown_function('processbounces_shutdown');
  369. $abort = ignore_user_abort(1);
  370. if (!empty($GLOBALS['commandline']) && isset($cline['f'])) {
  371. # force set, so kill other processes
  372. cl_output('Force set, killing other send processes');
  373. $process_id = getPageLock(1);
  374. } else {
  375. $process_id = getPageLock();
  376. }
  377. if (empty($process_id)) {
  378. return;
  379. }
  380. $download_report = '';
  381. switch ($bounce_protocol) {
  382. case "pop":
  383. $download_report = processPop ($bounce_mailbox_host,$bounce_mailbox_user,$bounce_mailbox_password);
  384. break;
  385. case "mbox":
  386. $download_report = processMbox($bounce_mailbox);
  387. break;
  388. default:
  389. Error($GLOBALS['I18N']->get("bounce_protocol not supported"));
  390. return;
  391. }
  392. # now we have filled database with all available bounces
  393. ## reprocess the unidentified ones, as the bounce detection has improved, so it might catch more
  394. cl_output('reprocessing');
  395. $reparsed = $count = 0;
  396. $reidentified = 0;
  397. $req = Sql_Query(sprintf('select * from %s where status = "unidentified bounce"',$tables['bounce']));
  398. $total = Sql_Affected_Rows();
  399. cl_output(s('%d bounces to reprocess',$total));
  400. while ($bounce = Sql_Fetch_Assoc($req)) {
  401. $count++;
  402. if ($count % 25 == 0) {
  403. cl_progress(s('%d out of %d processed',$count,$total));
  404. }
  405. $bounceBody = decodeBody($bounce['header'],$bounce['data']);
  406. $userid = findUserID($bounceBody);
  407. $messageid = findMessageId($bounceBody);
  408. if (!empty($userid) || !empty($messageid)) {
  409. $reparsed++;
  410. if (processBounceData($bounce['id'],$messageid,$userid)) {
  411. $reidentified++;
  412. }
  413. }
  414. }
  415. cl_output(s('%d out of %d processed',$count,$total));
  416. if (VERBOSE) {
  417. output(s('%d bounces were re-processed and %d bounces were re-identified',$reparsed,$reidentified));
  418. }
  419. $advanced_report = '';
  420. if (USE_ADVANCED_BOUNCEHANDLING) {
  421. output($GLOBALS['I18N']->get('Processing bounces based on active bounce rules'));
  422. $bouncerules = loadBounceRules();
  423. $matched = 0;
  424. $notmatched = 0;
  425. $limit = ' limit 10';
  426. $limit = ' limit 10000';
  427. $limit = '';
  428. # @@ not sure whether this one would make sense
  429. # $req = Sql_Query(sprintf('select * from %s as bounce, %s as umb,%s as user where bounce.id = umb.bounce
  430. # and user.id = umb.user and !user.confirmed and !user.blacklisted %s',
  431. # $GLOBALS['tables']['bounce'],$GLOBALS['tables']['user_message_bounce'],$GLOBALS['tables']['user'],$limit));
  432. $req = Sql_Query(sprintf('select * from %s as bounce, %s as umb where bounce.id = umb.bounce %s',
  433. $GLOBALS['tables']['bounce'],$GLOBALS['tables']['user_message_bounce'],$limit));
  434. while ($row = Sql_Fetch_Array($req)) {
  435. $alive = checkLock($process_id);
  436. if ($alive)
  437. keepLock($process_id);
  438. else
  439. ProcessError($GLOBALS['I18N']->get("Process Killed by other process"));
  440. # output('User '.$row['user']);
  441. $rule = matchBounceRules($row['data'],$bouncerules);
  442. # output('Action '.$rule['action']);
  443. # output('Rule'.$rule['id']);
  444. $userdata = array();
  445. if ($rule && is_array($rule)) {
  446. if ($row['user']) {
  447. $userdata = Sql_Fetch_Array_Query("select * from {$tables["user"]} where id = ".$row['user']);
  448. }
  449. $report_linkroot = $GLOBALS['admin_scheme'].'://'.$GLOBALS['website'].$GLOBALS['adminpages'];
  450. Sql_Query(sprintf('update %s set count = count + 1 where id = %d',
  451. $GLOBALS['tables']['bounceregex'],$rule['id']));
  452. Sql_Query(sprintf('insert ignore into %s (regex,bounce) values(%d,%d)',
  453. $GLOBALS['tables']['bounceregex_bounce'],$rule['id'],$row['bounce']));
  454. switch ($rule['action']) {
  455. case 'deleteuser':
  456. logEvent('User '.$userdata['email'].' deleted by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  457. $advanced_report .= 'User '.$userdata['email'].' deleted by bounce rule '.$rule['id']."\n";
  458. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  459. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  460. deleteUser($row['user']);
  461. break;
  462. case 'unconfirmuser':
  463. logEvent('User '.$userdata['email'].' unconfirmed by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  464. Sql_Query(sprintf('update %s set confirmed = 0 where id = %d',$GLOBALS['tables']['user'],$row['user']));
  465. $advanced_report .= 'User '.$userdata['email'].' made unconfirmed by bounce rule '.$rule['id']."\n";
  466. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  467. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  468. addUserHistory($userdata['email'],s('Auto Unconfirmed'),s('Subscriber auto unconfirmed for')." ".s('bounce rule').' '.$rule['id']);
  469. addSubscriberStatistics('auto unsubscribe',1);
  470. break;
  471. case 'deleteuserandbounce':
  472. logEvent('User '.$userdata['email'].' deleted by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  473. $advanced_report .= 'User '.$userdata['email'].' deleted by bounce rule '.$rule['id']."\n";
  474. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  475. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  476. deleteUser($row['user']);
  477. deleteBounce($row['bounce']);
  478. break;
  479. case 'unconfirmuseranddeletebounce':
  480. logEvent('User '.$userdata['email'].' unconfirmed by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  481. Sql_Query(sprintf('update %s set confirmed = 0 where id = %d',$GLOBALS['tables']['user'],$row['user']));
  482. $advanced_report .= 'User '.$userdata['email'].' made unconfirmed by bounce rule '.$rule['id']."\n";
  483. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  484. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  485. addUserHistory($userdata['email'],s('Auto unconfirmed'),s('Subscriber auto unconfirmed for')." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  486. addSubscriberStatistics('auto unsubscribe',1);
  487. deleteBounce($row['bounce']);
  488. break;
  489. case 'blacklistuser':
  490. logEvent('User '.$userdata['email'].' blacklisted by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  491. addUserToBlacklist($userdata['email'],$GLOBALS['I18N']->get("Auto Blacklisted"),$GLOBALS['I18N']->get("User auto blacklisted for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  492. $advanced_report .= 'User '.$userdata['email'].' blacklisted by bounce rule '.$rule['id']."\n";
  493. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  494. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  495. addUserHistory($userdata['email'],$GLOBALS['I18N']->get("Auto Unsubscribed"),$GLOBALS['I18N']->get("User auto unsubscribed for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  496. addSubscriberStatistics('auto blacklist',1);
  497. break;
  498. case 'blacklistuseranddeletebounce':
  499. logEvent('User '.$userdata['email'].' blacklisted by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  500. addUserToBlacklist($userdata['email'],$GLOBALS['I18N']->get("Auto Blacklisted"),$GLOBALS['I18N']->get("User auto blacklisted for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  501. $advanced_report .= 'User '.$userdata['email'].' blacklisted by bounce rule '.$rule['id']."\n";
  502. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  503. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  504. addUserHistory($userdata['email'],$GLOBALS['I18N']->get("Auto Unsubscribed"),$GLOBALS['I18N']->get("User auto unsubscribed for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  505. addSubscriberStatistics('auto blacklist',1);
  506. deleteBounce($row['bounce']);
  507. break;
  508. case 'blacklistemail':
  509. logEvent('email '.$userdata['email'].' blacklisted by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  510. addEmailToBlackList($userdata['email'],$GLOBALS['I18N']->get("Auto Blacklisted"),$GLOBALS['I18N']->get("email auto blacklisted for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  511. $advanced_report .= 'email '.$userdata['email'].' blacklisted by bounce rule '.$rule['id']."\n";
  512. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  513. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  514. addUserHistory($userdata['email'],$GLOBALS['I18N']->get("Auto Unsubscribed"),$GLOBALS['I18N']->get("email auto unsubscribed for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  515. addSubscriberStatistics('auto blacklist',1);
  516. break;
  517. case 'blacklistemailanddeletebounce':
  518. logEvent('email '.$userdata['email'].' blacklisted by bounce rule '.PageLink2('bouncerule&amp;id='.$rule['id'],$rule['id']));
  519. addEmailToBlackList($userdata['email'],$GLOBALS['I18N']->get("Auto Blacklisted"),$GLOBALS['I18N']->get("User auto blacklisted for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  520. $advanced_report .= 'email '.$userdata['email'].' blacklisted by bounce rule '.$rule['id']."\n";
  521. $advanced_report .= 'User: '.$report_linkroot.'/?page=user&amp;id='.$userdata['id']."\n";
  522. $advanced_report .= 'Rule: '.$report_linkroot.'/?page=bouncerule&amp;id='.$rule['id']."\n";
  523. addUserHistory($userdata['email'],$GLOBALS['I18N']->get("Auto Unsubscribed"),$GLOBALS['I18N']->get("User auto unsubscribed for")." ".$GLOBALS['I18N']->get("bounce rule").' '.$rule['id']);
  524. addSubscriberStatistics('auto blacklist',1);
  525. deleteBounce($row['bounce']);
  526. break;
  527. case 'deletebounce':
  528. deleteBounce($row['bounce']);
  529. break;
  530. }
  531. $matched++;
  532. } else {
  533. $notmatched++;
  534. }
  535. }
  536. output($matched.' '.$GLOBALS['I18N']->get('bounces processed by advanced processing'));
  537. output($notmatched.' '.$GLOBALS['I18N']->get('bounces were not matched by advanced processing rules'));
  538. }
  539. # have a look who should be flagged as unconfirmed
  540. output($GLOBALS['I18N']->get("Identifying consecutive bounces"));
  541. # we only need users who are confirmed at the moment
  542. $userid_req = Sql_query(sprintf('select distinct %s.user from %s,%s
  543. where %s.id = %s.user and %s.confirmed',
  544. $tables["user_message_bounce"],
  545. $tables["user_message_bounce"],
  546. $tables["user"],
  547. $tables["user"],
  548. $tables["user_message_bounce"],
  549. $tables["user"]
  550. ));
  551. $total = Sql_Affected_Rows();
  552. if (!$total)
  553. output($GLOBALS['I18N']->get("Nothing to do"));
  554. $usercnt = 0;
  555. $unsubscribed_users = "";
  556. while ($user = Sql_Fetch_Row($userid_req)) {
  557. keepLock($process_id);
  558. set_time_limit(600);
  559. $msg_req = Sql_Query(sprintf('select * from
  560. %s um left join %s umb on (um.messageid = umb.message and userid = user)
  561. where userid = %d and um.status = "sent"
  562. order by entered desc',
  563. $tables["usermessage"],$tables["user_message_bounce"],
  564. $user[0]));
  565. /* $cnt = 0;
  566. $alive = 1;$removed = 0;
  567. while ($alive && !$removed && $bounce = Sql_Fetch_Array($msg_req)) {
  568. $alive = checkLock($process_id);
  569. if ($alive)
  570. keepLock($process_id);
  571. else
  572. ProcessError($GLOBALS['I18N']->get("Process Killed by other process"));
  573. if (sprintf('%d',$bounce["bounce"]) == $bounce["bounce"]) {
  574. $cnt++;
  575. if ($cnt >= $bounce_unsubscribe_threshold) {
  576. $removed = 1;
  577. output(sprintf('unsubscribing %d -> %d bounces',$user[0],$cnt));
  578. $userurl = PageLink2("user&amp;id=$user[0]",$user[0]);
  579. logEvent($GLOBALS['I18N']->get("User")." $userurl ".$GLOBALS['I18N']->get("has consecutive bounces")." ($cnt) ".$GLOBALS['I18N']->get("over threshold, user marked unconfirmed"));
  580. $emailreq = Sql_Fetch_Row_Query("select email from {$tables["user"]} where id = $user[0]");
  581. addUserHistory($emailreq[0],$GLOBALS['I18N']->get("Auto Unsubscribed"),$GLOBALS['I18N']->get("User auto unsubscribed for")." $cnt ".$GLOBALS['I18N']->get("consecutive bounces"));
  582. Sql_Query(sprintf('update %s set confirmed = 0 where id = %d',$tables["user"],$user[0]));
  583. addSubscriberStatistics('auto unsubscribe',1);
  584. $email_req = Sql_Fetch_Row_Query(sprintf('select email from %s where id = %d',$tables["user"],$user[0]));
  585. $unsubscribed_users .= $email_req[0] . " [$user[0]] ($cnt)\n";
  586. }
  587. } elseif ($bounce["bounce"] == "") {
  588. $cnt = 0;
  589. }
  590. }*/
  591. #$alive = 1;$removed = 0; DT 051105
  592. $cnt=0;
  593. $alive = 1;
  594. $removed = $msgokay = $unconfirmed = $unsubscribed = 0;
  595. #while ($alive && !$removed && $bounce = Sql_Fetch_Array($msg_req)) { DT 051105
  596. while ($alive && !$removed && !$msgokay && $bounce = Sql_Fetch_Array($msg_req)) {
  597. $alive = checkLock($process_id);
  598. if ($alive) {
  599. keepLock($process_id);
  600. } else {
  601. ProcessError("Process Killed by other process");
  602. }
  603. if (sprintf('%d',$bounce["bounce"]) == $bounce["bounce"]) {
  604. $cnt++;
  605. if ($cnt >= $bounce_unsubscribe_threshold) {
  606. if (!$unsubscribed) {
  607. output(sprintf('unsubscribing %d -> %d bounces',$user[0],$cnt));
  608. $userurl = PageLink2("user&amp;id=$user[0]",$user[0]);
  609. logEvent(s('User (url:%s) has consecutive bounces (%d) over threshold (%d), user marked unconfirmed',$userurl,$cnt,$bounce_unsubscribe_threshold));
  610. $emailreq = Sql_Fetch_Row_Query("select email from {$tables["user"]} where id = $user[0]");
  611. addUserHistory($emailreq[0],s('Auto Unconfirmed'),s('Subscriber auto unconfirmed for %d consecutive bounces',$cnt));
  612. Sql_Query(sprintf('update %s set confirmed = 0 where id = %d',$tables["user"],$user[0]));
  613. $email_req = Sql_Fetch_Row_Query(sprintf('select email from %s where id = %d',$tables["user"],$user[0]));
  614. $unsubscribed_users .= $email_req[0]."\t\t($cnt)\t\t". $GLOBALS['scheme'].'://'.getConfig('website').$GLOBALS['adminpages'].'/?page=user&amp;id='.$user[0]. "\n";
  615. $unsubscribed = 1;
  616. }
  617. if (BLACKLIST_EMAIL_ON_BOUNCE && $cnt >= BLACKLIST_EMAIL_ON_BOUNCE) {
  618. $removed = 1;
  619. #0012262: blacklist email when email bounces
  620. cl_output(s('%d consecutive bounces, threshold reached, blacklisting subscriber',$cnt));
  621. addEmailToBlackList($emailreq[0], s('%d consecutive bounces, threshold reached',$cnt));
  622. }
  623. }
  624. } elseif ($bounce["bounce"] == "") {
  625. #$cnt = 0; DT 051105
  626. $cnt = 0;
  627. $msgokay = 1; #DT 051105 - escaping loop if message received okay
  628. }
  629. }
  630. if ($usercnt % 5 == 0) {
  631. # output($GLOBALS['I18N']->get("Identifying consecutive bounces"));
  632. cl_progress(s('processed %d out of %d subscribers',$usercnt, $total),1);
  633. }
  634. $usercnt++;
  635. flush();
  636. }
  637. if (!$GLOBALS["commandline"]) {
  638. print '<script language="Javascript" type="text/javascript"> finish(); </script>';
  639. }
  640. #output($GLOBALS['I18N']->get("Identifying consecutive bounces"));
  641. output("\n".s('total of %d subscribers processed',$total). ' ');
  642. $report = '';
  643. if ($advanced_report) {
  644. $report .= $GLOBALS['I18N']->get('Report of advanced bounce processing:')."\n$advanced_report\n";
  645. }
  646. if ($unsubscribed_users) {
  647. $report .= "\n".$GLOBALS['I18N']->get("Below are users who have been marked unconfirmed. The in () is the number of consecutive bounces.")."\n";
  648. $report .= "\n$unsubscribed_users";
  649. }
  650. if ($report) {
  651. $report = $GLOBALS['I18N']->get("Report:") . "\n$download_report\n" . $report;
  652. }
  653. # shutdown will take care of reporting
  654. #finish("info",$report);
  655. # IMAP errors following when Notices are on are a PHP bug
  656. # http://bugs.php.net/bug.php?id=7207