Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Tue, Feb 25, 1:23 PM

in-portal

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