Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F772366
email_send.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Feb 1, 8:59 AM
Size
54 KB
Mime Type
text/x-php
Expires
Mon, Feb 3, 8:59 AM (12 h, 50 m)
Engine
blob
Format
Raw Data
Handle
555836
Attached To
rINP In-Portal
email_send.php
View Options
<?php
/**
* @version $Id: email_send.php 12734 2009-10-20 19:28:11Z alex $
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined
(
'FULL_PATH'
)
or
die
(
'restricted access!'
);
/**
* 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
)
{
$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
.
'>, '
;
}
$this
->
SetHeader
(
$header
,
substr
(
$value
,
0
,
-
2
));
}
/**
* 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
->
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\x
0b
\"
'"
);
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
=
''
)
{
$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
.
'>'
;
$this
->
SetHeader
(
$header_name
,
substr
(
$value
,
2
));
}
/**
* 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
;
}
}
Event Timeline
Log In to Comment