Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F803521
in-portal
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Feb 25, 1:23 PM
Size
112 KB
Mime Type
text/x-diff
Expires
Thu, Feb 27, 1:23 PM (21 h, 22 s)
Engine
blob
Format
Raw Data
Handle
576331
Attached To
rINP In-Portal
in-portal
View Options
Index: branches/RC/core/kernel/utility/email_send.php
===================================================================
--- branches/RC/core/kernel/utility/email_send.php (revision 11656)
+++ branches/RC/core/kernel/utility/email_send.php (revision 11657)
@@ -1,1997 +1,2007 @@
<?php
/**
* Class used to compose email message (using MIME standarts) and send it via mail function or via SMTP server
*
*/
class kEmailSendingHelper extends kHelper {
/**
* headers of main header part
*
* @var Array
*/
var $headers = Array ();
/**
* Tells if all message parts were combined together
*
* @var int
*/
var $bodyPartNumber = false;
/**
* Composed message parts
*
* @var Array
*/
var $parts = Array();
/**
* Lines separator by MIME standart
*
* @var string
*/
var $line_break = "\n";
/**
* Charset used for message composing
*
* @var string
*/
var $charset = 'utf-8';
/**
* Name of mailer program (X-Mailer header)
*
* @var string
*/
var $mailerName = '';
/**
* Options used for message content-type & structure guessing
*
* @var Array
*/
var $guessOptions = Array ();
/**
* Send messages using selected method
*
* @var string
*/
var $sendMethod = 'Mail';
/**
* Parameters used to initiate SMTP server connection
*
* @var Array
*/
var $smtpParams = Array ();
/**
* List of supported authentication methods, in preferential order.
* @var array
* @access public
*/
var $smtpAuthMethods = Array('CRAM-MD5', 'LOGIN', 'PLAIN');
/**
* The socket resource being used to connect to the SMTP server.
* @var kSocket
* @access private
*/
var $smtpSocket = null;
/**
* The most recent server response code.
* @var int
* @access private
*/
var $smtpResponceCode = -1;
/**
* The most recent server response arguments.
* @var array
* @access private
*/
var $smtpRespoceArguments = Array();
/**
* Stores detected features of the SMTP server.
* @var array
* @access private
*/
var $smtpFeatures = Array();
function kEmailSendingHelper()
{
parent::kHelper();
// set default guess options
$this->guessOptions = Array (
'attachments' => Array (),
'inline_attachments' => Array (),
'text_part' => false,
'html_part' => false,
);
// read SMTP server connection params from config
$smtp_mapping = Array ('server' => 'Smtp_Server', 'port' => 'Smtp_Port');
if ($this->Application->ConfigValue('Smtp_Authenticate')) {
$smtp_mapping['username'] = 'Smtp_User';
$smtp_mapping['password'] = 'Smtp_Pass';
}
foreach ($smtp_mapping as $smtp_name => $config_name) {
$this->smtpParams[$smtp_name] = $this->Application->ConfigValue($config_name);
}
$this->smtpParams['use_auth'] = isset($this->smtpParams['username']) ? true : false;
$this->smtpParams['localhost'] = 'localhost'; // The value to give when sending EHLO or HELO.
$this->sendMethod = $this->smtpParams['server'] && $this->smtpParams['port'] ? 'SMTP' : 'Mail';
if ($this->sendMethod == 'SMTP') {
// create connection object if we will use SMTP
$this->smtpSocket =& $this->Application->makeClass('Socket');
}
$this->SetCharset(null, true);
}
/**
* Returns new message id header by sender's email address
*
* @param string $email_address email address
* @return string
*/
function GenerateMessageID($email_address)
{
list ($micros, $seconds) = explode(' ', microtime());
list ($user, $domain) = explode('@', $email_address, 2);
$message_id = strftime('%Y%m%d%H%M%S', $seconds).substr($micros, 1, 5).'.'.preg_replace('/[^A-Za-z]+/', '-', $user).'@'.$domain;
$this->SetHeader('Message-ID', '<'.$message_id.'>');
}
/**
* Returns extension of given filename
*
* @param string $filename
* @return string
*/
function GetFilenameExtension($filename)
{
$last_dot = mb_strrpos($filename, '.');
return $last_dot !== false ? mb_substr($filename, $last_dot + 1) : '';
}
/**
* Creates boundary for part by number (only if it's missing)
*
* @param int $part_number
*
*/
function CreatePartBoundary($part_number)
{
$part =& $this->parts[$part_number];
if (!isset($part['BOUNDARY'])) {
$part['BOUNDARY'] = md5(uniqid($part_number.time()));
}
}
/**
* Returns ready to use headers associative array of any message part by it's number
*
* @param int $part_number
* @return Array
*/
function GetPartHeaders($part_number)
{
$part =& $this->parts[$part_number];
if (!isset($part['Content-Type'])) {
return $this->SetError('MISSING_CONTENT_TYPE');
}
$full_type = strtolower($part['Content-Type']);
list ($type, $sub_type) = explode('/', $full_type);
$headers['Content-Type'] = $full_type;
switch ($type) {
case 'text':
case 'image':
case 'audio':
case 'video':
case 'application':
case 'message':
// 1. update content-type header
if (isset($part['CHARSET'])) {
$headers['Content-Type'] .= '; charset='.$part['CHARSET'];
}
if (isset($part['NAME'])) {
$headers['Content-Type'] .= '; name="'.$part['NAME'].'"';
}
// 2. set content-transfer-encoding header
if (isset($part['Content-Transfer-Encoding'])) {
$headers['Content-Transfer-Encoding'] = $part['Content-Transfer-Encoding'];
}
// 3. set content-disposition header
if (isset($part['DISPOSITION']) && $part['DISPOSITION']) {
$headers['Content-Disposition'] = $part['DISPOSITION'];
if (isset($part['NAME'])) {
$headers['Content-Disposition'] .= '; filename="'.$part['NAME'].'"';
}
}
break;
case 'multipart':
switch ($sub_type) {
case 'alternative':
case 'related':
case 'mixed':
case 'parallel':
$this->CreatePartBoundary($part_number);
$headers['Content-Type'] .= '; boundary="'.$part['BOUNDARY'].'"';
break;
default:
return $this->SetError('INVALID_MULTIPART_SUBTYPE', Array($sub_type));
}
break;
default:
return $this->SetError('INVALID_CONTENT_TYPE', Array($full_type));
}
// set content-id if any
if (isset($part['Content-ID'])) {
$headers['Content-ID'] = '<'.$part['Content-ID'].'>';
}
return $headers;
}
function GetPartBody($part_number)
{
$part =& $this->parts[$part_number];
if (!isset($part['Content-Type'])) {
return $this->SetError('MISSING_CONTENT_TYPE');
}
$full_type = strtolower($part['Content-Type']);
list ($type, $sub_type) = explode('/', $full_type);
$body = '';
switch ($type) {
// compose text/binary content
case 'text':
case 'image':
case 'audio':
case 'video':
case 'application':
case 'message':
// 1. get content of part
if (isset($part['FILENAME'])) {
// content provided via absolute path to content containing file
$filename = $part['FILENAME'];
$file_size = filesize($filename);
$body = file_get_contents($filename);
if ($body === false) {
return $this->SetError('FILE_PART_OPEN_ERROR', Array($filename));
}
$actual_size = strlen($body);
if (($file_size === false || $actual_size > $file_size) && get_magic_quotes_runtime()) {
$body = stripslashes($body);
}
if ($file_size !== false && $actual_size != $file_size) {
return $this->SetError('FILE_PART_DATA_ERROR', Array($filename));
}
}
else {
// content provided directly as one of part keys
if (!isset($part['DATA'])) {
return $this->SetError('FILE_PART_DATA_MISSING');
}
$body =& $part['DATA'];
}
// 2. get part transfer encoding
$encoding = isset($part['Content-Transfer-Encoding']) ? strtolower($part['Content-Transfer-Encoding']) : '';
if (!in_array($encoding, Array ('', 'base64', 'quoted-printable', '7bit'))) {
return $this->SetError('INVALID_ENCODING', Array($encoding));
}
if ($encoding == 'base64') {
// split base64 encoded text by 76 symbols at line (MIME requirement)
$body = chunk_split( base64_encode($body) );
}
break;
case 'multipart':
// compose multipart message
switch ($sub_type) {
case 'alternative':
case 'related':
case 'mixed':
case 'parallel':
$this->CreatePartBoundary($part_number);
$boundary = $this->line_break.'--'.$part['BOUNDARY'];
foreach ($part['PARTS'] as $multipart_number) {
$body .= $boundary.$this->line_break;
$part_headers = $this->GetPartHeaders($multipart_number);
if ($part_headers === false) {
// some of sub-part headers were invalid
return false;
}
foreach ($part_headers as $header_name => $header_value) {
$body .= $header_name.': '.$header_value.$this->line_break;
}
$part_body = $this->GetPartBody($multipart_number);
if ($part_body === false) {
// part body was invalid
return false;
}
$body .= $this->line_break.$part_body;
}
$body .= $boundary.'--'.$this->line_break;
break;
default:
return $this->SetError('INVALID_MULTIPART_SUBTYPE', Array($sub_type));
}
break;
default:
return $this->SetError('INVALID_CONTENT_TYPE', Array($full_type));
}
return $body;
}
/**
* Applies quoted-printable encoding to specified text
*
* @param string $text
* @param string $header_charset
* @param int $break_lines
* @return unknown
*/
function QuotedPrintableEncode($text, $header_charset = '', $break_lines = 1)
{
$ln = strlen($text);
$h = strlen($header_charset) > 0;
if ($h) {
$s = Array (
'=' => 1,
'?' => 1,
'_' => 1,
'(' => 1,
')' => 1,
'<' => 1,
'>' => 1,
'@' => 1,
',' => 1,
';' => 1,
'"' => 1,
'\\' => 1,
/*
'/' => 1,
'[' => 1,
']' => 1,
':' => 1,
'.' => 1,
*/
);
$b = $space = $break_lines = 0;
for ($i = 0; $i < $ln; $i++) {
if (isset($s[$text[$i]])) {
$b = 1;
break;
}
switch ($o = ord($text[$i])) {
case 9:
case 32:
$space = $i + 1;
$b = 1;
break 2;
case 10:
case 13:
break 2;
default:
if ($o < 32 || $o > 127) {
$b = 1;
break 2;
}
}
}
if($i == $ln) {
return $text;
}
if ($space > 0) {
return substr($text, 0, $space).($space < $ln ? $this->QuotedPrintableEncode(substr($text, $space), $header_charset, 0) : '');
}
}
for ($w = $e = '', $n = 0, $l = 0, $i = 0; $i < $ln; $i++) {
$c = $text[$i];
$o = ord($c);
$en = 0;
switch ($o) {
case 9:
case 32:
if (!$h) {
$w = $c;
$c = '';
}
else {
if ($b) {
if ($o == 32) {
$c = '_';
}
else {
$en = 1;
}
}
}
break;
case 10:
case 13:
if (strlen($w)) {
if ($break_lines && $l + 3 > 75) {
$e .= '='.$this->line_break;
$l = 0;
}
$e .= sprintf('=%02X', ord($w));
$l += 3;
$w = '';
}
$e .= $c;
if ($h) {
$e .= "\t";
}
$l = 0;
continue 2;
case 46:
case 70:
case 102:
$en = (!$h && ($l == 0 || $l + 1 > 75));
break;
default:
if ($o > 127 || $o < 32 || !strcmp($c, '=')) {
$en = 1;
}
elseif ($h && isset($s[$c])) {
$en = 1;
}
break;
}
if (strlen($w)) {
if ($break_lines && $l + 1 > 75) {
$e .= '='.$this->line_break;
$l = 0;
}
$e .= $w;
$l++;
$w = '';
}
if (strlen($c)) {
if ($en) {
$c = sprintf('=%02X', $o);
$el = 3;
$n = 1;
$b = 1;
}
else {
$el = 1;
}
if ($break_lines && $l + $el > 75) {
$e .= '='.$this->line_break;
$l = 0;
}
$e .= $c;
$l += $el;
}
}
if (strlen($w)) {
if ($break_lines && $l + 3 > 75) {
$e .= '='.$this->line_break;
}
$e .= sprintf('=%02X', ord($w));
}
return $h && $n ? '=?'.$header_charset.'?q?'.$e.'?=' : $e;
}
/**
* Sets message header + encodes is by quoted-printable using charset specified
*
* @param string $name
* @param string $value
* @param string $encoding_charset
*/
function SetHeader($name, $value, $encoding_charset = '')
{
if ($encoding_charset) {
// actually for headers base64 method may give shorter result
$value = $this->QuotedPrintableEncode($value, $encoding_charset);
}
$this->headers[$name] = $value;
}
/**
* Sets header + automatically encodes it using default charset
*
* @param string $name
* @param string $value
*/
function SetEncodedHeader($name, $value)
{
$this->SetHeader($name, $value, $this->charset);
}
/**
* Sets header which value is email and username +autoencode
*
* @param string $header
* @param string $address
* @param string $name
*/
function SetEncodedEmailHeader($header, $address, $name)
{
- if ($header == 'To') {
- // in case, when header is set directly
- $address = $this->_replaceRecipientEmail($address);
- }
-
$this->SetHeader($header, $this->QuotedPrintableEncode($name, $this->charset).' <'.$address.'>');
}
function SetMultipleEncodedEmailHeader($header, $addresses)
{
$value = '';
foreach ($addresses as $name => $address) {
$value .= $this->QuotedPrintableEncode($name, $this->charset).' <'.$address.'>, ';
}
$value = preg_replace('/(.*),$/', '\\1', $value);
$this->SetHeader($header, $value);
}
/**
* Adds new part to message and returns it's number
*
* @param Array $part_definition
* @param int $part_number number of new part
* @return int
*/
function AddPart(&$part_definition, $part_number = false)
{
$part_number = $part_number !== false ? $part_number : count($this->parts);
$this->parts[$part_number] =& $part_definition;
return $part_number;
}
/**
* Returns text version of HTML document
*
* @param string $html
* @return string
*/
function ConvertToText($html)
{
$search = Array (
"'(<\/td>.*)[\r\n]+(.*<td)'i",//formating text in tables
"'(<br[ ]?[\/]?>[\r\n]{0,2})|(<\/p>)|(<\/div>)|(<\/tr>)'i",
"'<head>(.*?)</head>'si",
"'<style(.*?)</style>'si",
"'<title>(.*?)</title>'si",
"'<script(.*?)</script>'si",
// "'^[\s\n\r\t]+'", //strip all spacers & newlines in the begin of document
// "'[\s\n\r\t]+$'", //strip all spacers & newlines in the end of document
"'&(quot|#34);'i",
"'&(amp|#38);'i",
"'&(lt|#60);'i",
"'&(gt|#62);'i",
"'&(nbsp|#160);'i",
"'&(iexcl|#161);'i",
"'&(cent|#162);'i",
"'&(pound|#163);'i",
"'&(copy|#169);'i",
"'&#(\d+);'e"
);
$replace = Array (
"\\1\t\\2",
"\n",
"",
"",
"",
"",
// "",
// "",
"\"",
"&",
"<",
">",
" ",
chr(161),
chr(162),
chr(163),
chr(169),
"chr(\\1)"
);
return strip_tags( preg_replace ($search, $replace, $html) );
}
/**
* Add text OR html part to message (optionally encoded)
*
* @param string $text part's text
* @param bool $is_html this html part or not
* @param bool $encode encode message using quoted-printable encoding
*
* @return int number of created part
*/
function CreateTextHtmlPart($text, $is_html = false, $encode = true)
{
if ($is_html) {
// if adding HTML part, then create plain-text part too
$this->CreateTextHtmlPart($this->ConvertToText($text));
}
// in case if text is from $_REQUEST, then line endings are "\r\n", but we need "\n" here
$text = str_replace("\r\n", "\n", $text); // possible case
$text = str_replace("\r", "\n", $text); // impossible case, but just in case replace this too
$definition = Array (
'Content-Type' => $is_html ? 'text/html' : 'text/plain',
'CHARSET' => $this->charset,
'DATA' => $encode ? $this->QuotedPrintableEncode($text) : $text,
);
if ($encode) {
$definition['Content-Transfer-Encoding'] = 'quoted-printable';
}
$guess_name = $is_html ? 'html_part' : 'text_part';
$part_number = $this->guessOptions[$guess_name] !== false ? $this->guessOptions[$guess_name] : false;
$part_number = $this->AddPart($definition, $part_number);
$this->guessOptions[$guess_name] = $part_number;
return $part_number;
}
/**
* Adds attachment part to message
*
* @param string $file name of the file with attachment body
* @param string $attach_name name for attachment (name of file is used, when not specified)
* @param string $content_type content type for attachment
* @param string $content body of file to be attached
* @param bool $inline is attachment inline or not
*
* @return int number of created part
*/
function AddAttachment($file = '', $attach_name = '', $content_type = '', $content = '', $inline = false)
{
$definition = Array (
'Disposition' => $inline ? 'inline' : 'attachment',
'Content-Type' => $content_type ? $content_type : 'automatic/name',
);
if ($file) {
// filename of attachment given
$definition['FileName'] = $file;
}
if ($attach_name) {
// name of attachment given
$definition['Name'] = $attach_name;
}
if ($content) {
// attachment data is given
$definition['Data'] = $content;
}
$definition =& $this->GetFileDefinition($definition);
$part_number = $this->AddPart($definition);
if ($inline) {
// it's inline attachment and needs content-id to be addressed by in message
$this->parts[$part_number]['Content-ID'] = md5(uniqid($part_number.time())).'.'.$this->GetFilenameExtension($attach_name ? $attach_name : $file);
}
$this->guessOptions[$inline ? 'inline_attachments' : 'attachments'][] = $part_number;
return $part_number;
}
/**
* Adds another MIME message as attachment to message being composed
*
* @param string $file name of the file with attachment body
* @param string $attach_name name for attachment (name of file is used, when not specified)
* @param string $content body of file to be attached
*
* @return int number of created part
*/
function AddMessageAttachment($file = '', $attach_name = '', $content = '')
{
$part_number = $this->AddAttachment($file, $attach_name, 'message/rfc822', $content, true);
unset($this->parts[$part_number]['Content-ID']); // messages don't have content-id, but have inline disposition
return $part_number;
}
/**
* Creates multipart of specified type and returns it's number
*
* @param Array $part_numbers
* @param string $multipart_type = {alternative,related,mixed,paralell}
* @return int
*/
function CreateMultipart($part_numbers, $multipart_type)
{
$types = Array ('alternative', 'related' , 'mixed', 'paralell');
if (!in_array($multipart_type, $types)) {
return $this->SetError('INVALID_MULTIPART_SUBTYPE', Array($multipart_type));
}
$definition = Array (
'Content-Type' => 'multipart/'.$multipart_type,
'PARTS' => $part_numbers,
);
return $this->AddPart($definition);
}
/**
* Creates missing content-id header for inline attachments
*
* @param int $part_number
*/
function CreateContentID($part_number)
{
$part =& $this->parts[$part_number];
if (!isset($part['Content-ID']) && $part['DISPOSITION'] == 'inline') {
$part['Content-ID'] = md5(uniqid($part_number.time())).'.'.$this->GetFilenameExtension($part['NAME']);
}
}
/**
* Returns attachment part based on file used in attachment
*
* @param Array $file
* @return Array
*/
function &GetFileDefinition ($file)
{
$name = '';
if (isset($file['Name'])) {
// if name is given directly, then use it
$name = $file['Name'];
}
else {
// auto-guess attachment name based on source filename
$name = isset($file['FileName']) ? basename($file['FileName']) : '';
}
if (!$name || (!isset($file['FileName']) && !isset($file['Data']))) {
// filename not specified || no filename + no direct file content
return $this->SetError('MISSING_FILE_DATA');
}
$encoding = 'base64';
if (isset($file['Content-Type'])) {
$content_type = $file['Content-Type'];
list ($type, $sub_type) = explode('/', $content_type);
switch ($type) {
case 'text':
case 'image':
case 'audio':
case 'video':
case 'application':
break;
case 'message':
$encoding = '7bit';
break;
case 'automatic':
if (!$name) {
return $this->SetError('MISSING_FILE_NAME');
}
$this->guessContentType($name, $content_type, $encoding);
break;
default:
return $this->SetError('INVALID_CONTENT_TYPE', Array($content_type));
}
}
else {
// encoding not passed in file part, then assume, that it's binary
$content_type = 'application/octet-stream';
}
$definition = Array (
'Content-Type' => $content_type,
'Content-Transfer-Encoding' => $encoding,
'NAME' => $name, // attachment name
);
if (isset($file['Disposition'])) {
$disposition = strtolower($file['Disposition']);
if ($disposition == 'inline' || $disposition == 'attachment') {
// valid disposition header value
$definition['DISPOSITION'] = $file['Disposition'];
}
else {
return $this->SetError('INVALID_DISPOSITION', Array($file['Disposition']));
}
}
if (isset($file['FileName'])) {
$definition['FILENAME'] = $file['FileName'];
}
elseif (isset($file['Data'])) {
$definition['DATA'] =& $file['Data'];
}
return $definition;
}
/**
* Returns content-type based on filename extension
*
* @param string $filename
* @param string $content_type
* @param string $encoding
*
* @todo Regular expression used is not completely finished, that's why if extension used for
* comparing in some other extension (from list) part, that partial match extension will be returned.
* Because of two extension that begins with same 2 letters always belong to same content type
* this unfinished regular expression still gives correct result in any case.
*/
function guessContentType($filename, &$content_type, &$encoding)
{
$file_extension = mb_strtolower( $this->GetFilenameExtension($filename) );
$mapping = '(xls:application/excel)(hqx:application/macbinhex40)(doc,dot,wrd:application/msword)(pdf:application/pdf)
(pgp:application/pgp)(ps,eps,ai:application/postscript)(ppt:application/powerpoint)(rtf:application/rtf)
(tgz,gtar:application/x-gtar)(gz:application/x-gzip)(php,php3:application/x-httpd-php)(js:application/x-javascript)
(ppd,psd:application/x-photoshop)(swf,swc,rf:application/x-shockwave-flash)(tar:application/x-tar)(zip:application/zip)
(mid,midi,kar:audio/midi)(mp2,mp3,mpga:audio/mpeg)(ra:audio/x-realaudio)(wav:audio/wav)(bmp:image/bitmap)(bmp:image/bitmap)
(gif:image/gif)(iff:image/iff)(jb2:image/jb2)(jpg,jpe,jpeg:image/jpeg)(jpx:image/jpx)(png:image/png)(tif,tiff:image/tiff)
(wbmp:image/vnd.wap.wbmp)(xbm:image/xbm)(css:text/css)(txt:text/plain)(htm,html:text/html)(xml:text/xml)
(mpg,mpe,mpeg:video/mpeg)(qt,mov:video/quicktime)(avi:video/x-ms-video)(eml:message/rfc822)';
if (preg_match('/[\(,]'.$file_extension.'[,]{0,1}.*?:(.*?)\)/s', $mapping, $regs)) {
if ($file_extension == 'eml') {
$encoding = '7bit';
}
$content_type = $regs[1];
}
else {
$content_type = 'application/octet-stream';
}
}
/**
* Using guess options combines all added parts together and returns combined part number
*
* @return int
*/
function PrepareMessageBody()
{
if ($this->bodyPartNumber === false) {
$part_number = false; // number of generated body part
// 1. set text content of message
if ($this->guessOptions['text_part'] !== false && $this->guessOptions['html_part'] !== false) {
// text & html parts present -> compose into alternative part
$parts = Array (
$this->guessOptions['text_part'],
$this->guessOptions['html_part'],
);
$part_number = $this->CreateMultipart($parts, 'alternative');
}
elseif ($this->guessOptions['text_part'] !== false) {
// only text part is defined, then leave as is
$part_number = $this->guessOptions['text_part'];
}
if ($part_number === false) {
return $this->SetError('MESSAGE_TEXT_MISSING');
}
// 2. if inline attachments found, then create related multipart from text & inline attachments
if ($this->guessOptions['inline_attachments']) {
$parts = array_merge(Array($part_number), $this->guessOptions['inline_attachments']);
$part_number = $this->CreateMultipart($parts, 'related');
}
// 3. if normal attachments found, then create mixed multipart from text & attachments
if ($this->guessOptions['attachments']) {
$parts = array_merge(Array($part_number), $this->guessOptions['attachments']);
$part_number = $this->CreateMultipart($parts, 'mixed');
}
$this->bodyPartNumber = $part_number;
}
return $this->bodyPartNumber;
}
/**
* Returns message headers and body part (by reference in parameters)
*
* @param Array $message_headers
* @param string $message_body
* @return bool
*/
function GetHeadersAndBody(&$message_headers, &$message_body)
{
$part_number = $this->PrepareMessageBody();
if ($part_number === false) {
return $this->SetError('MESSAGE_COMPOSE_ERROR');
}
$message_headers = $this->GetPartHeaders($part_number);
// join message headers and body headers
$message_headers = array_merge_recursive2($this->headers, $message_headers);
$message_headers['MIME-Version'] = '1.0';
if ($this->mailerName) {
$message_headers['X-Mailer'] = $this->mailerName;
}
$this->GenerateMessageID($message_headers['From']);
$valid_headers = $this->ValidateHeaders($message_headers);
if ($valid_headers) {
// set missing headers from existing
$from_headers = Array ('Reply-To', 'Errors-To');
foreach ($from_headers as $header_name) {
if (!isset($message_headers[$header_name])) {
$message_headers[$header_name] = $message_headers['From'];
}
}
$message_body = $this->GetPartBody($part_number);
return true;
}
return false;
}
/**
* Checks that all required headers are set and not empty
*
* @param Array $message_headers
* @return bool
*/
function ValidateHeaders($message_headers)
{
$from = isset($message_headers['From']) ? $message_headers['From'] : '';
if (!$from) {
return $this->SetError('HEADER_MISSING', Array('From'));
}
if (!isset($message_headers['To'])) {
return $this->SetError('HEADER_MISSING', Array('To'));
}
if (!isset($message_headers['Subject'])) {
return $this->SetError('HEADER_MISSING', Array('Subject'));
}
return true;
}
/**
* Returns full message source (headers + body) for sending to SMTP server
*
* @return string
*/
function GetMessage()
{
$composed = $this->GetHeadersAndBody($message_headers, $message_body);
if ($composed) {
// add headers to resulting message
$message = '';
foreach ($message_headers as $header_name => $header_value) {
$message .= $header_name.': '.$header_value.$this->line_break;
}
// add message body
$message .= $this->line_break.$message_body;
return $message;
}
return false;
}
/**
* Sets just happened error code
*
* @param string $code
* @param Array $params additional error params
* @return bool
*/
function SetError($code, $params = null, $fatal = true)
{
$error_msgs = Array (
'MAIL_NOT_FOUND' => 'the mail() function is not available in this PHP installation',
'MISSING_CONTENT_TYPE' => 'it was added a part without Content-Type: defined',
'INVALID_CONTENT_TYPE' => 'Content-Type: %s not yet supported',
'INVALID_MULTIPART_SUBTYPE' => 'multipart Content-Type sub_type %s not yet supported',
'FILE_PART_OPEN_ERROR' => 'could not open part file %s',
'FILE_PART_DATA_ERROR' => 'the length of the file that was read does not match the size of the part file %s due to possible data corruption',
'FILE_PART_DATA_MISSING' => 'it was added a part without a body PART',
'INVALID_ENCODING' => '%s is not yet a supported encoding type',
'MISSING_FILE_DATA' => 'file part data is missing',
'MISSING_FILE_NAME' => 'it is not possible to determine content type from the name',
'INVALID_DISPOSITION' => '%s is not a supported message part content disposition',
'MESSAGE_TEXT_MISSING' => 'text part of message was not defined',
'MESSAGE_COMPOSE_ERROR' => 'unknown message composing error',
'HEADER_MISSING' => 'header %s is required',
// SMTP errors
'INVALID_COMMAND' => 'Commands cannot contain newlines',
'CONNECTION_TERMINATED' => 'Connection was unexpectedly closed',
'HELO_ERROR' => 'HELO was not accepted: %s',
'AUTH_METHOD_NOT_SUPPORTED' => '%s is not a supported authentication method',
'AUTH_METHOD_NOT_IMPLEMENTED' => '%s is not a implemented authentication method',
);
if (!is_array($params)) {
$params = Array ();
}
trigger_error('mail error: '.vsprintf($error_msgs[$code], $params), $fatal ? E_USER_ERROR : E_USER_WARNING);
return false;
}
/**
* Simple method of message sending
*
* @param string $from_email
* @param string $to_email
* @param string $subject
* @param string $from_name
* @param string $to_name
*/
function Send($from_email, $to_email, $subject, $from_name = '', $to_name = '')
{
$this->SetSubject($subject);
$this->SetFrom($from_email, trim($from_name) ? trim($from_name) : $from_email);
if (!isset($this->headers['Return-Path'])) {
$this->SetReturnPath($from_email);
}
- $this->SetEncodedEmailHeader('To', $to_email, $to_name ? $to_name : $to_email);
+ $this->SetTo($to_email, $to_name ? $to_name : $to_email);
return $this->Deliver();
}
/**
* Prepares class for sending another message
*
*/
function Clear()
{
$this->headers = Array ();
$this->bodyPartNumber = false;
$this->parts = Array();
$this->guessOptions = Array (
'attachments' => Array(),
'inline_attachments' => Array (),
'text_part' => false,
'html_part' => false,
);
$this->SetCharset(null, true);
}
/**
* Sends message via php mail function
*
* @param Array $message_headers
* @param string $body
*
* @return bool
*/
function SendMail($message_headers, &$body)
{
if (!function_exists('mail')) {
return $this->SetError('MAIL_NOT_FOUND');
}
$to = $message_headers['To'];
$subject = $message_headers['Subject'];
$return_path = $message_headers['Return-Path'];
unset($message_headers['To'], $message_headers['Subject']);
$headers = '';
$header_separator = $this->Application->ConfigValue('MailFunctionHeaderSeparator') == 1 ? "\n" : "\r\n";
foreach ($message_headers as $header_name => $header_value) {
$headers .= $header_name.': '.$header_value.$header_separator;
}
if ($return_path) {
if (constOn('SAFE_MODE') || (defined('PHP_OS') && substr(PHP_OS, 0, 3) == 'WIN')) {
// safe mode restriction OR is windows
$return_path = '';
}
}
return mail($to, $subject, $body, $headers, $return_path ? '-f'.$return_path : null);
}
/**
* Sends message via SMTP server
*
* @param Array $message_headers
* @param string $body
*
* @return bool
*/
function SendSMTP($message_headers, &$message_body)
{
if (!$this->SmtpConnect()) {
return false;
}
$from = $this->ExtractRecipientEmail($message_headers['From']);
if (!$this->SmtpSetFrom($from)) {
return false;
}
$recipients = '';
$recipient_headers = Array ('To', 'Cc', 'Bcc');
foreach ($recipient_headers as $recipient_header) {
if (isset($message_headers[$recipient_header])) {
$recipients .= ' '.$message_headers[$recipient_header];
}
}
$recipients_accepted = 0;
$recipients = $this->ExtractRecipientEmail($recipients, true);
foreach ($recipients as $recipient) {
if ($this->SmtpAddTo($recipient)) {
$recipients_accepted++;
}
}
if ($recipients_accepted == 0) {
// none of recipients were accepted
return false;
}
$headers = '';
foreach ($message_headers as $header_name => $header_value) {
$headers .= $header_name.': '.$header_value.$this->line_break;
}
if (!$this->SmtpSendMessage($headers . "\r\n" . $message_body)) {
return false;
}
$this->SmtpDisconnect();
return true;
}
/**
* Send a command to the server with an optional string of
* arguments. A carriage return / linefeed (CRLF) sequence will
* be appended to each command string before it is sent to the
* SMTP server.
*
* @param string $command The SMTP command to send to the server.
* @param string $args A string of optional arguments to append to the command.
*
* @return bool
*
*/
function SmtpSendCommand($command, $args = '')
{
if (!empty($args)) {
$command .= ' ' . $args;
}
if (strcspn($command, "\r\n") !== strlen($command)) {
return $this->SetError('INVALID_COMMAND');
}
return $this->smtpSocket->write($command . "\r\n") === false ? false : true;
}
/**
* Read a reply from the SMTP server. The reply consists of a response code and a response message.
*
* @param mixed $valid The set of valid response codes. These may be specified as an array of integer values or as a single integer value.
*
* @return bool
*
*/
function SmtpParseResponse($valid)
{
$this->smtpResponceCode = -1;
$this->smtpRespoceArguments = array();
while ($line = $this->smtpSocket->readLine()) {
// If we receive an empty line, the connection has been closed.
if (empty($line)) {
$this->SmtpDisconnect();
return $this->SetError('CONNECTION_TERMINATED', null, false);
}
// Read the code and store the rest in the arguments array.
$code = substr($line, 0, 3);
$this->smtpRespoceArguments[] = trim(substr($line, 4));
// Check the syntax of the response code.
if (is_numeric($code)) {
$this->smtpResponceCode = (int)$code;
} else {
$this->smtpResponceCode = -1;
break;
}
// If this is not a multiline response, we're done.
if (substr($line, 3, 1) != '-') {
break;
}
}
// Compare the server's response code with the valid code.
if (is_int($valid) && ($this->smtpResponceCode === $valid)) {
return true;
}
// If we were given an array of valid response codes, check each one.
if (is_array($valid)) {
foreach ($valid as $valid_code) {
if ($this->smtpResponceCode === $valid_code) {
return true;
}
}
}
return false;
}
/**
* Attempt to connect to the SMTP server.
*
* @param int $timeout The timeout value (in seconds) for the socket connection.
* @param bool $persistent Should a persistent socket connection be used ?
*
* @return bool
*
*/
function SmtpConnect($timeout = null, $persistent = false)
{
$result = $this->smtpSocket->connect($this->smtpParams['server'], $this->smtpParams['port'], $persistent, $timeout);
if (!$result) {
return false;
}
if ($this->SmtpParseResponse(220) === false) {
return false;
}
elseif ($this->SmtpNegotiate() === false) {
return false;
}
if ($this->smtpParams['use_auth']) {
$result = $this->SmtpAuthentificate($this->smtpParams['username'], $this->smtpParams['password']);
if (!$result) {
// authentification failed
return false;
}
}
return true;
}
/**
* Attempt to disconnect from the SMTP server.
*
* @return bool
*/
function SmtpDisconnect()
{
if ($this->SmtpSendCommand('QUIT') === false) {
return false;
}
elseif ($this->SmtpParseResponse(221) === false) {
return false;
}
return $this->smtpSocket->disconnect();
}
/**
* Attempt to send the EHLO command and obtain a list of ESMTP
* extensions available, and failing that just send HELO.
*
* @return bool
*/
function SmtpNegotiate()
{
if (!$this->SmtpSendCommand('EHLO', $this->smtpParams['localhost'])) {
return false;
}
if (!$this->SmtpParseResponse(250)) {
// If we receive a 503 response, we're already authenticated.
if ($this->smtpResponceCode === 503) {
return true;
}
// If the EHLO failed, try the simpler HELO command.
if (!$this->SmtpSendCommand('HELO', $this->smtpParams['localhost'])) {
return false;
}
if (!$this->SmtpParseResponse(250)) {
return $this->SetError('HELO_ERROR', Array($this->smtpResponceCode), false);
}
return true;
}
foreach ($this->smtpRespoceArguments as $argument) {
$verb = strtok($argument, ' ');
$arguments = substr($argument, strlen($verb) + 1, strlen($argument) - strlen($verb) - 1);
$this->smtpFeatures[$verb] = $arguments;
}
return true;
}
/**
* Attempt to do SMTP authentication.
*
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
* @param string The requested authentication method. If none is specified, the best supported method will be used.
*
* @return bool
*/
function SmtpAuthentificate($uid, $pwd , $method = '')
{
if (empty($this->smtpFeatures['AUTH'])) {
// server doesn't understand AUTH command, then don't authentificate
return true;
}
$available_methods = explode(' ', $this->smtpFeatures['AUTH']); // methods supported by SMTP server
if (empty($method)) {
foreach ($this->smtpAuthMethods as $supported_method) {
// check if server supports methods, that we have implemented
if (in_array($supported_method, $available_methods)) {
$method = $supported_method;
break;
}
}
} else {
$method = strtoupper($method);
}
if (!in_array($method, $available_methods)) {
// coosen method is not supported by server
return $this->SetError('AUTH_METHOD_NOT_SUPPORTED', Array($method));
}
switch ($method) {
case 'CRAM-MD5':
$result = $this->_authCRAM_MD5($uid, $pwd);
break;
case 'LOGIN':
$result = $this->_authLogin($uid, $pwd);
break;
case 'PLAIN':
$result = $this->_authPlain($uid, $pwd);
break;
default:
return $this->SetError('AUTH_METHOD_NOT_IMPLEMENTED', Array($method));
break;
}
return $result;
}
/**
* Function which implements HMAC MD5 digest
*
* @param string $key The secret key
* @param string $data The data to protect
* @return string The HMAC MD5 digest
*/
function _HMAC_MD5($key, $data)
{
if (strlen($key) > 64) {
$key = pack('H32', md5($key));
}
if (strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}
$k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
$k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
$inner = pack('H32', md5($k_ipad . $data));
$digest = md5($k_opad . $inner);
return $digest;
}
/**
* Authenticates the user using the CRAM-MD5 method.
*
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
*
* @return bool
*/
function _authCRAM_MD5($uid, $pwd)
{
if (!$this->SmtpSendCommand('AUTH', 'CRAM-MD5')) {
return false;
}
// 334: Continue authentication request
if (!$this->SmtpParseResponse(334)) {
// 503: Error: already authenticated
return $this->smtpResponceCode === 503 ? true : false;
}
$challenge = base64_decode($this->smtpRespoceArguments[0]);
$auth_str = base64_encode($uid . ' ' . $this->_HMAC_MD5($pwd, $challenge));
if (!$this->SmtpSendCommand($auth_str)) {
return false;
}
// 235: Authentication successful
if (!$this->SmtpParseResponse(235)) {
return false;
}
return true;
}
/**
* Authenticates the user using the LOGIN method.
*
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
*
* @return bool
*/
function _authLogin($uid, $pwd)
{
if (!$this->SmtpSendCommand('AUTH', 'LOGIN')) {
return false;
}
// 334: Continue authentication request
if (!$this->SmtpParseResponse(334)) {
// 503: Error: already authenticated
return $this->smtpResponceCode === 503 ? true : false;
}
if (!$this->SmtpSendCommand(base64_encode($uid))) {
return false;
}
// 334: Continue authentication request
if (!$this->SmtpParseResponse(334)) {
return false;
}
if (!$this->SmtpSendCommand(base64_encode($pwd))) {
return false;
}
// 235: Authentication successful
if (!$this->SmtpParseResponse(235)) {
return false;
}
return true;
}
/**
* Authenticates the user using the PLAIN method.
*
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
*
* @return bool
*/
function _authPlain($uid, $pwd)
{
if (!$this->SmtpSendCommand('AUTH', 'PLAIN')) {
return false;
}
// 334: Continue authentication request
if (!$this->SmtpParseResponse(334)) {
// 503: Error: already authenticated
return $this->smtpResponceCode === 503 ? true : false;
}
$auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd);
if (!$this->SmtpSendCommand($auth_str)) {
return false;
}
// 235: Authentication successful
if (!$this->SmtpParseResponse(235)) {
return false;
}
return true;
}
/**
* Send the MAIL FROM: command.
*
* @param string $sender The sender (reverse path) to set.
* @param string $params String containing additional MAIL parameters, such as the NOTIFY flags defined by RFC 1891 or the VERP protocol.
*
* @return bool
*/
function SmtpSetFrom($sender, $params = null)
{
$args = "FROM:<$sender>";
if (is_string($params)) {
$args .= ' ' . $params;
}
if (!$this->SmtpSendCommand('MAIL', $args)) {
return false;
}
if (!$this->SmtpParseResponse(250)) {
return false;
}
return true;
}
/**
* Send the RCPT TO: command.
*
* @param string $recipient The recipient (forward path) to add.
* @param string $params String containing additional RCPT parameters, such as the NOTIFY flags defined by RFC 1891.
*
* @return bool
*/
function SmtpAddTo($recipient, $params = null)
{
$args = "TO:<$recipient>";
if (is_string($params)) {
$args .= ' ' . $params;
}
if (!$this->SmtpSendCommand('RCPT', $args)) {
return false;
}
if (!$this->SmtpParseResponse(array(250, 251))) {
return false;
}
return true;
}
/**
* Send the DATA command.
*
* @param string $data The message body to send.
*
* @return bool
*/
function SmtpSendMessage($data)
{
/* RFC 1870, section 3, subsection 3 states "a value of zero
* indicates that no fixed maximum message size is in force".
* Furthermore, it says that if "the parameter is omitted no
* information is conveyed about the server's fixed maximum
* message size". */
if (isset($this->smtpFeatures['SIZE']) && ($this->smtpFeatures['SIZE'] > 0)) {
if (strlen($data) >= $this->smtpFeatures['SIZE']) {
$this->SmtpDisconnect();
return $this->SetError('Message size excedes the server limit', null, false);
}
}
// Quote the data based on the SMTP standards
// Change Unix (\n) and Mac (\r) linefeeds into Internet-standard CRLF (\r\n) linefeeds.
$data = preg_replace(Array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
// Because a single leading period (.) signifies an end to the data,
// legitimate leading periods need to be "doubled" (e.g. '..')
$data = str_replace("\n.", "\n..", $data);
if (!$this->SmtpSendCommand('DATA')) {
return false;
}
if (!$this->SmtpParseResponse(354)) {
return false;
}
if ($this->smtpSocket->write($data . "\r\n.\r\n") === false) {
return false;
}
if (!$this->SmtpParseResponse(250)) {
return false;
}
return true;
}
/**
* Sets global charset for every message part
*
* @param string $charset
* @param bool set charset to default for current langauge
*/
function SetCharset($charset, $is_system = false)
{
if ($is_system) {
$language =& $this->Application->recallObject('lang.current');
/* @var $language LanguagesItem */
$charset = $language->GetDBField('Charset') ? $language->GetDBField('Charset') : 'ISO-8859-1';
}
$this->charset = $charset;
}
/**
* Allows to extract recipient's name from text by specifying it's email
*
* @param string $text
* @param string $email
* @return string
*/
function ExtractRecipientName($text, $email = '')
{
$lastspace = mb_strrpos($text, ' ');
$name = trim(mb_substr($text, 0, $lastspace - mb_strlen($text)), " \r\n\t\0\x0b\"'");
if (empty($name)) {
$name = $email;
}
return $name;
}
/**
* Takes $text and returns an email address from it
* Set $multiple to true to retrieve all found addresses
* Returns false if no addresses were found
*
* @param string $text
* @param bool $multiple
* @param bool $allowOnlyDomain
* @return string
*/
function ExtractRecipientEmail($text, $multiple = false, $allowOnlyDomain = false) {
if ($allowOnlyDomain) {
$pattern = '/(('.REGEX_EMAIL_USER.'@)?'.REGEX_EMAIL_DOMAIN.')/i';
} else {
$pattern = '/('.REGEX_EMAIL_USER.'@'.REGEX_EMAIL_DOMAIN.')/i';
}
if ($multiple) {
if (preg_match_all($pattern, $text, $findemail) >= 1) {
return $findemail[1];
} else {
return false;
}
} else {
if (preg_match($pattern, $text, $findemail) == 1) {
return $findemail[1];
} else {
return false;
}
}
}
/**
* Returns array of recipient names and emails
*
* @param string $list
* @param string $separator
* @return Array
*/
function GetRecipients($list, $separator = ';')
{
// by MIME specs recipients should be separated using "," symbol,
// but users can write ";" too (like in OutLook)
if (!trim($list)) {
return false;
}
$list = explode(',', str_replace($separator, ',', $list));
$ret = Array ();
foreach ($list as $recipient) {
$email = $this->ExtractRecipientEmail($recipient);
if (!$email) {
// invalid email format -> error
return false;
}
$name = $this->ExtractRecipientName($recipient, $email);
$ret[] = Array('Name' => $name, 'Email' => $email);
}
return $ret;
}
/* methods for nice header setting */
/**
* Sets "From" header.
*
* @param string $email
* @param string $first_last_name FirstName and LastName or just FirstName
* @param string $last_name LastName (if not specified in previous parameter)
*/
function SetFrom($email, $first_last_name, $last_name = '')
{
$name = rtrim($first_last_name.' '.$last_name, ' ');
$this->SetEncodedEmailHeader('From', $email, $name ? $name : $email);
if (!isset($this->headers['Return-Path'])) {
$this->SetReturnPath($email);
}
}
/**
+ * Sets "To" header.
+ *
+ * @param string $email
+ * @param string $first_last_name FirstName and LastName or just FirstName
+ * @param string $last_name LastName (if not specified in previous parameter)
+ */
+ function SetTo($email, $first_last_name, $last_name = '')
+ {
+ $name = rtrim($first_last_name.' '.$last_name, ' ');
+ $email = $this->_replaceRecipientEmail($email);
+
+ $this->SetEncodedEmailHeader('To', $email, $name ? $name : $email);
+ }
+
+ /**
* Sets "Return-Path" header (useful for spammers)
*
* @param string $email
*/
function SetReturnPath($email)
{
$this->SetHeader('Return-Path', $email);
}
/**
* Adds one more recipient into "To" header
*
* @param string $email
* @param string $first_last_name FirstName and LastName or just FirstName
* @param string $last_name LastName (if not specified in previous parameter)
*/
function AddTo($email, $first_last_name = '', $last_name = '')
{
- $email = $this->_replaceRecipientEmail($email);
-
$name = rtrim($first_last_name.' '.$last_name, ' ');
$this->AddRecipient('To', $email, $name);
}
/**
* Allows to replace recipient in all sent emails (used for debugging)
*
* @param string $email
* @return string
*/
function _replaceRecipientEmail($email)
{
if (defined('DBG_EMAIL') && DBG_EMAIL) {
if (substr(DBG_EMAIL, 0, 1) == '@') {
// domain
$email = str_replace('@', '_at_', $email) . DBG_EMAIL;
}
else {
$email = DBG_EMAIL;
}
}
return $email;
}
/**
* Adds one more recipient into "Cc" header
*
* @param string $email
* @param string $first_last_name FirstName and LastName or just FirstName
* @param string $last_name LastName (if not specified in previous parameter)
*/
function AddCc($email, $first_last_name = '', $last_name = '')
{
$name = rtrim($first_last_name.' '.$last_name, ' ');
$this->AddRecipient('Cc', $email, $name);
}
/**
* Adds one more recipient into "Bcc" header
*
* @param string $email
* @param string $first_last_name FirstName and LastName or just FirstName
* @param string $last_name LastName (if not specified in previous parameter)
*/
function AddBcc($email, $first_last_name = '', $last_name = '')
{
$name = rtrim($first_last_name.' '.$last_name, ' ');
$this->AddRecipient('Bcc', $email, $name);
}
/**
* Adds one more recipient to specified header
*
* @param string $header_name
* @param string $email
* @param string $name
*/
function AddRecipient($header_name, $email, $name = '')
{
+ $email = $this->_replaceRecipientEmail($email);
+
if (!$name) {
$name = $email;
}
$value = isset($this->headers[$header_name]) ? $this->headers[$header_name] : '';
$value .= ', '.$this->QuotedPrintableEncode($name, $this->charset).' <'.$email.'>';
$value = preg_replace('/^,(.*)/', '\\1', $value); // remove first comma
$this->SetHeader($header_name, $value);
}
/**
* Sets "Subject" header.
*
* @param string $subject message subject
*/
function SetSubject($subject)
{
$this->setEncodedHeader('Subject', $subject);
}
/**
* Sets HTML part of message
*
* @param string $html
*/
function SetHTML($html)
{
$this->CreateTextHtmlPart($html, true);
}
/**
* Sets Plain-Text part of message
*
* @param string $plain_text
*/
function SetPlain($plain_text)
{
$this->CreateTextHtmlPart($plain_text);
}
/**
* Sets HTML and optionally plain part of the message
*
* @param string $html
* @param string $plain_text
*/
function SetBody($html, $plain_text = '')
{
$this->SetHTML($html);
if ($plain_text) {
$this->SetPlain($plain_text);
}
}
/**
* Performs mail delivery (supports delayed delivery)
*
* @param string $mesasge message, if not given, then use composed one
* @param bool $immediate_send send message now or MailingId
* @param bool $immediate_clear clear message parts after message is sent
*
*/
function Deliver($message = null, $immediate_send = true, $immediate_clear = true)
{
if (isset($message)) {
// if message is given directly, then use it
if (is_array($message)) {
$message_headers =& $message[0];
$message_body =& $message[1];
}
else {
$message_headers = Array ();
list ($headers, $message_body) = explode("\n\n", $message, 2);
$headers = explode("\n", $headers);
foreach ($headers as $header) {
$header = explode(':', $header, 2);
$message_headers[ trim($header[0]) ] = trim($header[1]);
}
}
$composed = true;
} else {
// direct message not given, then assemble message from available parts
$composed = $this->GetHeadersAndBody($message_headers, $message_body);
}
if ($composed) {
if ($immediate_send === true) {
$send_method = 'Send'.$this->sendMethod;
$result = $this->$send_method($message_headers, $message_body);
if ($immediate_clear) {
$this->Clear();
}
return $result;
}
else {
$fields_hash = Array (
'ToEmail' => $message_headers['To'],
'Subject' => $message_headers['Subject'],
'Queued' => adodb_mktime(),
'SendRetries' => 0,
'LastSendRetry' => 0,
'MailingId' => (int)$immediate_send,
);
$fields_hash['MessageHeaders'] = serialize($message_headers);
$fields_hash['MessageBody'] =& $message_body;
$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'EmailQueue');
if ($immediate_clear) {
$this->Clear();
}
}
}
// if not immediate send, then send result is positive :)
return $immediate_send !== true ? true : false;
}
}
?>
\ No newline at end of file
Property changes on: branches/RC/core/kernel/utility/email_send.php
___________________________________________________________________
Modified: cvs2svn:cvs-rev
## -1 +1 ##
-1.3.2.6
\ No newline at end of property
+1.3.2.7
\ No newline at end of property
Index: branches/RC/core/units/admin/admin_events_handler.php
===================================================================
--- branches/RC/core/units/admin/admin_events_handler.php (revision 11656)
+++ branches/RC/core/units/admin/admin_events_handler.php (revision 11657)
@@ -1,1204 +1,1206 @@
<?php
class AdminEventsHandler extends kDBEventHandler {
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
'OnSaveColumns' => array('self' => true),
'OnClosePopup' => array('self' => true),
'OnSaveSetting' => array('self' => true),
// export/import permissions is checked within events
'OnExportCSV' => Array('self' => true),
'OnGetCSV' => Array('self' => true),
'OnCSVImportBegin' => Array('self' => true),
'OnCSVImportStep' => Array('self' => true),
'OnDropTempTablesByWID' => Array('self' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Checks permissions of user
*
* @param kEvent $event
*/
function CheckPermission(&$event)
{
$system_events = Array (
'OnResetModRwCache', 'OnResetCMSMenuCache', 'OnResetSections',
'OnResetConfigsCache', 'OnCompileTemplates', 'OnGenerateTableStructure',
'OnRebuildThemes', 'OnCheckPrefixConfig',
);
if ($this->Application->isDebugMode(false) && in_array($event->Name, $system_events)) {
return true;
}
$tools_events = Array (
'OnBackup' => 'in-portal:backup.view',
'OnBackupProgress' => 'in-portal:backup.view',
'OnDeleteBackup' => 'in-portal:backup.view',
'OnBackupCancel' => 'in-portal:backup.view',
'OnRestore' => 'in-portal:restore.view',
'OnRestoreProgress' => 'in-portal:restore.view',
'OnRestoreCancel' => 'in-portal:backup.view',
'OnSqlQuery' => 'in-portal:sql_query.view',
);
if (array_key_exists($event->Name, $tools_events)) {
return $this->Application->CheckPermission($tools_events[$event->Name]);
}
if ($event->Name == 'OnSaveMenuFrameWidth') {
if (!$this->Application->IsAdmin() || !$this->Application->LoggedIn()) {
return false;
}
return true;
}
return parent::CheckPermission($event);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnResetModRwCache(&$event)
{
if ($this->Application->GetVar('ajax') == 'yes') {
$event->status = erSTOP;
}
$this->Conn->Query('DELETE FROM '.TABLE_PREFIX.'Cache WHERE VarName LIKE "mod_rw%"');
}
function OnResetCMSMenuCache(&$event)
{
if ($this->Application->GetVar('ajax') == 'yes') {
$event->status = erSTOP;
}
$this->Conn->Query('DELETE FROM '.TABLE_PREFIX.'Cache WHERE VarName IN ("cms_menu", "StructureTree")');
}
function OnResetSections(&$event)
{
if ($this->Application->GetVar('ajax') == 'yes') {
$event->status = erSTOP;
}
$this->Conn->Query('DELETE FROM '.TABLE_PREFIX.'Cache WHERE VarName = "sections_parsed"');
if (isset($this->Application->Memcached)) {
$this->Application->Memcached->delete('master:sections_parsed');
}
}
function OnResetConfigsCache(&$event)
{
if ($this->Application->GetVar('ajax') == 'yes') {
$event->status = erSTOP;
}
$this->Conn->Query('DELETE FROM '.TABLE_PREFIX.'Cache WHERE VarName = "config_files" OR VarName = "configs_parsed" OR VarName = "sections_parsed"');
if (isset($this->Application->Memcached)) {
$this->Application->Memcached->delete('master:config_files');
$this->Application->Memcached->delete('master:configs_parsed');
$this->Application->Memcached->delete('master:sections_parsed');
}
}
function OnCompileTemplates(&$event)
{
$compiler =& $this->Application->recallObject('NParserCompiler');
/* @var $compiler NParserCompiler */
$compiler->CompileTemplatesStep();
$event->status = erSTOP;
}
/**
* Generates sturcture for specified table
*
* @param kEvent $event
* @author Alex
*/
function OnGenerateTableStructure(&$event)
{
$types_hash = Array(
'string' => 'varchar|text|mediumtext|longtext|date|datetime|time|timestamp|char|year|enum|set',
'int' => 'smallint|mediumint|int|bigint|tinyint',
'float' => 'float|double|decimal',
);
$table_name = $this->Application->GetVar('table_name');
if (!$table_name) {
echo 'error: no table name specified';
return ;
}
if (TABLE_PREFIX && !preg_match('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', $table_name) && (strtolower($table_name) != $table_name)) {
// table name without prefix, then add it (don't affect K3 tables named in lowercase)
$table_name = TABLE_PREFIX.$table_name;
}
if (!$this->Conn->TableFound($table_name)) {
// table with prefix doesn't exist, assume that just config prefix passed -> resolve table name from it
$prefix = preg_replace('/^' . preg_quote(TABLE_PREFIX, '/') . '/', '', $table_name);
if ($this->Application->prefixRegistred($prefix)) {
// when prefix is found -> use it's table (don't affect K3 tables named in lowecase)
$table_name = $this->Application->getUnitOption($prefix, 'TableName');
}
}
$table_info = $this->Conn->Query('DESCRIBE '.$table_name);
// 1. prepare config keys
$grids = Array (
'Default' => Array (
'Icons' => Array ('default' => 'icon16_custom.gif'),
'Fields' => Array (),
)
);
$grid_fields = Array();
$id_field = '';
$fields = Array();
$float_types = Array ('float', 'double', 'numeric');
foreach ($table_info as $field_info) {
if (preg_match('/l[\d]+_.*/', $field_info['Field'])) {
// don't put multilingual fields in config
continue;
}
$field_options = Array ();
$grid_col_options = Array(
'title' => 'la_col_' . $field_info['Field'],
'filter_block' => 'grid_like_filter',
);
// 1. get php field type by mysql field type
foreach ($types_hash as $php_type => $db_types) {
if (preg_match('/'.$db_types.'/', $field_info['Type'])) {
$field_options['type'] = $php_type;
break;
}
}
$default_value = $field_info['Default'];
if (in_array($php_type, $float_types)) {
// this is float number
if (preg_match('/'.$db_types.'\([\d]+,([\d]+)\)/i', $field_info['Type'], $regs)) {
// size is described in structure -> add formatter
$field_options['formatter'] = 'kFormatter';
$field_options['format'] = '%01.'.$regs[1].'f';
if ($field_info['Null'] != 'YES') {
// null fields, will most likely have NULL as default value
$default_value = 0;
}
}
else {
// no size information, just convert to float
if ($field_info['Null'] != 'YES') {
// null fields, will most likely have NULL as default value
$default_value = (float)$default_value;
}
}
}
if (preg_match('/varchar\(([\d]+)\)/i', $field_info['Type'], $regs)) {
$field_options['max_len'] = (int)$regs[1];
}
if ($field_info['Null'] != 'YES') {
$field_options['not_null'] = 1;
}
if ($field_info['Key'] == 'PRI') {
$default_value = 0;
$id_field = $field_info['Field'];
}
if ($php_type == 'int' && ($field_info['Null'] != 'YES' || is_numeric($default_value))) {
// is integer field AND not null
$field_options['default'] = (int)$default_value;
}
else {
$field_options['default'] = $default_value;
}
$fields[ $field_info['Field'] ] = $this->transformDump($field_options);
$grids_fields[ $field_info['Field'] ] = $this->transformDump($grid_col_options);
}
$grids['Default']['Fields'] = $grids_fields;
$ret = "'IDField' => '".$id_field."',\n'Fields' => A".substr(stripslashes(var_export($fields, true)), 1).',';
$ret .= "\n"."'Grids' => ".stripslashes(var_export($grids, true));
$ret = str_replace('array (', 'Array (', $ret);
$ret = preg_replace("/'(.*?)' => 'Array \((.*?), \)',/", "'\\1' => Array (\\2),", $ret);
$ret = preg_replace("/\n '/", "\n\t'", $ret);
+ $this->Application->InitParser();
ob_start();
+ echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"'));
?>
- <html>
- <head>
- <title>Table "<?php echo $table_name; ?>" Structure</title>
- </head>
- <body bgcolor="#E7E7E7">
- <a href="javascript:window_close();">Close Window</a><br />
- <?php echo $GLOBALS['debugger']->highlightString($ret); ?>
- <br /><a href="javascript:window_close();">Close Window</a><br />
- </body>
- </html>
+ <script type="text/javascript">
+ set_window_title('Table "<?php echo $table_name; ?>" Structure');
+ </script>
+
+ <a href="javascript:window_close();">Close Window</a><br /><br />
+ <?php echo $GLOBALS['debugger']->highlightString($ret); ?>
+ <br /><br /><a href="javascript:window_close();">Close Window</a><br />
<?php
+ echo $this->Application->ParseBlock(Array('name' => 'incs/footer'));
echo ob_get_clean();
$event->status = erSTOP;
}
function transformDump($dump)
{
if (is_array($dump)) {
$dump = var_export($dump, true);
}
$dump = preg_replace("/,\n[ ]*/", ', ', $dump);
$dump = preg_replace("/array \(\n[ ]*/", 'Array (', $dump); // replace array start
$dump = preg_replace("/,\n[ ]*\),/", "),", $dump); // replace array end
return $dump;
}
/**
* Refreshes ThemeFiles & Theme tables by actual content on HDD
*
* @param kEvent $event
*/
function OnRebuildThemes(&$event)
{
if ($this->Application->GetVar('ajax') == 'yes') {
$event->status = erSTOP;
}
$themes_helper =& $this->Application->recallObject('ThemesHelper');
/* @var $themes_helper kThemesHelper */
$themes_helper->refreshThemes();
}
function OnSaveColumns(&$event)
{
$picker_helper =& $this->Application->recallObject('ColumnPickerHelper');
/* @var $picker_helper kColumnPickerHelper */
$picker_helper->SetGridName($this->Application->GetLinkedVar('grid_name'));
$picked = trim($this->Application->GetVar('picked_str'), '|');
$hidden = trim($this->Application->GetVar('hidden_str'), '|');
$main_prefix = $this->Application->GetVar('main_prefix');
$picker_helper->SaveColumns($main_prefix, $picked, $hidden);
$this->finalizePopup($event);
}
/**
* Saves various admin settings via ajax
*
* @param kEvent $event
*/
function OnSaveSetting(&$event)
{
if ($this->Application->GetVar('ajax') != 'yes') {
return ;
}
$var_name = $this->Application->GetVar('var_name');
$var_value = $this->Application->GetVar('var_value');
$this->Application->StorePersistentVar($var_name, $var_value);
$event->status = erSTOP;
}
/**
* Just closes popup & deletes last_template & opener_stack if popup, that is closing
*
* @param kEvent $event
*/
function OnClosePopup(&$event)
{
$event->SetRedirectParam('opener', 'u');
}
/**
* Occurs right after initialization of the kernel, used mainly as hook-to event
*
* @param kEvent $event
*/
function OnStartup(&$event)
{
}
/**
* Occurs right before echoing the output, in Done method of application, used mainly as hook-to event
*
* @param kEvent $event
*/
function OnBeforeShutdown(&$event)
{
}
/**
* Is called after tree was build (when not from cache)
*
* @param kEvent $event
*/
function OnAfterBuildTree(&$event)
{
}
/**
* Called by AJAX to perform CSV export
*
* @param kEvent $event
*/
function OnExportCSV(&$event)
{
$export_helper =& $this->Application->recallObject('CSVHelper');
/* @var $export_helper kCSVHelper */
$prefix_special = $this->Application->GetVar('PrefixSpecial');
if(!$prefix_special) {
$prefix_special = $export_helper->ExportData('prefix');
}
$prefix_elems = split('\.|_', $prefix_special, 2);
$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) {
$this->Application->Redirect('no_permission');
}
$export_helper->PrefixSpecial = $prefix_special;
$export_helper->grid = $this->Application->GetVar('grid');
$export_helper->ExportStep();
$event->status = erSTOP;
}
/**
* Returning created by AJAX CSV file
*
* @param kEvent $event
*/
function OnGetCSV(&$event)
{
$export_helper =& $this->Application->recallObject('CSVHelper');
/* @var $export_helper kCSVHelper */
$prefix_special = $export_helper->ExportData('prefix');
$prefix_elems = split('\.|_', $prefix_special, 2);
$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) {
$this->Application->Redirect('no_permission');
}
$export_helper->GetCSV();
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnCSVImportBegin(&$event)
{
$prefix_special = $this->Application->GetVar('PrefixSpecial');
$prefix_elems = split('\.|_', $prefix_special, 2);
$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) {
$this->Application->Redirect('no_permission');
}
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
$field_values = array_shift($items_info);
$object->SetFieldsFromHash($field_values);
$event->redirect = false;
$result = 'required';
if($object->GetDBField('ImportFile')) {
$import_helper =& $this->Application->recallObject('CSVHelper');
/* @var $import_helper kCSVHelper */
$import_helper->PrefixSpecial = $this->Application->GetVar('PrefixSpecial');
$import_helper->grid = $this->Application->GetVar('grid');
$result = $import_helper->ImportStart( $object->GetField('ImportFile', 'file_paths') );
if($result === true) {
$event->redirect = $this->Application->GetVar('next_template');
$event->SetRedirectParam('PrefixSpecial', $this->Application->GetVar('PrefixSpecial'));
$event->SetRedirectParam('grid', $this->Application->GetVar('grid'));
}
}
if($event->redirect === false) {
$object->SetError('ImportFile', $result);
$event->status = erFAIL;
}
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnCSVImportStep(&$event)
{
$import_helper =& $this->Application->recallObject('CSVHelper');
/* @var $export_helper kCSVHelper */
$prefix_special = $import_helper->ImportData('prefix');
$prefix_elems = split('\.|_', $prefix_special, 2);
$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) {
$this->Application->Redirect('no_permission');
}
$import_helper->ImportStep();
$event->status = erSTOP;
}
/**
* Shows unit config filename, where requested prefix is defined
*
* @param kEvent $event
*/
function OnCheckPrefixConfig(&$event)
{
$prefix = $this->Application->GetVar('config_prefix');
$config_file = $this->Application->UnitConfigReader->prefixFiles[$prefix];
+ $this->Application->InitParser();
+
ob_start();
+ echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"'));
?>
- <html>
- <head>
- <title>Unit Config of "<?php echo $prefix; ?>" prefix</title>
- </head>
- <body bgcolor="#E7E7E7">
- <a href="javascript:window_close();">Close Window</a><br /><br />
- <strong>Prefix:</strong> <?php echo $prefix; ?><br />
- <strong>Unit Config:</strong> <?php echo $GLOBALS['debugger']->highlightString($config_file); ?><br />
- <br /><a href="javascript:window_close();">Close Window</a><br />
- </body>
- </html>
+ <script type="text/javascript">
+ set_window_title('Unit Config of "<?php echo $prefix; ?>" prefix');
+ </script>
+
+ <a href="javascript:window_close();">Close Window</a><br /><br />
+ <strong>Prefix:</strong> <?php echo $prefix; ?><br />
+ <strong>Unit Config:</strong> <?php echo $GLOBALS['debugger']->highlightString($config_file); ?><br />
+ <br /><a href="javascript:window_close();">Close Window</a><br />
+
<?php
+ echo $this->Application->ParseBlock(Array('name' => 'incs/footer'));
echo ob_get_clean();
$event->status = erSTOP;
}
function OnUploadFile(&$event)
{
// Flash uploader does NOT send correct cookies, so we need to make our own check
$cookie_name = 'adm_'.$this->Application->ConfigValue('SessionCookieName');
$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
$admin_ses =& $this->Application->recallObject('Session.admin');
/* @var $admin_ses Session */
$user = $admin_ses->RecallVar('user_id');
$perm_helper =& $this->Application->recallObject('PermissionsHelper');
/* @var $perm_helper kPermissionsHelper */
/*if() {
$prefix_special = $this->Application->GetVar('PrefixSpecial');
$prefix_elems = split('\.|_', $prefix_special, 2);
$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
$section = $perm_sections['main'];
}
else {*/
$section = $event->getSection();
/*}*/
if ($this->Application->GetVar('t') != 'import/import_start' && !$perm_helper->CheckUserPermission($user, $section.'.add') && !$perm_helper->CheckUserPermission($user, $section.'.edit')) {
$event->status = erPERM_FAIL;
header('HTTP/1.0 403 You don\'t have permissions to upload');
exit;
return;
}
if (!$cookie_name) $cookie_name = 'sid';
$value = $this->Application->GetVar('Filedata');
if (!$value) return ;
$tmp_path = WRITEABLE.'/tmp/';
$fname = $value['name'];
$id = $this->Application->GetVar('id');
if ($id) $fname = $id.'_'.$fname;
if (!is_writable($tmp_path)) {
header('HTTP/1.0 500 Write permissions not set on the server');
exit;
}
move_uploaded_file($value['tmp_name'], $tmp_path.$fname);
exit;
}
function OnDropTempTablesByWID(&$event)
{
$sid = $this->Application->GetSID();
$wid = $this->Application->GetVar('m_wid');
$tables = $this->Conn->GetCol('SHOW TABLES');
$mask_edit_table = '/'.TABLE_PREFIX.'ses_'.$sid.'_'.$wid.'_edit_(.*)$/';
foreach($tables as $table)
{
if( preg_match($mask_edit_table,$table,$rets) )
{
$this->Conn->Query('DROP TABLE IF EXISTS '.$table);
}
}
echo 'OK';
$event->status = erSTOP;
return ;
}
/**
* Backup all data
*
* @param kEvent $event
*/
function OnBackup(&$event)
{
$a_tables = $this->Conn->GetCol('SHOW TABLES'); // array_keys($tables);
$TableNames = Array();
for($x=0;$x<count($a_tables);$x++)
{
if(substr($a_tables[$x],0,strlen(TABLE_PREFIX))==TABLE_PREFIX)
{
if (!strstr($a_tables[$x], 'ses_')) {
$TableNames[] = $a_tables[$x];
}
}
}
// echo "<pre>"; print_r($TableNames); echo "</pre>";
// exit;
$backupProgress = Array (
'table_num' => 0,
'table_names' => $TableNames,
'table_count' => count($TableNames),
'record_count' => 0,
'file_name' => $this->Application->ConfigValue('Backup_Path')."/dump".adodb_mktime().".txt",
);
$this->Application->RemoveVar('adm.backupcomplete_filename');
$this->Application->RemoveVar('adm.backupcomplete_filesize');
$out = array();
for($x=0;$x<count($TableNames);$x++) {
if (!strstr($TableNames[$x], 'ses_')) {
$out[] = $this->GetTableCreate($TableNames[$x]);
}
}
$fp = fopen($backupProgress['file_name'], 'a');
$sql = "SELECT Name, Version FROM ".TABLE_PREFIX."Modules";
$r = $this->Conn->Query($sql);
foreach ($r AS $a_module) {
$version = $a_module['Version'];
fwrite($fp, "# ".$a_module['Name']." Version: $version;\n");
}
fwrite($fp, "#------------------------------------------\n\n");
fwrite($fp,implode("\n",$out));
fwrite($fp,"\n");
fclose($fp);
$this->Application->StoreVar('adm.backup_status', serialize($backupProgress));
$event->redirect = 'tools/backup2';
}
/**
* Perform next backup step
*
* @param kEvent $event
*/
function OnBackupProgress(&$event)
{
$done_percent = $this->performBackup();
if ($done_percent == 100) {
$event->redirect = 'tools/backup3';
return ;
}
echo $done_percent;
$event->status = erSTOP;
}
/**
* Stops Backup & redirect to Backup template
*
* @param kEvent $event
*/
function OnBackupCancel(&$event)
{
$event->redirect = 'tools/backup1';
}
/**
* Stops Restore & redirect to Restore template
*
* @param kEvent $event
*/
function OnRestoreCancel(&$event)
{
$event->redirect = 'tools/restore1';
}
function performBackup()
{
$backupProgress = unserialize($this->Application->RecallVar('adm.backup_status'));
// echo "<pre>"; print_r($backupProgress); echo "</pre>";
// exit;
$CurrentTable = $backupProgress['table_names'][$backupProgress['table_num']];
// get records
$a_records = $this->insert_data($CurrentTable,$backupProgress['record_count'],50,"");
// echo "<pre>"; print_r($a_records); echo "</pre>";
// exit;
if ($a_records['num'] < 50) {
$backupProgress['table_num']++;
// if ($backupProgress['table_names'][$backupProgress['table_num']] == TABLE_PREFIX.'Cache') {
// $backupProgress['table_num']++;
// }
$backupProgress['record_count'] = 0;
} else {
$backupProgress['record_count']+=50;
}
if ($a_records['sql']) {
$fp = fopen($backupProgress['file_name'], 'a');
fwrite($fp, $a_records['sql']);
fclose($fp);
}
$percent = ($backupProgress['table_num'] / $backupProgress['table_count']) * 100;
if ($percent >= 100) {
$percent = 100;
$this->Application->StoreVar('adm.backupcomplete_filename', $backupProgress['file_name']);
$this->Application->StoreVar('adm.backupcomplete_filesize', round(filesize($backupProgress['file_name'])/1024/1024, 2)); // Mbytes
} else {
$this->Application->StoreVar('adm.backup_status', serialize($backupProgress));
}
return round($percent);
}
//extracts the rows of data from tables using limits
function insert_data($table, $start, $limit, $mywhere)
{
// global $out;
if ($mywhere !="")
{
$whereclause= " WHERE ".$mywhere." ";
}
else
{
$whereclause = "";
}
$a_data = $this->Conn->Query("SELECT * from $table $whereclause LIMIT $start, $limit");
// echo "SELECT * from $table $whereclause LIMIT $start, $limit";
// echo "<pre>"; print_r($a_records); echo "</pre>";
// exit;
if (!$a_data) {
return Array(
'num' => 0,
'sql' => '',
);
}
// $prefix = GetTablePrefix();
$rowcount = 0;
$a_fields = array_keys($a_data[0]);
$fields_sql = '';
foreach ($a_fields AS $field_name) {
$fields_sql .= '`'.$field_name.'`,';
}
$fields_sql = preg_replace('/(.*),$/', '\\1', $fields_sql);
$temp = '';
foreach ($a_data AS $a_row)
{
$values_sql = '';
foreach ($a_row as $field_name => $field_value) {
$values_sql .= $this->Conn->qstr($field_value).',';
}
$values_sql = preg_replace('/(.*),$/', '\\1', $values_sql);
$sql = 'INSERT INTO '.$table.' ('.$fields_sql.') VALUES ('.$values_sql.');';
$sql=ereg_replace("\n","\\n", $sql);
$sql=ereg_replace("\r","\\r", $sql);
$temp .= $sql."\n";
}
if(strlen(TABLE_PREFIX))
{
$temp = str_replace("INSERT INTO ".TABLE_PREFIX, "INSERT INTO ", $temp);
}
return Array(
'num' => count($a_data),
'sql' => $temp,
);
}
function GetTableCreate($table, $crlf="\n")
{
$schema_create = 'DROP TABLE IF EXISTS ' . $table . ';' . $crlf;
$schema_create .="# --------------------------------------------------------".$crlf;
$this->Conn->Query("SET SQL_QUOTE_SHOW_CREATE = 0");
$tmpres = $this->Conn->Query("SHOW CREATE TABLE $table");
// echo "<pre>"; print_r($tmpres); echo "</pre>";
// exit;
if(is_array($tmpres) && isset($tmpres[0]))
{
$tmpres = $tmpres[0];
$pos = strpos($tmpres["Create Table"], ' (');
$pos2 = strpos($tmpres["Create Table"], '(');
if ($pos2 != $pos + 1)
{
$pos = $pos2;
$tmpres["Create Table"] = str_replace(",", ",\n ", $tmpres["Create Table"]);
}
$tmpres["Create Table"] = substr($tmpres["Create Table"], 0, 13)
. (($use_backquotes) ? $tmpres["Table"] : $tmpres["Table"])
. substr($tmpres["Create Table"], $pos);
$tmpres["Create Table"] = str_replace("\n", $crlf, $tmpres["Create Table"]);
if (preg_match_all('((,\r?\n[\s]*(CONSTRAINT|FOREIGN[\s]*KEY)[^\r\n,]+)+)', $tmpres["Create Table"], $regs)) {
if (!isset($sql_constraints)) {
if (isset($GLOBALS['no_constraints_comments'])) {
$sql_constraints = '';
} else {
$sql_constraints = $crlf . '#' . $crlf
. '# ' . $GLOBALS['strConstraintsForDumped'] . $crlf
. '#' . $crlf;
}
}
if (!isset($GLOBALS['no_constraints_comments'])) {
$sql_constraints .= $crlf .'#' . $crlf .'# ' . $GLOBALS['strConstraintsForTable'] . ' ' . $table . $crlf . '#' . $crlf;
}
$sql_constraints .= 'ALTER TABLE $table $crlf '
. preg_replace('/(,\r?\n|^)([\s]*)(CONSTRAINT|FOREIGN[\s]*KEY)/', '\1\2ADD \3', substr($regs[0][0], 2))
. ";\n";
$tmpres["Create Table"] = preg_replace('((,\r?\n[\s]*(CONSTRAINT|FOREIGN[\s]*KEY)[^\r\n,]+)+)', '', $tmpres["Create Table"]);
}
$schema_create .= $tmpres["Create Table"];
}
if(strlen($schema_create))
{
$schema_create = str_replace("DROP TABLE IF EXISTS ".TABLE_PREFIX,"DROP TABLE ",$schema_create);
$schema_create = str_replace("CREATE TABLE ".TABLE_PREFIX,"CREATE TABLE ",$schema_create);
while(strlen($schema_create && substr($schema_create,-1)!=")"))
{
$schema_create = substr($schema_create,0,-1);
}
}
$schema_create .= "\n# --------------------------------------------------------\n";
return $schema_create;
}
/**
* Deletes one backup file
*
* @param kEvent $event
*/
function OnDeleteBackup(&$event)
{
@unlink($this->get_backup_file());
}
function get_backup_file()
{
return $this->Application->ConfigValue('Backup_Path').'/dump'.$this->Application->GetVar('backupdate').'.txt';
}
/**
* Starts restore process
*
* @param kEvent $event
*/
function OnRestore(&$event)
{
$file = $this->get_backup_file();
$restoreProgress = Array (
'file_pos' => 0,
'file_name' => $file,
'file_size' => filesize($file),
);
$this->Application->RemoveVar('adm.restore_success');
$this->Application->StoreVar('adm.restore_status', serialize($restoreProgress));
$event->redirect = 'tools/restore3';
}
function OnRestoreProgress(&$event)
{
$done_percent = $this->performRestore();
if ($done_percent == -3) {
$event->redirect = 'tools/restore4';
return ;
}
if ($done_percent < 0) {
$this->Application->StoreVar('adm.restore_error', 'File read error'); $event->redirect = 'tools/restore4';
return ;
}
if ($done_percent == 100) {
$this->replaceRestoredFiles();
$this->Application->StoreVar('adm.restore_success', 1);
$event->redirect = 'tools/restore4';
return ;
}
echo $done_percent;
$event->status = erSTOP;
}
function replaceRestoredFiles()
{
// gather restored table names
$tables = $this->Conn->GetCol('SHOW TABLES');
$mask_restore_table = '/^restore'.TABLE_PREFIX.'(.*)$/';
foreach($tables as $table)
{
if( preg_match($mask_restore_table,$table,$rets) )
{
$old_table = substr($table, 7);
$this->Conn->Query('DROP TABLE IF EXISTS '.$old_table);
$this->Conn->Query('CREATE TABLE '.$old_table.' LIKE '.$table);
$this->Conn->Query('INSERT INTO '.$old_table.' SELECT * FROM '.$table);
$this->Conn->Query('DROP TABLE '.$table);
}
}
}
function performRestore()
{
$restoreProgress = unserialize($this->Application->RecallVar('adm.restore_status'));
$filename = $restoreProgress['file_name'];
$FileOffset = $restoreProgress['file_pos'];
$MaxLines = 200;
$size = filesize($filename);
if($FileOffset > $size) {
return -2;
}
$fp = fopen($filename,"r");
if(!$fp) {
return -1;
}
if($FileOffset>0)
{
fseek($fp,$FileOffset);
}
else
{
$EndOfSQL = FALSE;
$sql = "";
while(!feof($fp) && !$EndOfSQL)
{
$l = fgets($fp);
if(substr($l,0,11)=="INSERT INTO")
{
$EndOfSQL = TRUE;
}
else
{
$sql .= $l;
$FileOffset = ftell($fp) - strlen($l);
}
}
if(strlen($sql))
{
$error = $this->runSchemaText($sql);
if ($error != '') {
$this->Application->StoreVar('adm.restore_error', $error);
return -3;
}
}
fseek($fp,$FileOffset);
}
$LinesRead = 0;
$sql = "";
$AllSql = array();
while($LinesRead < $MaxLines && !feof($fp))
{
$sql = fgets($fp);
if(strlen($sql))
{
$AllSql[] = $sql;
$LinesRead++;
}
}
if(!feof($fp))
{
$FileOffset = ftell($fp);
}
else
{
$FileOffset = $size;
}
fclose($fp);
if(count($AllSql)>0) {
$error = $this->runSQLText($AllSql);
if ($error != '') {
$this->Application->StoreVar('adm.restore_error', $error);
return -3;
}
}
$restoreProgress['file_pos'] = $FileOffset;
$this->Application->StoreVar('adm.restore_status', serialize($restoreProgress));
return round($FileOffset/$size * 100);
// $this->Application->StoreVar('adm.restore_error', 'lalalal');
// $event->redirect = 'tools/restore4';
}
function runSchemaText($sql)
{
$table_prefix = 'restore'.TABLE_PREFIX;
// $table_prefix = TABLE_PREFIX;
if(strlen($table_prefix))
{
$what = "CREATE TABLE ";
$replace = "CREATE TABLE ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "DROP TABLE ";
$replace = "DROP TABLE IF EXISTS ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "INSERT INTO ";
$replace = "INSERT INTO ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "UPDATE ";
$replace = "UPDATE ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "ALTER TABLE ";
$replace = "ALTER TABLE ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
}
$commands = explode("# --------------------------------------------------------",$sql);
if(count($commands)>0)
{
// $query_func = getConnectionInterface('query',$dbo_type);
// $errorno_func = getConnectionInterface('errorno',$dbo_type);
// $errormsg_func = getConnectionInterface('errormsg',$dbo_type);
for($i = 0; $i < count($commands); $i++)
{
$cmd = $commands[$i];
$cmd = trim($cmd);
if(strlen($cmd)>0)
{
$this->Conn->Query($cmd);
if($this->Conn->errorCode != 0)
{
return $this->Conn->errorMessage." COMMAND:<PRE>$cmd</PRE>";
}
}
}
}
}
function runSQLText($allsql)
{
$line = 0;
// $query_func = getConnectionInterface('query',$dbo_type);
// $errorno_func = getConnectionInterface('errorno',$dbo_type);
// $errormsg_func = getConnectionInterface('errormsg',$dbo_type);
while($line<count($allsql))
{
$sql = $allsql[$line];
if(strlen(trim($sql))>0 && substr($sql,0,1)!="#")
{
$table_prefix = 'restore'.TABLE_PREFIX;
if(strlen($table_prefix))
{
$what = "CREATE TABLE ";
$replace = "CREATE TABLE ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "DELETE FROM ";
$replace = "DELETE FROM ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "DROP TABLE ";
$replace = "DROP TABLE IF EXISTS ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "INSERT INTO ";
$replace = "INSERT INTO ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "REPLACE INTO ";
$replace = "REPLACE INTO ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "UPDATE ";
$replace = "UPDATE ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
$what = "ALTER TABLE ";
$replace = "ALTER TABLE ".$table_prefix;
$sql = ereg_replace($what, $replace, $sql);
}
$sql = trim($sql);
if(strlen($sql)>0)
{
$this->Conn->Query($sql);
if($this->Conn->errorCode != 0)
{
return $this->Conn->errorMessage." COMMAND:<PRE>$sql</PRE>";
}
}
}
$line++;
}
}
/**
* Starts restore process
*
* @param kEvent $event
*/
function OnSqlQuery(&$event)
{
$sql = $this->Application->GetVar('sql');
if ($sql) {
$start = $this->getMoment();
$result = $this->Conn->Query($sql);
$this->Application->SetVar('sql_time', round($this->getMoment() - $start, 7));
if ($result)
{
if (is_array($result))
{
$this->Application->SetVar('sql_has_rows', 1);
$this->Application->SetVar('sql_rows', serialize($result));
}
}
$check_sql = trim(strtolower($sql));
if (
(substr($check_sql, 0, 6) == 'insert')
|| (substr($check_sql, 0, 6) == 'update')
|| (substr($check_sql, 0, 7) == 'replace')
|| (substr($check_sql, 0, 6) == 'delete')
) {
$this->Application->SetVar('sql_has_affected', 1);
$this->Application->SetVar('sql_affected', $this->Conn->getAffectedRows());
}
}
$this->Application->SetVar('query_status', 1);
$event->status = erFAIL;
}
function getMoment()
{
list($usec, $sec) = explode(' ', microtime());
return ((float)$usec + (float)$sec);
}
/**
* Occurs after unit config cache was successfully rebuilt
*
* @param kEvent $event
*/
function OnAfterCacheRebuild(&$event)
{
}
/**
* Removes "Community -> Groups" section when it is not allowed
*
* @param kEvent $event
*/
function OnAfterConfigRead(&$event)
{
parent::OnAfterConfigRead($event);
if (!$this->Application->ConfigValue('AdvancedUserManagement')) {
$section_ajustments = $this->Application->getUnitOption($event->Prefix, 'SectionAdjustments');
if (!$section_ajustments) {
$section_ajustments = Array ();
}
$section_ajustments['in-portal:user_groups'] = 'remove';
$this->Application->setUnitOption($event->Prefix, 'SectionAdjustments', $section_ajustments);
}
}
/**
* Saves menu (tree) frame width
*
* @param kEvent $event
*/
function OnSaveMenuFrameWidth(&$event)
{
$event->status = erSTOP;
if (!$this->Application->ConfigValue('ResizableFrames')) {
return ;
}
$sql = 'UPDATE ' . $this->Application->getUnitOption('conf', 'TableName') . '
SET VariableValue = ' . (int)$this->Application->GetVar('width') . '
WHERE VariableName = "MenuFrameWidth"';
$this->Conn->Query($sql);
if ($this->Conn->getAffectedRows()) {
$this->Application->UnitConfigReader->ResetParsedData(false);
}
}
}
\ No newline at end of file
Property changes on: branches/RC/core/units/admin/admin_events_handler.php
___________________________________________________________________
Modified: cvs2svn:cvs-rev
## -1 +1 ##
-1.8.2.29
\ No newline at end of property
+1.8.2.30
\ No newline at end of property
Index: branches/RC/core/units/general/helpers/mailing_list_helper.php
===================================================================
--- branches/RC/core/units/general/helpers/mailing_list_helper.php (revision 11656)
+++ branches/RC/core/units/general/helpers/mailing_list_helper.php (revision 11657)
@@ -1,270 +1,270 @@
<?php
class MailingListHelper extends kHelper {
var $_mailingId = false;
/**
* Adds new email from given mailing to emails queue
*
* @param string $email
* @param int $mailing_id
* @param Array $mailing_data
*/
function queueEmail($email, $mailing_id, &$mailing_data)
{
$esender =& $this->Application->recallObject('EmailSender');
/* @var $esender kEmailSendingHelper */
if ($this->_mailingId != $mailing_id) {
if (is_numeric($this->_mailingId)) {
// clear fields after previous mailing processing
$esender->Clear();
}
// 1. set headers same for all emails
list ($mailing_data['FromName'], $mailing_data['FromEmail']) = $this->_getSenderData($mailing_data);
$esender->SetFrom($mailing_data['FromEmail'], $mailing_data['FromName']);
$esender->SetSubject($mailing_data['Subject']);
$esender->SetBody($mailing_data['MessageHtml'], $mailing_data['MessageText']);
// 2. add attachment if any
$attachments = $mailing_data['Attachments'] ? explode('|', $mailing_data['Attachments']) : Array ();
foreach ($attachments as $attachment) {
$esender->AddAttachment(FULL_PATH . ITEM_FILES_PATH . $attachment);
}
$this->_mailingId = $mailing_id;
}
// 3. set recipient specific fields
- $esender->SetEncodedEmailHeader('To', $email, $email);
+ $esender->SetTo($email, $email);
$esender->Deliver(null, $mailing_id, false);
// 4. write to log
$log_fields_hash = Array (
'fromuser' => $mailing_data['FromName'] . '<' . $mailing_data['FromEmail'] . '>',
'addressto' => $email,
'subject' => $mailing_data['Subject'],
'timestamp' => adodb_mktime(),
'EventParams' => serialize( Array ('MailingId' => $mailing_id) ),
);
$this->Conn->doInsert($log_fields_hash, TABLE_PREFIX . 'EmailLog');
}
/**
* Returns mass mail sender name & email
*
* @return Array
*/
function _getSenderData(&$mailing_data)
{
$is_root = true;
if ($mailing_data['PortalUserId'] > 0) {
$sender =& $this->Application->recallObject('u.-item', null, Array ('skip_autoload' => true));
/* @var $sender UsersItem */
$sender->Load($mailing_data['PortalUserId']);
$email_address = $sender->GetDBField('Email');
$name = trim( $sender->GetDBField('FirstName') . ' ' . $sender->GetDBField('LastName') );
$is_root = false;
}
if ($is_root || !$email_address) {
$email_address = $this->Application->ConfigValue('Smtp_AdminMailFrom');
}
if ($is_root || !$name) {
$name = strip_tags( $this->Application->ConfigValue('Site_Name') );
}
return Array ($name, $email_address);
}
/**
* Generates recipients emails based on "To" field value
*
* @param int $id
* @param Array $fields_hash
*/
function generateRecipients($id, $fields_hash)
{
$recipients = explode(';', $fields_hash['To']);
// 1. group recipients by types
$recipients_grouped = Array ();
foreach ($recipients as $recipient) {
if (strpos($recipient, '_') !== false) {
list ($recipient_type, $recipient_id) = explode('_', $recipient);
}
else {
$recipient_type = 'direct';
$recipient_id = $recipient;
}
if (!array_key_exists($recipient_type, $recipients_grouped)) {
$recipients_grouped[$recipient_type] = Array ();
}
$recipients_grouped[$recipient_type][] = $recipient_id;
}
// for each group convert ids to names
$recpient_emails = Array ();
foreach ($recipients_grouped as $recipient_type => $group_recipients) {
$recpient_emails = array_merge($recpient_emails, $this->_getRecipientEmails($recipient_type, $group_recipients));
}
$recpient_emails = array_unique($recpient_emails);
return Array (
'ToParsed' => serialize($recpient_emails),
'EmailsTotal' => count($recpient_emails),
);
}
function _getRecipientEmails($recipient_type, $recipient_ids)
{
if (strpos($recipient_type, '.') !== false) {
// remove special
list ($recipient_type, ) = explode('.', $recipient_type);
}
if ($recipient_type != 'u' && $recipient_type != 'g') {
// theese are already emails
return $recipient_ids;
}
switch ($recipient_type) {
case 'u':
$sql = 'SELECT Email
FROM ' . TABLE_PREFIX . 'PortalUser
WHERE (PortalUserId IN (' . implode(',', $recipient_ids) . ')) AND (Email <> "")';
break;
case 'g':
$sql = 'SELECT u.Email
FROM ' . TABLE_PREFIX . 'UserGroup ug
LEFT JOIN ' . TABLE_PREFIX . 'PortalUser u ON u.PortalUserId = ug.PortalUserId
WHERE (ug.GroupId IN (' . implode(',', $recipient_ids) . ')) AND (u.Email <> "")';
break;
}
return $this->Conn->GetCol($sql);
}
function getRecipientNames($recipient_type, $recipient_ids)
{
if (strpos($recipient_type, '.') !== false) {
// remove special
list ($recipient_type, ) = explode('.', $recipient_type);
}
switch ($recipient_type) {
case 'u':
$title_field = 'Email';
break;
case 'g':
$title_field = 'Name';
break;
default:
$title_field = false;
break;
}
if ($title_field === false || !$recipient_ids) {
return $recipient_ids;
}
$id_field = $this->Application->getUnitOption($recipient_type, 'IDField');
$table_name = $this->Application->getUnitOption($recipient_type, 'TableName');
$sql = 'SELECT ' . $title_field . '
FROM ' . $table_name . '
WHERE ' . $id_field . ' IN (' . implode(',', $recipient_ids) . ')';
return $this->Conn->GetCol($sql);
}
/**
* Updates information about sent email count based on given totals by mailings
*
* @param Array $mailing_totals
*/
function _updateSentTotals($mailing_totals)
{
if (array_key_exists(0, $mailing_totals)) {
// don't update sent email count for mails queued directly (not via mailing lists)
unset($mailing_totals[0]);
}
$id_field = $this->Application->getUnitOption('mailing-list', 'IDField');
$table_name = $this->Application->getUnitOption('mailing-list', 'TableName');
// update sent email count for each processed mailing
foreach ($mailing_totals as $mailing_id => $mailing_total) {
$sql = 'UPDATE ' . $table_name . '
SET EmailsSent = EmailsSent + ' . $mailing_total . '
WHERE ' . $id_field . ' = ' . $mailing_id;
$this->Conn->Query($sql);
}
// mark mailings, that were processed completely
$sql = 'UPDATE ' . $table_name . '
SET Status = ' . MAILING_LIST_PROCESSED . '
WHERE (Status = ' . MAILING_LIST_PARTIALLY_PROCESSED . ') AND (EmailsSent = EmailsTotal)';
$this->Conn->Query($sql);
}
/**
* Sent given messages from email queue
*
* @param Array $messages
*/
function processQueue(&$messages)
{
$esender =& $this->Application->recallObject('EmailSender');
/* @var $esender kEmailSendingHelper */
$queue_table = $this->Application->getUnitOption('email-queue', 'TableName');
$i = 0;
$message = Array ();
$mailing_totals = Array ();
$message_count = count($messages);
while ($i < $message_count) {
$message[0] = unserialize($messages[$i]['MessageHeaders']);
$message[1] =& $messages[$i]['MessageBody'];
$delivered = $esender->Deliver($message, true); // immediate send!
if ($delivered) {
// send succseeded, delete from queue
$sql = 'DELETE FROM ' . $queue_table . '
WHERE EmailQueueId = ' . $messages[$i]['EmailQueueId'];
$this->Conn->Query($sql);
$mailing_id = $messages[$i]['MailingId'];
if (!array_key_exists($mailing_id, $mailing_totals)) {
$mailing_totals[$mailing_id] = 0;
}
$mailing_totals[$mailing_id]++;
}
else {
// send failed, increment retries counter
$sql = 'UPDATE ' . $queue_table . '
SET SendRetries = SendRetries + 1, LastSendRetry = ' . adodb_mktime() . '
WHERE EmailQueueId = ' . $messages[$i]['EmailQueueId'];
$this->Conn->Query($sql);
}
$i++;
}
$this->_updateSentTotals($mailing_totals);
}
}
\ No newline at end of file
Property changes on: branches/RC/core/units/general/helpers/mailing_list_helper.php
___________________________________________________________________
Modified: cvs2svn:cvs-rev
## -1 +1 ##
-1.1.2.1
\ No newline at end of property
+1.1.2.2
\ No newline at end of property
Index: branches/RC/core/units/email_messages/email_messages_event_handler.php
===================================================================
--- branches/RC/core/units/email_messages/email_messages_event_handler.php (revision 11656)
+++ branches/RC/core/units/email_messages/email_messages_event_handler.php (revision 11657)
@@ -1,389 +1,389 @@
<?php
class EmailMessagesEventHandler extends kDBEventHandler
{
/**
* Replace id passed with id of email message
*
* @param kEvent $event
*/
function getPassedID(&$event)
{
$event->setEventParam('raise_warnings', 0);
$parent = parent::getPassedID($event);
if ($parent) {
return $parent;
}
$email_event_id = (int)$this->getEmailEventId();
$object =& $event->getObject();
$parent_info = $object->getLinkedInfo();
$sql = 'SELECT '.$object->IDField.'
FROM '.$object->TableName.'
WHERE ('.$parent_info['ForeignKey'].' = '.$parent_info['ParentId'].') AND (EventId = '.$email_event_id.')';
return (int)$this->Conn->GetOne($sql);
}
function getEmailEventId()
{
return parent::getPassedID( new kEvent('emailevents:OnDummy') );
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @access protected
* @see OnListBuild
*/
function SetCustomQuery(&$event)
{
if ($event->Special == 'module') {
$object =& $event->getObject();
$module = $this->Application->GetVar('module');
$object->addFilter('module_filter', 'Module = '.$this->Conn->qstr($module));
}
}
/**
* If loading empty item, then set parent id
*
* @param kEvent $event
*/
function OnBeforeItemLoad(&$event)
{
if (!$event->getEventParam('id')) {
$this->OnNew($event);
$event->status = erFATAL;
}
}
/**
* Sets event id
*
* @param kEvent $event
*/
function OnNew(&$event)
{
parent::OnNew($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('EventId', $this->getEmailEventId());
$object->SetDBField('Headers', $this->Application->ConfigValue('Smtp_DefaultHeaders') );
}
/**
* Parse message template (split into header, subject & body)
*
* @param kEvent $event
*/
function OnAfterItemLoad(&$event)
{
$object =& $event->getObject();
$lines = explode("\n", $object->GetDBField('Template') );
$headers = Array();
foreach($lines as $line)
{
if( strlen(trim($line)) == 0 || ($line == '.') ) break;
$parts = explode(':', $line, 2);
if(strtolower($parts[0]) == 'subject')
{
$object->SetDBField('Subject', trim($parts[1]) );
}
else
{
$headers[] = $line;
}
}
$object->SetDBField('Headers', implode("\n", $headers) );
$message_body = '';
while( (list($line_id,$line) = each($lines)) )
{
$message_body .= $line;
}
$object->SetDBField('Body', $message_body);
}
/**
* Merge body+subject+headers into message template
*
* @param kEvent $event
*/
function OnBeforeItemUpdate(&$event)
{
$this->parseVirtualFields($event);
}
/**
* Merge body+subject+headers into message template
*
* @param kEvent $event
*/
function OnBeforeItemCreate(&$event)
{
$this->parseVirtualFields($event);
}
/**
* Merge body+subject+headers into message template
*
* @param kEvent $event
*/
function parseVirtualFields(&$event)
{
$object =& $event->getObject();
if( $object->GetDBField('Headers') || $object->GetDBField('Subject') || $object->GetDBField('Body') )
{
$ret = $object->GetDBField('Headers');
if($ret) $ret .= "\n";
$ret = $this->removeTrailingCRLF($ret);
$ret .= 'Subject: '.$object->GetDBField('Subject')."\n\n";
$ret .= $object->GetDBField('Body');
$object->SetDBField('Template', $ret);
}
}
/**
* Remove trailing CR/LF chars from string
*
* @param string $string
* @return string
*/
function removeTrailingCRLF($string)
{
return preg_replace('/(\n|\r)+/',"\\1",$string);
}
/**
* Prepares selected user(-s) or group(-s) for message sending
*
* @param kEvent $event
*/
function OnPrepareMassRecipients(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$object->Clear(0);
$event->redirect = false;
$this->Application->RemoveVar('recipient_ids');
$this->Application->RemoveVar('recipient_type');
$this->saveMassRecipients('u');
$this->saveMassRecipients('g', 'total');
}
function saveMassRecipients($prefix, $special = '')
{
$recipients = $this->Application->GetVar(rtrim($prefix.'_'.$special, '_'));
if ($recipients) {
$this->Application->StoreVar('recipient_ids', implode(',', array_keys($recipients)));
$this->Application->StoreVar('recipient_type', $prefix);
}
}
/**
* Sends mass mail
*
* @param kEvent $event
*/
function OnMassMail(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$object->setRequired('MassSubject', true);
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ($items_info) {
list($id, $field_values) = each($items_info);
$object->SetFieldsFromHash($field_values);
}
if (!$object->Validate()) {
$event->redirect = false;
$event->status = erFAIL;
$object->setID($id);
return ;
}
$fields_hash = $object->GetFieldValues();
list ($fields_hash['FromEmail'], $fields_hash['FromName']) = $this->getSenderData();
if ($fields_hash['MassAttachment']) {
$field_options = $object->GetFieldOptions('MassAttachment');
$fields_hash['MassAttachment'] = $field_options['upload_dir'].$fields_hash['MassAttachment'];
}
$this->Application->RemoveVar('email_prepare_progress');
$this->Application->StoreVar('email_prepare_fields', serialize($fields_hash));
$event->redirect = 'emails/prepare_queue';
}
/**
* Generates email queue using progress bar
*
* @param kEvent $event
* @todo Move to MailingList
*/
function OnPrepareEmailQueue(&$event)
{
$prepare_count = $this->Application->ConfigValue('MailingListQueuePerStep');
if ($prepare_count === false) {
// 10 recipients per script run (if none defined in config)
$prepare_count = 10;
}
$email_prepare_progress = $this->Application->RecallVar('email_prepare_progress');
if ($email_prepare_progress === false) {
$emails_prepared = 0;
$total_emails = $this->getRecipientEmails(true);
$this->Application->StoreVar('email_prepare_progress', $emails_prepared.':'.$total_emails);
}
else {
list ($emails_prepared, $total_emails) = explode(':', $email_prepare_progress);
}
$recipient_emails = $this->getRecipientEmails(false, $emails_prepared.','.$prepare_count);
$recipient_email_count = count($recipient_emails);
if (!$recipient_email_count) {
// no recipients left to prepare
$this->finalizeQueuePreparing($fields_hash['MassAttachment']);
}
$fields_hash = unserialize($this->Application->RecallVar('email_prepare_fields'));
$esender =& $this->Application->recallObject('EmailSender');
/* @var $esender kEmailSendingHelper */
// 1. set headers same for all emails
$esender->SetFrom($fields_hash['FromEmail'], $fields_hash['FromName']);
$esender->SetSubject($fields_hash['MassSubject']);
$esender->SetBody($fields_hash['MassHtmlMessage'], $fields_hash['MassTextMessage']);
// 2. add attachment if any
if ($fields_hash['MassAttachment']) {
$esender->AddAttachment(FULL_PATH.$fields_hash['MassAttachment']);
}
foreach ($recipient_emails as $recipient_email) {
// 3. set recipient specific fields
- $esender->SetEncodedEmailHeader('To', $recipient_email, $recipient_email);
+ $esender->SetTo($recipient_email, $recipient_email);
$esender->Deliver(null, false, false);
// 4. write to log
$log_fields_hash = Array (
'fromuser' => $fields_hash['FromName'],
'addressto' => $recipient_email,
'subject' => $fields_hash['MassSubject'],
'timestamp' => adodb_mktime(),
'event' => '',
);
$this->Conn->doInsert($log_fields_hash, TABLE_PREFIX.'EmailLog');
}
$emails_prepared += $recipient_email_count;
if ($emails_prepared >= $total_emails) {
$this->finalizeQueuePreparing($fields_hash['MassAttachment']);
}
$this->Application->StoreVar('email_prepare_progress', $emails_prepared.':'.$total_emails);
$event->status = erSTOP;
echo ($emails_prepared / $total_emails) * 100;
}
function finalizeQueuePreparing($attachment_file = null)
{
// variables from users/groups grid
$this->Application->RemoveVar('recipient_ids');
$this->Application->RemoveVar('recipient_type');
if ($attachment_file) {
unlink(FULL_PATH.$attachment_file);
}
// variables from email preparing process
// $this->Application->RemoveVar('email_prepare_progress');
// $this->Application->RemoveVar('email_prepare_fields');
// variables from email delivering process (not yet executed)
$this->Application->RemoveVar('email_queue_progress');
$this->Application->Redirect($this->Application->GetVar('finish_template'));
}
function getRecipientEmails($for_counting = false, $limit = null)
{
$recipient_type = $this->Application->RecallVar('recipient_type');
$recipient_ids = $this->Application->RecallVar('recipient_ids');
if (!$recipient_ids) {
return $for_counting ? 0 : Array ();
}
if ($recipient_type == 'u') {
$sql = 'SELECT '.($for_counting ? 'COUNT(*)' : 'Email').'
FROM '.TABLE_PREFIX.'PortalUser
WHERE PortalUserId IN ('.$recipient_ids.')';
}
else {
$sql = 'SELECT '.($for_counting ? 'COUNT(*)' : 'u.Email').'
FROM '.TABLE_PREFIX.'UserGroup ug
LEFT JOIN '.TABLE_PREFIX.'PortalUser u ON ug.PortalUserId = u.PortalUserId
WHERE ug.GroupId IN ('.$recipient_ids.')';
}
if ($for_counting) {
return $this->Conn->GetOne($sql);
}
if (isset($limit)) {
$sql .= ' LIMIT '.$limit;
}
return $this->Conn->GetCol($sql);
}
/**
* Returns mass mail sender name & email
*
* @return Array
*/
function getSenderData()
{
$user =& $this->Application->recallObject('u.current');
/* @var $user UsersItem */
if ($user->GetID() > 0) {
$email_address = $user->GetDBField('Email');
$name = $user->GetDBField('FirstName').' '.$user->GetDBField('LastName');
}
else {
$email_address = $this->Application->ConfigValue('Smtp_AdminMailFrom');
$name = strip_tags( $this->Application->ConfigValue('Site_Name') );
}
return Array ($email_address, $name);
}
}
?>
\ No newline at end of file
Property changes on: branches/RC/core/units/email_messages/email_messages_event_handler.php
___________________________________________________________________
Modified: cvs2svn:cvs-rev
## -1 +1 ##
-1.6.2.6
\ No newline at end of property
+1.6.2.7
\ No newline at end of property
Event Timeline
Log In to Comment