<?php
/*
* @(#) $Header: /var/cvsroot/pop3ml/includes/Attic/class.pop3ml.php,v 1.18.2.127 2010/05/27 07:27:08 cvs Exp $
*/
/* pop3ml - php Mailing list/Newsletter manager
Copyright (C) 2009- Giuseppe Lucarelli <hide@address.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Debugging / Contributers:
* Ron Schoellmann
* Gregor Buchholz
* Andrea Tempera
*/
//error_reporting(E_ALL & ~E_NOTICE);
require_once(SCRIPTS_DIR_PATH.DS.'smtp.php');
require_once(SCRIPTS_DIR_PATH.DS.'mime_parser.php');
require_once(SCRIPTS_DIR_PATH.DS.'rfc822_addresses.php');
require_once(SCRIPTS_DIR_PATH.DS."pop3.php");
require_once(SCRIPTS_DIR_PATH.DS.'sasl.php'); /* for gmail */
//--------------------------------------------------------------------------------
// if you want a more complex bounce detection or you if have almost one puclic ML use this extra class (refer to README file)
//--------------------------------------------------------------------------------
if(file_exists(SCRIPTS_DIR_PATH.DS."bounce_driver.class.php"))
require_once(SCRIPTS_DIR_PATH.DS.'bounce_driver.class.php');
class MyPop3 extends pop3_class {
var $decoded;
}
class Pop3Ml
{
/* public */
var $mltable = '';
var $messages = '';
var $listName = '';
var $listAddress = '';
var $listHostName = '';
var $listPort = '';
var $listTls = '';
var $listUser = '';
var $listPopPass = '';
var $allow = array();
var $deny = array();
var $removeAfterPop = '';
var $moderatedList = '';
var $modSublist = '';
var $listOwner = '';
var $maxMsgSize = '';
var $headersChange = array();
var $logHeader = '';
var $sublist = '';
var $digestSublist = '';
var $trailerFile = '';
var $forwardMailerTo = false;
var $maxPop3MsgLimit = false;
var $expireLock = false;
var $disableSubscription = false;
var $cacheMessages = false;
var $cachePath = false;
var $minTimeResendMsg = false;
var $scheduledTime = false;
var $sendDigest = false;
var $digestMaxMsg = false;
var $smtpServer = array();
/* private */
var $now = '';
var $dbconn = '';
var $dbrow = '';
var $pop3='';
var $decoded='';
var $smtp='';
var $smtpIndex='';
var $error='';
/**
* debug variables are used by 'test_pop3ml.php' script too.
* if you are using that script these variables are set automatically from the web form.
* for normal use instead you have to set manually their values
*/
var $debugOutput='';
var $debug=0;
var $smtpDebug=0;
/*
* the following variable is for normal use only. set its value to '1' for pop3 debug
*/
var $pop3Debug=0;
var $bodyTextPlain = '';
// this variable is used to avoid looping thru parent/children sublist
var $recursiveList = '';
var $scheduledPattern = false;
var $substate = false;
var $sender = false;
function Init() {
$this->debugOutput = '';
//-------------------
// thanks to Ron and Gregor
if(!in_array("pop3", stream_get_wrappers())) {
stream_wrapper_register('pop3', 'pop3_stream'); /* Register the pop3 stream handler class */
}
$this->pop3 = new pop3_class;
//-------------------
if(!$this->mltable) $this->mltable = 'mltable';
if(!$this->messages) $this->messages = 'messages';
if(!$this->queue) $this->queue = 'queue';
if(!$this->subqueue) $this->subqueue = 'subqueue';
if(!$this->subscribers) $this->subscribers = 'subscribers';
$query = "select * from $this->mltable where listname = '".$this->listName."'";
$command = @mysql_query($query,$this->dbconn);
if(!$result = @mysql_fetch_object($command)) {
if($this->debug) {
$this->debugOutput .= "\nERROR: no ML found, quit\n";
} else {
echo "no ML found: quit; ";
}
return false;
}
$this->dbrow = $result;
$this->listUser=$this->dbrow->listuser;
$this->listPopPass=$this->dbrow->listpoppass;
$this->maxMsgSize=$this->dbrow->msgsize;
$this->removeAfterPop=$this->dbrow->removeafterpop;
$this->moderatedList=$this->dbrow->moderatedlist;
$this->listOwner=$this->dbrow->listowneremail;
$this->listAddress=$this->dbrow->listaddr;
$this->trailerFile=str_replace("\r",'',$this->dbrow->trailerfile);
$this->smtpServer=explode("\n",$this->dbrow->smtpserver);
// for compatibily with previuos release 0.1
$this->headersChange=str_replace('::',' ',$this->SetText($this->dbrow->headerchange));
$this->smtpIndex=0; // use default smtp server
if(CACHE_MESSAGES == false)
$this->cacheMessages = false;
if(CACHE_PATH != false) {
$this->cachePath = CACHE_PATH;
}
if($this->cachePath && strlen($this->cachePath) > 1 && $this->cachePath[strlen($this->cachePath)-1] != '/') {
$this->cachePath .= '/';
}
if($this->minTimeResendMsg != false)
$this->minTimeResendMsg = MIN_TIME_RESEND_MSG;
$this->sendDigest = ($this->dbrow->senddigest ? $this->dbrow->senddigest :
SEND_DIGEST);
if(DIGEST_MAX_MSG == false) {
$this->digestMaxMsg = $this->dbrow->digestmaxmsg;
} else {
$this->digestMaxMsg = DIGEST_MAX_MSG;
}
if(!$this->digestMaxMsg)
$this->digestMaxMsg = 30;
if(!$this->scheduledTime) {
$this->scheduledTime = constant('SCHEDULED_TIME');
}
return true;
}
function InitSublistFields() {
$this->dbrow->sublist=str_replace(" ",'',trim($this->dbrow->sublist));
$this->dbrow->modsublist=str_replace(" ",'',trim($this->dbrow->modsublist));
$this->dbrow->digestsublist=str_replace(" ",'',trim($this->dbrow->digestsublist));
$this->dbrow->allowsublist=str_replace(" ",'',trim($this->dbrow->allowsublist));
$this->dbrow->denysublist=str_replace(" ",'',trim($this->dbrow->denysublist));
$this->sublist=$this->BuildRecursiveList($this->dbrow->sublist,'sublist',false);
$this->digestSublist=$this->BuildRecursiveList($this->dbrow->digestsublist,'digestsublist',false);
$this->modSublist=$this->BuildRecursiveList($this->dbrow->modsublist,'modsublist',false);
// $this->allow/$this->deny are build as array because every address may contain a REGEXP pattern
// (ie. '.*' '.*@bmsc.it')
$this->allow=$this->BuildRecursiveList($this->dbrow->allowsublist,'allowsublist',true);
$this->deny=$this->BuildRecursiveList($this->dbrow->denysublist,'denysublist',true);
// if 'parentlist' is set append parent subscribers/allow/digestsublist list as allowed addresses
// and parent denysublist as disabled addresses
if(strlen($this->dbrow->parentlist) > 0) {
$arr = explode(',',$this->dbrow->parentlist);
foreach($arr as $token) {
$token=trim($token);
$this->allow=array_merge((is_array($this->allow) ? $this->allow : array()),
$this->BuildRecursiveList($token,'sublist',true),
// enable it you want allow digest subscribers too
// $this->BuildRecursiveList($token,'digestsublist',true),
$this->BuildRecursiveList($token,'allowsublist',true));
$this->deny=array_merge((is_array($this->deny) ? $this->deny : array()),
$this->BuildRecursiveList($token,'denysublist',true));
$this->modSublist.="\n".$this->BuildRecursiveList($token,'modsublist',false);
}
}
}
function BuildRecursiveList($list,$listtype,$asarray = false, $depth = 0) {
$retval = '';
$sublist = ($asarray == true ? array() : '');
if($depth == 0) {
$this->recursiveList = '';
}
if(strlen($list) <= 2)
return $retval;
$retval = explode("\n",str_replace("\r",'',trim($list,"\n\r ,;")));
while(list($key,$value) = each($retval)) {
// check if address is valid or sublist name
//if(ereg("[ '\*\^\$\"\\\{\[]",$value)) { // got REGEXP value from address, jump
if(preg_match("/[ '\*\^\$\"\\\{\[]/",$value)) { // got REGEXP value from address, jump
continue;
}
if(strpos($value,'@'))
continue;
// got child list, check if already read
if(strstr($this->recursiveList,$value)) {
array_splice($retval,$key,1);
continue;
}
$this->recursiveList .= $value."\n";
$query = "select $listtype from $this->mltable where listname = '$value'";
if(!$command=@mysql_query($query,$this->dbconn))
continue;
// TODO: insert ml status check (if ml is disabled do something)
if(!$result = @mysql_fetch_row($command))
continue;
array_splice($retval,$key,1);
if(strlen($result[0]) > 7) {
if($asarray == true) {
$sublist = array_merge($sublist,
$this->BuildRecursiveList(str_replace(" ",'',$result[0]),$listtype,$asarray,++$depth));
} else {
$sublist .= $this->BuildRecursiveList(str_replace(" ",'',$result[0]),$listtype,$asarray,++$depth)."\n";
}
}
@mysql_free_result($result);
}
if($asarray == true) {
return array_merge($retval,$sublist);
} else {
return trim(implode("\n",$retval)."\n".$sublist,"\n");
}
}
//------------------------------------------------------------------------------
// if smtp server doesn't require pop auth, set to '' pop3_auth_port, user, password)
// host:port:ssl:[user]:[password]:[tls]
//------------------------------------------------------------------------------
function SmtpInit() {
$this->smtp=new smtp_class;
$this->smtp->localhost="localhost";
$this->smtp->direct_delivery=0;
$this->smtp->timeout=10;
$this->smtp->data_timeout=0;
$this->smtp->debug=0;
$this->smtp->html_debug=0;
$this->smtp->pop3_auth_host='';
$this->smtp->realm="";
$this->smtp->workstation="";
$this->smtp->authentication_mechanism="";
$state = strpos($this->smtpServer[$this->smtpIndex],"\t");
$token = explode(($state === false ? ':' : "\t"),trim($this->smtpServer[$this->smtpIndex]));
$this->smtp->host_name=$token[0];
$this->smtp->host_port=$token[1];
$this->smtp->ssl=$token[2];
$this->smtp->pop3authport='';
$this->smtp->user='';
$this->smtp->password='';
if(@$token[3]) $this->smtp->pop3authport=$token[3];
if(@$token[4]) $this->smtp->user=$token[4];
if(@$token[5]) $this->smtp->password=$token[5];
if(@$token[6]) $this->smtp->start_tls=$token[6];
}
function &SetText(&$text, $useraddress = '') {
$text = str_replace(array("__LISTADDRESS__","__LISTNAME__","__LISTOWNER__","__LISTHELP__"),
array($this->listAddress,$this->listName,$this->listOwner,$this->listAddress),$text);
if(strlen($useraddress) > 5) {
$text = str_replace("__USERADDRESS__",$useraddress,$text);
}
return $text;
}
function ArraySplit(&$item) {
$retval = array();
$recipientlimit = ($this->dbrow->recipientlimit ? $this->dbrow->recipientlimit : 0);
if($recipientlimit <= 0) {
$retval[] = $item;
return $retval;
}
for($i=0, $x=0; $i < sizeof($item); $i++) {
if($x == 0) {
$retval[] = array();
$split = &$retval[sizeof($retval)-1];
}
$split[] = $item[$i];
if(++$x >= $recipientlimit)
$x = 0;
}
return $retval;
}
function Stripos (& $haystack, $needle, $offset = 0) {
if(function_exists('stripos')) {
return stripos($haystack,$needle,$offset);
}
// PHP 4 doesn't define this function
preg_match('/'.str_replace('/','\/',$needle).'/i',$haystack,$matches,PREG_OFFSET_CAPTURE,1);
if(!@$matches || !@$matches[0][1]) {
return false;
}
return $matches[0][1];
}
function IsoDecode($data) {
$retval='';
if(!preg_match('/=\?.*\?([qb])\?.*\?=/im',$data)) {
return $data;
}
$token = preg_split('/=\?/i',$data);
foreach($token as $tok) {
if(strlen($tok) <= 0) {
continue;
}
if(!preg_match('/.*\?([qb])\?(.*)\?=(.*)/im',$tok,$matches,PREG_OFFSET_CAPTURE)) {
$retval.=$tok;
}
if(!strcasecmp(@$matches[1][0],'b')) {
$retval.=base64_decode($matches[2][0]).$matches[3][0];
} else if(!strcasecmp(@$matches[1][0],'q')) {
$retval.=str_replace('_',' ',quoted_printable_decode($matches[2][0])).$matches[3][0];
}
}
return $retval;
}
function GetSingleBodyPart(&$mailbody,$part) {
preg_match('/(.*)(\r|)\nContent-Type: '.str_replace('/','\/',$part).'/im',
$mailbody,$matches,PREG_OFFSET_CAPTURE,1);
if(@$matches[0][0]) {
if($pos=strpos(substr($mailbody,$matches[0][1]),"\r\n\r\n")) {
$pos+=4;
} else {
if($pos=strpos(substr($mailbody,$matches[0][1]),"\n\n")) {
$pos+=2;
} else {
return false;
}
}
$pos+=$matches[0][1];
$nextpart=trim(strtok($matches[0][0],"\r\n"));
// search for last 'NextPart' of this body part
$last=strpos(substr($mailbody,$pos),$nextpart);
return substr($mailbody,$pos,$last);
}
return false;
}
function ImplodeHeaders($headkey,$token) {
$retval = '';
if(!is_array($token)) {
return $retval;
}
foreach($token as $key => $val) {
if(is_array($val)) {
$retval.= $this->ImplodeHeaders($key,$val);
} else if(strlen($val) == 0) {
continue;
} else {
$retval.= (strcmp($headkey,'') ? $headkey : $key).' '.$val."\r\n";
}
}
return $retval;
}
function GetReturnPath($address) {
$retval = $address;
if(!@$this->decoded[0]['ExtractedAddresses'])
return $retval;
$headers = & $this->decoded[0]['ExtractedAddresses'];
if(array_key_exists('return-path:',$headers)) {
$retval = $headers['return-path:'][0]['address'];
} else if(!@empty($headers['reply-to:'])) {
$retval = $headers['reply-to:'][0]['address'];
}
if(strlen($retval) == 0 || preg_match('/error|bounce|no(-|_|)reply/i',$retval)) {
echo " got mailer address, no reply; ";
if($this->debug) {
$this->debugOutput .= "\nREPLY-TO ADDRESS ERROR: mailer\n";
}
return '';
} else {
return $retval;
}
}
function SmtpSend($mailto, $mailsubject, $mailfrom, $mailheader, &$mailbody) {
$this->SmtpInit();
if($this->smtp->direct_delivery)
{
if(!function_exists("GetMXRR"))
{
/*
* If possible specify in this array the address of at least on local
* DNS that may be queried from your network.
*/
$_NAMESERVERS=array();
include(SCRIPTS_DIR_PATH.DS."getmxrr.php");
}
/*
* If GetMXRR function is available but it is not functional, to use
* the direct delivery mode, you may use a replacement function.
*/
/*
else
{
$_NAMESERVERS=array();
if(count($_NAMESERVERS)==0)
Unset($_NAMESERVERS);
include(SCRIPTS_DIR_PATH.DS."rrcompat.php");
$this->smtp->getmxrr="_getmxrr";
}
*/
}
if($this->smtpDebug) {
$this->smtp->debug = $this->smtpDebug;
$this->debugOutput .= "\n\nSTARTING SMTP:\n";
ob_start();
} else if($this->debug) {
// if this variable is set, this page has been request from a subscribers using 'test_pop3ml.php' script
if(@$this->userRequest) {
$this->debugOutput .= "\n\n$mailbody";
} else {
$this->debugOutput .= "\nRCPT TO:\n".(is_array($mailto) ? implode("\n",$mailto) : $mailto).
"\n\n$mailheader\n$mailbody";
}
if($success=$this->smtp->Connect()) {
$this->smtp->Disconnect();
$retval = 'OK.';
$this->debugOutput .= "\n\nSMTP CONNECTION STATUS: passed\n";
return $retval;
} else {
$retval = $success;
$this->debugOutput .= "\n\nSMTP CONNECTION STATUS: failed\n";
return $retval;
}
}
// remove all '\r' from header. they will be set from 'smtp.php'
$mailheader = trim(str_replace("\r",'',$mailheader));
if($this->smtp->SendMessage($mailfrom,
(is_array($mailto) ? $mailto : array($mailto)),
explode("\n",$mailheader), $mailbody)) {
$retval = "OK.";
} else {
$retval = $this->smtp->error;
}
if($this->smtpDebug) {
$this->debugOutput .= "\n\n".ob_get_contents();
ob_end_clean();
} else if(!$this->debug) {
$this->debugOutput .= "\n\n$retval\n\n";
}
return $retval;
}
function GetText($def,$returndef = false) {
$retval = false;
if(!preg_match('/\[\[:'.$def.'=.*/im',$this->dbrow->language,$matches,PREG_OFFSET_CAPTURE)) {
return ($returndef ? $def : $retval);
}
$start = $matches[0][1]+strlen($def)+4;
if(!$end = strpos(substr($this->dbrow->language,$matches[0][1]),':]]')) {
if(!$end = strpos(substr($this->dbrow->language,$pos),'[[:')) {
return ($returndef ? $def : false);
} else {
$end -= -4;
}
} else {
$end -= (strlen($def) + 4);
}
$retval = substr($this->dbrow->language,$start,$end);
return $retval;
}
function NotifyUser($address,$digest,&$mailsubject,&$mailbody) {
$mailheader = '';
$digesttext = '';
if($digest) {
if(!$digesttext=$this->GetText('DIGEST MODE')) {
$digesttext = "Digest Mode";
}
}
$mailbody = $this->SetText($mailbody);
$mailbody = str_replace("__DIGESTMODE__", $digesttext, $mailbody);
// change the first header to send bounced emails/user reply to whatever you want
// (ie. hide@address.com)
$mailheader.="Return-Path: ".$this->listOwner."\r\n";
$mailheader.="Reply-To: ".$this->listAddress."\r\n";
$mailheader.="To: ".str_replace("\n",', ',$address)."\r\n";
$mailheader.="From: ".$this->listAddress."\r\nSubject: $mailsubject\r\n";
$mailheader.= "Precedence: bulk\r\n";
$error = $this->SmtpSend($address, $mailsubject, $this->listAddress, $mailheader, $mailbody);
if($error != 'OK.') {
$this->NotifyOwner($address,'User notification failed, ml ['.$this->listAddress.']',
'Notification failure for ml ['.$this->listAddress.'] for address <'.$address."> smtp error [$error]");
}
}
function NotifySubscriptionRequest($address,$digest,$mailbody) {
if($digest) {
if(!$digesttext=$this->GetText('DIGEST MODE')) {
$digesttext = "Digest Mode";
}
}
$mailsubject = $this->GetText('NOTIFY OWNER SUBSCRIBE MOD',true).'['.$this->listAddress."] <$address>";
$mailbody = str_replace("__DIGESTMODE__", $digesttext, $mailbody);
if(!$this->NotifyOwner($address,$mailsubject,$mailbody)) {
return false;
}
return true;
}
function SendSubscribeConfirmation($address,$command,$digest = false,$msgcode = false, $modrequest = false) {
//$subscribe = $this->GetText('SUBSCRIBE',true);
$confirm = $this->GetText('CONFIRM',true);
$toggle = $this->GetText('TOGGLE',true);
$id = md5(uniqid(rand(), true));
//$mailsubject = 'confirm subscribe to '.$this->listAddress;
$mailsubject = "$confirm $command $this->listAddress";
$mailbody = "__SUBSCRIBE__";
$confirmtext = ($modrequest != false ? $modrequest.'.' : '').
"$confirm.$command.".($digest ? 'digest.' : '').$this->listName.'.'.$id.'.'.$address;
if(!$mailbody=$this->GetText(($msgcode != false ? $msgcode : 'SUBSCRIBE CONF'))) {
$mailbody = "Please confirm subscription for <__SUBSCRIBE__> to <__LISTADDRESS__> __DIGESTMODE__\r\n\r\nThanks.\r\n";
}
$mailbody = str_replace("__SUBSCRIBE__", $confirmtext,
str_replace('__TOGGLE__',$toggle,$this->SetText($mailbody,$address)));
if(!$this->debug) {
$query="insert into $this->subqueue values ('".$this->listName.':'.
($modrequest != false ? $modrequest.'-' : '').
"$address','subscription','$id',now(),'');";
if(!@mysql_query($query,$this->dbconn)) { // there is another request, replace it
$query="update ".$this->subqueue.
" set request = 'subscription', keyvalue = '$id' where code = '".$this->listName.':'.
($modrequest != false ? $modrequest.'-' : '').
"$address';";
@mysql_query($query,$this->dbconn);
}
}
if($modrequest != false) {
if(!$this->NotifySubscriptionRequest($address,$digest,$mailbody)) {
return false;
}
} else {
if(!$this->NotifyUser($address,$digest,$mailsubject,$mailbody)) {
return false;
}
}
}
function SendWelcomeMessage($address,$digest, $msgcode = false, $password = '') {
//$mailsubject = 'WELCOME to '.$this->listAddress;
$mailsubject = $this->GetText('WELCOME',true).$this->listAddress;
$mailbody = '';
if(!$mailbody=$this->GetText(($msgcode == false ? 'SUBSCRIBE WELCOME' : $msgcode))) {
$mailbody = "The address <__SUBSCRIBE__> has been added to <__LISTADDRESS__> __DIGESTMODE__\r\n\r\nThanks.\r\n";
}
$mailbody = str_replace('__SUBSCRIBE__', $address, $mailbody);
$mailbody = str_replace('__USERPASS__',
(strlen($password) > 1 ? "[$password]" : $this->GetText('USERPASS SET')), $mailbody);
if(!$this->NotifyUser($address,$digest,$mailsubject,$mailbody)) {
return false;
}
}
function ToggleAddress($address) {
$digest = false;
if($this->debug) {
return;
}
if(preg_match('/(^|\n)'.$address.'(\n|$)/i',$this->dbrow->sublist)) {
$this->dbrow->sublist=preg_replace("/(^|\n)$address(\n|$)/",'\1',
trim($this->dbrow->sublist));
$this->dbrow->digestsublist=trim($this->dbrow->digestsublist)."\n".$address;
$digest = true;
} else {
$this->dbrow->digestsublist=preg_replace("/(^|\n)$address(\n|$)/",'\1',
trim($this->dbrow->digestsublist));
$this->dbrow->sublist=trim($this->dbrow->sublist)."\n".$address;
}
$query = 'update '.$this->mltable.' set sublist = \''.trim($this->dbrow->sublist).'\', digestsublist = \''.
trim($this->dbrow->digestsublist).'\' where listname = \''.$this->listName.'\'';
if($command = @mysql_query($query,$this->dbconn)) {
echo "toggled address [$address]\n";
$this->SendWelcomeMessage($address,$digest,'TOGGLED MESSAGE');
}
}
function SetConfirmedAddress($address, $digest = false) {
$sql = 'sublist';
if($this->debug) {
$this->debugOutput .= "\n\nsubscription confirmed\n\n";
return;
}
if($digest) {
$this->dbrow->digestsublist=trim($this->dbrow->digestsublist)."\n".$address;
$sublist = &$this->dbrow->digestsublist;
$sql = 'digestsublist';
} else {
$this->dbrow->sublist=trim($this->dbrow->sublist)."\n".$address;
$sublist = &$this->dbrow->sublist;
$sql = 'sublist';
}
$query = 'update '.$this->mltable.' set '.$sql.' = \''.
trim($sublist).'\' where listname = \''.$this->listName.'\'';
if(!$command = @mysql_query($query,$this->dbconn)) {
return;
}
echo "confirmed subscription [$address]\n";
// check if subscriber is register yet, otherwise write a new record
$query = 'select count(*) from '.$this->subscribers." where emailaddress = '$address'";
if($result = @mysql_query($query,$this->dbconn)) {
$row = @mysql_fetch_row($result);
if($row[0] <= 0) {
require_once(CLASSES_DIR_PATH.DS.'class.genpass.php');
$password = GenPass::CreatePassword(8);
$query = 'insert into '.$this->subscribers.
" (id,emailaddress,state,webpass,rowlock) values (0,'$address','enabled',password('".
$password."'),'')";
@mysql_query($query,$this->dbconn);
$this->SendWelcomeMessage($address,$digest,'SUBSCRIBE WELCOME',$password);
} else {
$this->SendWelcomeMessage($address,$digest);
}
@mysql_free_result($result);
return;
}
$this->SendWelcomeMessage($address,$digest);
}
function SendSubscriptionError($address,$digest,$errorcode) {
if(strlen($address) < 7)
return;
$mailsubject = $this->GetText('NOTIFY SUBJECT',true).' '.$this->listAddress;
$mailheader = '';
$mailbody = '';
if(!$mailbody=$this->GetText($errorcode)) {
$mailbody = "Subscription error for address <__USERADDRESS__>, list <__LISTADDRESS__> __DIGESTMODE__\r\n\r\n";
}
$mailbody = $this->SetText($mailbody,$address);
$this->NotifyUser($address,$digest,$mailsubject,$mailbody);
echo " sent subscription error message with code [$errorcode];\n";
}
/**
* if you want a more complex 'SUBSCRIBE' text, you can use a regexp instead of a simple word, for example changing
* the value of 'SUBSCRIBE' text (mltable->language field) to [[:SUBSCRIBE=(\s+|)(i|)(\s+|)subscribe:]]
* to identify body messages as 'subscribe' request, with text like: "i subscribe of course......" or simply "subscribe"
*/
function IsSubscription(&$bodytest, $address, & $substate) {
$toggle = $this->GetText('TOGGLE',true);
$subscribe = $this->GetText('SUBSCRIBE',true);
$confirm = $this->GetText('CONFIRM',true);
$owner = $this->GetText('OWNER',true);
$digest = preg_match("/\bdigest\b/i",$bodytest);
if(preg_match("/^$toggle/i",ltrim($bodytest," \n\r\t*<>"))) {
if($this->debug) $this->debugOutput .= "\nGot command: TOGGLE\n";
if(!strcmp($substate,'subscribeddigest') // both subscriptions
|| (!strstr($substate,'subscribed') && !strstr($substate,'digest'))) { // no subscription
$this->SendSubscriptionError($address,$digest,'TOGGLE ERROR STATE');
return true;
}
if(strcasecmp($this->dbrow->confirmsub,'yes')) {
$this->ToggleAddress($address);
} else {
$this->SendSubscribeConfirmation($address,$toggle,false,'TOGGLE CONF');
echo "toggle [$address]. require confirmation: ";
}
return true;
//} else if(eregi("^".$subscribe,ltrim($bodytest," \n\r\t*<>"))) {
} else if(preg_match("/^$subscribe/i",ltrim($bodytest," \n\r\t*<>"))) {
// if ml has disabled subscription mails ignore request
if($this->disableSubscription == true) {
return true;
}
// got "subscribe" command, check if user already exists
if($this->debug) $this->debugOutput .= "\nGot command: SUBSCRIBE\n";
// if user exists in digest mode, ignore request
if($digest && strstr($substate,'digest')) {
$this->SendSubscriptionError($address,$digest,'SUBSCRIPTION ERROR STATE');
return true;
}
// if user exists in normal mode, need to toggle subscription
if($digest && !strcmp($substate,'subscribed')) {
$this->SendSubscribeConfirmation($address,$subscribe,$digest,'SUBSCRIPTION ERROR MODE');
return true;
}
// if user exists in normal mode, ignore request
if(!$digest && !strcmp($substate,'subscribed')) {
$this->SendSubscriptionError($address,$digest,'SUBSCRIPTION ERROR STATE');
return true;
}
// if user exists in digest mode, need to toggle subscription
if(!$digest && strstr($substate,'digest')) {
$this->SendSubscribeConfirmation($address,$subscribe,$digest,'SUBSCRIPTION ERROR MODE');
return true;
}
/* no user, normal subscription request */
if(strcasecmp($this->dbrow->confirmsub,'yes')) {
$this->SetConfirmedAddress($address,$digest);
} else {
echo "subscribe [$address]. require confirmation: ";
$this->SendSubscribeConfirmation($address,$subscribe,$digest);
}
return true;
}
// if preg failed it's not a subscription request
if(!preg_match('/(\b'.$owner."\b|).\b$confirm\b.\b($subscribe|$toggle)\b.".$this->listName.
"\.([A-Za-z0-9]+)\.(.*)/i", "--".$bodytest, $matches,PREG_OFFSET_CAPTURE)) {
return false;
}
if($this->debug) $this->debugOutput .= "\nGot command: SUBSCRIBE CONFIRMATION\n";
$originaladdress = false;
if(strlen($matches[1][0]) > 1 && !strcasecmp($owner,$matches[1][0])) {
if(!$pos = strpos($matches[4][0],'>')) {
if(!$pos = strpos($matches[4][0],' ')) {
return false;
}
}
$originaladdress = rtrim(substr($matches[4][0],0,$pos));
$query = "select * from ".$this->subqueue." where code = '".
$this->listName.":$owner-$originaladdress' and request = 'subscription'";
} else {
$query = "select * from ".$this->subqueue." where code = '".
$this->listName.':'.$address."' and request = 'subscription'";
}
$command = @mysql_query($query,$this->dbconn);
if(!$result = @mysql_fetch_object($command)) {
return true;
}
if($matches[3][0] && strcmp($matches[3][0],$result->keyvalue)) {
$this->SendSubscriptionError($address,$digest,'SUBSCRIPTION CONFIRM ERROR');
return true;
}
$query = "delete from ".$this->subqueue." where code = '".
$this->listName.':'.
($originaladdress != false ? "$owner-$originaladdress" : $address).
"' and request = 'subscription'";
if(!$this->debug) {
$command = @mysql_query($query,$this->dbconn);
}
//if(eregi($toggle,$this->decoded[0]['Body'])) {
if(preg_match("/$toggle/i",$this->decoded[0]['Body'])) {
$this->ToggleAddress($originaladdress ? $originaladdress : $address);
return true;
}
//if(!strcasecmp($this->dbrow->subscriptionmod,'yes') && !eregi($owner,$matches[0][0])) {
if(!strcasecmp($this->dbrow->subscriptionmod,'yes') && !preg_match("/$owner/i",$matches[0][0])) {
echo "subscription confirmed [$address]. require owner confirmation: ";
$this->SendSubscribeConfirmation($address,$subscribe,$digest,'MOD SUBSCRIBE CONF',$owner);
return true;
}
$this->SetConfirmedAddress($originaladdress ? $originaladdress : $address,$digest);
return true;
}
function SendUnsubscribeConfirmation($address, $digest = false, $msgcode = false) {
$unsubscribe = $this->GetText('UNSUBSCRIBE',true);
$confirm = $this->GetText('CONFIRM',true);
$toggle = $this->GetText('TOGGLE',true);
$id = md5(uniqid(rand(), true));
$mailsubject = "$confirm $unsubscribe $this->listAddress";
$mailbody = "__UNSUBSCRIBE__";
$confirmtext = "$confirm.$unsubscribe.".($digest ? 'digest.' : '').$this->listName.'.'.$id.'.'.$address;
if(!$mailbody=$this->GetText(($msgcode != false ? $msgcode : 'UNSUBSCRIBE CONF'))) {
$mailbody = "Please confirm unsubscription for <__SUBSCRIBE__> to <__LISTADDRESS__> __DIGESTMODE__\r\n\r\nThanks.\r\n";
}
$mailbody = str_replace("__UNSUBSCRIBE__", $confirmtext,
str_replace("__TOGGLE__", $toggle, $this->SetText($mailbody,$address)));
if(!$this->debug) {
$query="insert into $this->subqueue values ('".$this->listName.":$address','unsubscription','$id',now(),'');";
if(!@mysql_query($query,$this->dbconn)) { // there is another request, replace it
$query="update ".$this->subqueue." set request = 'unsubscription', keyvalue = '$id' where code = '".
$this->listName.":$address';";
@mysql_query($query,$this->dbconn);
}
}
if(!$this->NotifyUser($address,$digest,$mailsubject,$mailbody)) {
return false;
}
}
function SendGoodbyeMessage($address,$digest) {
$mailsubject = $this->GetText('GOODBYE',true).$this->listAddress;
$mailheader = '';
$mailbody = '';
if(!$mailbody=$this->GetText('UNSUBSCRIBE GOODBYE',true)) {
$mailbody = "The address <__USERADDRESS__>, has been removed from <__LISTADDRESS__> DIGESTMODE__\r\n\r\nThanks.\r\n";
}
$mailbody = $this->SetText($mailbody,$address);
$this->NotifyUser($address,$digest,$mailsubject,$mailbody);
}
function SendUnsubscriptionError($address,$digest,$errorcode) {
$mailsubject = $this->GetText('NOTIFY SUBJECT',true).' '.$this->listAddress;
$mailheader = '';
$mailbody = '';
//$digesttext = '';
if(!$mailbody=$this->GetText($errorcode)) {
$mailbody = "The address <__USERADDRESS__>, is subscribed to list <__LISTADDRESS__> __DIGESTMODE__\r\n\r\nThanks.\r\n";
}
$mailbody = $this->SetText($mailbody,$address);
$mailbody = str_replace('__TOGGLE__',$this->GetText('TOGGLE',true),$this->SetText($mailbody,$address));
$this->NotifyUser($address,$digest,$mailsubject,$mailbody);
echo " sent unsubscription error message;\n";
}
function UnsetRegisteredAddress($address, $digest = false) {
$sql = 'sublist';
if($this->debug) {
$this->debugOutput .= "\n\nUnsubscription confirmed\n\n";
return;
}
/** DISABLED ***********************************
* if you want to remove both "unsubscription" requests (normal mode/digest mode), enable rows below
$this->dbrow->digestsublist=preg_replace("/(^|\n)$address(\n|$)/",'\1',
trim($this->dbrow->digestsublist));
$this->dbrow->sublist=preg_replace("/(^|\n)$address(\n|$)/",'\1',trim($this->dbrow->sublist));
$query = 'update '.$this->mltable.' set sublist= \''.
trim($this->dbrow->sublist).'\', digestsublist = \''.
trim($this->dbrow->digestsublist).'\' where listname = \''.$this->listName.'\'';
************************************************/
if($digest) {
$this->dbrow->digestsublist=preg_replace("/(^|\n)$address(\n|$)/",'\1',
trim($this->dbrow->digestsublist));
$sublist = &$this->dbrow->digestsublist;
$sql = 'digestsublist';
} else {
$this->dbrow->sublist=preg_replace("/(^|\n)$address(\n|$)/",'\1',trim($this->dbrow->sublist));
$sublist = &$this->dbrow->sublist;
$sql = 'sublist';
}
$query = 'update '.$this->mltable.' set '.$sql.' = \''.
trim($sublist).'\' where listname = \''.$this->listName.'\'';
if($command = @mysql_query($query,$this->dbconn)) {
echo "confirmed unsubscription [$address]\n";
$this->SendGoodbyeMessage($address,$digest);
}
}
function IsUnsubscription(& $bodytest, $address, & $substate) {
$error = '';
$unsubscribe = $this->GetText('UNSUBSCRIBE',true);
$confirm = $this->GetText('CONFIRM',true);
//$digest = eregi("digest",preg_replace("/\n|\r|=/",'',$bodytest));
$digest = preg_match("/\bdigest\b/i",$bodytest);
//if(eregi("^".$unsubscribe,preg_replace("/\n|\r|=/",'',$bodytest))) {
if(preg_match("/^$unsubscribe/i",preg_replace("/\n|\r|=/",'',$bodytest))) {
if($this->debug) $this->debugOutput .= "\nGot command: UNSUBSCRIBE\n";
// if user exists in normal mode, need to toggle subscription
if($digest && !strcmp($substate,'subscribed')) {
$this->SendUnsubscriptionError($address,$digest,'UNSUBSCRIPTION ERROR STATE');
return true;
}
// if user doesn't exist in digest mode, ignore request
if($digest && !strstr($substate,'digest')) {
$this->SendUnsubscriptionError($address,$digest,'UNSUBSCRIPTION ERROR STATE');
return true;
}
// if user exists in digest mode, need to toggle subscription
if(!$digest && strstr($substate,'digest')) {
$this->SendUnsubscriptionError($address,$digest,'UNSUBSCRIPTION ERROR MODE');
return true;
}
// if user doesn't exist in normal mode, ignore request
if(!$digest && strcmp($substate,'subscribed')) {
$this->SendUnsubscriptionError($address,$digest,'UNSUBSCRIPTION ERROR STATE');
return true;
}
// got user, normal unsubscription request
if(strcasecmp($this->dbrow->confirmunsub,'yes')) {
$this->UnsetRegisteredAddress($address,$digest);
} else {
echo "unsubscribe [$address]. require confirmation\n";
$this->SendUnsubscribeConfirmation($address,$digest);
}
return true;
}
$query = "select * from ".$this->subqueue." where code = '".
$this->listName.':'.$address."' and request = 'unsubscription'";
$command = @mysql_query($query,$this->dbconn);
if(!$result = @mysql_fetch_object($command)) {
return false;
}
$error = "[$address] unsubscription request pending, ";
echo $error;
$this->debugOutput .= "\n\n".$error;
//if(!eregi($confirm.'.*'.$unsubscribe.'.*'.$this->listName.'.*'.$result->keyvalue.'.*'.$address,
if(!preg_match('/'.$confirm.'.*'.$unsubscribe.'.*'.$this->listName.'.*'.$result->keyvalue.'.*'.$address.'/i',
preg_replace("/\n|\r|=/",'',$this->decoded[0]['Body']))) {
if($this->debug) $this->debugOutput .= "\nGot unsubscription error: $error\n";
$this->SendUnsubscriptionError($address,$digest,'UNSUBSCRIPTION PENDING');
return true;
}
if(!$this->debug) {
$query = "delete from ".$this->subqueue." where code = '".
$this->listName.':'.$address."' and request = 'unsubscription'";
$command = @mysql_query($query,$this->dbconn);
}
$this->UnsetRegisteredAddress($address,$digest);
return true;
}
function RemoveMessage($message) {
$this->error = '';
if(!$this->debug) {
$this->error=$this->pop3->DeleteMessage($message);
}
return true;
}
function NotifyMessageModeration($address,$scheduledTime) {
$id = md5(uniqid(rand(), true));
$mailheader = '';
$mailsubject = $this->GetText('NOTIFY OWNER MESSAGE MOD',true)."[$this->listAddress] <$address>";
$confirmtext =
($scheduledTime ? str_replace(' ','_',$this->scheduledTime.": $scheduledTime.") : '').
$this->GetText('CONFIRM',true).'.'.
$this->GetText('MESSAGE',true).".$this->listName.$id.$address";
if(!$mailbody=$this->GetText('MOD MESSAGE CONF')) {
$mailbody = "Please confirm message moderation for <__SUBSCRIBE__> to <__LISTADDRESS__>\r\n\r\nThanks.\r\n";
}
$mailbody = str_replace("__MESSAGEID__", $confirmtext, $this->SetText($mailbody));
echo 'message moderation for ml owner from <'.$address.">;\n";
$boundary='----NextPart'.md5(date('r', time()));
$mailheader.="From: ".$this->listAddress."\r\nMIME-Version: 1.0\r\nContent-Type: multipart/report; boundary=".
$boundary."\r\n";
$mailheader.='Subject: '.$mailsubject."\r\n";
$mailbody ='--'.$boundary."\r\nContent-Type: text/plain; charset: us-ascii\r\n\r\n".
$mailbody."\r\noriginal message:\r\n\r\n";
$mailbody.='--'.$boundary."\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"msg.eml\"\r\n\r\n".str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers']))."\r\n".str_replace("'","\\'",$this->decoded[0]['Body'])."\r\n\r\n".'--'.$boundary."--\r\n\r\n";
$error = $this->SmtpSend(explode("\n",$this->modSublist), $mailsubject, $this->listAddress, $mailheader, $mailbody);
if($error != 'OK.') {
echo "owner notify failed [$error]";
return;
}
if($this->debug) {
return;
}
$query = "insert into ".$this->messages.
" (id,date,state,listname,smtp,mailfrom,subject,message,header,keyvalue,rowlock) values(0,now(),'".
"pending','$this->listName','$this->smtpIndex','$address','".
str_replace("'","\\'",$this->IsoDecode($this->decoded[0]['Headers']['subject:']))."','".
str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers']).
"\r\n\r\n".$this->decoded[0]['Body'])."','".
str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers']))
// enable it if you want the md5 value into header too
// ."X-OriginalModerationID: $id\r\n'
."','$id','')";
if(!$command = @mysql_query($query,$this->dbconn)) {
echo "inserting message into table failed; [".mysql_error()."]\n";
}
}
function NotifyOwner($address, $mailsubject = false, $mailbody = '') {
$id = md5(uniqid(rand(), true));
$mailheader = '';
if(!$mailsubject)
$mailsubject = 'Owner request for ['.$this->listName.'] from <'.$address.'>';
if(strlen($mailbody) <= 0)
$mailbody = $mailsubject;
echo 'message for ml owner from <'.$address.">;\n";
$boundary='----NextPart'.md5(date('r', time()));
// change the first header to not send bounced emails to list again and avoid looping
// i'm using the same domain of pop3 ml account
// you can also use an empty value for it:
// Return-Path:
$mailheader.="Return-Path: no-reply".substr($this->listAddress,strpos($this->listAddress,'@')).
"\r\nPrecedence: bulk\r\n";
$mailheader.="From: ".$this->listAddress."\r\nMIME-Version: 1.0\r\nContent-Type: multipart/report; boundary=".
$boundary."\r\n";
$mailheader.='Subject: '.$mailsubject."\r\n";
$mailbody ='--'.$boundary."\r\nContent-Type: text/plain; charset: us-ascii\r\n\r\n".
$mailbody."\r\n\r\noriginal message:\r\n\r\n";
$mailbody.='--'.$boundary.
"\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"msg.eml\"\r\n\r\n".
str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers']))."\r\n".
str_replace("'","\\'",$this->decoded[0]['Body'])."\r\n\r\n".'--'.$boundary."--\r\n\r\n";
$error = $this->SmtpSend($this->listOwner, $mailsubject, $this->listAddress, $mailheader, $mailbody);
if($error != 'OK.') {
echo "owner notify failed [$error]";
return;
}
}
function NotifyBounce($messageid,$bounceerror) {
$id = md5(uniqid(rand(), true));
$mailheader = '';
$mailbody = '';
$mailsubject = 'Delivery Status Notification (Failure) for ['.$this->listName.']';
echo "bouncing for message [$messageid] errors [$bounceerror];\n";
$query = "select * from $this->messages where id = $messageid";
if(!$result = @mysql_query($query,$this->dbconn)) {
return;
}
if(!$row = @mysql_fetch_object($result)) {
return;
}
$boundary='----NextPart'.md5(date('r', time()));
$mailheader.="Return-Path: no-reply".substr($this->listAddress,strpos($this->listAddress,'@')).
"\r\nPrecedence: bulk\r\n";
$mailheader.="From: ".$this->listAddress."\r\nMIME-Version: 1.0\r\nContent-Type: multipart/report; boundary=".
$boundary."\r\n";
$mailheader.='Subject: '.$mailsubject."\r\n";
$mailbody.='--'.$boundary."\r\nContent-Type: text/plain; charset: us-ascii\r\n\r\n".
"bounce error for:\r\n\r\n$bounceerror\r\n\r\n";
$mailbody.='--'.$boundary.
"\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"msg.eml\"\r\n\r\n".
$row->message."\r\n\r\n";
$mailbody.='--'.$boundary."--\r\n\r\n";
$error = $this->SmtpSend($this->listOwner, $mailsubject, $this->listAddress, $mailheader, $mailbody);
if($error != 'OK.') {
echo "owner notify failed [$error]";
return;
}
@mysql_free_result($result);
}
function BounceMessage($sender,$badaddress,$action='Failed',$status='5.1.1',$diagcode='550 5.1.1 User unknown') {
$id = md5(uniqid(rand(), true));
$mailheader = '';
$mailbody = '';
$mailsubject = 'Delivery Status Notification (Failure) for ['.$this->listName.']';
$boundary='----NextPart'.md5(date('r', time()));
$mailheader.="Return-Path:\r\nPrecedence: bulk\r\n";
$mailheader.="From: ".$this->listAddress."\r\nMIME-Version: 1.0\r\nContent-Type: multipart/report; boundary=".
$boundary."\r\n";
$mailheader.='Subject: '.$mailsubject."\r\n";
$mailbody.='--'.$boundary."\r\nContent-Type: text/plain; charset: us-ascii\r\n\r\n".
"bounce error:\r\n\r\nSender: $sender\r\nAddress: $badaddress\r\nAction: $action\r\nStatus: $status\r\nDiagnostic code: $diagcode\r\n\r\n";
$mailbody.='--'.$boundary.
"\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"msg.eml\"\r\n\r\n".
$this->ImplodeHeaders('',$this->decoded[0]['Headers'])."\r\n\r\n".$this->decoded[0]['Body']."\r\n\r\n";
$mailbody.='--'.$boundary."--\r\n\r\n";
$error = $this->SmtpSend($this->listOwner, $mailsubject, $this->listAddress, $mailheader, $mailbody);
if($error != 'OK.') {
echo "owner notify failed [$error]";
return;
}
if($this->debug) {
return;
}
/*
* check if there are queued messages with this badaddress
*/
$query = "select * from $this->queue where addresses REGEXP '(^|,)$badaddress(,|$)'";
if(!$result = @mysql_query($query,$this->dbconn)) {
return;
}
while($row = @mysql_fetch_object($result)) {
/*
* if there is badaddress only, remove queuerecord and set message state to 'sent'
*/
if(!strcmp(trim($row->addresses),$badaddress)) {
@mysql_query('update '.$this->messageTable.
' set state = \'sent\' where id = '.$row->messageid,$this->dbconn);
@mysql_query('delete from '.$this->queue.
' where id = '.$row->id,$this->dbconn);
continue;
}
/*
* there are more then one address, remove it and update queue record
*/
$row->addresses = preg_replace("/(^|,)$badaddress(,|$)/",'\1',$row->addresses);
$row->addresses = trim($row->addresses,"\r\n, ");
@mysql_query('update '.$this->queue." set addresses = '$row->addresses'",$this->dbconn);
}
@mysql_free_result($result);
}
function CheckBounce() {
$address = & $this->decoded[0]['ExtractedAddresses']['from:'][0]['address'];
if(!$address)
return;
$mailbody= & $this->decoded[0]['Body'];
// check for gmail first
if(preg_match('/(google|gmail)/i',$address)) {
if($pos=$this->Stripos($mailbody,"Delivery to the following recipient failed permanently:")) {
$pos+=57; // text plus "\r\n" or "\n\n"
if(!$end=strpos(substr($mailbody,$pos+4),"\r\n\r\n")) {
if(!$end=strpos(substr($mailbody,$pos+4),"\n\n")) {
return; // no match
}
}
$badaddress = trim(substr($mailbody,$pos,$end+4));
$this->BounceMessage($address,$badaddress,'failed','5.1.1','550 5.1.1 User unknown');
return;
}
}
//$this->header = & $this->decoded[0]
if(!$pos = $this->stripos($mailbody,'Content-Type: message/delivery-status')) {
return;
}
if($this->decoded[0]['Headers']['content-type:']
//&& eregi('multipart\/report.*report-type=delivery-status',$this->decoded[0]['Headers']['content-type:'])) {
&& preg_match('/multipart\/report.*report-type=delivery-status/i',$this->decoded[0]['Headers']['content-type:'])) {
if(preg_match('/Final-Recipient:[[:space:]]+(rfc822;|)[[:space:]]+.*(\r|\n)/i',
substr($mailbody,$pos),$matches,PREG_OFFSET_CAPTURE)) {
$pos = strrpos($matches[0][0]," ");
$badaddress = trim(substr($matches[0][0],$pos+1));
$pos = $matches[0][1];
if(preg_match('/Action:[[:space:]]+.*(\r|\n)/i',
substr($mailbody,$pos),$matches,PREG_OFFSET_CAPTURE)) {
$action = trim(substr($matches[0][0],strrpos($matches[0][0]," ")+1));
}
if(preg_match('/Status:[[:space:]]+.*(\r|\n)/i',
substr($mailbody,$pos),$matches,PREG_OFFSET_CAPTURE)) {
$status = trim(substr($matches[0][0],strrpos($matches[0][0]," ")+1));
}
if(preg_match('/Diagnostic-Code:.*(\r|\n)/i',
substr($mailbody,$pos),$matches,PREG_OFFSET_CAPTURE)) {
$diagcode = trim(substr($matches[0][0],strlen('Diagnostic-Code: ')));
}
if(strlen($badaddress) >= 10) {
echo "BOUNCE RESULT [badaddress: $badaddress]; ";
$this->BounceMessage($address,$badaddress,
($action ? $action : ''),
($status ? $status : ''),
($diagcode ? $diagcode : ''));
}
}
}
}
/**
* PASSWORD command to set subscribers password for web access to all ML email messages
* syntax: password NewPassword OldPassword (to change/set a password value)
*/
function SetUserPassword($address, $body) {
$list = null; // array for messages list
$get = $this->GetText('GET',true);
$mailsubject = $this->GetText('NOTIFY SUBJECT', true)." $this->listAddress";
$mailbody = $this->GetText('GENERIC ERROR',true);
$cmd = strtok($body,"\r\n");
$cmd = trim(strtok($cmd," \r\n\t"));
$newpw = trim(strtok(' '),"\r\n\t");
$oldpw = trim(strtok(" \r\n\t"));
$query = "select *,password('$oldpw') as oldpw from ".$this->subscribers." where emailaddress = '$address'";
$result = @mysql_query($query,$this->dbconn);
if(!$row = @mysql_fetch_object($result)) {
$row->webpass = null;
}
$pwmatch = strcmp($row->webpass,$row->oldpw);
if($pwmatch == 0) { // match with old password
if($row->webpass === null) {
$query = 'insert into '.$this->subscribers.
" (id,emailaddress,state,webpass,rowlock) values (0,'$address','enabled',password('".$newpw."'),'')";
} else {
$query = 'update '.$this->subscribers." set webpass = password('$newpw') where emailaddress = '$address'";
}
if(!$this->debug) {
$result = @mysql_query($query,$this->dbconn);
} else {
$result = true;
}
if($result) {
$mailbody = $this->GetText('USERPASS CHANGED',true)."\n$newpw";
}
echo "changed password for [$address]; ";
} else {
$mailbody = $this->GetText('USERPASS WRONG',true);
echo "wrong password for [$address] changing request; ";
}
@mysql_free_result($result);
$this->NotifyUser($address,false,$mailsubject,$mailbody);
}
/**
* GET command:
* get 1 // to retrieve message 1
* get 1,2,3 // to retrieve 1,2 and 3 message
* get 1,2,5-10,11 // to retrieve 1,2,from 5 to 10 and 11 message
*/
function GetCommand($address, $body) {
$list = null; // array for messages list
$get = $this->GetText('GET',true);
$body = trim($body,"\n\r ,.<>");
$bodytest = trim(substr($body,strlen($get)+1),"\n\r ,.<>");
if(strlen($bodytest) < 1)
return;
if(strstr($bodytest,',')) { // request for specific message(s) ie. 'get 1,4,10'
$list = explode(',',$bodytest);
} else {
$list[] = &$bodytest;
}
// retrieve all 'sent' messages and 'queued' owned by request sender
$query = "select * from $this->messages where (state = 'sent' || (state = 'queued' && mailfrom = '".
$address."')) and (";
foreach($list as $request) {
$token = explode('-',$request);
$query .=
(sizeof($token) == 1 ?
('id = '.$token[0])
:
('(id >= '.$token[0].' and id <= '.$token[1]).')');
$query .= ' or ';
}
if(!$result=@mysql_query(substr($query,0,-4).')',$this->dbconn)) {
return false;
}
$mailsubject = 'Subject: '.$this->listName.': '.$body;
$mailheader = '';
$mailbody = '';
$boundary = '';
$messagelist = '';
// foreach($this->headersChange as $hk => $hv) { if(strcmp($hv,'')) { $mailheader.= $hk.': '.$hv."\r\n"; } }
$mailheader.= "Return-Path: ".$this->listOwner."\r\n";
$mailheader.= "Precedence: bulk\r\n";
$boundary='----NextPart'.md5(date('r', time()));
$mailheader.="From: ".$this->listAddress."\r\nMIME-Version: 1.0\r\nContent-Type: multipart/digest; boundary=".
$boundary."\r\n";
$mailbody.='--'.$boundary."\r\nContent-Type: text/plain; charset: us-ascii\r\n\r\n".
$this->listName." $body:\r\n\r\n";
echo 'get request response for # '.$body."; ";
while($row = @mysql_fetch_object($result)) {
$mailbody.="Id: ".$row->id."\r\nSender: ".$row->mailfrom."\n\nSubject: ".$row->subject."\r\n";
$messagelist.='--'.$boundary.
"\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"msg".$row->id.".
eml\"\r\n\r\n".$row->message."\r\n\r\n";
$mailbody.="\r\n\r\n";
}
@mysql_free_result($result);
$mailheader.=$mailsubject;
$mailbody.=$messagelist.'--'.$boundary."--\r\n\r\n";
unset($messagelist);
$error = $this->SmtpSend($address,$mailsubject,$this->listAddress,$mailheader,$mailbody);
if($error != 'OK.') {
echo " get-error [$error] for [$mailaddress];";
}
return;
}
/**
* @return '' no scheduled mails
* @return schedules mail list
*/
function GetScheduledMails(& $address) {
$retval = "\r\n";
$query = "select id,date,subject from $this->messages where state = 'queued' and listname = '".
$this->listName."' and mailfrom = '$address' and time_to_sec(timediff(now(),date)) < 0 order by date";
if(!$result=@mysql_query($query,$this->dbconn)) {
return $retval;
}
while($row = @mysql_fetch_object($result)) {
$retval.="Id: ".$row->id." - ".$row->date." - ".$row->subject."\r\n";
}
return $retval;
}
/**
* SCHEDDROP command:
* SCHEDDROP 1 drop scheduled message with id = 1
*/
function DropScheduledMail($address, $body) {
$list = null; // array for messages list
$command = $this->GetText('SCHEDDROP',true);
$retval = $body."\r\n\r\n".$this->GetText('SCHEDULED DROP RESULT',true);
if($this->debug) {
return $retval." [".$this->GetText('DONE',true)."]\r\n";
}
$bodytest = trim($body,"\n\r ,.<>");
$bodytest = trim(substr($body,strlen($command)+1),"\n\r ,.<>");
$query = "delete from $this->messages where state = 'queued' and id = $bodytest";
mysql_query($query,$this->dbconn);
if(mysql_affected_rows($this->dbconn) <= 0) {
return $retval."\r\n\r\n".$this->GetText('GENERIC ERROR',true);
}
$query = "delete from $this->queue where messageid = $bodytest";
mysql_query($query,$this->dbconn);
if(mysql_affected_rows($this->dbconn) <= 0) {
return $retval."\r\n\r\n".$this->GetText('GENERIC ERROR',true);
}
return $retval." [".$this->GetText('DONE',true)."]\r\n";
}
// TODO: insert 'LIST' command to list all messages
function CheckMlCommand($address, & $substate) {
if(!$bodytest=$this->GetSingleBodyPart($this->decoded[0]['Body'],'text/plain')) {
$bodytest = $this->decoded[0]['Body'];
}
$bodytest = ltrim($bodytest,"\r\n <>");
$cmd = strtok($bodytest,"\r\n");
if(preg_match("/^".$this->GetText('GET',true)."/i",$cmd)) {
if($this->debug) $this->debugOutput .= "\nGot command: GET\n";
$this->GetCommand($address,$bodytest);
return true;
} else if(preg_match("/^".$this->GetText('NOTIFY OWNER',true)."/i",$cmd)) {
if($this->debug) $this->debugOutput .= "\nGot command: NOTIFY OWNER\n";
$this->NotifyOwner($address);
return true;
//} else if(preg_match("/^".$this->GetText('HELP',true)."/i",$cmd)) {
} else if(!strcasecmp($this->GetText('HELP',true),$cmd)) {
if($this->debug) $this->debugOutput .= "\nGot command: HELP\n";
$this->NotifyUser($address,false,
$this->GetText('HELP SUBJECT',true),
str_replace('X-Scheduled',$this->scheduledTime,$this->GetText('HELP MESSAGE',true)));
return true;
} else if(preg_match("/^PASSWORD/i",$cmd)) {
if($this->debug) $this->debugOutput .= "\nGot command: PASSWORD\n";
$this->SetUserPassword($address,$bodytest);
return true;
} else if(preg_match("/^".$this->GetText('SCHEDLIST',true)."/i",$cmd)) {
if($this->debug) $this->debugOutput .= "\nGot command: SCHEDLIST\n";
echo "got command: SCHEDLIST; ";
if(strcasecmp($substate,'mailer') && strcasecmp($substate,'deny')) {
$mailsubject = $this->GetText('NOTIFY SUBJECT',true).$this->listAddress;
$mailbody = $this->GetText('SCHEDULED MAIL LIST',true).$this->GetScheduledMails($address);
$this->NotifyUser($address,false,$mailsubject,$mailbody);
return true;
}
} else if(preg_match("/^".$this->GetText('SCHEDDROP',true)."/i",$cmd)) {
if($this->debug) $this->debugOutput .= "\nGot command: SCHEDDROP\n";
echo "got command: SCHEDDROP; ";
if(strcasecmp($substate,'mailer') && strcasecmp($substate,'deny')) {
$mailsubject = $this->GetText('NOTIFY SUBJECT',true).$this->listAddress;
$mailbody = $this->DropScheduledMail($address,$bodytest);
$this->NotifyUser($address,false,$mailsubject,$mailbody);
return true;
}
}
$bodytest = preg_replace("/\n|\r|=/",'',$bodytest);
if($this->IsSubscription($bodytest, $address,$substate) == true) {
return true;
}
if($this->IsUnsubscription($bodytest, $address,$substate) == true) {
return true;
}
// no ml command
$this->bodyTextPlain = & $bodytest;
return false;
}
/**
* return values:
* 'deny' -> no access to ML
* 'subscribed' -> address exists into 'sublist' field
* 'digest' -> address exists into 'digestsublist' field
* 'subscribeddigest' -> address exists into both 'sublist/digestsublist' fields
* 'allow' -> address exists into 'allow' field
* 'mailer' -> it's automatic mailer
* 'moderator' -> moderator or owner address
* 'public' -> address isn't subscribed but ml is configured as public mailing list
*/
function CheckSender($address) {
$retval = 'deny';
if(!$address || strlen($address) < 7) { // invalid sender, ignore email
return $retval;
}
if(class_exists('Bouncehandler')) {
$head = str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers']));
$body = str_replace("'","\\'",$this->decoded[0]['Body']);
$multiArray = Bouncehandler::get_the_facts($head."\r\n".$body);
if(!@empty($multiArray['recipient'])) { // email bounced, notify owner
$this->BounceMessage($address,$multiArray[0]['recipient'],
$multiArray[0]['action'],
$multiArray[0]['status'],
$multiArray[0]['status']);
return 'mailer';
}
// make a specific body test
$head_hash = BounceHandler::parse_head($head);
$boundary = @$head_hash['Content-type']['boundary'];
$mime_sections = BounceHandler::parse_body_into_mime_sections($body, $boundary);
$rpt_hash=BounceHandler::parse_machine_parsable_body_part(@$mime_sections['machine_parsable_body_part']);
for($i=0; $i<count(@$rpt_hash['per_recipient']); $i++){
if(!@empty($rpt_hash['per_recipient'][$i]['Action'])) {
$this->BounceMessage($address,
implode(", ",$rpt_hash['per_recipient'][$i]['Final-recipient']),
$rpt_hash['per_recipient'][$i]['Action'],
$rpt_hash['per_recipient'][$i]['Status'],
$rpt_hash['per_recipient'][$i]['X-supplementary-info']);
return 'mailer';
}
}
} // else if($this->decoded[0]............... to do only once 'bounce check'
if(preg_match('/(mailer-daemon|majordomo|virus|scanner|automated-response|smtp[.-]gateway|mailadmin|mailmaster|surfcontrol|postmaster|no(-|)reply|nobody|devnull)/i',
$this->decoded[0]['ExtractedAddresses']['from:'][0]['address'])
|| preg_match('/auto(_|-|)reply/i',@$this->decoded[0]['Headers']['precedence:'])) {
$this->CheckBounce();
return 'mailer';
}
// be sure it's not an undetected autoresponder (ie. 'From: "Gmail Team" <mail-hide@address.com>')
if(isset($this->decoded[0]['Headers']['precedence:'])
//&& eregi('(bulk|junk)',$this->decoded[0]['Headers']['precedence:'])
&& preg_match('/(bulk|junk)/i',$this->decoded[0]['Headers']['precedence:'])
|| preg_match('/no(-|_|)reply/i',
$this->decoded[0]['ExtractedAddresses']['from:'][0]['address'])) {
return 'mailer';
}
if(!strcmp($address,$this->listOwner)
//|| eregi("(^|\n)$address(\n|$)",$this->modSublist)) {
|| preg_match("/(^|\n)$address(\n|$)/im",$this->modSublist)) {
return 'moderator';
}
if(is_array($this->allow)) {
foreach($this->allow as $pattern) {
//if(eregi($pattern, $address)) {
if(preg_match("/$pattern/i", $address)) {
return 'allow';
}
}
}
// it's no 'allow/modsublist' address, so if it's a newsletter, quit
// (if newsletter, only 'allow'/modsublist' addresses can post)
if($this->dbrow->mltype != 'm') {
return 'deny';
}
if(is_array($this->deny)) {
foreach($this->deny as $pattern) {
//if(eregi($pattern, $address)) {
if(preg_match("/$pattern/i", $address)) {
return 'deny';
}
}
}
/*
* check if exists a 'subscribers' record for this sender
*/
$query = 'select state from '.$this->subscribers." where emailaddress = '$address' and state != 'enabled'";
if($result = @mysql_query($query,$this->dbconn)) {
if($row = @mysql_fetch_object($result)) {
// there is a disabled/suspended 'subscribers' record, don't allow post
mysql_free_result($result);
return 'deny';
}
mysql_free_result($result);
}
if(preg_match('/(^|\n)'.$address.'(\n|$)/i',$this->sublist)) {
$retval = 'subscribed';
}
if(preg_match('/(^|\n)'.$address.'(\n|$)/i',$this->digestSublist)) {
$retval = (!strcmp($retval,'deny') ? '' : $retval) . 'digest';
}
if(strcasecmp($this->dbrow->subscribersonly,'yes') && !strcmp($retval,'deny')) { // ml is open to all
$retval = 'public';
}
return $retval;
}
function ListMessages() {
$result=$this->pop3->ListMessages("",0);
if(GetType($result)=="array") {
for(Reset($result),$message=0;$message<count($result);Next($result),$message++) {
echo "Message ",Key($result)," - ",$result[Key($result)]," bytes.\n";
}
$result=$this->pop3->ListMessages("",1);
if(GetType($result)=="array") {
for(Reset($result),$message=0;$message<count($result);Next($result),$message++) {
echo "Message ",Key($result),", Unique ID - \"",$result[Key($result)],"\"\n";
}
}
}
}
function CacheMessage() {
if(!file_exists($this->cachePath)) {
return;
}
if(!file_put_contents($this->cachePath.$this->listName.'-'.date("Ymd-H:i:s").'.eml',
str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers'])).
"\r\n".str_replace("'","\\'",$this->decoded[0]['Body']))) {
return;
}
sleep(1); // to avoid 2 messages with the same path
}
function Pop3Start() {
$state = strpos($this->dbrow->hostname,"\t");
$token = explode(($state === false ? ':' : "\t"),$this->dbrow->hostname);
$this->pop3->hostname=$this->listHostName=$token[0]; /* POP 3 server host name */
$this->pop3->port=$this->listPort=$token[1]; /* POP 3 server host port,
usually 110 but some servers use other ports
Gmail uses 995 */
$this->pop3->tls=$this->listTls=$token[2]; /* Establish secure connections using TLS */
$this->pop3->realm=""; /* Authentication realm or domain */
$this->pop3->workstation=""; /* Workstation for NTLM authentication */
$this->pop3->authentication_mechanism="USER"; /* SASL authentication mechanism */
if($this->pop3Debug)
$this->pop3->debug=1; /* Output debug information */
//$this->pop3->html_debug=1; /* Debug information is in HTML */
//$this->pop3->join_continuation_header_lines=1; /* Concatenate headers split in multiple lines */
}
/* check for new messages */
function Pop3Read() {
$retval = '';
$apop=0;
$user=$this->listUser;
$password=$this->listPopPass;
$this->error='';
$messages='';
$address = '';
$substate = 'deny';
$messageid = '';
if(($this->error=$this->pop3->Open())!="") {
echo "Opening connection error: $this->error\n";
return $this->error;
}
if(($this->error=$this->pop3->Login($user,$password,$apop))!="") {
echo "Connection error: $this->error\n";
return $this->error;
}
if(($this->error=$this->pop3->Statistics($messages,$size))=="")
{
echo "$messages mess.".($size > 0 ? " total size [$size]" : '');
if($this->maxPop3MsgLimit && $messages > $this->maxPop3MsgLimit) {
$messages = $this->maxPop3MsgLimit;
echo " WARNING, APPLIED LIMIT TO MAX [$messages] MESSAGES.";
}
for($i=0; $i < $messages; $i++)
{
$error = '';
$this->pop3->GetConnectionName($connection_name);
$message=$i + 1;
$size = $this->pop3->ListMessages($message,0);
echo "\n # $message, size [$size]: ";
$message_file='pop3://'.$connection_name.'/'.$message;
$mime=new mime_parser_class;
$mime->decode_bodies = 0;
if($this->maxMsgSize && $size > $this->maxMsgSize) {
$mime->decode_bodies = 1;
echo " Warning [exceeding].";
}
$parameters=array(
'File'=>$message_file //, 'SaveBody'=>1,
);
$success=$mime->Decode($parameters, $this->decoded);
if(!$success) {
echo 'MIME message decoding error: '.HtmlSpecialChars($mime->error)." .";
} else {
if($this->cacheMessages) {
$this->CacheMessage();
}
if(!strcasecmp($this->removeAfterPop,'yes')) {
$this->error.=$this->RemoveMessage($message)."\n";
}
$address = @$this->decoded[0]['ExtractedAddresses']['from:'][0]['address'];
echo " sender [$address]: ";
/* enable if you want to log 'messageid'
* if(isset($this->decoded[0]['Headers']['message-id:'])) {
* $messageid = $this->decoded[0]['Headers']['message-id:'];
* echo " message-id [$messageid]: ";
* }
*/
if(@$this->logSubject && @$this->decoded[0]['Headers']['subject:']) {
echo 'subject ['.substr($this->decoded[0]['Headers']['subject:'],0,$this->logSubject).']: ';
}
if(!$address) {
echo ' bad address for ['.$this->GetReturnPath('').'ignoring message (saved into cache); ';
$this->CacheMessage();
continue;
}
// now rebuild sublist fields because there could be some changes in this ML
// or, eventually, parent/children ML
$this->InitSublistFields();
$this->substate=$substate=$this->CheckSender($address);
if($this->CheckMlCommand($address,$substate) == true) { // got ML command, jump
continue;
}
echo "[$substate] ";
if(strstr($substate,'mailer')) {
if(!$this->forwardMailerTo) {
echo "dropping message";
continue;
} else if(!strcasecmp($this->forwardMailerTo,'cache') && !$this->cacheMessages) {
echo "storing message";
$this->CacheMessage();
continue;
// check if email address to forward isn't the list address email to avoid message loop
} else if(strcasecmp($this->forwardMailerTo,'LISTNAME')
&& strcasecmp($this->forwardMailerTo, $this->listAddress)) {
$mailsubject = "DROPPED MESSAGE FROM MAILER [$address]; ".
stripslashes($this->decoded[0]['Headers']['subject:'])."]";
$recipient = explode(',',str_replace(' ','',str_replace("\r",'',$this->forwardMailerTo)));
if($this->MailFilter($address) != true) {
echo "applying MailFilter to message for [$this->forwardMailerTo]";
}
echo "forwarding message to [$this->forwardMailerTo]: ";
$error = $this->SmtpSend($recipient, $mailsubject, $this->listAddress,
$this->ImplodeHeaders('',$this->decoded[0]['Headers']), $this->decoded[0]['Body']);
if($error != 'OK.') {
$this->NotifyOwner($this->forwardMailerTo,
'User notification failed, ml ['.$this->listAddress.']',
'Notification failure for ml ['.$this->listAddress.'] for address <'.
$this->forwardMailerTo."> smtp error [$error]");
} else {
echo 'sent to ['.sizeof($recipient).'] users. End';
}
continue;
}
// if got here it means 'forwardMailerTo' is set to listaddress email, so proceed as normal msg
echo "forwarding message to list: ";
}
if(strstr($substate,'deny')) {
echo " sorry, you are not subscribed yet and/or not allowed to post.";
$this->SendSubscriptionError(
$this->GetReturnPath($address),false,'UNSUBSCRIPTION ERROR STATE');
continue;
}
/* message is ready for delivery, check 'filter' for any kind of rule */
if($this->MailFilter($address) == true) {
$this->SendMessage($address,$size);
}
}
}
}
echo $this->logcr."\n";
// moved after this function to ensure closing the connection
//$this->error=$this->pop3->Close();
$retval = $this->error;
return $retval;
}
function AddTrailer(&$mailbody) {
$retval = '';
if(strlen($this->trailerFile) <= 0) {
return;
}
$content_type = @explode(';',$this->decoded[0]['Headers']['content-type:']);
$content_type[0] = trim($content_type[0]);
// if(!strstr($content_type[0],'alternative') && !strstr($content_type[0],'digest')
// && !strstr($content_type[0],'mixed')) {
if(!$content_type[0] || strstr($content_type[0],'text/plain')) {
$mailbody.="\r\n".$this->SetText($this->trailerFile)."\r\n";
return;
}
// no text/plain message. search 'text/plain' first, 'text/html' after
preg_match('/(.*)(\r|)\nContent-Type: text\/plain/i',
$mailbody,$matches,PREG_OFFSET_CAPTURE,1);
if(@$matches[0][0]) {
$pos=$matches[0][1];
$pos+=25; // add 'Content-Type: text/plain' character length
$nextpart=trim(strtok($matches[0][0],"\r\n"));
// search for last 'NextPart' of this body part
$last=strpos(substr($mailbody,$pos),$nextpart);
$last+=$pos - 2;
if($mailbody[$last] == "\n" || $mailbody[$last] == "\r") {
$last-=2;
}
$mailbody=substr($mailbody,0,$last)."\r\n".
$this->SetText($this->trailerFile).
substr($mailbody,$last)."\r\n";
}
preg_match('/(.*)(\r|)\nContent-Type: text\/html/i',
$mailbody,$matches,PREG_OFFSET_CAPTURE,1);
if(@$matches[0][0]) {
$pos=$matches[0][1];
$pos+=24; // add 'Content-Type: text/html' character length
$nextpart=trim(strtok($matches[0][0],"\r\n"));
// search for last 'NextPart' of this body part
$last=strpos(substr($mailbody,$pos),$nextpart);
$last+=$pos - 2;
preg_match('/(\r|)\n<\/body>(\r|)\n<\/html>(\r|)\n/i',
substr($mailbody,$pos,$last),$matches,PREG_OFFSET_CAPTURE,1);
if($matches && @$matches[0][1]) {
$mailbody=substr($mailbody,0,$pos+$matches[0][1])."\r\n<br>".
$this->SetText(str_replace("\n","\r\n<br>",$this->trailerFile)).
substr($mailbody,$pos+$matches[0][1]+1)."\r\n";
}
}
return;
}
/*
* @return false not ready to send
* @return true ready to send
*/
function CheckQueueTime(&$row, &$msgrow) {
$messagetime=strtotime($msgrow->date);
$queuetime=strtotime($row->date);
$timeqm=$queuetime-$messagetime;
$mult=($timeqm - ($timeqm % (24*60*60))) / (24*60*60) + 1;
if($this->debug) {
echo "messagetime [$messagetime] [".date(DATE_RFC822,$messagetime)."]\n";
echo "queuetime [$queuetime] [".date(DATE_RFC822,$queuetime)."]\n";
echo "now [$this->now] [".date(DATE_RFC822,$this->now)."]\n";
echo "timeqm: [".$timeqm."]\n";
echo "mult: [".$mult."]\n";
echo "time: [".($this->minTimeResendMsg *$mult)."]\n";
echo "time passed from last send and message date: [$timeqm]\n";
echo "time passed from last send and now: [".($this->now-$queuetime)."]\n";
}
if($this->now < $queuetime) { // message has been scheduled and not yet ready to send
return false;
}
if(($this->now - $queuetime) <= ($this->minTimeResendMsg * $mult)) {
return false;
}
return true;
}
/**
* this function looks for scheduling mail pattern into message's header
*/
function IsScheduledMail(& $queuerow, & $msgrow) {
if(!file_exists(CLASSES_DIR_PATH.DS.'class.scheduledate.php')) {
return;
}
$pattern = $msgrow->header;
$date = false;
$repeat = false;
if(!$pattern) {
return false;
}
if(!preg_match("/(^|\r|\n)".$this->scheduledTime."(:|) +(.*).*(\r|\n|$)/i",
trim($pattern),$matches,PREG_OFFSET_CAPTURE)) {
return false;
}
if(!@$matches || !@$matches[3][0]) {
return false;
}
$pattern = $matches[3][0];
$matches = null;
require_once(CLASSES_DIR_PATH.DS.'class.scheduledate.php');
if(ScheduleDate::Parse($pattern,$matches) === false) {
return false;
}
$sd = new ScheduleDate;
if(($week=$this->GetText('SCHEDULED WEEK',false)) !== false) {
$sd->week = explode(',',$week);
}
if(($date=$sd->Renew($pattern,$queuerow->date,$this->now)) === false) {
return false;
}
if($this->debug) {
echo "\n # message renewed to [$date]; ";
return true;
}
$date = date("Y-m-d H:i:s",$date); // $date = $sd->date;
// date has been renewed, so create new queue/message records
$query = "insert into ".$this->messages.
" (id,date,state,listname,smtp,mailfrom,subject,message,header,rowlock) values(0,'$date','queued',".
"'$msgrow->listname','$msgrow->smtp','$msgrow->mailfrom','$msgrow->subject','$msgrow->message','".
str_replace($this->scheduledTime.': '.$pattern,$this->scheduledTime.': '.$pattern,$msgrow->header)."','')";
$command = @mysql_query($query,$this->dbconn);
$messageid = @mysql_insert_id($this->dbconn);
$query = "insert into $this->queue (id,date,smtp,listname,messageid,addresses,rowlock) values(0,'$date','".
"$queuerow->smtp','$queuerow->listname',$messageid,'$queuerow->addresses','')";
$command = @mysql_query($query,$this->dbconn);
echo "\n # $messageid renewed to [$date]; ";
return true;
}
function SendFromQueue() {
$query = "select * from $this->queue where listname = '$this->listName'";
$result = @mysql_query($query,$this->dbconn);
while($row = @mysql_fetch_object($result)) {
// get entire message row
$messageid = $row->messageid;
$query = "select * from $this->messages where id = $messageid";
$msgresult = @mysql_query($query,$this->dbconn);
if(!$msgrow = @mysql_fetch_object($msgresult)) {
// there is not a associated message, remove queue record
$query = "delete from $this->queue where id = $row->id";
$command = @mysql_query($query,$this->dbconn);
continue;
}
// check if message is expired
if($this->CheckQueueTime($row,$msgrow) != true) {
// not enaught time has passed, retry later
continue;
}
// ok, try to resend message
echo "\n # $messageid sent from queue; ";
// change smtp server if any
$this->smtpIndex = $row->smtp + 1;
if($this->smtpIndex > (sizeof($this->smtpServer) - 1)) {
$this->smtpIndex = 0;
}
$addresses = explode(',', $row->addresses);
$bounceerror = '';
$addresserror = '';
foreach($addresses as $address) {
// remove error code (if exists) from address
$address = preg_replace('/^\[.*\]/','',$address);
$pos=strpos($msgrow->message,"\r\n\r\n");
if(!$pos) $pos=strpos($msgrow->message,"\n\n");
$error = $this->SmtpSend($address, $msgrow->subject, $this->listAddress,
substr($msgrow->message,0,$pos),
substr($msgrow->message,$pos+2));
if($error != 'OK.') {
if($error[0] == '5') {
$bounceerror.="[$error]$address\n";
} else {
echo " error [$error] for [$address];";
$addresserror.=((strlen($addresserror) <= 0) ? '': ',').
"[$error]$address";
}
}
}
// check if it is a scheduled message
$this->IsScheduledMail($row,$msgrow);
if(strlen($addresserror) > 1) {
$query = "update $this->queue set date = now()".
", addresses = '".str_replace("'","\\'",$addresserror).
"', smtp = $this->smtpIndex where id = $row->id";
$command = @mysql_query($query,$this->dbconn);
} else {
$query = "update $this->messages set state = 'sent', smtp = '".
$this->smtpIndex."' where id = $messageid";
$command = @mysql_query($query,$this->dbconn);
$query = "delete from $this->queue where id = $row->id";
$command = @mysql_query($query,$this->dbconn);
}
if(strlen($bounceerror) > 1) {
$this->NotifyBounce($messageid,$bounceerror);
}
@mysql_free_result($msgresult);
echo "\n ";
}
@mysql_free_result($result);
$this->smtpIndex = 0;
return true;
}
function ApplyNotifyFilterCommand($address, $text) {
$id = md5(uniqid(rand(), true));
$mailheader = '';
$mailsubject = $this->GetText('NOTIFY SUBJECT',true)."[$this->listAddress] <$address>";
$boundary='----NextPart'.md5(date('r', time()));
$mailheader.="Return-Path: ".$this->listOwner."\r\nPrecedence: bulk\r\n";
$mailheader.="From: ".$this->listAddress."\r\nMIME-Version: 1.0\r\nContent-Type: multipart/report; boundary=".
$boundary."\r\n";
$mailheader.='Subject: '.$mailsubject."\r\n";
$mailbody ='--'.$boundary."\r\nContent-Type: text/plain; charset: us-ascii\r\n\r\n".
$text."\r\n\r\noriginal message:\r\n\r\n";
$mailbody.='--'.$boundary.
"\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"msg.eml\"\r\n\r\n".
str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers']))."\r\n".
str_replace("'","\\'",$this->decoded[0]['Body'])."\r\n\r\n".'--'.$boundary."--\r\n\r\n";
$address = explode(',',str_replace(' ','',str_replace("\r",'',$address)));
$error = $this->SmtpSend($address, $mailsubject, $this->listAddress, $mailheader, $mailbody);
if($error != 'OK.') {
$this->NotifyOwner($address,'User notification failed, ml ['.$this->listAddress.'] for address <'.
$address.'>');
}
}
function ApplyCommand(& $address, & $header, & $body, $command) {
echo " matching filter, ";
$retval = true;
//$cmd=explode(' ',$command);
for($i=0; $i < sizeof($command); $i++) {
$cmd = trim(strtolower(strtok($command[$i],' ')));
if($this->debug) {
$this->debugOutput .= "\nMATCHING FILTER: $cmd\n";
}
// check if command is commented with `#' to disable it
if($cmd[0] && $cmd[0] == '#') {
echo "ignoring filter; ";
continue;
}
switch(strtolower(trim($cmd))) {
case 'drop': // catched "drop", don't send message
echo "applying [$cmd] filter; ";
$retval = false;
break;
case 'owner': // notify owner
$this->NotifyOwner($address,
'Filter notification, ml ['.$this->listAddress.'] for address <'.
$address.'>',
strtok('')."\r\n\r\n");
break;
case 'store': // store message if CACHE_MESSAGES is not true
echo "applying [$cmd] filter; ";
if($this->cacheMessages == false) {
$this->CacheMessage();
}
break;
case 'notify': // notify user
echo "applying [$cmd] filter; ";
$this->ApplyNotifyFilterCommand($address,strtok(''));
break;
case 'redirect': // redirect message
case 'forward': // alias of redirect
echo "applying [$cmd] filter; ";
$recipients = explode(',',str_replace(' ','',strtok('')));
$error = $this->SmtpSend($recipients, $mailsubject, $address, $header, $body);
if($error != 'OK.') {
$this->NotifyOwner($address,"Filter [$cmd] failed, ml [$this->listAddress] for address <$address>");
}
break;
}
}
return $retval;
}
function ApplyFilter(& $address, & $pattern, & $buffer) {
foreach($pattern as $rgx) {
if(strlen($rgx) <= 1)
continue;
$flag = false;
if($rgx[0] == '!') {
$flag = true; // invert regexp result
$rgx = substr($rgx,1);
}
$stat = @preg_match('/'.str_replace('/','\/',$rgx).'/im',$buffer);
if((!$stat && $flag == false) || ($stat && $flag == true)) { // one regexp failed, no match, quit
return false;
}
}
// every regexp matched, apply command
return true;
}
function MailFilter($address) {
if(strlen($this->dbrow->mailfilter) < 10)
return true;
$header = '';
$retval = true;
$body = & $this->decoded[0]['Body'];
$filter = explode(':0',$this->dbrow->mailfilter);
foreach($filter as $rule) {
if(strlen($rule) < 3)
continue;
$rule = str_replace("\r",'',$rule);
$pos = strpos($rule,"\n");
$flag = substr($rule,0,$pos);
$rule = substr($rule,$pos);
$pattern = array();
$command = array();
while(strlen($rule)) {
$token = trim(strtok($rule,"\n"));
if(strlen($token) > 1) {
if($token[0] == '*') {
$pattern[] = trim(substr($token,1),"\n\r;,. ");
} else {
$command[] = trim($token,"\n\r;,. ");
}
}
$rule = strtok("");
}
$status = false;
if(!strcasecmp($flag,' h') || !strcasecmp($flag,'')) {
if(!$header) {
$header = $this->ImplodeHeaders('',$this->decoded[0]['Headers']);
}
if($this->ApplyFilter($address,$pattern,$header) == true) {
$status = true;
}
}
if(!strcasecmp($flag,' b') || !strcasecmp($flag,'')) {
if($this->ApplyFilter($address,$pattern,$body) == true) {
$status = true;
}
}
if($status == true) {
// if there is just one "drop" set retval to false, message won't be sent
if($this->ApplyCommand($address,$header,$body,$command) == false) {
$retval = false;
}
}
}
return $retval;
}
/*
* regexp sample:
* thanks to Gregor Buchholz
*
* Subject [Bigband-__LISTNAME__] \\1 {/(.*)/i}
* '/(\[Bigband-__LISTNAME__\]).+\[Bigband-__LISTNAME__\]/','\1'
*/
function RebuildHeader (& $mailheader) {
$headers = str_replace("__USERADDRESS__",$this->sender,$this->headersChange);
$headers=explode("\n",$headers);
$decodedheaders = $this->decoded[0]['Headers'];
foreach($headers as $token) {
if(@$token[1] == '/') { // got regexp, jump (it will be executed later)
continue;
}
$hkey = trim(strtolower(strtok($token,': ')));
$opt = false;
if(strlen($hkey) > 1 && ($hkey[0] == '!' || $hkey[0] == '|')) {
$opt = true;
$hkey = substr($hkey,1);
}
$replace = trim(strtok("{"));
$pattern = trim(strtok(''),"}\n\r ");
$header = & $decodedhaders["$hkey:"];
/**
* if first character is '!' add header only if not already set
*/
if($header && !$opt) {
if(strlen($replace) <= 0) { // header key only, no pattern, no value, remove header
unset($decodedheaders["$hkey:"]);
continue;
}
} else {
if(strlen($replace) <= 0) { // no header, but no value, jump
continue;
}
$decodedheaders["$hkey:"] = '';
$header = & $decodedheaders["$hkey:"];
}
if(strlen($pattern) <= 0) {
$pattern = '/(.*)/';
}
$header = preg_replace($pattern,$replace,$header,1);
}
// rebuild mail headers
foreach($decodedheaders as $key => $val) {
if(is_array($val)) {
foreach($val as $token) {
$mailheader .= $key.' '.trim($token,"\n\r ")."\r\n";
}
} else if(strlen($val) > 0) {
$mailheader .= $key.' '.trim($val,"\n\r ")."\r\n";
}
}
// now check for header regexp like '/^X-.*\r\n/im',''
foreach($headers as $token) {
if(@$token[1] != '/') {
continue;
}
eval("\$mailheader = preg_replace($token,\$mailheader);");
}
}
/**
* check if exists extra header 'X-Scheduled' (or initial part of subject) to queue message
* @return = false no schedule request, message will not be scheduled
* @return = true there is an invalid schedule request, message will not be scheduled and sender alerted by mail
* @return = 'VALID DATE' there is a valid schedule request, message will be scheduled and sender confirmed by mail
*/
function CheckScheduledTime(& $sender) {
$pattern = false;
$retval = false;
$repeat = false;
if(!file_exists(CLASSES_DIR_PATH.DS.'class.scheduledate.php')) {
return false;
}
// think about using 'similar_text' function to avoid malformed request sent to ML
// if(strcasecmp($this->removeAfterPop,'yes')) {
// return false;
// }
if(@$this->decoded[0]['Headers'][strtolower($this->scheduledTime).':']) {
$pattern = trim($this->decoded[0]['Headers'][strtolower($this->scheduledTime).':'],"\"' ");
} else if(preg_match("/^\[".$this->scheduledTime."(:|) +(.*)\]/i",
@$this->decoded[0]['Headers']['subject:'],$matches,PREG_OFFSET_CAPTURE)) {
if(@$matches[2][0]) {
$pattern = trim($matches[2][0],"\"' ");
}
$this->decoded[0]['Headers']['subject:'] =
ltrim(substr($this->decoded[0]['Headers']['subject:'],strlen($matches[0][0])));
} else if(preg_match("/^".$this->scheduledTime."(:|) +(.*).*(\r|\n|$)/i",
ltrim($this->bodyTextPlain,'*'),$matches,PREG_OFFSET_CAPTURE)) {
if(@$matches[2][0]) {
$pattern = trim($matches[2][0],"\"' ");
}
if(!@$this->decoded[0]['Headers']['content-type:']
|| strcasecmp(substr($this->decoded[0]['Headers']['content-type:'],0,10),'text/plain')) {
$this->decoded[0]['Body'] =
ltrim(substr($this->decoded[0]['Body'],strlen($matches[0][0])));
}
} else {
return false;
}
require_once(CLASSES_DIR_PATH.DS.'class.scheduledate.php');
if(ScheduleDate::Parse($pattern,$matches) !== true) {
$mailsubject = $this->GetText('NOTIFY SUBJECT',true).' '.$this->listAddress;
$mailbody = $this->GetText('SCHEDULED MAIL ERROR',true)." MALFORMED DATE [$pattern]".
"\r\n\r\n[".$this->decoded[0]['Headers']['subject:']."]\r\n";
$this->NotifyUser($sender,false,$mailsubject,$mailbody);
return true;
}
$sd = new ScheduleDate;
if(($week=$this->GetText('SCHEDULED WEEK',false)) !== false) {
$sd->week = explode(',',$week);
}
$sd->matches = $matches;
$this->scheduledPattern = $pattern;
$retval = date("Y-m-d H:i:s",$sd->GetFirstRun($pattern,$this->now));
if(!@$this->decoded[0]['Headers'][strtolower($this->scheduledTime).':']) {
$this->headersChange.="\n".$this->scheduledTime.' '.$pattern;
}
return $retval;
}
/* send section */
function SendMessage($sender,$msgSize) {
$error = '';
$messageid = -1;
$mailheader = '';
$mailbody = '';
$mailsubject = '';
$ok = 0;
$originalSender = false;
$this->sender = (@$this->decoded[0]['Headers']['from:'] ? $this->decoded[0]['Headers']['from:'] : $sender);
if(($scheduledTime = $this->CheckScheduledTime($sender)) === true) { // invalid schedule request
echo "scheduling error; ";
return;
}
$modstate = 'sent';
if(!strcmp($this->moderatedList,'yes')) {
$modstate = 'pending';
}
if($this->maxMsgSize && $msgSize > $this->maxMsgSize) {
$mailsubject=$this->GetText('NOTIFY SUBJECT',true).' '.$this->listAddress;
$mailbody=$this->GetText('EXCEEDING MSG SIZE',true).$this->maxMsgSize."\r\n\r\n".
"------------------------------------------------\r\n\r\n".$mailsubject."\r\n\r\n";
// TODO: insert original message as attachment
$this->NotifyUser($sender,false,$mailsubject,$mailbody);
return;
}
$mailbody=&$this->decoded[0]['Body'];
$mailbody.=$this->AddTrailer($mailbody);
$mailsender = $this->listAddress; // if smtp require authentication
$mailsubject = @stripslashes($this->decoded[0]['Headers']['subject:']);
if(!strcmp($modstate,'pending')) {
//if(eregi("(^|\n)$sender(\n|$)",$this->modSublist)
//if(preg_match("/(^|\n)$sender(\n|$)/im",$this->modSublist)
if(!strcasecmp($this->substate,'moderator')
&& preg_match('/\b'.$this->GetText('CONFIRM',true).'\b.\b('.$this->GetText('MESSAGE',true).')\b.'.$this->listName."\.([A-Za-z0-9]+)\.(.*)/i", '--'.$this->bodyTextPlain, $matches,PREG_OFFSET_CAPTURE)) {
if(@$matches[3][0]) {
$originalSender = trim(strtok($matches[3][0]," >\r\n\t"));
}
$query="select * from $this->messages where state = 'pending' && keyvalue = '".
$matches[2][0]."'";
if($result = @mysql_query($query,$this->dbconn)) {
if($row = @mysql_fetch_object($result)) {
$query = "update $this->messages set state = 'queued' where id = $row->id";
if(!$this->debug) {
@mysql_query($query,$this->dbconn);
}
$messageid = $row->id;
$parameters=array(
'Data'=>$row->message //, 'SaveBody'=>1,
);
unset($this->decoded);
$mime=new mime_parser_class;
$mime->decode_bodies = 0;
$success=$mime->Decode($parameters, $this->decoded);
if(!$success) {
echo 'MIME message decoding error: '.HtmlSpecialChars($mime->error)." .";
return;
}
$this->sender = $row->mailfrom;
$mailbody=& $this->decoded[0]['Body'];
@mysql_free_result($result);
} else { // key doesn't match, notify owner
$this->NotifyOwner($sender,
'Message moderation failed, ml ['.$this->listAddress.'] '.$originalSender.'>');
@mysql_free_result($result);
return;
}
}
$modstate = 'sent';
// now check for scheduled time
if(preg_match("/\b".$this->scheduledTime.":_(.*)\.".$this->GetText('CONFIRM',true).'\b/im',
'--'.$this->bodyTextPlain, $matches,PREG_OFFSET_CAPTURE)) {
$this->bodyTextPlain = str_replace('_',' ',@$matches[1][0]);
if(($scheduledTime = $this->CheckScheduledTime($sender)) === true) {
echo "scheduling error; ";
return;
}
}
} else {
$this->NotifyMessageModeration($sender,trim($this->scheduledPattern));
return;
}
}
$this->RebuildHeader($mailheader);
$addresserror = '';
$bounceerror = '';
echo "Sending, ";
$sublist = explode("\n",$this->sublist);
if(sizeof($sublist) > 0) {
// set recipients number per smtp connection
$recipients = $this->ArraySplit($sublist);
foreach($recipients as $recipient) {
// if set 'X-Scheduled' header/subject, mail has to be sent at 'datetime' value, so mark msg as queued
if($scheduledTime) {
echo "schedule request, queued; ";
$addresserror.="[scheduled]".implode(",[scheduled]",$recipient).',';
continue;
}
$error = $this->SmtpSend($recipient, $mailsubject, $mailsender,
$mailheader, $mailbody);
if($error != 'OK.') {
echo " sending error [$error];";
// if ok is a permanent error, don't queue and notify to owner
if($error[0] == '5') {
$bounceerror.="[$error]".implode("\n[$error]",$recipient)."\n";
} else {
$addresserror.="[$error]".implode(",[$error]",$recipient).',';
}
} else {
$ok += sizeof($recipient);
}
}
if(strlen($bounceerror) > 1)
$bounceerror = substr($bounceerror,0,-1);
if(strlen($addresserror) > 1)
$addresserror = substr($addresserror,0,-1);
}
if(!strcmp($modstate,'sent') && strlen($addresserror) > 1) {
$modstate = 'queued';
}
// ok, insert message into archive
if($messageid > 0) {
$query = "update $this->messages set state = '$modstate' where id = $messageid";
} else {
$query = "insert into ".$this->messages.
" (id,date,state,listname,smtp,mailfrom,subject,message,header,rowlock) values(0,".
($scheduledTime === false ? 'now()' : "'$scheduledTime'").",'".
"$modstate','$this->listName','$this->smtpIndex','$sender','".
str_replace("'","\\'",$this->IsoDecode($mailsubject))."','".
str_replace("'","\\'",$mailheader)."\r\n".str_replace("'","\\'",$mailbody)."','".
str_replace("'","\\'",$this->ImplodeHeaders('',$this->decoded[0]['Headers'])).
"','')";
}
if(!$this->debug) {
$command = @mysql_query($query,$this->dbconn);
if($messageid == -1) {
$messageid = @mysql_insert_id($this->dbconn);
}
echo "msgid [$messageid]:";
// insert unsent message into queue only if original pop3 message's been removed
if(strlen($addresserror) > 1) { // && !strcasecmp($this->removeAfterPop,'yes')) {
$query = "insert into $this->queue (id,date,smtp,listname,messageid,addresses,rowlock) values(0,".
($scheduledTime === false ? 'now()' : "'$scheduledTime'").",'".
"$this->smtpIndex','$this->listName',$messageid,'".
str_replace("'","\\'",$addresserror)."','')";
$command = @mysql_query($query,$this->dbconn);
}
}
// notify user mail has been scheduled
if($scheduledTime) {
$mailbody = $this->GetText('SCHEDULED MAIL CONFIRM',true).' '.$scheduledTime."\r\n".
"\r\n\r\nId [$messageid] [$mailsubject]\r\n";
$mailsubject = $this->GetText('NOTIFY SUBJECT',true).' '.$this->listAddress;
$this->NotifyUser(($originalSender !== false ? $originalSender : $sender),false,$mailsubject,$mailbody);
}
// check if there are permanent error and send them to owner
if(strlen($bounceerror) > 1) {
$this->NotifyBounce($messageid,$bounceerror);
}
$this->sender = false;
echo " sent to [$ok] users. End";
}
function CheckDigestByDate() {
$exptime=$this->sendDigest;
if(!is_numeric(substr($exptime,0,1))) {
$daycheck=strtolower(substr($exptime,0,3));
if(strcasecmp($daycheck,date('D',$this->now))) {
return false;
}
// ok, the day is good, go on
$exptime=substr($exptime,4);
}
if(strpos($exptime,':')) {
if(strcmp($exptime,date('H:i',$this->now))) {
return false;
}
// there is no constant time, but numeric value ('1' for every hour,'12' for every 12 hours)
} else {
$now=(date('H',$this->now) * 60)+date('i',$this->now);
if((($now % ($this->sendDigest *60)) / ($this->sendDigest *60)) <> 0) {
return false;
}
}
// ok it's send time
$query = "select * from $this->messages where state = 'sent' and listname = '".$this->listName."' and".
// if you want to check 'from date' too enable row below
// (" date > '".date("Y/m/d H:i:s",$this->now - ($this->sendDigest * 60 * 60)) . "' and") : '').
" date <= '".date("Y/m/d H:i:s",$this->now)."' order by date limit ".$this->digestMaxMsg;
if(!$result=@mysql_query($query,$this->dbconn)) {
return false;
}
return $result;
}
function SetMessagesState() {
$query = "update $this->messages set state = 'sentdigest' where listname = '".$this->listName.
"' and state = 'sent' limit ".
$this->digestMaxMsg;
if(!$result=@mysql_query($query,$this->dbconn)) {
echo " digest-error, messages 'state' not changed;";
}
}
function SendDigest() {
$result=null;
if(!$result=$this->CheckDigestByDate())
return;
if(strlen($this->sublist) <= 0 && strlen($this->digestSublist) <= 0) {
$this->InitSublistFields();
}
if(strlen($this->digestSublist) < 5) { // no digesto subscribers, exit from here
$this->SetMessagesState();
return;
}
$mailsubject = '';
$mailheader = '';
$mailbody = '';
$boundary = '';
$digestlist = '';
$maxsize=(DIGEST_MAX_SIZE != false ? DIGEST_MAX_SIZE : $this->dbrow->digestmaxsize);
if(!$maxsize)
$maxsize = '64K';
// foreach($this->headersChange as $hk => $hv) { if(strcmp($hv,'')) { $mailheader.= $hk.': '.$hv."\r\n"; } }
$mailheader.= "Precedence: bulk\r\n";
$boundary='----NextPart'.md5(date('r', time()));
$mailheader.="From: ".$this->listAddress."\r\nMIME-Version: 1.0\r\nContent-Type: multipart/digest; boundary=".
$boundary."\r\n";
$mailsubject.='Subject: '.$this->listName." Digest: ";
$mailbody.='--'.$boundary."\r\nContent-Type: text/plain; charset: us-ascii\r\n\r\n".
$this->listName." Digest:\r\n\r\n";
$firstid=null;
$lastid=null;
if(!strcasecmp($maxsize[strlen($maxsize)-1],'k')) {
$maxsize*=1024;
} else if(!strcasecmp($maxsize[strlen($maxsize)-1],'m')) {
$maxsize*=1024*1024;
}
echo ' digest for msg(s): ';
while($row = @mysql_fetch_object($result)) {
if($firstid==null) {
$firstid=$row->id;
}
$lastid=$row->id;
echo $row->id."[".$row->date."]; ";
$mailbody.="Id: ".$row->id."\r\nSender: ".$row->mailfrom."\n\nSubject: ".$row->subject."\r\n";
$maxsize -= strlen($row->message);
if($maxsize <= 0) { // digest message exceeds max size
$mailbody.="Warning! Exceding max size, this message can be retrieved with ML command:\r\nget.".
$row->id."\r\n";
} else {
$digestlist.='--'.$boundary.
"\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"msg".$row->id.".
eml\"\r\n\r\n".$row->message."\r\n\r\n";
}
$mailbody.="\r\n\r\n";
}
echo "\n";
@mysql_free_result($result);
if(!$firstid) { // no messages for digest
return;
}
$mailsubject.=$firstid.'-'.$lastid."\r\n";
$mailheader.=$mailsubject;
$mailbody.=$digestlist.'--'.$boundary."--\r\n\r\n";
unset($digestlist);
$digestsublist=explode("\n",$this->digestSublist);
$ok = 0;
if(sizeof($digestsublist) > 0) {
foreach($digestsublist as $mailaddress) {
if(strlen($mailaddress) < 5) continue;
$mailaddress = stripslashes($mailaddress);
$error = $this->SmtpSend($mailaddress,$mailsubject,$this->listAddress,$mailheader,$mailbody);
if($error != 'OK.') {
echo " digest-error [$error] for [$mailaddress];";
$addresserror.=((strlen($addresserror) <= 0) ? '': ',').$mailaddress;
} else {
$ok++;
}
}
}
if($ok > 0) {
$this->SetMessagesState();
}
}
function ValidateLockTime($buffer) {
if(!strcmp($buffer,''))
return true;
$buffer = substr($buffer, strpos($buffer,'-')+1);
// echo "<!-- diff # ".(strtotime('now') - strtotime(str_replace('-',' ',$buffer)))."-->\n";
if((strtotime('now') - strtotime(str_replace('-',' ',$buffer))) > ($this->expireLock ? $this->expireLock : EXPIRE_LOCK)) {
return true;
}
return false;
}
function LockMl() {
if($this->ValidateLockTime($this->dbrow->rowlock) != true) {
return false;
}
$query = "update ".$this->mltable." set rowlock = '".
$_SERVER['REMOTE_ADDR'].'-'.date('Y/m/d-H:i:s')."' where listname = '".$this->listName."'";
if(!@mysql_query($query,$this->dbconn)) {
return false;
}
return true;
}
function UnlockMl() {
$query = "update ".$this->mltable." set rowlock = '' where listname = '".$this->listName."'";
if(!@mysql_query($query,$this->dbconn)) {
return false;
}
}
function Run() {
if(!$this->now) {
$this->now = strtotime('now');
}
echo $this->logHeader;
echo '-'.date("Y/m/d H:i:s")." ".$this->listName.": ";
if($this->Init() != true) {
echo $this->logfooter;
return;
}
if(!strcmp($this->dbrow->shutdown,'yes')) {
echo "ML disabled. quit\n";
} else if($this->LockMl() != true) {
echo "already locked. quit";
} else {
$this->SendFromQueue();
$this->Pop3Start();
$this->error=$this->Pop3Read();
if(strcasecmp($this->pop3->state,"DISCONNECTED"))
$this->error=$this->pop3->Close();
$this->SendDigest();
$this->UnlockMl();
}
if($this->debug) {
echo str_replace("\n","<br>\n",htmlentities($this->debugOutput)."\n");
}
echo $this->logfooter;
}
};
?>