* composeEmailAddress( new ezcMailAddress( 'sender@example.com', 'John Doe' ) ); * * * Returns: *
* John Doe* * The name part of $item will be surrounded by quotes if it contains any of * these characters: , @ < > : ; ' " * * @param ezcMailAddress $item * @return string */ public static function composeEmailAddress( ezcMailAddress $item ) { $name = trim( $item->name ); if ( $name !== '' ) { // remove the quotes around the name part if they are already there if ( $name{0} === '"' && $name{strlen( $name ) - 1} === '"' ) { $name = substr( $name, 1, -1 ); } // add slashes to " and \ and surround the name part with quotes if ( strpbrk( $name, ",@<>:;'\"" ) !== false ) { $name = str_replace( '\\', '\\\\', $name ); $name = str_replace( '"', '\"', $name ); $name = "\"{$name}\""; } switch ( strtolower( $item->charset ) ) { case 'us-ascii': $text = $name . ' <' . $item->email . '>'; break; case 'iso-8859-1': case 'iso-8859-2': case 'iso-8859-3': case 'iso-8859-4': case 'iso-8859-5': case 'iso-8859-6': case 'iso-8859-7': case 'iso-8859-8': case 'iso-8859-9': case 'iso-8859-10': case 'iso-8859-11': case 'iso-8859-12': case 'iso-8859-13': case 'iso-8859-14': case 'iso-8859-15' :case 'iso-8859-16': case 'windows-1250': case 'windows-1251': case 'windows-1252': case 'utf-8': if ( strpbrk( $name, "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" ) === false ) { $text = $name . ' <' . $item->email . '>'; break; } // break intentionally missing default: $preferences = array( 'input-charset' => $item->charset, 'output-charset' => $item->charset, 'scheme' => 'Q', 'line-break-chars' => ezcMailTools::lineBreak() ); $name = iconv_mime_encode( 'dummy', $name, $preferences ); $name = substr( $name, 7 ); // "dummy: " + 1 $text = $name . ' <' . $item->email . '>'; break; } } else { $text = $item->email; } return $text; } /** * Returns the array $items consisting of ezcMailAddress objects * as one RFC822 compliant address string. * * Set foldLength to control how many characters each line can have before a line * break is inserted according to the folding rules specified in RFC2822. * * @param array(ezcMailAddress) $items * @param int $foldLength * @return string */ public static function composeEmailAddresses( array $items, $foldLength = null ) { $textElements = array(); foreach ( $items as $item ) { $textElements[] = ezcMailTools::composeEmailAddress( $item ); } if ( $foldLength === null ) // quick version { return implode( ', ', $textElements ); } $result = ""; $charsSinceFold = 0; foreach ( $textElements as $element ) { $length = strlen( $element ); if ( ( $charsSinceFold + $length + 2 /* comma, space */ ) > $foldLength ) { // fold last line if there is any if ( $result != '' ) { $result .= "," . ezcMailTools::lineBreak() .' '; $charsSinceFold = 0; } $result .= $element; } else { if ( $result == '' ) { $result = $element; } else { $result .= ', ' . $element; } } $charsSinceFold += $length + 1 /*space*/; } return $result; } /** * Returns an ezcMailAddress object parsed from the address string $address. * * You can set the encoding of the name part with the $encoding parameter. * If $encoding is omitted or set to "mime" parseEmailAddress will asume that * the name part is mime encoded. * * This method does not perform validation. It will also accept slightly * malformed addresses. * * If the mail address given can not be decoded null is returned. * * Example: **
* ezcMailTools::parseEmailAddress( 'John Doe ' );
*
*
* @param string $address
* @param string $encoding
* @return ezcMailAddress
*/
public static function parseEmailAddress( $address, $encoding = "mime" )
{
// we don't care about the "group" part of the address since this is not used anywhere
$matches = array();
$pattern = '/\"?[a-zA-Z0-9!#\$\%\&\'\*\+\-\/=\?\^_`{\|}~\.]+\"?@[a-zA-Z0-9!#\$\%\&\'\*\+\-\/=\?\^_`{\|}~\.]+>?$/';
if ( preg_match( trim( $pattern ), $address, $matches, PREG_OFFSET_CAPTURE ) != 1 )
{
return null;
}
$name = substr( $address, 0, $matches[0][1] );
// trim <> from the address and "" from the name
$name = trim( $name, '" ' );
$mail = trim( $matches[0][0], '<>' );
// remove any quotes found in mail addresses like "bah,"@example.com
$mail = str_replace( '"', '', $mail );
if ( $encoding == 'mime' )
{
// the name may contain interesting character encoding. We need to convert it.
$name = ezcMailTools::mimeDecode( $name );
}
else
{
$name = ezcMailCharsetConverter::convertToUTF8( $name, $encoding );
}
$address = new ezcMailAddress( $mail, $name, 'utf-8' );
return $address;
}
/**
* Returns an array of ezcMailAddress objects parsed from the address string $addresses.
*
* You can set the encoding of the name parts with the $encoding parameter.
* If $encoding is omitted or set to "mime" parseEmailAddresses will asume that
* the name parts are mime encoded.
*
* Example:
*
* ezcMailTools::parseEmailAddresses( 'John Doe ' );
*
*
* @param string $addresses
* @param string $encoding
* @return array(ezcMailAddress)
*/
public static function parseEmailAddresses( $addresses, $encoding = "mime" )
{
$addressesArray = array();
$inQuote = false;
$last = 0; // last hit
$length = strlen( $addresses );
for ( $i = 0; $i < $length; $i++ )
{
if ( $addresses[$i] == '"' )
{
$inQuote = !$inQuote;
}
else if ( $addresses[$i] == ',' && !$inQuote )
{
$addressesArray[] = substr( $addresses, $last, $i - $last );
$last = $i + 1; // eat comma
}
}
// fetch the last one
$addressesArray[] = substr( $addresses, $last );
$addressObjects = array();
foreach ( $addressesArray as $address )
{
$addressObject = self::parseEmailAddress( $address, $encoding );
if ( $addressObject !== null )
{
$addressObjects[] = $addressObject;
}
}
return $addressObjects;
}
/**
* Returns true if $address is a valid email address, false otherwise.
*
* By default it will only validate against the same regular expression
* used in ext/filter. It follows
* {@link http://www.faqs.org/rfcs/rfc822.html RFC822} and
* {@link http://www.faqs.org/rfcs/rfc2822.html RFC2822}.
*
* If $checkMxRecords is true, then an MX records check will be performed
* also, by sending a test mail (RCPT TO) to $address using the MX records
* found for the domain part of $address.
*
* The input email address $address should be trimmed from white spaces
* and/or quotes around it before calling this function (if needed).
*
* An email address has this form:
*
* localpart@domainpart
*
*
* The localpart has these rules:
* - allowed characters: . + ~ / ' - _ ` ^ $ % & ! ' | {
* - the dot (.) cannot be the first or the last character
* - the double-quote character (") can only surround the localpart (so
* if it appears it must be the first and the last character of localpart)
* - spaces are allowed if the localpart is surrounded in double-quotes
* - other ASCII characters (even from the extended-ASCII set) are allowed
* if the localparts is surrounded in double-quotes
* - the double-quotes character (") cannot be escaped to appear in a
* localpart surrounded by double quotes (so "john"doe"@example.com is not
* a valid email address)
*
* The domainpart has the same rules as a domain name, as defined in
* {@link http://www.faqs.org/rfcs/rfc822.html RFC822} and
* {@link http://www.faqs.org/rfcs/rfc2822.html RFC2822}.
*
* See also the test files (in the "Mail/tests/tools/data" directory) for
* examples of correct and incorrect email addresses.
*
* @param string $address
* @param bool $checkMxRecords
* @return bool
*/
public static function validateEmailAddress( $address, $checkMxRecords = false )
{
$pattern = '/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]{2,}))$/';
if ( preg_match( $pattern, $address ) )
{
if ( $checkMxRecords )
{
return self::validateEmailAddressMx( $address );
}
else
{
// $address passed through regexp, with no MX checks
return true;
}
}
else
{
// $address did not pass through regexp
return false;
}
}
/**
* Checks if the email address $address is valid based on its MX records.
*
* Steps:
* - the MX records are fetched for the domain part of $address, along with
* their weights
* - the MX records are sorted based on the weights
* - for each MX record a connection is open
* - a test mail (RCPT TO) is tried to be sent to $address
* - if one test mail succeeds, then the address is valid, else invalid
*
* @param string $address
* @return bool
*/
protected static function validateEmailAddressMx( $address )
{
$timeoutOpen = 3; // for fsockopen()
$timeoutConnection = 5; // for stream_set_timeout()
list( $local, $domain ) = explode( '@', $address );
if ( !empty( $domain ) )
{
if ( getmxrr( $domain, $hosts, $weights ) )
{
for ( $i = 0; $i < count( $hosts ); $i++ )
{
$mx[$hosts[$i]] = $weights[$i];
}
asort( $mx );
$mx = array_keys( $mx );
}
elseif ( checkdnsrr( $domain, 'A' ) )
{
$mx[0] = gethostbyname( $domain );
}
else
{
$mx = array();
}
if ( ( $numberOfMx = count( $mx ) ) > 0 )
{
$smtp = array(
"HELO smtp.ez.no",
"MAIL FROM: