First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,537 @@
<?php
/**
* File containing the ezcMailComposer class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Convenience class for writing mail.
*
* This class allows you to create
* text and/or HTML mail with attachments. If you need to create more
* advanced mail use the ezcMail class and build the body from scratch.
*
* ezcMailComposer is used with the following steps:
* 1. Create a composer object.
* 2. Set the subject and recipients.
* 3. Set the plainText and htmlText message parts. You can set only one
* or both. If you set both, the client will display the htmlText if it
* supports HTML. Otherwise the client will display plainText.
* 4. Add any attachments (addFileAttachment() or addStringAttachment()).
* 5. Call the build method.
*
* This example shows how to send an HTML mail with a text fallback and
* attachments. The HTML message has an inline image.
* <code>
* $mail = new ezcMailComposer();
* $mail->from = new ezcMailAddress( 'john@example.com', 'John Doe' );
* $mail->addTo( new ezcMailAddress( 'cindy@example.com', 'Cindy Doe' ) );
* $mail->subject = "Example of an HTML email with attachments";
* $mail->plainText = "Here is the text version of the mail. This is displayed if the client can not understand HTML";
* $mail->htmlText = "<html>Here is the HTML version of your mail with an image: <img src='file://path_to_image.jpg' /></html>";
* $mail->addFileAttachment( 'path_to_attachment.file' );
* $mail->build();
* $transport = new ezcMailMtaTransport();
* $transport->send( $mail );
* </code>
*
* By default, if the htmlText property contains an HTML image tag with file://
* in href, that file will be included in the created message.
*
* Example:
* <code>
* <img src="file:///home/me/image.jpg" />
* </code>
*
* This can be a security risk if a user links to another file, for example logs
* or password files. With the automaticImageInclude option (default value true)
* from {@link ezcMailComposerOptions}, the automatic inclusion of files can be
* turned off.
*
* Example:
* <code>
* $options = new ezcMailComposerOptions();
* $options->automaticImageInclude = false; // default value is true
*
* $mail = new ezcMailComposer( $options );
*
* // ... add To, From, Subject, etc to $mail
* $mail->htmlText = "<html>Here is the image: <img src="file:///etc/passwd" /></html>";
*
* // ... send $mail
* </code>
*
* After running the above code, the sent mail will not contain the file specified
* in the htmlText property.
*
* The file name in the attachment can be different than the file name on disk, by
* passing an {@link ezcMailContentDispositionHeader} object to the function
* addFileAttachment(). Example:
* <code>
* $mail = new ezcMailComposer();
* $mail->from = new ezcMailAddress( 'john@example.com', 'John Doe' );
* $mail->addTo( new ezcMailAddress( 'cindy@example.com', 'Cindy Doe' ) );
* $mail->subject = "Example of an HTML email with attachments and custom attachment file name";
* $mail->plainText = "Here is the text version of the mail. This is displayed if the client can not understand HTML";
* $mail->htmlText = "<html>Here is the HTML version of your mail with an image: <img src='file://path_to_image.jpg' /></html>";
*
* $disposition = new ezcMailContentDispositionHeader();
* $disposition->fileName = 'custom name for attachment.txt';
* $disposition->fileNameCharSet = 'utf-8'; // if using non-ascii characters in the file name
* $disposition->disposition = 'attachment'; // default value is 'inline'
*
* $mail->addFileAttachment( 'path_to_attachment.file', null, null, $disposition );
* $mail->build();
*
* $transport = new ezcMailMtaTransport();
* $transport->send( $mail );
* </code>
*
* Use the function addStringAttachment() if you want to add an attachment which
* is stored in a string variable. A file name for a string attachment needs to
* be specified as well. Example:
*
* <code>
* $contents = 'contents for mail attachment'; // can be a binary string, eg. image file contents
* $mail->addStringAttachment( 'filename', $contents );
* </code>
*
* @property string $plainText
* Contains the message of the mail in plain text.
* @property string $htmlText
* Contains the message of the mail in HTML. You should also provide
* the text of the HTML message in the plainText property. Both will
* be sent and the receiver will see the HTML message if his/her
* client supports HTML. If the HTML message contains links to
* local images and/or files these will be included into the mail
* when generateBody is called. Links to local files must start with
* "file://" in order to be recognized. You can use the option
* automaticImageInclude (default value is true) from
* {@link ezcMailComposerOptions} to turn off the
* automatic inclusion of files in the generated mail.
* @property string $charset
* Contains the character set for both $plainText and $htmlText.
* Default value is 'us-ascii'. This does not set any specific
* charset for the subject, you need the subjectCharset property for
* that.
* @property string $encoding
* Contains the encoding for both $plainText and $htmlText.
* Default value is ezcMail::EIGHT_BIT. Other values are found
* as constants in the class {@link ezcMail}.
* @property ezcMailComposerOptions $options
* Options for composing mail. See {@link ezcMailComposerOptions}.
*
* @package Mail
* @version //autogen//
* @mainclass
*/
class ezcMailComposer extends ezcMail
{
/**
* Holds the attachments filenames.
*
* The array contains relative or absolute paths to the attachments.
*
* @var array(string)
*/
private $attachments = array();
/**
* Holds the options for this class.
*
* @var ezcMailComposerOptions
*/
protected $options;
/**
* Constructs an empty ezcMailComposer object.
*
* @param ezcMailComposerOptions $options
*/
public function __construct( ezcMailComposerOptions $options = null )
{
$this->properties['plainText'] = null;
$this->properties['htmlText'] = null;
$this->properties['charset'] = 'us-ascii';
$this->properties['encoding'] = ezcMail::EIGHT_BIT;
if ( $options === null )
{
$options = new ezcMailComposerOptions();
}
$this->options = $options;
parent::__construct();
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'plainText':
case 'htmlText':
case 'charset':
case 'encoding':
$this->properties[$name] = $value;
break;
case 'options':
if ( !$value instanceof ezcMailComposerOptions )
{
throw new ezcBaseValueException( $name, $value, 'ezcMailComposerOptions' );
}
$this->options = $value;
break;
default:
parent::__set( $name, $value );
}
}
/**
* Returns the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'plainText':
case 'htmlText':
case 'charset':
case 'encoding':
return $this->properties[$name];
case 'options':
return $this->options;
default:
return parent::__get( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'plainText':
case 'htmlText':
case 'charset':
case 'encoding':
return isset( $this->properties[$name] );
case 'options':
return isset( $this->options );
default:
return parent::__isset( $name );
}
}
/**
* Adds the file $fileName to the list of attachments.
*
* If $content is specified, $fileName is not checked if it exists.
* $this->attachments will also contain in this case the $content,
* $contentType and $mimeType.
*
* The $contentType (default = application) and $mimeType (default =
* octet-stream) control the complete mime-type of the attachment.
*
* If $contentDisposition is specified, the attached file will have its
* Content-Disposition header set according to the $contentDisposition
* object and the filename of the attachment in the generated mail will be
* the one from the $contentDisposition object.
*
* @throws ezcBaseFileNotFoundException
* if $fileName does not exists.
* @throws ezcBaseFilePermissionProblem
* if $fileName could not be read.
* @param string $fileName
* @param string $content
* @param string $contentType
* @param string $mimeType
* @param ezcMailContentDispositionHeader $contentDisposition
* @apichange This function might be removed in a future iteration of
* the Mail component. Use addFileAttachment() and
* addStringAttachment() instead.
*/
public function addAttachment( $fileName, $content = null, $contentType = null, $mimeType = null, ezcMailContentDispositionHeader $contentDisposition = null )
{
if ( is_null( $content ) )
{
$this->addFileAttachment( $fileName, $contentType, $mimeType, $contentDisposition );
}
else
{
$this->addStringAttachment( $fileName, $content, $contentType, $mimeType, $contentDisposition );
}
}
/**
* Adds the file $fileName to the list of attachments.
*
* The $contentType (default = application) and $mimeType (default =
* octet-stream) control the complete mime-type of the attachment.
*
* If $contentDisposition is specified, the attached file will have its
* Content-Disposition header set according to the $contentDisposition
* object and the filename of the attachment in the generated mail will be
* the one from the $contentDisposition object.
*
* @throws ezcBaseFileNotFoundException
* if $fileName does not exists.
* @throws ezcBaseFilePermissionProblem
* if $fileName could not be read.
* @param string $fileName
* @param string $contentType
* @param string $mimeType
* @param ezcMailContentDispositionHeader $contentDisposition
*/
public function addFileAttachment( $fileName, $contentType = null, $mimeType = null, ezcMailContentDispositionHeader $contentDisposition = null )
{
if ( is_readable( $fileName ) )
{
$this->attachments[] = array( $fileName, null, $contentType, $mimeType, $contentDisposition );
}
else
{
if ( file_exists( $fileName ) )
{
throw new ezcBaseFilePermissionException( $fileName, ezcBaseFileException::READ );
}
else
{
throw new ezcBaseFileNotFoundException( $fileName );
}
}
}
/**
* Adds the file $fileName to the list of attachments, with contents $content.
*
* The file $fileName is not checked if it exists. An attachment is added
* to the mail, with the name $fileName, and the contents $content.
*
* The $contentType (default = application) and $mimeType (default =
* octet-stream) control the complete mime-type of the attachment.
*
* If $contentDisposition is specified, the attached file will have its
* Content-Disposition header set according to the $contentDisposition
* object and the filename of the attachment in the generated mail will be
* the one from the $contentDisposition object.
*
* @param string $fileName
* @param string $content
* @param string $contentType
* @param string $mimeType
* @param ezcMailContentDispositionHeader $contentDisposition
*/
public function addStringAttachment( $fileName, $content, $contentType = null, $mimeType = null, ezcMailContentDispositionHeader $contentDisposition = null )
{
$this->attachments[] = array( $fileName, $content, $contentType, $mimeType, $contentDisposition );
}
/**
* Builds the complete email message in RFC822 format.
*
* This method must be called before the message is sent.
*
* @throws ezcBaseFileNotFoundException
* if any of the attachment files can not be found.
*/
public function build()
{
$mainPart = false;
// create the text part if there is one
if ( $this->plainText != '' )
{
$mainPart = new ezcMailText( $this->plainText, $this->charset );
}
// create the HTML part if there is one
$htmlPart = false;
if ( $this->htmlText != '' )
{
$htmlPart = $this->generateHtmlPart();
// create a MultiPartAlternative if a text part exists
if ( $mainPart != false )
{
$mainPart = new ezcMailMultipartAlternative( $mainPart, $htmlPart );
}
else
{
$mainPart = $htmlPart;
}
}
// build all attachments
// special case, mail with no text and one attachment.
// A fix for issue #14220 was added by wrapping the attachment in
// an ezcMailMultipartMixed part
if ( $mainPart == false && count( $this->attachments ) == 1 )
{
if ( isset( $this->attachments[0][1] ) )
{
if ( is_resource( $this->attachments[0][1] ) )
{
$mainPart = new ezcMailMultipartMixed( new ezcMailStreamFile( $this->attachments[0][0], $this->attachments[0][1], $this->attachments[0][2], $this->attachments[0][3] ) );
}
else
{
$mainPart = new ezcMailMultipartMixed( new ezcMailVirtualFile( $this->attachments[0][0], $this->attachments[0][1], $this->attachments[0][2], $this->attachments[0][3] ) );
}
}
else
{
$mainPart = new ezcMailMultipartMixed( new ezcMailFile( $this->attachments[0][0], $this->attachments[0][2], $this->attachments[0][3] ) );
}
$mainPart->contentDisposition = $this->attachments[0][4];
}
else if ( count( $this->attachments ) > 0 )
{
$mainPart = ( $mainPart == false )
? new ezcMailMultipartMixed()
: new ezcMailMultipartMixed( $mainPart );
// add the attachments to the mixed part
foreach ( $this->attachments as $attachment )
{
if ( isset( $attachment[1] ) )
{
if ( is_resource( $attachment[1] ) )
{
$part = new ezcMailStreamFile( $attachment[0], $attachment[1], $attachment[2], $attachment[3] );
}
else
{
$part = new ezcMailVirtualFile( $attachment[0], $attachment[1], $attachment[2], $attachment[3] );
}
}
else
{
$part = new ezcMailFile( $attachment[0], $attachment[2], $attachment[3] );
}
$part->contentDisposition = $attachment[4];
$mainPart->appendPart( $part );
}
}
$this->body = $mainPart;
}
/**
* Returns an ezcMailPart based on the HTML provided.
*
* This method adds local files/images to the mail itself using a
* {@link ezcMailMultipartRelated} object.
*
* @throws ezcBaseFileNotFoundException
* if $fileName does not exists.
* @throws ezcBaseFilePermissionProblem
* if $fileName could not be read.
* @return ezcMailPart
*/
private function generateHtmlPart()
{
$result = false;
if ( $this->htmlText != '' )
{
$matches = array();
if ( $this->options->automaticImageInclude === true )
{
// recognize file:// and file:///, pick out the image, add it as a part and then..:)
preg_match_all( "/<img[\s\*\s]src=[\'\"]file:\/\/([^ >\'\"]+)/i", $this->htmlText, $matches );
// pictures/files can be added multiple times. We only need them once.
$matches = array_unique( $matches[1] );
}
$result = new ezcMailText( $this->htmlText, $this->charset, $this->encoding );
$result->subType = "html";
if ( count( $matches ) > 0 )
{
$htmlPart = $result;
// wrap already existing message in an alternative part
$result = new ezcMailMultipartRelated( $result );
// create a filepart and add it to the related part
// also store the ID for each part since we need those
// when we replace the originals in the HTML message.
foreach ( $matches as $fileName )
{
if ( is_readable( $fileName ) )
{
// @todo waiting for fix of the fileinfo extension
// $contents = file_get_contents( $fileName );
$mimeType = null;
$contentType = null;
if ( ezcBaseFeatures::hasExtensionSupport( 'fileinfo' ) )
{
// if fileinfo extension is available
$filePart = new ezcMailFile( $fileName );
}
elseif ( ezcMailTools::guessContentType( $fileName, $contentType, $mimeType ) )
{
// if fileinfo extension is not available try to get content/mime type
// from the file extension
$filePart = new ezcMailFile( $fileName, $contentType, $mimeType );
}
else
{
// fallback in case fileinfo is not available and could not get content/mime
// type from file extension
$filePart = new ezcMailFile( $fileName, "application", "octet-stream" );
}
$cid = $result->addRelatedPart( $filePart );
// replace the original file reference with a reference to the cid
$this->htmlText = str_replace( 'file://' . $fileName, 'cid:' . $cid, $this->htmlText );
}
else
{
if ( file_exists( $fileName ) )
{
throw new ezcBaseFilePermissionException( $fileName, ezcBaseFileException::READ );
}
else
{
throw new ezcBaseFileNotFoundException( $fileName );
}
// throw
}
}
// update mail, with replaced URLs
$htmlPart->text = $this->htmlText;
}
}
return $result;
}
}
?>

View file

@ -0,0 +1,31 @@
<?php
/**
* File containing the ezcMailInvalidLimitException class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* The ezcMailInvalidLimitException is thrown when request is made to
* fetch messages with the offset outside of the existing message range.
*
* @package Mail
* @version //autogen//
*/
class ezcMailInvalidLimitException extends ezcMailException
{
/**
* Constructs an ezcMailInvalidLimitException
*
* @param mixed $offset
* @param mixed $count
*/
public function __construct( $offset, $count )
{
parent::__construct( "The message count '{$count}' is not allowed for the message subset '{$offset}', '{$count}'." );
}
}
?>

View file

@ -0,0 +1,29 @@
<?php
/**
* File containing the ezcMailException class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailExceptions are thrown when an exceptional state
* occures in the Mail package.
*
* @package Mail
* @version //autogen//
*/
class ezcMailException extends ezcBaseException
{
/**
* Constructs a new ezcMailException with error message $message.
*
* @param string $message
*/
public function __construct( $message )
{
parent::__construct( $message );
}
}
?>

View file

@ -0,0 +1,30 @@
<?php
/**
* File containing the ezcMailNoSuchMessageException class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* The ezcMailNoSuchMessageException is thrown when a message with an ID is
* requested that doesn't exist in the transport.
*
* @package Mail
* @version //autogen//
*/
class ezcMailNoSuchMessageException extends ezcMailException
{
/**
* Constructs an ezcMailNoSuchMessageException
*
* @param mixed $messageId
*/
public function __construct( $messageId )
{
parent::__construct( "The message with ID '{$messageId}' could not be found." );
}
}
?>

View file

@ -0,0 +1,31 @@
<?php
/**
* File containing the ezcMailOffsetOutOfRangeException class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* The ezcMailOffsetOutOfRangeException is thrown when request is made to
* fetch messages with the offset outside of the existing message range.
*
* @package Mail
* @version //autogen//
*/
class ezcMailOffsetOutOfRangeException extends ezcMailException
{
/**
* Constructs an ezcMailOffsetOutOfRangeException
*
* @param mixed $offset
* @param mixed $count
*/
public function __construct( $offset, $count )
{
parent::__construct( "The offset '{$offset}' is outside of the message subset '{$offset}', '{$count}'." );
}
}
?>

View file

@ -0,0 +1,30 @@
<?php
/**
* File containing the ezcMailTransportException class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Transport exceptions are thrown when either sending or receiving
* mail transports fail to do their job properly.
*
* @package Mail
* @version //autogen//
*/
class ezcMailTransportException extends ezcMailException
{
/**
* Constructs an ezcMailTransportException with low level information $message.
*
* @param string $message
*/
public function __construct( $message = '' )
{
parent::__construct( "An error occured while sending or receiving mail. " . $message );
}
}
?>

View file

@ -0,0 +1,34 @@
<?php
/**
* File containing the ezcMailTransportSmtpException class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @access private
*/
/**
* ezcMailTransportSmtpException is thrown when an exceptional state
* occures internally in the ezcMailSmtpTransport class. As it never enters
* "userspace" the class is marked as private.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailTransportSmtpException extends ezcMailException
{
/**
* Constructs an ezcMailTransportSmtpException with the highlevel error
* message $message.
*
* @param string $message
*/
public function __construct( $message )
{
parent::__construct( $message );
}
}
?>

View file

@ -0,0 +1,467 @@
<?php
/**
* File containing the ezcMailPart class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Abstract base class for all mail MIME parts.
*
* This base class provides functionality to store headers and to generate
* the mail part. Implementations of this class must handle the body of that
* parts themselves. They must also implement {@link generateBody()} which is
* called when the message part is generated.
*
* @property ezcMailContentDispositionHeader $contentDisposition
* Contains the information from the Content-Disposition field of
* this mail. This useful especially when you are investigating
* retrieved mail to see if a part is an attachment or should be
* displayed inline. However, it can also be used to set the same
* on outgoing mail. Note that the ezcMailFile part sets the
* Content-Disposition field itself based on it's own properties
* when sending mail.
* @property int $size
* The size of the mail part in bytes. It is set when parsing a
* mail {@link ezcMailParser->parseMail()}.
* @property-read ezcMailHeadersHolder $headers
* Contains the header holder object, taking care of the
* headers of this part. Can be retreived for reasons of
* extending this class and its derivals.
*
* @package Mail
* @version //autogen//
*/
abstract class ezcMailPart
{
/**
* An associative array containing all the headers set for this part.
*
* @var ezcMailHeadersHolder
*/
private $headers = null;
/**
* An associative array containing the charsets for the headers in this
* part.
*
* @var array(string=>string)
*/
private $headerCharsets = array();
/**
* An array of headers to exclude when generating the headers.
*
* @var array(string)
*/
private $excludeHeaders = array();
/**
* Holds the properties of this class.
*
* @var array(string=>mixed)
*/
protected $properties = array();
/**
* Constructs a new mail part.
*/
public function __construct()
{
$this->headers = new ezcMailHeadersHolder();
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @throws ezcBasePropertyPermissionException
* if the property is read-only.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'contentDisposition':
case 'size':
$this->properties[$name] = $value;
break;
case 'headers':
throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ );
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'contentDisposition':
case 'size':
return isset( $this->properties[$name] ) ? $this->properties[$name] : null;
case "headers":
return $this->headers;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'contentDisposition':
case 'size':
return isset( $this->properties[$name] );
case "headers":
return isset( $this->headers );
default:
return false;
}
}
/**
* Returns the RAW value of the header $name.
*
* Returns an empty string if the header is not found.
* Getting headers is case insensitive. Getting the header
* 'Message-Id' will match both 'Message-ID' and 'MESSAGE-ID'
* as well as 'Message-Id'.
*
* The raw value is MIME-encoded, so if you want to decode it,
* use {@link ezcMailTools::mimeDecode()} or implement your own
* MIME-decoding function.
*
* If $returnAllValues is true, the function will return all
* the values of the header $name from the mail in an array. If
* it is false it will return only the first value as a string
* if there are multiple values present in the mail.
*
* @param string $name
* @param bool $returnAllValues
* @return mixed
*/
public function getHeader( $name, $returnAllValues = false )
{
if ( isset( $this->headers[$name] ) )
{
if ( $returnAllValues === true )
{
return $this->headers[$name];
}
else if ( is_array( $this->headers[$name] ) )
{
// return only the first value in order to not break compatibility
// see issue #14257
return $this->headers[$name][0];
}
else
{
return $this->headers[$name];
}
}
return '';
}
/**
* Sets the header $name to the value $value and its charset to $charset.
*
* If the header is already set it will override the old value.
*
* Headers set should be folded at 76 or 998 characters according to
* the folding rules described in RFC 2822.
*
* If $charset is specified, it is associated with the header $name. It
* defaults to 'us-ascii' if not specified. The text in $value is encoded
* with $charset after calling generateHeaders().
*
* Note: The header Content-Disposition will be overwritten by the
* contents of the contentsDisposition property if set.
*
* @see generateHeaders()
*
* @param string $name
* @param string $value
* @param string $charset
*/
public function setHeader( $name, $value, $charset = 'us-ascii' )
{
$this->headers[$name] = $value;
$this->setHeaderCharset( $name, $charset );
}
/**
* Adds the headers $headers.
*
* The headers specified in the associative array $headers will overwrite
* any existing header values.
*
* The array $headers can have one of these 2 forms:
* - array( header_name => header_value ) - by default the 'us-ascii' charset
* will be associated with all headers
* - array( header_name => array( header_value, header_charset ) ) - if
* header_charset is missing it will default to 'us-ascii'
*
* Headers set should be folded at 76 or 998 characters according to
* the folding rules described in RFC 2822.
*
* @param array(string=>mixed) $headers
*/
public function setHeaders( array $headers )
{
foreach ( $headers as $key => $value )
{
if ( is_array( $value ) )
{
$this->headers[$key] = $value[0];
$charset = isset( $value[1] ) ? $value[1] : 'us-ascii';
$this->setHeaderCharset( $key, $charset );
}
else
{
$this->headers[$key] = $value;
$this->setHeaderCharset( $key );
}
}
}
/**
* Returns the headers set for this part as a RFC 822 string.
*
* Each header is separated by a line break.
* This method does not add the required two lines of space
* to separate the headers from the body of the part.
*
* It also encodes the headers (with the 'Q' encoding) if the charset
* associated with the header is different than 'us-ascii' or if it
* contains characters not allowed in mail headers.
*
* This function is called automatically by generate() and
* subclasses can override this method if they wish to set additional
* headers when the mail is generated.
*
* @see setHeader()
*
* @return string
*/
public function generateHeaders()
{
// set content disposition header
if ( $this->contentDisposition !== null &&
( $this->contentDisposition instanceof ezcMailContentDispositionHeader ) )
{
$cdHeader = $this->contentDisposition;
$cd = "{$cdHeader->disposition}";
if ( $cdHeader->fileName !== null )
{
$fileInfo = null;
if ( $cdHeader->fileNameCharSet !== null )
{
$fileInfo .= "*0*=\"{$cdHeader->fileNameCharSet}";
if ( $cdHeader->fileNameLanguage !== null )
{
$fileInfo .= "'{$cdHeader->fileNameLanguage}'";
}
else
{
// RFC 2184: the single quote delimiters MUST be present
// even when one of the field values is omitted
$fileInfo .= "''";
}
}
if ( $fileInfo !== null )
{
$cd .= "; filename{$fileInfo}{$cdHeader->fileName}\"";
}
else
{
$cd .= "; filename=\"{$cdHeader->fileName}\"";
}
}
if ( $cdHeader->creationDate !== null )
{
$cd .= "; creation-date=\"{$cdHeader->creationDate}\"";
}
if ( $cdHeader->modificationDate !== null )
{
$cd .= "; modification-date=\"{$cdHeader->modificationDate}\"";
}
if ( $cdHeader->readDate !== null )
{
$cd .= "; read-date=\"{$cdHeader->readDate}\"";
}
if ( $cdHeader->size !== null )
{
$cd .= "; size={$cdHeader->size}";
}
foreach ( $cdHeader->additionalParameters as $addKey => $addValue )
{
$cd .="; {$addKey}=\"{$addValue}\"";
}
$this->setHeader( 'Content-Disposition', $cd );
}
// generate headers
$text = "";
foreach ( $this->headers->getCaseSensitiveArray() as $header => $value )
{
if ( is_array( $value ) )
{
$value = $value[0];
}
// here we encode every header, even the ones that we don't add to
// the header set directly. We do that so that transports sill see
// all the encoded headers which they then can use accordingly.
$charset = $this->getHeaderCharset( $header );
switch ( strtolower( $charset ) )
{
case 'us-ascii':
$value = ezcMailHeaderFolder::foldAny( $value );
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( $value, "\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 )
{
$value = ezcMailHeaderFolder::foldAny( $value );
break;
}
// break intentionally missing
default:
$preferences = array(
'input-charset' => $charset,
'output-charset' => $charset,
'line-length' => ezcMailHeaderFolder::getLimit(),
'scheme' => 'Q',
'line-break-chars' => ezcMailTools::lineBreak()
);
$value = iconv_mime_encode( 'dummy', $value, $preferences );
$value = substr( $value, 7 ); // "dummy: " + 1
// just to keep compatibility with code which might read
// the headers after generateHeaders() has been called
$this->setHeader( $header, $value, $charset );
break;
}
if ( in_array( strtolower( $header ), $this->excludeHeaders ) === false )
{
$text .= "$header: $value" . ezcMailTools::lineBreak();
}
}
return $text;
}
/**
* The array $headers will be excluded when the headers are generated.
*
* @see generateHeaders()
*
* @param array(string) $headers
*/
public function appendExcludeHeaders( array $headers )
{
$lowerCaseHeaders = array();
foreach ( $headers as $header )
{
$lowerCaseHeaders[] = strtolower( $header );
}
$this->excludeHeaders = array_merge( $this->excludeHeaders, $lowerCaseHeaders );
}
/**
* Returns the body of this part as a string.
*
* This method is called automatically by generate() and subclasses must
* implement it.
*
* @return string
*/
abstract public function generateBody();
/**
* Returns the complete mail part including both the header and the body
* as a string.
*
* @return string
*/
public function generate()
{
return $this->generateHeaders() . ezcMailTools::lineBreak() . $this->generateBody();
}
/**
* Returns the charset registered for the header $name.
*
* @param string $name
* @return string
*/
protected function getHeaderCharset( $name )
{
if ( isset( $this->headerCharsets[$name] ) )
{
return $this->headerCharsets[$name];
}
// if no charset is set then return 'us-ascii'
return 'us-ascii';
}
/**
* Sets the charset of the header $name to $value.
*
* If $value is not specified it defaults to 'us-ascii'.
*
* @param string $name
* @param string $value
*/
protected function setHeaderCharset( $name, $value = 'us-ascii' )
{
$this->headerCharsets[$name] = $value;
}
}
?>

View file

@ -0,0 +1,28 @@
<?php
/**
* File containing the ezcMailTransport class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Interface for classes that implement a mail transport.
*
* Subclasses must implement the send() method.
*
* @package Mail
* @version //autogen//
*/
interface ezcMailTransport
{
/**
* Sends the contents of $mail.
*
* @param ezcMail $mail
*/
public function send( ezcMail $mail );
}
?>

View file

@ -0,0 +1,129 @@
<?php
/**
* File containing the ezcMailCharsetConverter class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing common character set conversion methods.
*
* By calling the static function ezcMailCharsetConverter::setConvertMethod()
* before doing mail parsing, another callback function can be used for
* character conversion to UTF-8 in place of the normal iconv() conversion.
*
* The callback function must have this signature:
* <code>
* public static function function_name( $text, $originalCharset );
* </code>
*
* where:
* - $text = string to convert to UTF-8
* - $originalCharset = in what charset is $text
*
* Example:
* <code>
* // specify another function for character set conversion
* ezcMailCharsetConverter::setConvertMethod( array( 'myConverter', 'convertToUTF8IconvIgnore' ) );
*
* // ...code for mail parsing...
* </code>
*
* where myConverter is (along with some other examples of charset conversion
* functions which can be used):
* <code>
* class myConverter
* {
* public static function convertToUTF8IconvIgnore( $text, $originalCharset )
* {
* if ( $originalCharset === 'unknown-8bit' || $originalCharset === 'x-user-defined' )
* {
* $originalCharset = "latin1";
* }
* return iconv( $originalCharset, 'utf-8//IGNORE', $text );
* }
*
* public static function convertToUTF8IconvTranslit( $text, $originalCharset )
* {
* if ( $originalCharset === 'unknown-8bit' || $originalCharset === 'x-user-defined' )
* {
* $originalCharset = "latin1";
* }
* return iconv( $originalCharset, 'utf-8//TRANSLIT', $text );
* }
*
* public static function convertToUTF8Mbstring( $text, $originalCharset )
* {
* return mb_convert_encoding( $text, "UTF-8", $originalCharset );
* }
* }
* </code>
*
* Developers can choose to use the error suppresion operator ('@') in front of
* the iconv() calls in the above examples, in order to ignore the notices thrown
* when processing broken text (issue #8369).
*
* @package Mail
* @version //autogen//
*/
class ezcMailCharsetConverter
{
/**
* Callback function to use for character set conversion to UTF8.
*
* @var callback
*/
private static $method = array( __CLASS__, 'convertToUTF8Iconv' );
/**
* Sets the callback function used for character set conversion to UTF-8.
*
* Call this method before doing mail parsing if you need a special way
* of converting the character set to UTF-8.
*
* @param callback $method
*/
public static function setConvertMethod( $method )
{
self::$method = $method;
}
/**
* Converts the $text with the charset $originalCharset to UTF-8.
*
* It calls the function specified by using the static method
* setConvertMethod(). By default it calls convertToUTF8Iconv() defined
* in this class.
*
* @param string $text
* @param string $originalCharset
* @return string
*/
public static function convertToUTF8( $text, $originalCharset )
{
return call_user_func( self::$method, $text, $originalCharset );
}
/**
* Converts the $text with the charset $originalCharset to UTF-8.
*
* In case $originalCharset is 'unknown-8bit' or 'x-user-defined' then
* it is assumed to be 'latin1' (ISO-8859-1).
*
* @param string $text
* @param string $originalCharset
* @return string
*/
public static function convertToUTF8Iconv( $text, $originalCharset )
{
if ( $originalCharset === 'unknown-8bit' || $originalCharset === 'x-user-defined' )
{
$originalCharset = "latin1";
}
return iconv( $originalCharset, 'utf-8', $text );
}
}
?>

View file

@ -0,0 +1,146 @@
<?php
/**
* File containing the ezcMailHeaderFolder class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @access private
*/
/**
* Internal class folding headers according to RFC 2822.
*
* RFC 2822 specifies two line length restrictions:
*
* "There are two limits that this standard places on the number of
* characters in a line. Each line of characters MUST be no more than
* 998 characters, and SHOULD be no more than 78 characters, excluding
* the CRLF."
*
* The 76 character limit is because of readability. The 998 character limit
* is a result of SMTP limitations.
*
* The rule for folding is:
* "wherever this standard allows for folding white space (not
* simply WSP characters), a CRLF may be inserted before any WSP."
*
* This is described in more detail in section 3.2.3.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailHeaderFolder
{
/**
* The soft limit of 76 characters per line.
*/
const SOFT_LIMIT = 76;
/**
* The soft limit of 998 characters per line.
*/
const HARD_LIMIT = 998;
/**
* The default folding limit.
*
* @var int
*/
static private $limit = 76;
/**
* Sets the number of allowed characters before folding to $numCharacters.
*
* $numCharacters must be one of:
* - ezcMailHeaderFolder::SOFT_LIMIT (76 characters)
* - ezcMailHeaderFolder::HARD_LIMIT (998 characters)
*
* @param int $numCharacters
*/
static public function setLimit( $numCharacters )
{
self::$limit = $numCharacters;
}
/**
* Returns the maximum number of characters allowed per line.
*
* @return int
*/
static public function getLimit()
{
return self::$limit;
}
/**
* Returns $text folded to the 998 character limit on any whitespace.
*
* The algorithm tries to minimize the number of comparisons by searching
* backwards from the maximum number of allowed characters on a line.
*
* @param string $text
* @return string
*/
static public function foldAny( $text )
{
// Don't fold unless we have to.
if ( strlen( $text ) <= self::$limit )
{
return $text;
}
// go to 998'th char.
// search back to whitespace
// fold
$length = strlen( $text );
$folded = "";
// find first occurence of whitespace searching backwards
$search = 0;
$previousFold = 0;
while ( ( $search + self::$limit ) < $length )
{
// search from the max possible length of the substring
$search += self::$limit;
while ( $text[$search] != " " && $text[$search] != "\t" && $search > $previousFold )
{
$search--;
}
if ( $search == $previousFold )
{
// continuous string of more than limit chars.
// We will just have to continue searching forwards to the next whitespace instead
// This is not confirming to standard.. but what can we do?
$search += self::$limit; // back to where we started
while ( $search < $length && $text[$search] != " " && $text[$search] != "\t" )
{
$search++;
}
}
// lets fold
if ( $folded === "" )
{
$folded = substr( $text, $previousFold, $search - $previousFold );
}
else
{
$folded .= ezcMailTools::lineBreak() .
substr( $text, $previousFold, $search - $previousFold );
}
$previousFold = $search;
}
// we need to append the rest if there is any
if ( $search < $length )
{
$folded .= ezcMailTools::lineBreak() . substr( $text, $search );
}
return $folded;
}
}
?>

View file

@ -0,0 +1,517 @@
<?php
/**
* File containing the ezcMail class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* The main mail class.
*
* You can use ezcMail together with the other classes derived from ezcMailPart
* to build email messages. When the mail is built, use the Transport classes
* to send the mail.
*
* This example builds and sends a simple text mail message:
* <code>
* $mail = new ezcMail;
* $mail->from = new ezcMailAddress( 'sender@example.com', 'Adrian Ripburger' );
* $mail->addTo( new ezcMailAddress( 'receiver@example.com', 'Maureen Corley' ) );
* $mail->subject = "Hi";
* $mail->body = new ezcMailText( "I just mail to say I love you!" );
* $transport = new ezcMailMtaTransport();
* $transport->send( $mail );
* </code>
*
* You can also derive your own mail classes from this class if you have
* special requirements. An example of this is the ezcMailComposer class which
* is a convenience class to send simple mail structures and HTML mail.
*
* There are several headers you can set on the mail object to achieve various
* effects:
* - Reply-To - Set this to an email address if you want people to reply to an
* address other than the from address.
* - Errors-To - If the mail can not be delivered the error message will be
* sent to this address.
*
* @property ezcMailAddress $from Contains the from address as an
* ezcMailAddress object.
* @property array(ezcMailAddress) $to Contains an array of ezcMailAddress objects.
* @property array(ezcMailAddress) $cc Contains an array of ezcMailAddress objects.
* @property array(ezcMailAddress) $bcc Contains an array of ezcMailAddress objects.
* @property string $subject
* Contains the subject of the e-mail.
* Use setSubject if you require a
* special encoding.
* @property string $subjectCharset
* The encoding of the subject.
* @property ezcMailPart $body The body part of the message.
*
* @property-read string $messageId
* The message ID of the message. Treat
* as read-only unless you're 100% sure
* what you're doing. Also accessible through
* the deprecated property messageID.
* @property-read integer $timestamp
* The date/time of when the message was
* sent as Unix Timestamp.
* @property ezcMailAddress $returnPath Contains the Return-Path address as an
* ezcMailAddress object.
*
* @apichange Remove the support for the deprecated property messageID.
*
* @package Mail
* @version //autogen//
* @mainclass
*/
class ezcMail extends ezcMailPart
{
/**
* 7 bit encoding.
*/
const SEVEN_BIT = "7bit";
/**
* 8 bit encoding.
*/
const EIGHT_BIT = "8bit";
/**
* Binary encoding.
*/
const BINARY = "binary";
/**
* Quoted printable encoding.
*/
const QUOTED_PRINTABLE = "quoted-printable";
/**
* Base 64 encoding.
*/
const BASE64 = "base64";
/**
* Constructs an empty ezcMail object.
*/
public function __construct()
{
parent::__construct();
$this->properties['from'] = null;
$this->properties['to'] = array();
$this->properties['cc'] = array();
$this->properties['bcc'] = array();
$this->properties['subject'] = null;
$this->properties['subjectCharset'] = 'us-ascii';
$this->properties['body'] = null;
$this->properties['messageId'] = null;
$this->properties['returnPath'] = null;
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @throws ezcBasePropertyPermissionException
* if the property is read-only.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'from':
case 'returnPath':
if ( $value !== null && !$value instanceof ezcMailAddress )
{
throw new ezcBaseValueException( $name, $value, 'ezcMailAddress or null' );
}
$this->properties[$name] = $value;
break;
case 'to':
case 'cc':
case 'bcc':
if ( !is_array( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'array( ezcMailAddress )' );
}
foreach ( $value as $key => $obj )
{
if ( !$obj instanceof ezcMailAddress )
{
throw new ezcBaseValueException( "{$name}[{$key}]", $obj, 'ezcMailAddress' );
}
}
$this->properties[$name] = $value;
break;
case 'subject':
$this->properties['subject'] = trim( $value );
break;
case 'subjectCharset':
$this->properties['subjectCharset'] = $value;
break;
case 'body':
if ( !$value instanceof ezcMailPart )
{
throw new ezcBaseValueException( $name, $value, 'ezcMailPart' );
}
$this->properties['body'] = $value;
break;
case 'messageId':
case 'messageID':
$this->properties['messageId'] = $value;
break;
case 'timestamp':
throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ );
break;
default:
parent::__set( $name, $value );
break;
}
}
/**
* Returns the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'to':
case 'cc':
case 'bcc':
return (array) $this->properties[$name];
case 'from':
case 'subject':
case 'subjectCharset':
case 'body':
case 'messageId':
case 'returnPath':
return $this->properties[$name];
case 'messageID': // deprecated version
return $this->properties['messageId'];
case 'timestamp':
return strtotime( $this->getHeader( "Date" ) );
default:
return parent::__get( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'to':
case 'cc':
case 'bcc':
case 'from':
case 'subject':
case 'subjectCharset':
case 'body':
case 'messageId':
case 'returnPath':
return isset( $this->properties[$name] );
case 'messageID': // deprecated version
return isset( $this->properties['messageId'] );
case 'timestamp':
return $this->getHeader( "Date" ) != null;
default:
return parent::__isset( $name );
}
}
/**
* Adds the ezcMailAddress $address to the list of 'to' recipients.
*
* @param ezcMailAddress $address
*/
public function addTo( ezcMailAddress $address )
{
$this->properties['to'][] = $address;
}
/**
* Adds the ezcMailAddress $address to the list of 'cc' recipients.
*
* @param ezcMailAddress $address
*/
public function addCc( ezcMailAddress $address )
{
$this->properties['cc'][] = $address;
}
/**
* Adds the ezcMailAddress $address to the list of 'bcc' recipients.
*
* @param ezcMailAddress $address
*/
public function addBcc( ezcMailAddress $address )
{
$this->properties['bcc'][] = $address;
}
/**
* Returns the generated body part of this mail.
*
* Returns an empty string if no body has been set.
*
* @return string
*/
public function generateBody()
{
if ( is_subclass_of( $this->body, 'ezcMailPart' ) )
{
return $this->body->generateBody();
}
return '';
}
/**
* Returns the generated headers for the mail.
*
* This method is called automatically when the mail message is built.
* You can re-implement this method in subclasses if you wish to set
* different mail headers than ezcMail.
*
* @return string
*/
public function generateHeaders()
{
// set our headers first.
if ( $this->from !== null )
{
$this->setHeader( "From", ezcMailTools::composeEmailAddress( $this->from ) );
}
if ( $this->to !== null )
{
$this->setHeader( "To", ezcMailTools::composeEmailAddresses( $this->to ) );
}
if ( count( $this->cc ) )
{
$this->setHeader( "Cc", ezcMailTools::composeEmailAddresses( $this->cc ) );
}
if ( count( $this->bcc ) )
{
$this->setHeader( "Bcc", ezcMailTools::composeEmailAddresses( $this->bcc ) );
}
$this->setHeader( 'Subject', $this->subject, $this->subjectCharset );
$this->setHeader( 'MIME-Version', '1.0' );
$this->setHeader( 'User-Agent', 'eZ Components' );
$this->setHeader( 'Date', date( 'r' ) );
$idhost = $this->from != null && $this->from->email != '' ? $this->from->email : 'localhost';
if ( is_null( $this->messageId ) )
{
$this->setHeader( 'Message-Id', '<' . ezcMailTools::generateMessageId( $idhost ) . '>' );
}
else
{
$this->setHeader( 'Message-Id', $this->messageID );
}
// if we have a body part, include the headers of the body
if ( is_subclass_of( $this->body, "ezcMailPart" ) )
{
return parent::generateHeaders() . $this->body->generateHeaders();
}
return parent::generateHeaders();
}
/**
* Returns an array of mail parts from the current mail.
*
* The array returned contains objects of classes:
* - ezcMailText
* - ezcMailFile
* - ezcMailRfc822Digest
* If the method is called with $includeDigests as true, then the returned
* array will not contain ezcMailRfc822Digest objects, but instead the mail
* parts inside the digests.
* The parameter $filter can be used to restrict the returned mail parts,
* eg. $filter = array( 'ezcMailFile' ) to return only file mail parts.
*
* A typical use for this function is to get a list of attachments from a mail.
* Example:
* <code>
* // $mail is an ezcMail object
* $parts = $mail->fetchParts();
* // after the above line is executed, $parts will contain an array of mail parts objects,
* // for example one ezcMailText object ($parts[0]) and two ezcMailRfc822Digest objects ($parts[1] and $parts[2]).
* // the ezcMailText object will be used to render the mail text, and the
* // other two objects will be displayed as links ("view attachment")
*
* // when user clicks on one of the two attachments, the parts of that attachment
* // must be retrieved in order to render the attached digest:
* $subparts = $parts[1]->mail->fetchParts();
* // after the above line is executed, $subparts will contain an array of mail parts objects,
* // for example one ezcMailText object and one ezcMailFile object
* </code>
*
* @param array(string) $filter
* @param bool $includeDigests
* @return array(ezcMailPart)
*/
public function fetchParts( $filter = null, $includeDigests = false )
{
$context = new ezcMailPartWalkContext( array( __CLASS__, 'collectPart' ) );
$context->includeDigests = $includeDigests;
$context->filter = $filter;
$context->level = 0;
$this->walkParts( $context, $this );
return $context->getParts();
}
/**
* Walks recursively through the mail parts in the specified mail object.
*
* $context is an object of class ezcMailPartWalkContext, which must contain
* a valid callback function name to be applied to all mail parts. You can use
* the collectPart() method, or create your own callback function which can
* for example save the mail parts to disk or to a database.
*
* For the properties you can set to the walk context see: {@link ezcMailPartWalkContext}
*
* Example:
* <code>
* class App
* {
* public static function saveMailPart( $context, $mailPart )
* {
* // code to save the $mailPart object to disk
* }
* }
*
* // use the saveMailPart() function as a callback in walkParts()
* // where $mail is an ezcMail object.
* $context = new ezcMailPartWalkContext( array( 'App', 'saveMailPart' ) );
* $context->includeDigests = true; // if you want to go through the digests in the mail
* $mail->walkParts( $context, $mail );
* </code>
*
* @param ezcMailPartWalkContext $context
* @param ezcMailPart $mail
*/
public function walkParts( ezcMailPartWalkContext $context, ezcMailPart $mail )
{
$className = get_class( $mail );
$context->level++;
switch ( $className )
{
case 'ezcMail':
case 'ezcMailComposer':
if ( $mail->body !== null )
{
$this->walkParts( $context, $mail->body );
}
break;
case 'ezcMailMultipartMixed':
case 'ezcMailMultipartAlternative':
case 'ezcMailMultipartDigest':
case 'ezcMailMultipartReport':
foreach ( $mail->getParts() as $part )
{
$this->walkParts( $context, $part );
}
break;
case 'ezcMailMultipartRelated':
$this->walkParts( $context, $mail->getMainPart() );
foreach ( $mail->getRelatedParts() as $part )
{
$this->walkParts( $context, $part );
}
break;
case 'ezcMailRfc822Digest':
if ( $context->includeDigests )
{
$this->walkParts( $context, $mail->mail );
}
elseif ( empty( $context->filter ) || in_array( $className, $context->filter ) )
{
call_user_func( $context->callbackFunction, $context, $mail );
}
break;
case 'ezcMailText':
case 'ezcMailFile':
case 'ezcMailDeliveryStatus':
if ( empty( $context->filter ) || in_array( $className, $context->filter ) )
{
call_user_func( $context->callbackFunction, $context, $mail );
}
break;
default:
// for cases where a custom mail class has been specified with $parser->options->mailClass
if ( in_array( 'ezcMail', class_parents( $className ) ) )
{
if ( $mail->body !== null )
{
$this->walkParts( $context, $mail->body );
}
}
// for cases where a custom file class has been specified with $parser->options->fileClass
if ( in_array( 'ezcMailFile', class_parents( $className ) ) )
{
if ( empty( $context->filter ) || in_array( $className, $context->filter ) )
{
call_user_func( $context->callbackFunction, $context, $mail );
}
}
}
$context->level--;
}
/**
* Saves $mail in the $context object.
*
* This function is used as a callback in the fetchParts() method.
*
* @param ezcMailPartWalkContext $context
* @param ezcMailPart $mail
*/
protected static function collectPart( ezcMailPartWalkContext $context, ezcMailPart $mail )
{
$context->appendPart( $mail );
}
}
?>

View file

@ -0,0 +1,81 @@
<?php
/**
* Autoloader definition for the Mail component.
*
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @version //autogentag//
* @filesource
* @package Mail
*/
return array(
'ezcMailException' => 'Mail/exceptions/mail_exception.php',
'ezcMailInvalidLimitException' => 'Mail/exceptions/invalid_limit.php',
'ezcMailNoSuchMessageException' => 'Mail/exceptions/no_such_message.php',
'ezcMailOffsetOutOfRangeException' => 'Mail/exceptions/offset_out_of_range.php',
'ezcMailTransportException' => 'Mail/exceptions/transport_exception.php',
'ezcMailTransportSmtpException' => 'Mail/exceptions/transport_smtp_exception.php',
'ezcMailPart' => 'Mail/interfaces/part.php',
'ezcMailPartParser' => 'Mail/parser/interfaces/part_parser.php',
'ezcMailTransport' => 'Mail/interfaces/transport.php',
'ezcMail' => 'Mail/mail.php',
'ezcMailFilePart' => 'Mail/parts/file.php',
'ezcMailMtaTransport' => 'Mail/transports/mta/mta_transport.php',
'ezcMailMultipart' => 'Mail/parts/multipart.php',
'ezcMailMultipartParser' => 'Mail/parser/parts/multipart_parser.php',
'ezcMailParserSet' => 'Mail/parser/interfaces/parser_set.php',
'ezcMailSmtpTransport' => 'Mail/transports/smtp/smtp_transport.php',
'ezcMailTransportOptions' => 'Mail/options/transport_options.php',
'ezcMailAddress' => 'Mail/structs/mail_address.php',
'ezcMailCharsetConverter' => 'Mail/internal/charset_convert.php',
'ezcMailComposer' => 'Mail/composer.php',
'ezcMailComposerOptions' => 'Mail/options/composer_options.php',
'ezcMailContentDispositionHeader' => 'Mail/structs/content_disposition_header.php',
'ezcMailDeliveryStatus' => 'Mail/parts/delivery_status.php',
'ezcMailDeliveryStatusParser' => 'Mail/parser/parts/delivery_status_parser.php',
'ezcMailFile' => 'Mail/parts/fileparts/disk_file.php',
'ezcMailFileParser' => 'Mail/parser/parts/file_parser.php',
'ezcMailFileSet' => 'Mail/transports/file/file_set.php',
'ezcMailHeaderFolder' => 'Mail/internal/header_folder.php',
'ezcMailHeadersHolder' => 'Mail/parser/headers_holder.php',
'ezcMailImapSet' => 'Mail/transports/imap/imap_set.php',
'ezcMailImapSetOptions' => 'Mail/options/imap_set_options.php',
'ezcMailImapTransport' => 'Mail/transports/imap/imap_transport.php',
'ezcMailImapTransportOptions' => 'Mail/options/imap_options.php',
'ezcMailMboxSet' => 'Mail/transports/mbox/mbox_set.php',
'ezcMailMboxTransport' => 'Mail/transports/mbox/mbox_transport.php',
'ezcMailMultipartAlternative' => 'Mail/parts/multiparts/multipart_alternative.php',
'ezcMailMultipartAlternativeParser' => 'Mail/parser/parts/multipart_alternative_parser.php',
'ezcMailMultipartDigest' => 'Mail/parts/multiparts/multipart_digest.php',
'ezcMailMultipartDigestParser' => 'Mail/parser/parts/multipart_digest_parser.php',
'ezcMailMultipartMixed' => 'Mail/parts/multiparts/multipart_mixed.php',
'ezcMailMultipartMixedParser' => 'Mail/parser/parts/multipart_mixed_parser.php',
'ezcMailMultipartRelated' => 'Mail/parts/multiparts/multipart_related.php',
'ezcMailMultipartRelatedParser' => 'Mail/parser/parts/multipart_related_parser.php',
'ezcMailMultipartReport' => 'Mail/parts/multiparts/multipart_report.php',
'ezcMailMultipartReportParser' => 'Mail/parser/parts/multipart_report_parser.php',
'ezcMailParser' => 'Mail/parser/parser.php',
'ezcMailParserOptions' => 'Mail/options/parser_options.php',
'ezcMailParserShutdownHandler' => 'Mail/parser/shutdown_handler.php',
'ezcMailPartWalkContext' => 'Mail/structs/walk_context.php',
'ezcMailPop3Set' => 'Mail/transports/pop3/pop3_set.php',
'ezcMailPop3Transport' => 'Mail/transports/pop3/pop3_transport.php',
'ezcMailPop3TransportOptions' => 'Mail/options/pop3_options.php',
'ezcMailRfc2231Implementation' => 'Mail/parser/rfc2231_implementation.php',
'ezcMailRfc822Digest' => 'Mail/parts/rfc822_digest.php',
'ezcMailRfc822DigestParser' => 'Mail/parser/parts/rfc822_digest_parser.php',
'ezcMailRfc822Parser' => 'Mail/parser/parts/rfc822_parser.php',
'ezcMailSmtpTransportOptions' => 'Mail/options/smtp_options.php',
'ezcMailStorageSet' => 'Mail/transports/storage/storage_set.php',
'ezcMailStreamFile' => 'Mail/parts/fileparts/stream_file.php',
'ezcMailText' => 'Mail/parts/text.php',
'ezcMailTextParser' => 'Mail/parser/parts/text_parser.php',
'ezcMailTools' => 'Mail/tools.php',
'ezcMailTransportConnection' => 'Mail/transports/transport_connection.php',
'ezcMailTransportMta' => 'Mail/transports/mta/transport_mta.php',
'ezcMailTransportSmtp' => 'Mail/transports/smtp/transport_smtp.php',
'ezcMailVariableSet' => 'Mail/transports/variable/var_set.php',
'ezcMailVirtualFile' => 'Mail/parts/fileparts/virtual_file.php',
);
?>

View file

@ -0,0 +1,83 @@
<?php
/**
* File containing the ezcMailComposerOptions class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing the options for the mail composer.
*
* Example of how to use the composer options:
* <code>
* $options = new ezcMailComposerOptions();
* $options->automaticImageInclude = false; // default value is true
*
* $mail = new ezcMailComposer( $options );
* </code>
*
* Alternatively, you can set the options direcly:
* <code>
* $mail = new ezcMailComposer();
* $mail->options->automaticImageInclude = false;
* </code>
*
* @property bool $automaticImageInclude
* Specifies whether to include in the generated mail the content of
* the files specified with "file://" in image tags. Default value
* is true (the contents are included).
*
* @package Mail
* @version //autogen//
*/
class ezcMailComposerOptions extends ezcBaseOptions
{
/**
* Constructs an object with the specified values.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param array(string=>mixed) $options
*/
public function __construct( array $options = array() )
{
$this->automaticImageInclude = true; // default is to include the contents of "file://" from image tags
parent::__construct( $options );
}
/**
* Sets the option $propertyName to $propertyValue.
*
* @throws ezcBasePropertyNotFoundException
* if the property $propertyName is not defined
* @throws ezcBaseValueException
* if $propertyValue is not correct for the property $propertyName
* @param string $propertyName
* @param mixed $propertyValue
* @ignore
*/
public function __set( $propertyName, $propertyValue )
{
switch ( $propertyName )
{
case 'automaticImageInclude':
if ( !is_bool( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
}
$this->properties[$propertyName] = $propertyValue;
break;
default:
throw new ezcBasePropertyNotFoundException( $propertyName );
}
}
}
?>

View file

@ -0,0 +1,79 @@
<?php
/**
* File containing the ezcMailImapTransportOptions class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing the options for IMAP transport.
*
* The options from {@link ezcMailTransportOptions} are inherited.
*
* Example of how to use IMAP transport options:
* <code>
* $options = new ezcMailImapTransportOptions();
* $options->ssl = true;
* $options->timeout = 3;
* $options->uidReferencing = true;
*
* $imap = new ezcMailImapTransport( 'imap.example.com', null, $options );
* </code>
*
* @property bool $uidReferencing
* Specifies if the IMAP commands will operate with message unique
* IDs or with message numbers (default).
*
* @package Mail
* @version //autogen//
*/
class ezcMailImapTransportOptions extends ezcMailTransportOptions
{
/**
* Constructs an object with the specified values.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param array(string=>mixed) $options
*/
public function __construct( array $options = array() )
{
$this->uidReferencing = false;
parent::__construct( $options );
}
/**
* Sets the value of the option $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name is not defined
* @throws ezcBaseValueException
* if $value is not correct for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'uidReferencing':
if ( !is_bool( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'bool' );
}
$this->properties[$name] = $value;
break;
default:
parent::__set( $name, $value );
}
}
}
?>

View file

@ -0,0 +1,67 @@
<?php
/**
* File containing the ezcMailImapSetOptions class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing the options for IMAP mail set.
*
* @property bool $uidReferencing
* Specifies if the IMAP commands will operate with message unique
* IDs or with message numbers (default).
*
* @package Mail
* @version //autogen//
*/
class ezcMailImapSetOptions extends ezcBaseOptions
{
/**
* Constructs an object with the specified values.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param array(string=>mixed) $options
*/
public function __construct( array $options = array() )
{
$this->uidReferencing = false;
parent::__construct( $options );
}
/**
* Sets the value of the option $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name is not defined
* @throws ezcBaseValueException
* if $value is not correct for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'uidReferencing':
if ( !is_bool( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'bool' );
}
$this->properties[$name] = $value;
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
}
?>

View file

@ -0,0 +1,132 @@
<?php
/**
* File containing the ezcMailParserOption class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing the basic options for the mail parser.
*
* Example of how to use the parser options:
* <code>
* $options = new ezcMailParserOptions();
* $options->mailClass = 'myCustomMailClass'; // extends ezcMail
* $options->fileClass = 'myCustomFileClass'; // extends ezcMailFile
* $options->parseTextAttachmentsAsFiles = true; // to get the text attachments in ezcMailFile objects
*
* $parser = new ezcMailParser( $options );
* </code>
*
* Another way to specify the options is:
* <code>
* $parser = new ezcMailParser();
* $parser->options->mailClass = 'myCustomMailClass'; // extends ezcMail
* $parser->options->fileClass = 'myCustomFileClass'; // extends ezcMailFile
* $parser->options->parseTextAttachmentsAsFiles = true;
* </code>
*
* @property string $mailClass
* Specifies a class descending from ezcMail which can be returned by the
* parser if you plan to use another class instead of ezcMail. The default
* value is ezcMail.
* @property string $fileClass
* Specifies a class descending from ezcMailFile which can be instanciated
* by the parser to handle file attachments. The default value is
* ezcMailFile.
* @property string $parseTextAttachmentsAsFiles
* Specifies whether to parse the text attachments in an ezcMailTextPart
* (default) or in an ezcMailFile (by setting the option to true).
* @package Mail
* @version //autogen//
*/
class ezcMailParserOptions extends ezcBaseOptions
{
/**
* Constructs an object with the specified values.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param array(string=>mixed) $options
*/
public function __construct( array $options = array() )
{
$this->mailClass = 'ezcMail'; // default value for mail class is 'ezcMail'
$this->fileClass = 'ezcMailFile'; // default value for file attachment class is 'ezcMailFile'
$this->parseTextAttachmentsAsFiles = false; // default is to parse text attachments in ezcMailTextPart objects
parent::__construct( $options );
}
/**
* Sets the option $propertyName to $propertyValue.
*
* @throws ezcBasePropertyNotFoundException
* if the property $propertyName is not defined
* @throws ezcBaseValueException
* if $propertyValue is not correct for the property $propertyName
* @throws ezcBaseInvalidParentClassException
* if the class name passed as replacement mailClass does not
* inherit from ezcMail.
* @throws ezcBaseInvalidParentClassException
* if the class name passed as replacement fileClass does not
* inherit from ezcMailFile.
* @param string $propertyName
* @param mixed $propertyValue
* @ignore
*/
public function __set( $propertyName, $propertyValue )
{
switch ( $propertyName )
{
case 'mailClass':
if ( !is_string( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'string that contains a class name' );
}
// Check if the passed classname actually implements the
// correct parent class.
if ( 'ezcMail' !== $propertyValue && !in_array( 'ezcMail', class_parents( $propertyValue ) ) )
{
throw new ezcBaseInvalidParentClassException( 'ezcMail', $propertyValue );
}
$this->properties[$propertyName] = $propertyValue;
break;
case 'fileClass':
if ( !is_string( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'string that contains a class name' );
}
// Check if the passed classname actually implements the
// correct parent class.
if ( 'ezcMailFile' !== $propertyValue && !in_array( 'ezcMailFile', class_parents( $propertyValue ) ) )
{
throw new ezcBaseInvalidParentClassException( 'ezcMailFile', $propertyValue );
}
$this->properties[$propertyName] = $propertyValue;
ezcMailFileParser::$fileClass = $propertyValue;
break;
case 'parseTextAttachmentsAsFiles':
if ( !is_bool( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
}
$this->properties[$propertyName] = $propertyValue;
ezcMailPartParser::$parseTextAttachmentsAsFiles = $propertyValue;
break;
default:
throw new ezcBasePropertyNotFoundException( $propertyName );
}
}
}
?>

View file

@ -0,0 +1,81 @@
<?php
/**
* File containing the ezcMailPop3TransportOptions class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing the options for POP3 transport.
*
* The options from {@link ezcMailTransportOptions} are inherited.
*
* Example of how to use POP3 transport options:
* <code>
* $options = new ezcMailPop3TransportOptions();
* $options->ssl = true;
* $options->timeout = 3;
* $options->authenticationMethod = ezcMailPop3Transport::AUTH_APOP;
*
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com', null, $options );
* </code>
*
* @property int $authenticationMethod
* Specifies the method to connect to the POP3 transport. The methods
* supported are {@link ezcMailPop3Transport::AUTH_PLAIN_TEXT} and
* {@link ezcMailPop3Transport::AUTH_APOP}.
*
* @package Mail
* @version //autogen//
*/
class ezcMailPop3TransportOptions extends ezcMailTransportOptions
{
/**
* Constructs an object with the specified values.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param array(string=>mixed) $options
*/
public function __construct( array $options = array() )
{
// default authentication method is PLAIN
$this->authenticationMethod = ezcMailPop3Transport::AUTH_PLAIN_TEXT;
parent::__construct( $options );
}
/**
* Sets the option $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name is not defined
* @throws ezcBaseValueException
* if $value is not correct for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'authenticationMethod':
if ( !is_numeric( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'int' );
}
$this->properties[$name] = (int) $value;
break;
default:
parent::__set( $name, $value );
}
}
}
?>

View file

@ -0,0 +1,123 @@
<?php
/**
* File containing the ezcMailSmtpTransportOptions class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing the options for SMTP transport.
*
* The options from {@link ezcMailTransportOptions} are inherited.
*
* Example of how to use SMTP transport options:
* <code>
* $options = new ezcMailSmtpTransportOptions();
* $options->timeout = 3;
* $options->connectionType = ezcMailSmtpTransport::CONNECTION_SSL;
* $options->preferredAuthMethod = ezcMailSmtpTransport::AUTH_NTLM;
*
* $smtp = new ezcMailSmtpTransport( 'smtp.example.com', 'user', 'password', null, $options );
*
* // the options can also be set via the options property of the SMTP transport:
* $smtp->options->preferredAuthMethod = ezcMailSmtpTransport::AUTH_NTLM;
* </code>
*
* @property string $connectionType
* Specifies the protocol used to connect to the SMTP server. See the
* CONNECTION_* constants in the {@link ezcMailSmtpTransport} class.
* @property array(mixed) $connectionOptions
* Specifies additional options for the connection. Must be in this format:
* array( 'wrapper_name' => array( 'option_name' => 'value' ) ).
* @property bool $ssl
* This option belongs to {@link ezcMailTransportOptions}, but it is
* not used in SMTP.
* When trying to set this to true the connectionType option will be set to
* {@link ezcMailSmtpTransport::CONNECTION_SSL}.
* When trying to set this to false the connectionType option will be set to
* {@link ezcMailSmtpTransport::CONNECTION_PLAIN}.
* @property string $preferredAuthMethod
* Specifies which authentication method should be attempted. Default is
* null which means that that the transport should try to
* authenticate using the methods supported by the SMTP server in their
* decreasing strength order. If one method fails an exception will be
* thrown. See the AUTH_* constants in the {@link ezcMailSmtpTransport}
* class.
*
* @package Mail
* @version //autogen//
*/
class ezcMailSmtpTransportOptions extends ezcMailTransportOptions
{
/**
* Constructs an object with the specified values.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param array(string=>mixed) $options
*/
public function __construct( array $options = array() )
{
$this->connectionType = ezcMailSmtpTransport::CONNECTION_PLAIN; // default is plain connection
$this->connectionOptions = array(); // default is no extra connection options
$this->preferredAuthMethod = null; // default is to try the AUTH methods supported by the SMTP server
parent::__construct( $options );
}
/**
* Sets the option $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name is not defined
* @throws ezcBaseValueException
* if $value is not correct for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'connectionType':
$this->properties[$name] = $value;
break;
case 'connectionOptions':
if ( !is_array( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'array' );
}
$this->properties[$name] = $value;
break;
case 'ssl':
if ( !is_bool( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'bool' );
}
$this->properties['connectionType'] = ( $value === true ) ? ezcMailSmtpTransport::CONNECTION_SSL : ezcMailSmtpTransport::CONNECTION_PLAIN;
break;
case 'preferredAuthMethod':
$supportedAuthMethods = ezcMailSmtpTransport::getSupportedAuthMethods();
$supportedAuthMethods[] = ezcMailSmtpTransport::AUTH_AUTO;
if ( !in_array( $value, $supportedAuthMethods ) )
{
throw new ezcBaseValueException( $name, $value, implode( ' | ', $supportedAuthMethods ) );
}
$this->properties[$name] = $value;
break;
default:
parent::__set( $name, $value );
}
}
}
?>

View file

@ -0,0 +1,78 @@
<?php
/**
* File containing the ezcMailTransportOption class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Class containing the basic options for mail transports.
*
* @property int $timeout
* Specifies the time in seconds until the connection is closed if
* there is no activity through the connection.
* @property bool $ssl
* Specifies whether to use an SSL connection or not.
*
* @package Mail
* @version //autogen//
*/
class ezcMailTransportOptions extends ezcBaseOptions
{
/**
* Constructs an object with the specified values.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param array(string=>mixed) $options
*/
public function __construct( array $options = array() )
{
$this->timeout = 5; // default value for timeout is 5 seconds
$this->ssl = false; // default value for ssl is false
parent::__construct( $options );
}
/**
* Sets the option $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name is not defined
* @throws ezcBaseValueException
* if $value is not correct for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'timeout':
if ( !is_numeric( $value ) || ( $value < 1 ) )
{
throw new ezcBaseValueException( $name, $value, 'int >= 1' );
}
$this->properties[$name] = (int) $value;
break;
case 'ssl':
if ( !is_bool( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'bool' );
}
$this->properties[$name] = $value;
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
}
?>

View file

@ -0,0 +1,129 @@
<?php
/**
* File containing the ezcMailHeaderHolder class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Holds the headers of a mail during parsing and allows case insensitive lookup
* but case sensitive storage.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailHeadersHolder implements ArrayAccess
{
/**
* Holds the mapping between the case insensitive key and the real key.
*
* Format: array(lowerCaseKey, mixedCaseKey)
*
* @var array(string=>string)
*/
private $lookup = array();
/**
* Holds the normal associative array between keys in correct case and values.
*
* Format: array(mixedCaseKey, value)
*
* @var array(string=>string)
*/
private $map = array();
/**
* Constructs a new case insensitive associtive array formed around the array
* $map with mixed case keys.
*
* @param array(string=>string) $map
*/
public function __construct( array $map = array() )
{
$this->map = $map;
foreach ( $map as $key => $value )
{
$this->lookup[strtolower( $key )] = $key;
}
}
/**
* Returns true if the $key exists in the array.
*
* @param string $key
* @return bool
*/
public function offsetExists( $key )
{
return array_key_exists( strtolower( $key ), $this->lookup );
}
/**
* Returns the value recognized with $key.
*
* @param string $key
* @return mixed
*/
public function offsetGet( $key )
{
$key = strtolower( $key );
if ( !array_key_exists( $key, $this->lookup ) )
{
return null;
}
return $this->map[$this->lookup[$key]];
}
/**
* Sets the offset $key to the value $value.
*
* If it is a new entry the case in $key will be stored. If the $key exists already
* using a case insensitive lookup the new spelling will be discarded.
*
* @param string $key
* @param mixed $value
*/
public function offsetSet( $key, $value )
{
$lowerKey = strtolower( $key );
if ( !array_key_exists( $lowerKey, $this->lookup ) )
{
$this->map[$key] = $value;
$this->lookup[$lowerKey] = $key;
}
else // use old case
{
$this->map[$this->lookup[$lowerKey]] = $value;
}
}
/**
* Unsets the key $key.
*
* @param string $key
*/
public function offsetUnset( $key )
{
$key = strtolower( $key );
if ( array_key_exists( $key, $this->lookup ) )
{
unset( $this->map[$this->lookup[$key]] );
unset( $this->lookup[$key] );
}
}
/**
* Returns a copy of the associative array with the case of the keys preserved.
*
* @return array(string=>string)
*/
public function getCaseSensitiveArray()
{
return $this->map;
}
}
?>

View file

@ -0,0 +1,49 @@
<?php
/**
* File containing the ezcMailParserSet interface
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Interface common to all parser sets.
*
* A parser set provides a simple interface to fetch mail data line by
* line from a set of mail.
*
* @package Mail
* @version //autogen//
*/
interface ezcMailParserSet
{
/**
* Returns one line of data from the current mail in the set
* including the ending linebreak.
*
* Null is returned if there is no current mail in the set or
* the end of the mail is reached,
*
* @return string
*/
public function getNextLine();
/**
* Moves the set to the next mail and returns true upon success.
*
* False is returned if there are no more mail in the set.
*
* @return bool
*/
public function nextMail();
/**
* Returns true if mail data is available for parsing.
*
* @return bool
*/
public function hasData();
}
?>

View file

@ -0,0 +1,265 @@
<?php
/**
* File containing the ezcMailPartParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Base class for all parser parts.
*
* Parse process
* 1. Figure out the headers of the next part.
* 2. Based on the headers, create the parser for the bodyPart corresponding to
* the headers.
* 3. Parse the body line by line. In the case of a multipart or a digest recursively
* start this process. Note that in the case of RFC822 messages the body contains
* headers.
* 4. call finish() on the partParser and retrieve the ezcMailPart
*
* Each parser part gets the header for that part through the constructor
* and is responsible for parsing the body of that part.
* Parsing of the body is done on a push basis trough the parseBody() method
* which is called repeatedly by the parent part for each line in the message.
*
* When there are no more lines the parent part will call finish() and the mail
* part corresponding to the part you are parsing should be returned.
*
* @todo case on headers
* @package Mail
* @version //autogen//
* @access private
*/
abstract class ezcMailPartParser
{
/**
* Mail headers which can appear maximum one time in a mail message,
* as defined by RFC 2822.
*
* @var array(string)
*/
protected static $uniqueHeaders = array( 'bcc', 'cc', 'content-type',
'content-disposition', 'from',
'content-transfer-encoding',
'return-path',
'in-reply-to', 'references',
'message-id', 'date', 'reply-to',
'sender', 'subject', 'sender', 'to' );
/**
* The default is to parse text attachments into ezcMailTextPart objects.
*
* Setting this to true before calling the parser will parse text attachments
* into ezcMailFile objects. Use the parser options for this:
*
* <code>
* $parser = new ezcMailParser();
* $parser->options->parseTextAttachmentsAsFiles = true;
* // call $parser->parseMail( $set );
* </code>
*
* @var bool
*/
public static $parseTextAttachmentsAsFiles = false;
/**
* The name of the last header parsed.
*
* This variable is used when glueing together multi-line headers.
*
* @var string
*/
private $lastParsedHeader = null;
/**
* Parse the body of a message line by line.
*
* This method is called by the parent part on a push basis. When there
* are no more lines the parent part will call finish() to retrieve the
* mailPart.
*
* @param string $line
*/
abstract public function parseBody( $line );
/**
* Return the result of the parsed part.
*
* This method is called when all the lines of this part have been parsed.
*
* @return ezcMailPart
*/
abstract public function finish();
/**
* Returns a part parser corresponding to the given $headers.
*
* @throws ezcBaseFileNotFoundException
* if a neccessary temporary file could not be openened.
* @param ezcMailHeadersHolder $headers
* @return ezcMailPartParser
*/
static public function createPartParserForHeaders( ezcMailHeadersHolder $headers )
{
// default as specified by RFC2045 - #5.2
$mainType = 'text';
$subType = 'plain';
// parse the Content-Type header
if ( isset( $headers['Content-Type'] ) )
{
$matches = array();
// matches "type/subtype; blahblahblah"
preg_match_all( '/^(\S+)\/([^;]+)/',
$headers['Content-Type'], $matches, PREG_SET_ORDER );
if ( count( $matches ) > 0 )
{
$mainType = strtolower( $matches[0][1] );
$subType = strtolower( $matches[0][2] );
}
}
$bodyParser = null;
// create the correct type parser for this the detected type of part
switch ( $mainType )
{
/* RFC 2045 defined types */
case 'image':
case 'audio':
case 'video':
case 'application':
$bodyParser = new ezcMailFileParser( $mainType, $subType, $headers );
break;
case 'message':
switch ( $subType )
{
case "rfc822":
$bodyParser = new ezcMailRfc822DigestParser( $headers );
break;
case "delivery-status":
$bodyParser = new ezcMailDeliveryStatusParser( $headers );
break;
default:
$bodyParser = new ezcMailFileParser( $mainType, $subType, $headers );
break;
}
break;
case 'text':
// UNASSESSED: Based on diffing civi's packages with upstream 1.7beta1, this appears
// to be a local change, but I haven't found any history for why.
if ( (ezcMailPartParser::$parseTextAttachmentsAsFiles === true) &&
(preg_match('/\s*filename="?([^;"]*);?/i', $headers['Content-Disposition']) ||
preg_match( '/\s*name="?([^;"]*);?/i' , $headers['Content-Type']) ) )
{
$bodyParser = new ezcMailFileParser( $mainType, $subType, $headers );
}
else
{
$bodyParser = new ezcMailTextParser( $subType, $headers );
}
break;
case 'multipart':
switch ( $subType )
{
case 'mixed':
$bodyParser = new ezcMailMultipartMixedParser( $headers );
break;
case 'alternative':
$bodyParser = new ezcMailMultipartAlternativeParser( $headers );
break;
case 'related':
$bodyParser = new ezcMailMultipartRelatedParser( $headers );
break;
case 'digest':
$bodyParser = new ezcMailMultipartDigestParser( $headers );
break;
case 'report':
$bodyParser = new ezcMailMultipartReportParser( $headers );
break;
default:
$bodyParser = new ezcMailMultipartMixedParser( $headers );
break;
}
break;
/* extensions */
default:
// we treat the body as binary if no main content type is set
// or if it is unknown
$bodyParser = new ezcMailFileParser( $mainType, $subType, $headers );
break;
}
return $bodyParser;
}
/**
* Parses the header given by $line and adds to $headers.
*
* This method is usually used to parse the headers for a subpart. The
* only exception is RFC822 parts since you know the type in advance.
*
* @todo deal with headers that are listed several times
* @param string $line
* @param ezcMailHeadersHolder $headers
*/
protected function parseHeader( $line, ezcMailHeadersHolder $headers )
{
$matches = array();
preg_match_all( "/^([\w-_]*):\s?(.*)/", $line, $matches, PREG_SET_ORDER );
if ( count( $matches ) > 0 )
{
if ( !in_array( strtolower( $matches[0][1] ), self::$uniqueHeaders ) )
{
$arr = $headers[$matches[0][1]];
$arr[0][] = str_replace( "\t", " ", trim( $matches[0][2] ) );
$headers[$matches[0][1]] = $arr;
}
else
{
$headers[$matches[0][1]] = str_replace( "\t", " ", trim( $matches[0][2] ) );
}
$this->lastParsedHeader = $matches[0][1];
}
else if ( $this->lastParsedHeader !== null ) // take care of folding
{
if ( !in_array( strtolower( $this->lastParsedHeader ), self::$uniqueHeaders ) )
{
$arr = $headers[$this->lastParsedHeader];
$arr[0][count( $arr[0] ) - 1] .= str_replace( "\t", " ", $line );
$headers[$this->lastParsedHeader] = $arr;
}
else
{
$headers[$this->lastParsedHeader] .= str_replace( "\t", " ", $line );
}
}
// else -invalid syntax, this should never happen.
}
/**
* Scans through $headers and sets any specific header properties on $part.
*
* Currently we only have Content-Disposition on the ezcMailPart level.
* All parser parts must call this method once.
*
* @param ezcMailHeadersHolder $headers
* @param ezcMailPart $part
*/
static public function parsePartHeaders( ezcMailHeadersHolder $headers, ezcMailPart $part )
{
if ( isset( $headers['Content-Disposition'] ) )
{
$part->contentDisposition = ezcMailRfc2231Implementation::parseContentDisposition( $headers['Content-Disposition'] );
}
}
}
?>

View file

@ -0,0 +1,283 @@
<?php
/**
* File containing the ezcMailParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses a mail in RFC822 format to an ezcMail structure.
*
* By default an object of class {@link ezcMail} is returned by the parser. If
* you want to use your own mail class (which extends {@link ezcMail}),
* use {@link ezcMailParserOptions}. Example:
*
* <code>
* $options = new ezcMailParserOptions();
* $options->mailClass = 'myCustomMailClass'; // extends ezcMail
*
* $parser = new ezcMailParser( $options );
* </code>
*
* Another way to do this is:
* <code>
* $parser = new ezcMailParser();
* $parser->options->mailClass = 'myCustomMailClass'; // extends ezcMail
* </code>
*
* File attachments will be written to disk in a temporary directory.
* This temporary directory and the file attachment will be removed
* when PHP ends execution. If you want to keep the file you should move it
* to another directory.
*
* By default objects of class {@link ezcMailFile} are created to handle file
* attachments. If you want to use your own file class (which extends
* {@link ezcMailFile}), use {@link ezcMailParserOptions}. Example:
*
* <code>
* $options = new ezcMailParserOptions();
* $options->fileClass = 'myCustomFileClass'; // extends ezcMailFile
*
* $parser = new ezcMailParser( $options );
* </code>
*
* Another way to do this is:
* <code>
* $parser = new ezcMailParser();
* $parser->options->fileClass = 'myCustomFileClass'; // extends ezcMailFile
* </code>
*
* By default objects of class {@link ezcMailTextPart} are created for text
* attachments. If you want to use ezcMailFile objects instead, use
* {@link ezcMailParserOptions}. Example:
*
* <code>
* $options = new ezcMailParserOptions();
* $options->parseTextAttachmentsAsFiles = true;
*
* $parser = new ezcMailParser( $options );
* </code>
*
* Another way to do this is:
* <code>
* $parser = new ezcMailParser();
* $parser->options->parseTextAttachmentsAsFiles = true;
* </code>
*
* @property ezcMailParserOptions $options
* Holds the options you can set to the mail parser.
*
* @package Mail
* @version //autogen//
* @mainclass
*/
class ezcMailParser
{
/**
* Holds the parser of the current mail.
*
* @var ezcMailPartParser
*/
private $partParser = null;
/**
* Holds the directory where parsed mail should store temporary files.
*
* @var string
*/
private static $tmpDir = null;
/**
* Holds options you can be set to the mail parser.
*
* @var ezcMailParserOptions
*/
private $options;
/**
* Constructs a new mail parser.
*
* For options you can set to the mail parser see {@link ezcMailParserOptions}.
*
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param ezcMailParserOptions|array(string=>mixed) $options
*/
public function __construct( $options = array() )
{
if ( $options instanceof ezcMailParserOptions )
{
$this->options = $options;
}
else if ( is_array( $options ) )
{
$this->options = new ezcMailParserOptions( $options );
}
else
{
throw new ezcBaseValueException( "options", $options, "ezcMailParserOptions|array" );
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @throws ezcBaseValueException
* if $value is not accepted for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'options':
if ( !( $value instanceof ezcMailParserOptions ) )
{
throw new ezcBaseValueException( 'options', $value, 'instanceof ezcMailParserOptions' );
}
$this->options = $value;
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns the value of the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @param string $name
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'options':
return $this->options;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'options':
return true;
default:
return false;
}
}
/**
* Returns an array of ezcMail objects parsed from the mail set $set.
*
* You can optionally use ezcMailParserOptions to provide an alternate class
* name which will be instantiated instead of ezcMail, if you need to extend
* ezcMail.
*
* Example:
* <code>
* $options = new ezcMailParserOptions();
* $options->mailClass = 'MyMailClass';
*
* $parser = new ezcMailParser( $options );
* // if you want to use MyMailClass which extends ezcMail
* </code>
*
* @apichange Remove second parameter
*
* @throws ezcBaseFileNotFoundException
* if a neccessary temporary file could not be openened.
* @param ezcMailParserSet $set
* @param string $class Deprecated. Use $mailClass in ezcMailParserOptions class instead.
* @return array(ezcMail)
*/
public function parseMail( ezcMailParserSet $set, $class = null )
{
$mail = array();
if ( !$set->hasData() )
{
return $mail;
}
if ( $class === null )
{
$class = $this->options->mailClass;
}
do
{
$this->partParser = new ezcMailRfc822Parser();
$data = "";
$size = 0;
while ( ( $data = $set->getNextLine() ) !== null )
{
$this->partParser->parseBody( $data );
$size += strlen( $data );
}
$part = $this->partParser->finish( $class );
$part->size = $size;
$mail[] = $part;
} while ( $set->nextMail() );
return $mail;
}
/**
* Sets the temporary directory.
*
* The temporary directory must be writeable by PHP. It will be used to store
* file attachments.
*
* @todo throw if the directory is not writeable.
* @param string $dir
*/
public static function setTmpDir( $dir )
{
self::$tmpDir = $dir;
}
/**
* Returns the temporary directory.
*
* Uses the PHP 5.2.1 function sys_get_temp_dir().
*
* Note that the directory name returned will have a "slash" at the end
* ("/" for Linux and "\" for Windows).
*
* @return string
*/
public static function getTmpDir()
{
if ( self::$tmpDir === null )
{
self::$tmpDir = sys_get_temp_dir();
if ( substr( self::$tmpDir, strlen( self::$tmpDir ) - 1 ) !== DIRECTORY_SEPARATOR )
{
self::$tmpDir = self::$tmpDir . DIRECTORY_SEPARATOR;
}
}
return self::$tmpDir;
}
}
?>

View file

@ -0,0 +1,115 @@
<?php
/**
* File containing the ezcMailDeliveryStatusParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses mail parts of type "delivery-status".
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailDeliveryStatusParser extends ezcMailPartParser
{
/**
* This mail part will be returned by the method finish().
*
* @var ezcMailDeliveryStatus
*/
private $part = null;
/**
* The current section of the parsing of delivery-status headers.
*
* 0 = the per-message section
* 1, ... = the per-recipient section
*
* @var int
*/
private $section;
/**
* Holds the size of the mail part.
*
* @var int
*/
private $size;
/**
* Constructs a new ezcMailDeliveryStatusParser with additional headers $headers.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
$this->headers = $headers;
$this->section = 0;
$this->part = new ezcMailDeliveryStatus();
$this->size = 0;
}
/**
* Parses each line of the mail part.
*
* @param string $line
*/
public function parseBody( $line )
{
$this->parseHeader( $line, $this->headers );
$this->size += strlen( $line );
}
/**
* Parses the header given by $line.
*
* @param string $line
* @param ezcMailHeadersHolder $headers
*/
protected function parseHeader( $line, ezcMailHeadersHolder $headers )
{
$matches = array();
preg_match_all( "/^([\w-_]*):\s?(.*)/", $line, $matches, PREG_SET_ORDER );
if ( count( $matches ) > 0 )
{
$this->lastParsedHeader = $matches[0][1];
$this->headerValue = trim( $matches[0][2] );
}
else if ( isset( $this->lastParsedHeader ) && $this->lastParsedHeader !== null ) // take care of folding
{
$this->headerValue .= $line;
}
if ( strlen( trim( $line ) ) == 0 )
{
$this->section++;
$this->part->createRecipient();
return;
}
if ( $this->section == 0 )
{
$this->part->message[$this->lastParsedHeader] = $this->headerValue;
}
else
{
$this->part->recipients[$this->section - 1][$this->lastParsedHeader] = $this->headerValue;
}
}
/**
* Returns the ezcMailDeliveryStatus part corresponding to the parsed message.
*
* @return ezcMailDeliveryStatus
*/
public function finish()
{
unset( $this->part->recipients[$this->section - 1] ); // because one extra recipient is created in parseHeader()
$this->part->size = $this->size;
return $this->part;
}
}
?>

View file

@ -0,0 +1,309 @@
<?php
/**
* File containing the ezcMailFileParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses application/image/video and audio parts.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailFileParser extends ezcMailPartParser
{
/**
* Default class to handle file attachments when parsing mails
* is ezcMailFile.
*
* Change this to your own file class with:
* <code>
* $parser = new ezcMailParser();
* $parser->options->fileClass = 'myCustomFileClass';
* // call $parser->parseMail( $set );
* </code>
*
* where myCustomFileClass extends ezcMailFile.
*
* @var string
*/
public static $fileClass = 'ezcMailFile';
/**
* Holds the headers for this part.
*
* @var ezcMailHeadersHolder
*/
private $headers = null;
/**
* Holds the maintype of the parsed part.
*
* @var string
*/
private $mainType = null;
/**
* Holds the subtype of the parsed part.
*
* @var string
*/
private $subType = null;
/**
* Holds the filepointer to the attachment.
*
* @var resource
*/
private $fp = null;
/**
* Holds the full path and filename of the file to save to.
*
* @var string
*/
private $fileName = null;
/**
* Static counter used to generate unique directory names.
*
* @var int
*/
private static $counter = 1;
/**
* Holds if data has been written to the output file or not.
*
* This is used for delayed filter adding neccessary for quoted-printable.
*
* @var bool
*/
private $dataWritten = false;
/**
* Constructs a new ezcMailFileParser with maintype $mainType subtype $subType
* and headers $headers.
*
* @throws ezcBaseFileNotFoundException
* if the file attachment file could not be openened.
* @param string $mainType
* @param string $subType
* @param ezcMailHeadersHolder $headers
*/
public function __construct( $mainType, $subType, ezcMailHeadersHolder $headers )
{
$this->mainType = $mainType;
$this->subType = $subType;
$this->headers = $headers;
// figure out the base filename
// search Content-Disposition first as specified by RFC 2183
$matches = array();
if ( preg_match( '/\s*filename="?([^;"]*);?/i',
$this->headers['Content-Disposition'], $matches ) )
{
$fileName = trim( $matches[1], '"' );
}
// fallback to the name parameter in Content-Type as specified by RFC 2046 4.5.1
else if ( preg_match( '/\s*name="?([^;"]*);?/i',
$this->headers['Content-Type'], $matches ) )
{
$fileName = trim( $matches[1], '"' );
}
else // default
{
$fileName = "filename";
}
// clean file name (replace unsafe characters with underscores)
$fileName = strtr( $fileName, "/\\\0\"|?*<:;>+[]", '______________' );
$this->fp = $this->openFile( $fileName ); // propagate exception
}
/**
* Returns the filepointer of the opened file $fileName in a unique directory..
*
* This method will create a new unique folder in the temporary directory specified in ezcMailParser.
* The fileName property of this class will be set to the location of the new file.
*
* @throws ezcBaseFileNotFoundException
* if the file could not be opened.
* @param string $fileName
* @return resource
*/
private function openFile( $fileName )
{
// The filename is now relative, we need to extend it with the absolute path.
// To provide uniqueness we put the file in a directory based on processID and rand.
$dirName = ezcMailParser::getTmpDir() . getmypid() . '-' . self::$counter++ . '/';
if ( !is_dir( $dirName ) )
{
mkdir( $dirName, 0700 );
}
// remove the directory and the file when PHP shuts down
ezcMailParserShutdownHandler::registerForRemoval( $dirName );
$this->fileName = $dirName . $fileName;
$fp = fopen( $this->fileName, 'w' );
if ( $this->fp === false )
{
throw new ezcBaseFileNotFoundException( $this->fileName );
}
return $fp;
}
/**
* Destructs the parser object.
*
* Closes and removes any open file.
*/
public function __destruct()
{
// finish() was not called. The mail is completely broken.
// we will clean up the mess
if ( $this->fp !== null )
{
fclose( $this->fp );
$this->fp = null;
if ( $this->fileName !== null && file_exists( $this->fileName ) )
{
unlink( $this->fileName );
}
}
}
/**
* Sets the correct stream filters for the attachment.
*
* $line should contain one line of data that should be written to file.
* It is used to correctly determine the type of linebreak used in the mail.
*
* @param string $line
*/
private function appendStreamFilters( $line )
{
// append the correct decoding filter
switch ( strtolower( $this->headers['Content-Transfer-Encoding'] ) )
{
case 'base64':
stream_filter_append( $this->fp, 'convert.base64-decode' );
break;
case 'quoted-printable':
// fetch the type of linebreak
preg_match( "/(\r\n|\r|\n)$/", $line, $matches );
$lb = count( $matches ) > 0 ? $matches[0] : ezcMailTools::lineBreak();
$param = array( 'line-break-chars' => $lb );
stream_filter_append( $this->fp, 'convert.quoted-printable-decode',
STREAM_FILTER_WRITE, $param );
break;
case '7bit':
case '8bit':
// do nothing here, file is already just binary
break;
default:
// 7bit default
break;
}
}
/**
* Parse the body of a message line by line.
*
* This method is called by the parent part on a push basis. When there
* are no more lines the parent part will call finish() to retrieve the
* mailPart.
*
* The file will be decoded and saved to the given temporary directory within
* a directory based on the process ID and the time.
*
* @param string $line
*/
public function parseBody( $line )
{
if ( $line !== '' )
{
if ( $this->dataWritten === false )
{
$this->appendStreamFilters( $line );
$this->dataWritten = true;
}
fwrite( $this->fp, $line );
}
}
/**
* Return the result of the parsed file part.
*
* This method is called automatically by the parent part.
*
* @return ezcMailFile
*/
public function finish()
{
fclose( $this->fp );
$this->fp = null;
// FIXME: DIRTY PGP HACK
// When we have PGP support these lines should be removed. They are here now to hide
// PGP parts since they will show up as file attachments if not.
if ( $this->mainType == "application" &&
( $this->subType == 'pgp-signature'
|| $this->subType == 'pgp-keys'
|| $this->subType == 'pgp-encrypted' ) )
{
return null;
}
// END DIRTY PGP HACK
$filePart = new self::$fileClass( $this->fileName );
// set content type
$filePart->setHeaders( $this->headers->getCaseSensitiveArray() );
ezcMailPartParser::parsePartHeaders( $this->headers, $filePart );
switch ( strtolower( $this->mainType ) )
{
case 'image':
$filePart->contentType = ezcMailFile::CONTENT_TYPE_IMAGE;
break;
case 'audio':
$filePart->contentType = ezcMailFile::CONTENT_TYPE_AUDIO;
break;
case 'video':
$filePart->contentType = ezcMailFile::CONTENT_TYPE_VIDEO;
break;
case 'application':
$filePart->contentType = ezcMailFile::CONTENT_TYPE_APPLICATION;
break;
}
// set mime type
$filePart->mimeType = $this->subType;
// set inline disposition mode if set.
$matches = array();
if ( preg_match( '/^\s*inline;?/i',
$this->headers['Content-Disposition'], $matches ) )
{
$filePart->dispositionType = ezcMailFile::DISPLAY_INLINE;
}
if ( preg_match( '/^\s*attachment;?/i',
$this->headers['Content-Disposition'], $matches ) )
{
$filePart->dispositionType = ezcMailFile::DISPLAY_ATTACHMENT;
}
$filePart->size = filesize( $this->fileName );
return $filePart;
}
}
?>

View file

@ -0,0 +1,68 @@
<?php
/**
* File containing the ezcMailMultipartAlternativeParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses multipart/mixed mail parts.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailMultipartAlternativeParser extends ezcMailMultipartParser
{
/**
* Holds the ezcMailMultipartAlternative part corresponding to the data parsed with this parser.
*
* @var ezcMailMultipartAlternative
*/
private $part = null;
/**
* Constructs a new ezcMailMultipartAlternativeParser.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
parent::__construct( $headers );
$this->part = new ezcMailMultipartAlternative();
}
/**
* Adds the part $part to the list of multipart messages.
*
* This method is called automatically by ezcMailMultipartParser
* each time a part is parsed.
*
* @param ezcMailPart $part
*/
public function partDone( ezcMailPart $part )
{
$this->part->appendPart( $part );
}
/**
* Returns the parts parsed for this multipart.
*
* @return ezcMailMultipartAlternative
*/
public function finishMultipart()
{
$size = 0;
foreach ( $this->part->getParts() as $part )
{
$size += $part->size;
}
$this->part->size = $size;
return $this->part;
}
}
?>

View file

@ -0,0 +1,68 @@
<?php
/**
* File containing the ezcMailMultipartDigestParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses multipart/digest mail parts.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailMultipartDigestParser extends ezcMailMultipartParser
{
/**
* Holds the ezcMailMultipartDigest part corresponding to the data parsed with this parser.
*
* @var ezcMailMultipartDigest
*/
private $part = null;
/**
* Constructs a new ezcMailMultipartDigestParser.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
parent::__construct( $headers );
$this->part = new ezcMailMultipartDigest();
}
/**
* Adds the part $part to the list of multipart messages.
*
* This method is called automatically by ezcMailMultipartParser
* each time a part is parsed.
*
* @param ezcMailPart $part
*/
public function partDone( ezcMailPart $part )
{
$this->part->appendPart( $part );
}
/**
* Returns the parts parsed for this multipart.
*
* @return ezcMailMultipartDigest
*/
public function finishMultipart()
{
$size = 0;
foreach ( $this->part->getParts() as $part )
{
$size += $part->size;
}
$this->part->size = $size;
return $this->part;
}
}
?>

View file

@ -0,0 +1,67 @@
<?php
/**
* File containing the ezcMailMultipartMixedParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses multipart/mixed mail parts.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailMultipartMixedParser extends ezcMailMultipartParser
{
/**
* Holds the ezcMailMultipartMixed part corresponding to the data parsed with this parser.
*
* @var ezcMailMultipartMixed
*/
private $part = null;
/**
* Constructs a new ezcMailMultipartMixedParser.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
parent::__construct( $headers );
$this->part = new ezcMailMultipartMixed();
}
/**
* Adds the part $part to the list of multipart messages.
*
* This method is called automatically by ezcMailMultipartParser
* each time a part is parsed.
*
* @param ezcMailPart $part
*/
public function partDone( ezcMailPart $part )
{
$this->part->appendPart( $part );
}
/**
* Returns the parts parsed for this multipart.
*
* @return ezcMailMultipartMixed
*/
public function finishMultipart()
{
$size = 0;
foreach ( $this->part->getParts() as $part )
{
$size += $part->size;
}
$this->part->size = $size;
return $this->part;
}
}
?>

View file

@ -0,0 +1,230 @@
<?php
/**
* File containing the ezcMailMultipartParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Base class for Multipart parsers.
*
* @package Mail
* @version //autogen//
* @access private
*/
abstract class ezcMailMultipartParser extends ezcMailPartParser
{
/**
* The boundary separator string.
*
* @var string
*/
private $boundary = null;
/**
* The headers for the multipart.
*
* @var ezcMailHeadersHolder
*/
protected $headers = null;
/**
* The headers for the current subpart.
*
* @var ezcMailHeadersHolder
*/
private $currentPartHeaders = null;
/**
* The current part.
*
* @var ezcMailPartParser
*/
private $currentPartParser = null;
/**
* This state is used prior to hitting the first part.
*/
const PARSE_STATE_PRE_FIRST = 1;
/**
* This state is used when the parser is parsing headers.
*/
const PARSE_STATE_HEADERS = 2;
/**
* This state is used when the parser is parsing the body.
*/
const PARSE_STATE_BODY = 3;
/**
* This state is set after the last of the parts is closed.
*/
const PARSE_STATE_POST_LAST = 4;
/**
* Stores the state of the parser.
*
* @var int
*/
private $parserState = self::PARSE_STATE_PRE_FIRST;
/**
* Constructs a new Multipart parser.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
$this->headers = $headers;
// get the boundary
preg_match( '/\s*boundary="?([^;"]*);?/i',
$this->headers['Content-Type'],
$parameters );
if ( count( $parameters ) > 0 )
{
$this->boundary = trim( $parameters[1], '"' );
}
else
{
// no boundary?!? Houston, we have a problem.
// todo: try to detect the boundary by scanning for --lines
}
}
/**
* Parses a multipart body.
*
* @throws ezcBaseFileNotFoundException
* if a neccessary temporary file could not be opened.
* @param string $origLine
*/
public function parseBody( $origLine )
{
if ( $this->parserState == self::PARSE_STATE_POST_LAST )
{
return;
}
$line = rtrim( $origLine, "\r\n" );
// check if we hit any of the boundaries
$newPart = false;
$endOfMultipart = false;
if ( strlen( $line ) > 0 && $line[0] == "-" )
{
if ( strcmp( trim( $line ), '--' . $this->boundary ) === 0 )
{
$newPart = true;
}
else if ( strcmp( trim( $line ), '--' . $this->boundary . '--' ) === 0 )
{
$endOfMultipart = true;
}
}
// actions to do when starting or finishing a part
if ( $newPart || $endOfMultipart )
{
if ( $this->parserState != self::PARSE_STATE_BODY )
{
// something is b0rked, we got a new separator before getting a body
// we'll skip this part and continue to the next
$this->currentPartParser = null;
$this->currentPartHeaders = new ezcMailHeadersHolder();
$this->parserState = $newPart ? self::PARSE_STATE_HEADERS : self::PARSE_STATE_POST_LAST;
}
else
{
// complete the work on the current part if there was any
if ( $this->currentPartParser !== null )
{
$part = $this->currentPartParser->finish();
if ( $part !== null ) // parsing failed
{
$this->partDone( $part );
}
}
// prepare for a new part if any
$this->currentPartParser = null;
$this->parserState =self::PARSE_STATE_POST_LAST;
if ( $newPart )
{
$this->parserState = self::PARSE_STATE_HEADERS;
$this->currentPartHeaders = new ezcMailHeadersHolder();
}
}
}
// normal data, pass to headers or current body
else
{
if ( $this->parserState == self::PARSE_STATE_HEADERS && $line == '' )
{
$this->currentPartParser = self::createPartParserForHeaders( $this->currentPartHeaders );
$this->parserState = self::PARSE_STATE_BODY;
}
else if ( $this->parserState == self::PARSE_STATE_HEADERS )
{
$this->parseHeader( $line, $this->currentPartHeaders );
}
else if ( $this->parserState == self::PARSE_STATE_BODY )
{
if ( $this->currentPartParser ) // we may have none if the part type was unknown
{
// send body data to the part
$this->currentPartParser->parseBody( $origLine );
}
}
// we are done parsing the multipart, ignore anything else pushed to us.
}
}
/**
* Completes the parsing of the multipart and returns the corresponding part.
*
* This method should not be overriden. Use finishMultipart() instead.
*
* @return ezcMailMultipart
*/
public function finish()
{
if ( $this->parserState != self::PARSE_STATE_POST_LAST )
{
// this should never happen
// let's give the last parser a chance to clean up after himself
if ( $this->currentPartParser !== null )
{
$part = $this->currentPartParser->finish();
$this->partDone( $part );
$this->currentPartParser = null;
}
}
$multipart = $this->finishMultipart();
ezcMailPartParser::parsePartHeaders( $this->headers, $multipart );
$multipart->boundary = $this->boundary;
return $multipart;
}
/**
* This function will be called every time a part has been parsed.
*
* Implementors should put the part into the correct multitype part.
* @param ezcMailPart $part
*/
abstract public function partDone( ezcMailPart $part );
/**
* Returns the multipart part corresponding to the parsed object.
*
* This method is called by finish() when all parts have been parsed.
*
* @return ezcMailMultipart
*/
abstract public function finishMultipart();
}
?>

View file

@ -0,0 +1,78 @@
<?php
/**
* File containing the ezcMailMultipartRelatedParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses multipart/related mail parts.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailMultipartRelatedParser extends ezcMailMultipartParser
{
/**
* Holds the ezcMailMultipartRelated part corresponding to the data parsed with this parser.
*
* @var ezcMailMultipartRelated
*/
private $part = null;
/**
* Constructs a new ezcMailMultipartRelatedParser.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
parent::__construct( $headers );
$this->part = new ezcMailMultipartRelated();
}
/**
* Adds the part $part to the list of multipart messages.
*
* This method is called automatically by ezcMailMultipartParser
* each time a part is parsed.
*
* @param ezcMailPart $part
*/
public function partDone( ezcMailPart $part )
{
// TODO: support Content-Type: start= as specified by RFC 2387
if ( !$this->part->getMainPart() )
{
$this->part->setMainPart( $part );
return;
}
$this->part->addRelatedPart( $part );
}
/**
* Returns the parts parsed for this multipart.
*
* @return ezcMailMultipartRelated
*/
public function finishMultipart()
{
$size = 0;
if ( $this->part->getMainPart() )
{
$size = $this->part->getMainPart()->size;
}
foreach ( $this->part->getRelatedParts() as $part )
{
$size += $part->size;
}
$this->part->size = $size;
return $this->part;
}
}
?>

View file

@ -0,0 +1,94 @@
<?php
/**
* File containing the ezcMailMultipartReportParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses multipart/report mail parts.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailMultipartReportParser extends ezcMailMultipartParser
{
/**
* Holds the ezcMailMultipartReport part corresponding to the data parsed with this parser.
*
* @var ezcMailMultipartReport
*/
private $report;
/**
* Holds the mail parts which will be part of the returned multipart report.
*
* @var array(ezcMailPart)
*/
private $parts;
/**
* Constructs a new ezcMailMultipartReportParser.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
parent::__construct( $headers );
$this->report = new ezcMailMultipartReport();
$this->parts = array();
preg_match( '/\s*report-type="?([^;"]*);?/i',
$this->headers['Content-Type'],
$parameters );
if ( count( $parameters ) > 0 )
{
$this->report->reportType = trim( $parameters[1], '"' );
}
}
/**
* Adds the part $part to the list of multipart messages.
*
* This method is called automatically by ezcMailMultipartParser
* each time a part is parsed.
*
* @param ezcMailPart $part
*/
public function partDone( ezcMailPart $part )
{
$this->parts[] = $part;
}
/**
* Returns the parts parsed for this multipart.
*
* @return ezcMailMultipartReport
*/
public function finishMultipart()
{
if ( isset( $this->parts[0] ) )
{
$this->report->setReadablePart( $this->parts[0] );
}
if ( isset( $this->parts[1] ) )
{
$this->report->setMachinePart( $this->parts[1] );
}
if ( isset( $this->parts[2] ) )
{
$this->report->setOriginalPart( $this->parts[2] );
}
$size = 0;
foreach ( $this->report->getParts() as $part )
{
$size += $part->size;
}
$this->report->size = $size;
return $this->report;
}
}
?>

View file

@ -0,0 +1,82 @@
<?php
/**
* File containing the ezcMailRfc822DigestParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses RFC822 messages.
*
* Note that this class does not parse RFC822 digest messages containing of an extra header block.
* Use the RFC822DigestParser to these.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailRfc822DigestParser extends ezcMailPartParser
{
/**
* Holds the headers for this part.
*
* @var ezcMailHeadersHolder
*/
private $headers = null;
/**
* Holds the digested message parser.
*
* @var ezcMailPartParser
*/
private $mailParser = null;
/**
* Holds the size of the digest.
*
* @var int
*/
private $size;
/**
* Constructs a new digest parser with the headers $headers.
*
* @param ezcMailHeadersHolder $headers
*/
public function __construct( ezcMailHeadersHolder $headers )
{
$this->headers = $headers;
$this->mailParser = new ezcMailRfc822Parser();
$this->size = 0;
}
/**
* Parses each line of the digest body.
*
* Every line is part of the digested mail. It is sent directly to the mail parser.
*
* @param string $line
*/
public function parseBody( $line )
{
$this->mailParser->parseBody( $line );
$this->size += strlen( $line );
}
/**
* Returns a ezcMailRfc822Digest with the digested mail in it.
*
* @return ezcMailRfc822Digest
*/
public function finish()
{
$digest = new ezcMailRfc822Digest( $this->mailParser->finish() );
ezcMailPartParser::parsePartHeaders( $this->headers, $digest );
$digest->size = $this->size;
return $digest;
}
}
?>

View file

@ -0,0 +1,165 @@
<?php
/**
* File containing the ezcMailRfc822Parser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses RFC822 messages.
*
* Note that this class does not parse RFC822 digest messages containing an extra header block.
* Use the RFC822DigestParser to these.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailRfc822Parser extends ezcMailPartParser
{
/**
* Holds the headers parsed.
*
* @var ezcMailHeadersHolder
*/
private $headers = null;
/**
* This state is used when the parser is parsing headers.
*/
const PARSE_STATE_HEADERS = 1;
/**
* This state is used when the parser is parsing the body.
*/
const PARSE_STATE_BODY = 2;
/**
* Stores the state of the parser.
*
* @var int
*/
private $parserState = self::PARSE_STATE_HEADERS;
/**
* The parser of the body.
*
* This will be set after the headers have been parsed.
*
* @var ezcMailPartParser
*/
private $bodyParser = null;
/**
* Constructs a new ezcMailRfc822Parser.
*/
public function __construct()
{
$this->headers = new ezcMailHeadersHolder();
}
/**
* Parses the body of an rfc 2822 message.
*
* @throws ezcBaseFileNotFoundException
* if a neccessary temporary file could not be openened.
* @param string $origLine
*/
public function parseBody( $origLine )
{
$line = rtrim( $origLine, "\r\n" );
if ( $this->parserState == self::PARSE_STATE_HEADERS && $line == '' )
{
$this->parserState = self::PARSE_STATE_BODY;
// clean up headers for the part
// the rest of the headers should be set on the mail object.
$headers = new ezcMailHeadersHolder();
$headers['Content-Type'] = $this->headers['Content-Type'];
if ( isset( $this->headers['Content-Transfer-Encoding'] ) )
{
$headers['Content-Transfer-Encoding'] = $this->headers['Content-Transfer-Encoding'];
}
if ( isset( $this->headers['Content-Disposition'] ) )
{
$headers['Content-Disposition'] = $this->headers['Content-Disposition'];
}
// get the correct body type
$this->bodyParser = self::createPartParserForHeaders( $headers );
}
else if ( $this->parserState == self::PARSE_STATE_HEADERS )
{
$this->parseHeader( $line, $this->headers );
}
else // we are parsing headers
{
$this->bodyParser->parseBody( $origLine );
}
}
/**
* Returns an ezcMail corresponding to the parsed message.
* You can specify an alternate class using the $class parameter, if you
* extended ezcMail.
*
* @param string $class Class to instanciate instead of ezcMail.
* @return ezcMail
*/
public function finish( $class = "ezcMail" )
{
$mail = new $class();
$mail->setHeaders( $this->headers->getCaseSensitiveArray() );
ezcMailPartParser::parsePartHeaders( $this->headers, $mail );
// from
if ( isset( $this->headers['From'] ) )
{
$mail->from = ezcMailTools::parseEmailAddress( $this->headers['From'] );
}
// to
if ( isset( $this->headers['To'] ) )
{
$mail->to = ezcMailTools::parseEmailAddresses( $this->headers['To'] );
}
// cc
if ( isset( $this->headers['Cc'] ) )
{
$mail->cc = ezcMailTools::parseEmailAddresses( $this->headers['Cc'] );
}
// bcc
if ( isset( $this->headers['Bcc'] ) )
{
$mail->bcc = ezcMailTools::parseEmailAddresses( $this->headers['Bcc'] );
}
// subject
if ( isset( $this->headers['Subject'] ) )
{
$mail->subject = ezcMailTools::mimeDecode( $this->headers['Subject'] );
$mail->subjectCharset = 'utf-8';
}
// message ID
if ( isset( $this->headers['Message-Id'] ) )
{
$mail->messageID = $this->headers['Message-Id'];
}
// Return-Path
if ( isset( $this->headers['Return-Path'] ) )
{
$mail->returnPath = ezcMailTools::parseEmailAddress( $this->headers['Return-Path'] );
}
if ( $this->bodyParser !== null )
{
$mail->body = $this->bodyParser->finish();
}
return $mail;
}
}
?>

View file

@ -0,0 +1,111 @@
<?php
/**
* File containing the ezcMailTextParser class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Parses mail parts of type "text".
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailTextParser extends ezcMailPartParser
{
/**
* Stores the parsed text of this part.
*
* @var string $text
*/
private $text = null;
/**
* Holds the headers of this text part.
*
* @var ezcMailHeadersHolder
*/
private $headers = null;
/**
* Holds the subtype of the parsed part.
*
* @var string
*/
private $subType = null;
/**
* Constructs a new ezcMailTextParser of the subtype $subType and
* additional headers $headers.
*
* @param string $subType
* @param ezcMailHeadersHolder $headers
*/
public function __construct( $subType, ezcMailHeadersHolder $headers )
{
$this->subType = $subType;
$this->headers = $headers;
}
/**
* Adds each line to the body of the text part.
*
* @param string $line
*/
public function parseBody( $line )
{
$line = rtrim( $line, "\r\n" );
if ( $this->text === null )
{
$this->text = $line;
}
else
{
$this->text .= "\n" . $line;
}
}
/**
* Returns the ezcMailText part corresponding to the parsed message.
*
* @return ezcMailText
*/
public function finish()
{
$charset = "us-ascii"; // RFC 2822 default
if ( isset( $this->headers['Content-Type'] ) )
{
preg_match( '/\s*charset\s?=\s?"?([^;"\s]*);?/',
$this->headers['Content-Type'],
$parameters );
if ( count( $parameters ) > 0 )
{
$charset = strtolower( trim( $parameters[1], '"' ) );
}
}
$encoding = strtolower( $this->headers['Content-Transfer-Encoding'] );
if ( $encoding == ezcMail::QUOTED_PRINTABLE )
{
$this->text = quoted_printable_decode( $this->text );
}
else if ( $encoding == ezcMail::BASE64 )
{
$this->text = base64_decode( $this->text );
}
$this->text = ezcMailCharsetConverter::convertToUTF8( $this->text, $charset );
$part = new ezcMailText( $this->text, 'utf-8', ezcMail::EIGHT_BIT, $charset );
$part->subType = $this->subType;
$part->setHeaders( $this->headers->getCaseSensitiveArray() );
ezcMailPartParser::parsePartHeaders( $this->headers, $part );
$part->size = strlen( $this->text );
return $part;
}
}
?>

View file

@ -0,0 +1,192 @@
<?php
/**
* File containing the ezcMailRfc2231Implementation class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @access private
*/
/**
* This class parses header fields that conform to RFC2231.
*
* Headers conforming to this specification are Content-Type and Content-Disposition.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailRfc2231Implementation
{
/**
* Returns the parsed header $header according to RFC 2231.
*
* This method returns the parsed header as a structured array and is
* intended for internal usage. Use parseContentDisposition and
* parseContentType to retrieve the correct header structs directly.
*
* @param string $header
* @return array( 'argument', array( 'paramName' => array( value => string, charset => string,
* language => string ) ) );
*/
public static function parseHeader( $header )
{
$result = array();
// argument
if ( preg_match( '/^\s*([^;]*);?/i', $header, $matches ) )
{
$result[0] = $matches[1];
}
// We must go through all parameters and store this data because
// parameters can be unordered. We will store them in this buffer
// array( paramName => array( array( value => string, encoding ) ) )
$parameterBuffer = array();
// parameters
if ( preg_match_all( '/\s*(\S*?)="?([^;"]*);?/i', $header, $matches, PREG_SET_ORDER ) )
{
foreach ( $matches as $parameter )
{
// if normal parameter, simply add it
if ( !preg_match( '/([^\*]+)\*(\d+)?(\*)?/', $parameter[1], $metaData ) )
{
$result[1][$parameter[1]] = array( 'value' => $parameter[2] );
}
else // coded and/or folded
{
// metaData [1] holds the param name
// metaData [2] holds the count or is not set in case of charset only
// metaData [3] holds '*' if there is charset in addition to folding
if ( isset( $metaData[2] ) ) // we have folding
{
$parameterBuffer[$metaData[1]][$metaData[2]]['value'] = $parameter[2];
$parameterBuffer[$metaData[1]][$metaData[2]]['encoding'] =
isset( $metaData[3] ) ? true : false;;
}
else
{
$parameterBuffer[$metaData[1]][0]['value'] = $parameter[2];
$parameterBuffer[$metaData[1]][0]['encoding'] = true;
}
}
}
// whohooo... we have all the parameters nicely sorted.
// Now we must go through them all and convert them into the end result
foreach ( $parameterBuffer as $paramName => $parts )
{
// fetch language and encoding if we have it
// syntax: '[charset]'[language]'encoded_string
$language = null;
$charset = null;
if ( $parts[0]['encoding'] == true )
{
preg_match( "/(\S*)'(\S*)'(.*)/", $parts[0]['value'], $matches );
$charset = $matches[1];
$language = $matches[2];
$parts[0]['value'] = urldecode( $matches[3] ); // rewrite value: todo: decoding
$result[1][$paramName] = array( 'value' => $parts[0]['value'] );
}
$result[1][$paramName] = array( 'value' => $parts[0]['value'] );
if ( strlen( $charset ) > 0 )
{
$result[1][$paramName]['charset'] = $charset;
}
if ( strlen( $language ) > 0 )
{
$result[1][$paramName]['language'] = $language;
}
if ( count( $parts > 1 ) )
{
for ( $i = 1; $i < count( $parts ); $i++ )
{
$result[1][$paramName]['value'] .= $parts[$i]['encoding'] ?
urldecode( $parts[$i]['value'] ) : $parts[$i]['value'];
}
}
}
}
return $result;
}
/**
* Returns the a ezcMailContentDispositionHeader for the parsed $header.
*
* If $cd is provided this object will be used to fill in the blanks. This function
* will not clear out any old values in the object.
*
* @param string $header
* @param ezcMailContentDispositionHeader $cd
* @return ezcMailContentDispositionHeader
*/
public static function parseContentDisposition( $header, ezcMailContentDispositionHeader $cd = null )
{
if ( $cd === null )
{
$cd = new ezcMailContentDispositionHeader();
}
$parsedHeader = self::parseHeader( $header );
$cd->disposition = $parsedHeader[0];
if ( isset( $parsedHeader[1] ) )
{
foreach ( $parsedHeader[1] as $paramName => $data )
{
switch ( $paramName )
{
case 'filename':
$cd->fileName = $data['value'];
$cd->displayFileName = trim( $data['value'], '"' );
if ( isset( $data['charset'] ) )
{
$cd->fileNameCharSet = $data['charset'];
$cd->displayFileName = ezcMailCharsetConverter::convertToUTF8Iconv( $cd->displayFileName, $cd->fileNameCharSet );
}
// Work around for bogus email clients that think
// it's allowed to use mime-encoding for filenames.
// It isn't, see RFC 2184, and issue #13038.
else if ( preg_match( '@^=\?[^?]+\?[QqBb]\?@', $cd->displayFileName ) )
{
$cd->displayFileName = ezcMailTools::mimeDecode( $cd->displayFileName );
}
if ( isset( $data['language'] ) )
{
$cd->fileNameLanguage = $data['language'];
}
break;
case 'creation-date':
$cd->creationDate = $data['value'];
break;
case 'modification-date':
$cd->modificationDate = $data['value'];
break;
case 'read-date':
$cd->readDate = $data['value'];
break;
case 'size':
$cd->size = $data['value'];
break;
default:
$cd->additionalParameters[$paramName] = $data['value'];
if ( isset( $data['charset'] ) )
{
$cd->additionalParametersMetaData[$paramName]['charSet'] = $data['charset'];
}
if ( isset( $data['language'] ) )
{
$cd->additionalParametersMetaData[$paramName]['language'] = $data['language'];
}
break;
}
}
}
return $cd;
}
}
?>

View file

@ -0,0 +1,117 @@
<?php
/**
* File containing the ezcMailParserShutdownHandler class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailParserShutDownHandler removes temporary files
* and directories when PHP shuts down.
*
* Example:
* <code>
* ezcMailParserShutdownHandler::registerForRemoval( "/tmp/file.txt" );
* </code>
*
* The code above will result in file.txt being removed from the system
* (if it still exists) when PHP shuts down.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailParserShutdownHandler
{
/**
* Holds if the handler is registered or not.
*
* @var boolean
*/
private static $isRegistered = false;
/**
* Holds the array of directories that are marked for removal
* when PHP shuts down.
*
* @var array(string)
*/
private static $directories = array();
/**
* Registers the directory $dir for removal when PHP shuts down.
*
* The directory and all of its contents will be removed recursively.
*
* @param string $dir
*/
public static function registerForRemoval( $dir )
{
if ( self::$isRegistered === false )
{
register_shutdown_function( array( "ezcMailParserShutdownHandler", "shutdownCallback" ) );
self::$isRegistered = true;
}
self::$directories[] = $dir;
}
/**
* Recursively deletes all registered folders and any contents of the registered
* directories.
*
* Files or directories that can't be deleted are left without warning.
*
* @return void
*/
public static function shutdownCallback()
{
foreach ( self::$directories as $directory )
{
self::remove( $directory );
}
}
/**
* Recursively removes a directory and its contents or a file.
*
* Returns true on success and false on error.
*
* @param string $itemName
* @return bool
*/
public static function remove( $itemName )
{
$returnVar = true;
if ( !is_dir( $itemName ) && file_exists( $itemName ) )
{
unlink( $itemName );
return true;
}
if ( !file_exists( $itemName ) )
{
return true;
}
$dir = dir( $itemName );
$item = $dir->read();
while ( $item !== false )
{
if ( $item != '.' && $item != '..' )
{
self::remove( $dir->path . DIRECTORY_SEPARATOR . $item );
$returnVar = false;
}
$item = $dir->read();
}
$dir->close();
$returnVar = rmdir( $itemName ) && $returnVar ? true : false; // if rmdir succeeds and everything else succeeded
return $returnVar;
}
}
?>

View file

@ -0,0 +1,178 @@
<?php
/**
* File containing the ezcMailDeliveryStatus class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part used for sending delivery status message.
*
* Multipart/Report: RFC 3462 {@link http://tools.ietf.org/html/rfc3462}
* Delivery Status Notifications: RFC 3464 {@link http://tools.ietf.org/html/rfc3464}
*
* This mail part consists of only headers. The headers are organized into section.
* There is a per-message section ($message), and several per-recipient sections ($recipients).
*
* To access the headers of this part, look at the following example:
* <code>
* // $delivery is an object of type ezcMailDeliveryStatus
* $reportingMta = $delivery->message["Reporting-MTA"];
* $date = $delivery->message["Arrival-Date"];
* // get the status received from the first recipient
* $status1 = $delivery->recipients[0]["Status"];
* // get the status received from the second recipient
* $status2 = $delivery->recipients[1]["Status"];
* </code>
*
* @property ezcMailHeadersHolder $message
* Holds the per-message headers of the delivery-status message.
* @property ArrayObject(ezcMailHeadersHolder) $recipients
* Holds the recipients of the delivery-status message.
*
* @package Mail
* @version //autogen//
*/
class ezcMailDeliveryStatus extends ezcMailPart
{
/**
* Constructs a new DeliveryStatus part.
*/
public function __construct()
{
$this->message = new ezcMailHeadersHolder();
$this->recipients = new ArrayObject();
parent::__construct();
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'message':
case 'recipients':
$this->properties[$name] = $value;
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Returns the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'message':
case 'recipients':
return $this->properties[$name];
break;
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'message':
case 'recipients':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Returns the headers set for this part as a RFC822 compliant string.
*
* This method does not add the required two lines of space
* to separate the headers from the body of the part.
*
* @see setHeader()
* @return string
*/
public function generateHeaders()
{
$this->setHeader( "Content-Type", "message/delivery-status" );
return parent::generateHeaders();
}
/**
* Returns the generated text body of this part as a string.
*
* @return string
*/
public function generateBody()
{
$result = $this->addHeadersSection( $this->message ) . ezcMailTools::lineBreak();
for ( $i = 0; $i < count( $this->recipients ); $i++ )
{
$result .= $this->addHeadersSection( $this->recipients[$i] ) . ezcMailTools::lineBreak();
}
return $result;
}
/**
* Returns the generated text for a section of the delivery-status part.
*
* @param ezcMailHeadersHolder $headers
* @return string
*/
private function addHeadersSection( ezcMailHeadersHolder $headers )
{
$result = "";
foreach ( $headers->getCaseSensitiveArray() as $header => $value )
{
$result .= $header . ": " . $value . ezcMailTools::lineBreak();
}
return $result;
}
/**
* Adds a new recipient to this delivery-status message and returns the index
* of the last added recipient.
*
* @return int
*/
public function createRecipient()
{
$result = count( $this->recipients );
$this->recipients[$result] = new ezcMailHeadersHolder();
return $result;
}
}
?>

View file

@ -0,0 +1,235 @@
<?php
/**
* File containing the ezcMailFilePart class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part for all forms of binary data.
*
* @todo MimeType recognition
*
* @property string $fileName
* The name of the file which is to be attached to the email.
* @property string $mimeType
* The mimetype of the file.
* @property string $contentType
* The content type of the file.
* Possible values are: CONTENT_TYPE_IMAGE, CONTENT_TYPE_VIDEO and
* CONTENT_TYPE_APPLICATION.
* @property string $dispositionType
* If the file should be shown inline in the mail or as an
* attachment. Possible values are: DISPLAY_ATTACHMENT and
* DISPLAY_INLINE.
* @property int $contentId
* The ID of this part. Used for internal links within an email.
* Setting this also sets the header Content-ID.
*
* @package Mail
* @version //autogen//
*/
abstract class ezcMailFilePart extends ezcMailPart
{
/**
* Image content type. Use this if the contents of the file is an image.
*/
const CONTENT_TYPE_IMAGE = "image";
/**
* Video content type. Use this if the contents of the file is a video.
*/
const CONTENT_TYPE_VIDEO = "video";
/**
* Audio content type. Use this if the contents of the file is an audio.
*/
const CONTENT_TYPE_AUDIO = "audio";
/**
* Application content type. Use this if the file non of the other
* content types match.
*/
const CONTENT_TYPE_APPLICATION = "application";
/**
* Use DISPLAY_ATTACHMENT if you want the file to be displayed as an
* attachment to the recipients of the mail.
*/
const DISPLAY_ATTACHMENT = "attachment";
/**
* Use DISPLAY_INLINE if you want the file to be displayed inline in the
* mail to the recipients.
*/
const DISPLAY_INLINE = "inline";
/**
* Constructs a new attachment with $fileName.
*
* @param string $fileName
*/
public function __construct( $fileName )
{
parent::__construct();
// initialize properties that may be touched automatically
// this is to avoid notices
$this->properties['contentType'] = null;
$this->properties['mimeType'] = null;
$this->properties['dispositionType'] = null;
$this->properties['contentId'] = null;
$this->fileName = $fileName;
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'fileName':
$this->properties['fileName'] = $value;
break;
case 'mimeType':
$this->properties['mimeType'] = $value;
break;
case 'contentType':
$this->properties['contentType'] = $value;
break;
case 'dispositionType':
$this->properties['dispositionType'] = $value;
break;
case 'contentId':
$this->properties['contentId'] = $value;
$this->setHeader( 'Content-ID', '<' . $value . '>' );
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Returns the value of property $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'fileName':
case 'mimeType':
case 'contentType':
case 'dispositionType':
case 'contentId':
return $this->properties[$name];
break;
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'fileName':
case 'mimeType':
case 'contentType':
case 'dispositionType':
case 'contentId':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Sets the Content-Type header.
*
* Based on the contentType, mimeType and fileName.
*/
private function setHeaderContentType()
{
$fileName = basename( $this->fileName );
if ( $this->contentDisposition !== null && $this->contentDisposition->fileName !== null )
{
$fileName = $this->contentDisposition->fileName;
}
$this->setHeader( 'Content-Type',
$this->contentType . '/' . $this->mimeType . '; ' . 'name="' . $fileName . '"' );
}
/**
* Sets the Content-Disposition header based on the properties $dispositionType and $fileName.
*
* Does not set the fileNameCharSet and fileNameLanguage properties of the
* Content-Disposition header. For this purpose set directly
* $this->contentDisposition with an object of class ezcMailContentDispositionHeader.
*/
private function setHeaderContentDisposition()
{
if ( !isset( $this->dispositionType ) )
{
$this->dispositionType = self::DISPLAY_ATTACHMENT;
}
if ( $this->contentDisposition == null )
{
$this->contentDisposition = new ezcMailContentDispositionHeader();
// modified for issue #14025: set the file name and disposition
// only if the contentDisposition was null (to not overwrite
// the value set by the user)
$this->contentDisposition->disposition = $this->dispositionType;
$this->contentDisposition->fileName = basename( $this->fileName );
}
}
/**
* Override of the generate() method from ezcMailPart. Used to set headers before
* generating the part.
*
* @return string
*/
public function generate()
{
$this->setHeaderContentType();
$this->setHeader( 'Content-Transfer-Encoding', 'base64' );
$this->setHeaderContentDisposition();
return parent::generate();
}
}
?>

View file

@ -0,0 +1,140 @@
<?php
/**
* File containing the ezcMailFile class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part for binary data from the file system.
*
* @package Mail
* @version //autogen//
*/
class ezcMailFile extends ezcMailFilePart
{
/**
* Constructs a new attachment with $fileName.
*
* If the $mimeType and $contentType are not specified they are extracted
* with the fileinfo extension if it is available, otherwise they are set
* to application/octet-stream.
*
* @param string $fileName
* @param string $contentType
* @param string $mimeType
*/
public function __construct( $fileName, $contentType = null, $mimeType = null )
{
parent::__construct( $fileName );
if ( $contentType != null && $mimeType != null )
{
$this->contentType = $contentType;
$this->mimeType = $mimeType;
}
elseif ( ezcBaseFeatures::hasExtensionSupport( 'fileinfo' ) )
{
// get mime and content type
$fileInfo = finfo_open( FILEINFO_MIME );
$mimeParts = finfo_file( $fileInfo, $fileName );
if ( $mimeParts !== false && strpos( $mimeParts, '/' ) !== false )
{
list( $this->contentType, $this->mimeType ) = explode( '/', $mimeParts );
}
else
{
// default to mimetype application/octet-stream
$this->contentType = self::CONTENT_TYPE_APPLICATION;
$this->mimeType = "octet-stream";
}
finfo_close( $fileInfo );
}
else
{
// default to mimetype application/octet-stream
$this->contentType = self::CONTENT_TYPE_APPLICATION;
$this->mimeType = "octet-stream";
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @throws ezcBaseFileNotFoundException
* when setting the property with an invalid filename.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'fileName':
if ( is_readable( $value ) )
{
parent::__set( $name, $value );
}
else
{
throw new ezcBaseFileNotFoundException( $value );
}
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Returns the value of property $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
default:
return parent::__isset( $name );
}
}
/**
* Returns the contents of the file with the correct encoding.
*
* @return string
*/
public function generateBody()
{
return chunk_split( base64_encode( file_get_contents( $this->fileName ) ), 76, ezcMailTools::lineBreak() );
}
}
?>

View file

@ -0,0 +1,129 @@
<?php
/**
* File containing the ezcMailStreamFile class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part for data in a stream.
*
* @property string $stream
* The stream object to be read and added as an attachment. The
* mimeType and contentType are set in the constructor or if not
* specified they are extracted with the fileinfo extension if it
* is available, otherwise they are set to application/octet-stream.
*
* @package Mail
* @version //autogen//
*/
class ezcMailStreamFile extends ezcMailFilePart
{
/**
* Constructs a new attachment with $fileName and $stream.
*
* If the $mimeType and $contentType are not specified they are set
* to application/octet-stream.
*
* @param string $fileName
* @param resource $stream
* @param string $contentType
* @param string $mimeType
*/
public function __construct( $fileName, $stream, $contentType = null, $mimeType = null )
{
parent::__construct( $fileName );
$this->stream = $stream;
if ( $contentType != null && $mimeType != null )
{
$this->contentType = $contentType;
$this->mimeType = $mimeType;
}
else
{
// default to mimetype application/octet-stream
$this->contentType = self::CONTENT_TYPE_APPLICATION;
$this->mimeType = "octet-stream";
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'stream':
$this->properties[$name] = $value;
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Returns the value of property $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'stream':
return $this->properties[$name];
break;
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'stream':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Returns the contents of the file with the correct encoding.
*
* The stream might become unusable after this if it doesn't support seek.
*
* @return string
*/
public function generateBody()
{
$contents = stream_get_contents( $this->stream );
return chunk_split( base64_encode( $contents ), 76, ezcMailTools::lineBreak() );
}
}
?>

View file

@ -0,0 +1,144 @@
<?php
/**
* File containing the ezcMailVirtualFile class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part for binary data in memory.
*
* @property string $contents
* The contents to be added as an attachment. The mimeType and
* contentType are set in the constructor or if not specified they
* are extracted with the fileinfo extension if it is available,
* otherwise they are set to application/octet-stream.
*
* @package Mail
* @version //autogen//
*/
class ezcMailVirtualFile extends ezcMailFilePart
{
/**
* Constructs a new attachment with $fileName and $contents.
*
* If the $mimeType and $contentType are not specified they are extracted
* with the fileinfo extension if it is available, otherwise they are set
* to application/octet-stream.
*
* @param string $fileName
* @param string $contents
* @param string $contentType
* @param string $mimeType
*/
public function __construct( $fileName, $contents, $contentType = null, $mimeType = null )
{
parent::__construct( $fileName );
$this->contents = $contents;
if ( $contentType != null && $mimeType != null )
{
$this->contentType = $contentType;
$this->mimeType = $mimeType;
}
elseif ( ezcBaseFeatures::hasExtensionSupport( 'fileinfo' ) )
{
// get mime and content type
$fileInfo = new finfo( FILEINFO_MIME );
$mimeParts = $fileInfo->buffer( $contents );
if ( $mimeParts !== false && strpos( $mimeParts, '/' ) !== false )
{
list( $this->contentType, $this->mimeType ) = explode( '/', $mimeParts );
}
else
{
// default to mimetype application/octet-stream
$this->contentType = self::CONTENT_TYPE_APPLICATION;
$this->mimeType = "octet-stream";
}
}
else
{
// default to mimetype application/octet-stream
$this->contentType = self::CONTENT_TYPE_APPLICATION;
$this->mimeType = "octet-stream";
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'contents':
$this->properties[$name] = $value;
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Returns the value of property $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'contents':
return $this->properties[$name];
break;
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'contents':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Returns the contents of the file with the correct encoding.
*
* @return string
*/
public function generateBody()
{
return chunk_split( base64_encode( $this->contents ), 76, ezcMailTools::lineBreak() );
}
}
?>

View file

@ -0,0 +1,196 @@
<?php
/**
* File containing the ezcMailMultipart class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Abstract base class for all multipart types.
*
* This class provides writing functionality that is common for all multipart
* types. Multiparts will be written to the mail in the order that they are set
* to the $parts variable.
*
* @property string $boundary
* The boundary string to use between parts. This string is
* automatically generated and should only be changed for special
* requirements.
* @property string $noMimeMessage
* Message to display to non-MIME capable email clients. The default
* value is stored in the constant {@link self::DEFAULT_NO_MIME_MESSAGE}.
*
* @package Mail
* @version //autogen//
*/
abstract class ezcMailMultipart extends ezcMailPart
{
/**
* Default message displayed to non-MIME capable email clients.
*/
const DEFAULT_NO_MIME_MESSAGE = "This message is in MIME format. Since your mail reader does not understand\r\nthis format, some or all of this message may not be legible.";
/**
* An array holding the parts of this multipart.
*
* @var array(ezcMailPart)
*/
protected $parts = array();
/**
* The counter is unique between all multipart types and is used to
* generate unique boundary strings.
*
* @var int
*/
private static $counter = 0;
/**
* Constructs a new ezcMailMultipart with the parts $parts.
*
* Subclasses typically accept an arbitrary number of parts in the
* constructor and pass them along using func_get_args().
*
* $parts should be of the format array(array(ezcMailPart)|ezcMailPart)
*
* Subclasses must call this method in the constructor.
* @param array $parts
*/
public function __construct( array $parts )
{
parent::__construct();
$this->noMimeMessage = self::DEFAULT_NO_MIME_MESSAGE;
$this->boundary = $this->generateBoundary();
$this->setHeader( "Content-Type", 'multipart/' . $this->multipartType() . '; '
. 'boundary="' . $this->boundary . '"' );
foreach ( $parts as $part )
{
if ( $part instanceof ezcMailPart )
{
$this->parts[] = $part;
}
elseif ( is_array( $part ) ) // add each and everyone of the parts in the array
{
foreach ( $part as $array_part )
{
if ( $array_part instanceof ezcMailPart )
{
$this->parts[] = $array_part;;
}
}
}
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'boundary':
$this->properties[$name] = $value;
$this->setHeader( 'Content-Type', 'multipart/' . $this->multipartType() . '; ' .
'boundary="' . $this->boundary . '"' );
break;
case 'noMimeMessage':
$this->properties[$name] = $value;
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Returns the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'boundary':
case 'noMimeMessage':
return $this->properties[$name];
break;
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'boundary':
case 'noMimeMessage':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Returns the generated body for all multipart types.
*
* @return string
*/
public function generateBody()
{
$data = $this->noMimeMessage . ezcMailTools::lineBreak();
foreach ( $this->parts as $part )
{
$data .= ezcMailTools::lineBreak() . '--' . $this->boundary . ezcMailTools::lineBreak();
$data .= $part->generate();
}
$data .= ezcMailTools::lineBreak() . '--' . $this->boundary . '--';
return $data;
}
/**
* Returns the type of multipart.
*
* @return string
*/
abstract public function multipartType();
/**
* Returns a unique boundary string.
*
* @return string
*/
protected static function generateBoundary()
{
return date( "YmdGHjs" ) . ':' . getmypid() . ':' . self::$counter++;
}
}
?>

View file

@ -0,0 +1,84 @@
<?php
/**
* File containing the ezcMailMultipartAlternative class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailMultipartAlternative is used to bundle a group of mail parts
* where only one should be shown.
*
* This is useful e.g if you have a text in some fancy format but you also want
* to provide a backup plain text format to make sure everyone can read the
* mail. The alternatives should be added in an order of increasing
* faithfulness to the original content. In general, the best choice is the
* LAST part of a type supported by the recipients mail client.
*
* The following example shows a HTML mail with a plain text backup in case
* the recipients client can't display HTML mail.
* <code>
* $mail = new ezcMail();
* $mail->from = new ezcMailAddress( 'sender@example.com', 'Adrian Ripburger' );
* $mail->addTo( new ezcMailAddress( 'receiver@example.com', 'Maureen Corley' ) );
* $mail->subject = "Example of an HTML email with attachments";
* $plainText = new ezcMailText( "This is the plain text part" );
* $htmlText = new ezcMailText( "<html>This is the HTML part</html>" );
* $htmlText->subType = 'html';
* $mail->body = new ezcMailMultipartAlternative( $plainText, $htmlText );
* </code>
*
* @package Mail
* @version //autogen//
*/
class ezcMailMultipartAlternative extends ezcMailMultipart
{
/**
* Constructs a new ezcMailMultipartAlternative
*
* The constructor accepts an arbitrary number of ezcMailParts or arrays with ezcMailparts.
* Parts are added in the order provided. Parameters of the wrong
* type are ignored.
*
* @param ezcMailPart|array(ezcMailPart) $...
*/
public function __construct()
{
$args = func_get_args();
parent::__construct( $args );
}
/**
* Appends a part to the list of parts.
*
* @param ezcMailPart $part
*/
public function appendPart( ezcMailPart $part )
{
$this->parts[] = $part;
}
/**
* Returns the mail parts associated with this multipart.
*
* @return array(ezcMailPart)
*/
public function getParts()
{
return $this->parts;
}
/**
* Returns "alternative".
*
* @return string
*/
public function multipartType()
{
return "alternative";
}
}
?>

View file

@ -0,0 +1,98 @@
<?php
/**
* File containing the ezcMailMultipartDigest class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* The digest multipart type is used to bundle a list of mail objects.
*
* Each part will be shown in the mail in the order provided. It is not
* necessary to bundle digested mail using a digest object. However, it is
* considered good practice to do so when several digested mail are sent
* together.
*
* @package Mail
* @version //autogen//
*/
class ezcMailMultipartDigest extends ezcMailMultipart
{
/**
* Constructs a new ezcMailMultipartDigest
*
* The constructor accepts an arbitrary number of ezcMail/ezcMailRfc822Digest objects
* or arrays with objects of these types.
*
* Objects of the type ezcMail are wrapped into an ezcMailRfc822Digest object.
*
* Parts are added in the order provided. Parameters of the wrong
* type are ignored.
*
* @param ezcMailRfc822Digest|array(ezcMailRfc822Digest) $...
*/
public function __construct()
{
$args = func_get_args();
parent::__construct( array() );
foreach ( $args as $part )
{
if ( $part instanceof ezcMail )
{
$this->parts[] = new ezcMailRfc822Digest( $part );
}
else if ( $part instanceof ezcMailRfc822Digest )
{
$this->parts[] = $part;
}
else if ( is_array( $part ) ) // add each and everyone of the parts in the array
{
foreach ( $part as $array_part )
{
if ( $array_part instanceof ezcMail )
{
$this->parts[] = new ezcMailRfc822Digest( $array_part );
}
else if ( $array_part instanceof ezcMailRfc822Digest )
{
$this->parts[] = $array_part;
}
}
}
}
}
/**
* Appends a part to the list of parts.
*
* @param ezcMailRfc822Digest $part
*/
public function appendPart( ezcMailRfc822Digest $part )
{
$this->parts[] = $part;
}
/**
* Returns the mail parts associated with this multipart.
*
* @return array(ezcMail)
*/
public function getParts()
{
return $this->parts;
}
/**
* Returns "digest".
*
* @return string
*/
public function multipartType()
{
return "digest";
}
}
?>

View file

@ -0,0 +1,76 @@
<?php
/**
* File containing the ezcMailMultipartMixed class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* The mixed multipart type is used to bundle an ordered list of mail
* parts.
*
* Each part will be shown in the mail in the order provided.
*
* The following example shows how to build a mail with a text part
* and an attachment using ezcMailMultipartMixed.
* <code>
* $mixed = new ezcMailMultipartMixed( new ezcMailTextPart( "Picture of me flying!" ),
* new ezcMailFile( "fly.jpg" ) );
* $mail = new ezcMail();
* $mail->body = $mixed;
* </code>
*
* @package Mail
* @version //autogen//
*/
class ezcMailMultipartMixed extends ezcMailMultipart
{
/**
* Constructs a new ezcMailMultipartMixed
*
* The constructor accepts an arbitrary number of ezcMailParts or arrays with ezcMailparts.
* Parts are added in the order provided. Parameters of the wrong
* type are ignored.
*
* @param ezcMailPart|array(ezcMailPart) $...
*/
public function __construct()
{
$args = func_get_args();
parent::__construct( $args );
}
/**
* Appends a part to the list of parts.
*
* @param ezcMailPart $part
*/
public function appendPart( ezcMailPart $part )
{
$this->parts[] = $part;
}
/**
* Returns the mail parts associated with this multipart.
*
* @return array(ezcMailPart)
*/
public function getParts()
{
return $this->parts;
}
/**
* Returns "mixed".
*
* @return string
*/
public function multipartType()
{
return "mixed";
}
}
?>

View file

@ -0,0 +1,182 @@
<?php
/**
* File containing the ezcMailMultipartRelated class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailMultipartRelated is intended for mail parts consisting of
* several inter-related body parts.
*
* A typical example is an HTML mail with embedded images.
* When you want to refer to a related part you can use content id's
* (cid). Set the 'Content-ID' header of the related part to a valid
* unique url-addr-spec (specified by RFC 822) and refer to it through
* the form cid:unique-string.
*
* Example:
* This example shows how you can use ezcMailMultipartRelated to create an
* HTML mail with an inline image.
* <code>
* $mail = new ezcMail();
* $mail->from = new ezcMailAddress( 'sender@example.com', 'Adrian Ripburger' );
* $mail->addTo( new ezcMailAddress( 'receiver@example.com', 'Maureen Corley' ) );
* $mail->subject = "Example of an HTML email with attachments";
* $htmlText = new ezcMailText( "<html>Image <img src='cid:image@12345' /></html>" );
* $htmlText->subType = 'html';
* $image = new ezcMailFile( "path_to_my_image.jpg" );
* $image->contentId = 'image@12345';
* $mail->body = new ezcMailMultipartRelated( $htmlText, $image );
* </code>
*
* @package Mail
* @version //autogen//
*/
class ezcMailMultipartRelated extends ezcMailMultipart
{
/**
* Constructs a new ezcMailMultipartRelated.
*
* The constructor accepts an arbitrary number of ezcMailParts or arrays with ezcMailparts.
* Parts are added in the order provided and the first part will be recognized
* as the main body. Parameters of the wrong type are ignored.
*
* @param ezcMailPart|array(ezcMailPart) $...
*/
public function __construct()
{
$args = func_get_args();
parent::__construct( $args );
}
/**
* Sets the main part $part of this alternative multipart.
*
* @param ezcMailPart $part
*/
public function setMainPart( ezcMailPart $part )
{
$this->parts[0] = $part;
}
/**
* Adds $part to the list of parts and returns the Content-ID of the part.
*
* @param ezcMailPart $part
* @return string
*/
public function addRelatedPart( ezcMailPart $part )
{
// it doesn't have a Content-ID, we must set one.
$contentId = '';
if ( $part->getHeader( 'Content-ID' ) == '' )
{
if ( $part instanceof ezcMailFile )
{
$part->contentId = ezcMailTools::generateContentId( basename( $part->fileName ) );
}
else
{
$part->setHeader( 'Content-ID', ezcMailTools::generateContentId( 'part' ) );
}
}
$contentId = trim( $part->getHeader( 'Content-ID' ), '<>' );
// Set the content ID property of the ezcMailFile if one was found
if ( $part instanceof ezcMailFile )
{
$part->contentId = $contentId;
}
if ( count( $this->parts ) > 0 )
{
$this->parts[] = $part;
}
else
{
$this->parts[1] = $part;
}
return $contentId;
}
/**
* Returns the main part of this multipart or null if there is no such part.
*
* @return array(ezcMailPart)
*/
public function getMainPart()
{
if ( isset( $this->parts[0] ) )
{
return $this->parts[0];
}
return null;
}
/**
* Returns the mail parts associated with this multipart.
*
* @return array(ezcMailPart)
*/
public function getRelatedParts()
{
if ( is_null( $this->getMainPart() ) )
{
return array_slice( $this->parts, 0 );
}
return array_slice( $this->parts, 1 );
}
/**
* Returns the part associated with the passed Content-ID.
*
* @param string $cid
* @return ezcMailPart
*/
public function getRelatedPartByID( $cid )
{
$parts = $this->getRelatedParts();
foreach ( $parts as $part )
{
if ( ( $part->getHeader( 'Content-ID' ) !== '' ) &&
( $part->getHeader( 'Content-ID' ) == "<$cid>" ) )
{
return $part;
}
}
return false;
}
/**
* Returns "related".
*
* @return string
*/
public function multipartType()
{
return "related";
}
/**
* Substitutes links in all ezcMailText parts with the subType HTML in this multipart/alternative.
*
* This method will perform substitution of CID's and absolute and relative links as specified by
* RFC 2557 for links that resolve to files. Links to other message parts must be resolved when
* displaying the message.
*
* - provide methods for substitution of these as well (inclusive listing of which they are)
* @todo Move to separate class.
*/
private function resolveHtmlLinks()
{
// 1. Check that the main part is a html part
// 2. Go through the related parts and build up a structure of available
// CIDS and locations
// 3. Substitute in this message.
}
}
?>

View file

@ -0,0 +1,225 @@
<?php
/**
* File containing the ezcMailMultipartReport class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part multipart/report used primarily to send delivery status notification messages.
*
* Multipart/Report: RFC 3462 {@link http://tools.ietf.org/html/rfc3462}
* Delivery Status Notifications: RFC 3464 {@link http://tools.ietf.org/html/rfc3464}
*
* The subparts of this mail part are according to RFC 3462:
*
* 1. A human readable part. The purpose of this part is to provide an easily understood
* description of the condition(s) that caused the report to be generated.
* Use the methods getReadablePart() and setReadablePart() to work with this part.
*
* 2. A machine parsable body part containing an account of
* the reported message handling event. The purpose of this body part is
* to provide a machine-readable description of the condition(s) that
* caused the report to be generated, along with details not present in
* the first body part that may be useful to human experts.
* Use the methods getMachinePart() and setMachinePart() to work with this part.
*
* 3. Optional. A body part containing the returned message or a
* portion thereof. This information may be useful to aid human experts
* in diagnosing problems.
* Use the methods getOriginalPart() and setOriginalPart() to work with this part.
*
* @property string $reportType
* The report type of the multipart report. Default is "delivery-status".
*
* @package Mail
* @version //autogen//
*/
class ezcMailMultipartReport extends ezcMailMultipart
{
/**
* Constructs a new ezcMailMultipartReport.
*
* @param ezcMailPart|array(ezcMailPart) $...
*/
public function __construct()
{
$args = func_get_args();
parent::__construct( $args );
$this->reportType = "delivery-status";
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'reportType':
$this->properties[$name] = $value;
$this->setHeader( 'Content-Type', 'multipart/' . $this->multipartType() . '; ' .
'report-type=' . $this->reportType . '; ' .
'boundary="' . $this->boundary . '"' );
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Returns the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'reportType':
return $this->properties[$name];
break;
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'reportType':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Appends a part to the list of parts.
*
* @param ezcMailPart $part
*/
public function appendPart( ezcMailPart $part )
{
$this->parts[] = $part;
}
/**
* Returns the mail parts associated with this multipart.
*
* @return array(ezcMailPart)
*/
public function getParts()
{
return $this->parts;
}
/**
* Sets the readable $part of this report multipart.
*
* @param ezcMailPart $part
*/
public function setReadablePart( ezcMailPart $part )
{
$this->parts[0] = $part;
}
/**
* Returns the readable part of this multipart or null if there is no such part.
*
* @return ezcMailPart
*/
public function getReadablePart()
{
if ( isset( $this->parts[0] ) )
{
return $this->parts[0];
}
return null;
}
/**
* Sets the machine $part of this report multipart.
*
* @param ezcMailPart $part
*/
public function setMachinePart( ezcMailPart $part )
{
$this->parts[1] = $part;
}
/**
* Returns the machine part of this multipart or null if there is no such part.
*
* @return ezcMailPart
*/
public function getMachinePart()
{
if ( isset( $this->parts[1] ) )
{
return $this->parts[1];
}
return null;
}
/**
* Sets the original content $part of this report multipart.
*
* @param ezcMailPart $part
*/
public function setOriginalPart( ezcMailPart $part )
{
$this->parts[2] = $part;
}
/**
* Returns the original content part of this multipart or null if there is no such part.
*
* @return ezcMailPart
*/
public function getOriginalPart()
{
if ( isset( $this->parts[2] ) )
{
return $this->parts[2];
}
return null;
}
/**
* Returns "report".
*
* @return string
*/
public function multipartType()
{
return "report";
}
}
?>

View file

@ -0,0 +1,126 @@
<?php
/**
* File containing the ezcMailRfc822Digest class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part or mail digest parts.
*
* This class is used to insert mail into mail.
*
*
* This example assumes that the mail object to digest is availble in the $digest variable:
* <code>
* $mail = new ezcMail();
* $mail->from = new ezcMailAddress( 'sender@example.com', 'Largo LaGrande' );
* $mail->addTo( new ezcMailAddress( 'receiver@example.com', 'Wally B. Feed' ) );
* $mail->subject = "This is the subject of the mail with a mail digest.";
* $textPart = new ezcMailText( "This is the body of the mail with a mail digest." );
*
* $mail->body = new ezcMailMultipartMixed( $textPart, new ezcMailRfc822Digest( $digest ) );
*
* $transport = new ezcMailMtaTransport();
* $transport->send( $mail );
* </code>
*
* @property string $mail
* The mail object to digest.
*
* @package Mail
* @version //autogen//
*/
class ezcMailRfc822Digest extends ezcMailPart
{
/**
* Constructs a new ezcMailDigest with the mail $mail.
*
* @param ezcMail $mail
*/
public function __construct( ezcMail $mail )
{
parent::__construct();
$this->mail = $mail;
$this->setHeader( 'Content-Type', 'message/rfc822' );
$this->setHeader( 'Content-Disposition', 'inline' );
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'mail':
$this->properties[$name] = $value;
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'mail':
return $this->properties[$name];
break;
default:
return parent::__get( $name );
break;
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'mail':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Returns the body part of this mail consisting of the digested mail.
*
* @return string
*/
public function generateBody()
{
return $this->mail->generate();
}
}
?>

View file

@ -0,0 +1,188 @@
<?php
/**
* File containing the ezcMailText class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Mail part used for sending all forms of plain text.
*
* Example: ezcMailText in a plain text message
* <code>
* $textPart = new ezcMailText( "This is a text message" );
* </code>
*
* Example: ezcMailText in a HTML message
* <code>
* $textPart = new ezcMailText( "<html>This is an <b>HTML</b> message"</html> );
* $textPart->subType = 'html';
* </code>
*
* @property string $charset
* The characterset used for this text part. Defaults to 'us-ascii'
* while creating mail, and is always 'utf-8' while parsing mail.
* @property string $subType
* The subtype of this text part.
* Defaults to 'plain' for plain text.
* Use 'html' for HTML messages.
* @property string $encoding
* The encoding of the text. Defaults to eight bit.
* @property string $text
* The main data of this text part.
* @property-read string $originalCharset
* The characterset in which a text part originally was before
* the conversion to UTF-8 when parsing incomming mail.
*
* @package Mail
* @version //autogen//
*/
class ezcMailText extends ezcMailPart
{
/**
* Constructs a new TextPart with the given $text, $charset and $encoding.
*
* OriginalCharset is only used when parsing mail. Parsed mail will always
* be converted to UTF-8 in this case $originalCharset will hold the
* charset before it was converted.
*
* @param string $text
* @param string $charset
* @param string $encoding
* @param string $originalCharset
*/
public function __construct( $text, $charset = "us-ascii", $encoding = ezcMail::EIGHT_BIT, $originalCharset = 'us-ascii' )
{
parent::__construct();
$this->text = $text;
$this->charset = $charset;
$this->encoding = $encoding;
$this->subType = 'plain';
// We need to set this directly in the array as it's a read-only property.
$this->properties['originalCharset'] = $originalCharset;
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @throws ezcBasePropertyPermissionException
* if the property is read-only.
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'charset':
case 'subType':
case 'encoding':
case 'text':
$this->properties[$name] = $value;
break;
case 'originalCharset':
throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ );
break;
default:
return parent::__set( $name, $value );
break;
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property does not exist.
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'charset':
case 'originalCharset':
case 'subType':
case 'encoding':
case 'text':
return $this->properties[$name];
default:
return parent::__get( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'charset':
case 'originalCharset':
case 'subType':
case 'encoding':
case 'text':
return isset( $this->properties[$name] );
default:
return parent::__isset( $name );
}
}
/**
* Returns the headers set for this part as a RFC822 compliant string.
*
* This method does not add the required two lines of space
* to separate the headers from the body of the part.
*
* @see setHeader()
* @return string
*/
public function generateHeaders()
{
$this->setHeader( "Content-Type", "text/" . $this->subType . "; charset=" . $this->charset );
$this->setHeader( "Content-Transfer-Encoding", $this->encoding );
return parent::generateHeaders();
}
/**
* Returns the generated text body of this part as a string.
*
* @return string
*/
public function generateBody()
{
switch ( $this->encoding )
{
case ezcMail::BASE64:
// leaves a \r\n to much at the end, but since it is base64 it will decode
// properly so we just leave it
return chunk_split( base64_encode( $this->text ), 76, ezcMailTools::lineBreak() );
break;
case ezcMail::QUOTED_PRINTABLE:
$text = preg_replace( '/[^\x21-\x3C\x3E-\x7E\x09\x20]/e',
'sprintf( "=%02X", ord ( "$0" ) ) ;', $this->text );
preg_match_all( '/.{1,73}([^=]{0,2})?/', $text, $match );
$text = implode( '=' . ezcMailTools::lineBreak(), $match[0] );
return $text;
break;
default:
return preg_replace( "/\r\n|\r|\n/", ezcMailTools::lineBreak(), $this->text );
}
}
}
?>

View file

@ -0,0 +1,192 @@
<?php
/**
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @version //autogentag//
* @filesource
* @package Mail
*/
/**
* A container to store a Content-Disposition header as described in http://www.faqs.org/rfcs/rfc2183.
*
* This container is used on the contentDisposition property on mail parts.
* Use it for reading and setting the Content-Disposition header.
*
* @package Mail
* @version //autogentag//
*/
class ezcMailContentDispositionHeader extends ezcBaseStruct
{
/**
* The disposition type, either "inline" or "attachment".
*
* @var string
*/
public $disposition;
/**
* The filename of the attachment.
*
* The filename should never include path information.
*
* @var string
*/
public $fileName;
/**
* The filename of the attachment, formatted for display. Used only for
* parsing, not used when generating a mail.
*
* The filename should never include path information.
*
* Added for issue #13038. If you use __set_state() be sure to set this
* property also.
*
* @var string
*/
public $displayFileName;
/**
* The language of the filename.
*
* @var string
*/
public $fileNameLanguage;
/**
* The characterset of the file name.
*
* @var string
*/
public $fileNameCharSet;
/**
* The creation date of the file attachment.
*
* The time should be formatted as specified by http://www.faqs.org/rfcs/rfc822.html
* section 5.
*
* A typical example is: Sun, 21 May 2006 16:00:50 +0400
*
* @var string
*/
public $creationDate;
/**
* The last modification date of the file attachment.
*
* The time should be formatted as specified by http://www.faqs.org/rfcs/rfc822.html
* section 5.
*
* A typical example is: Sun, 21 May 2006 16:00:50 +0400
*
* @var string
*/
public $modificationDate;
/**
* The last date the file attachment was read.
*
* The time should be formatted as specified by http://www.faqs.org/rfcs/rfc822.html
* section 5.
*
* A typical example is: Sun, 21 May 2006 16:00:50 +0400
*
* @var string
*/
public $readDate;
/**
* The size of the content in bytes.
*
* @var int
*/
public $size;
/**
* Any additional parameters provided in the Content-Disposition header.
*
* The format of the field is array(parameterName=>parameterValue)
*
* @var array(string=>string)
*/
public $additionalParameters = array();
/**
* Holds language and characterset data for the additional parameters.
*
* Format: array(parameterName=>array('charSet'=>string,'language'=>string))
*
* @apichange Merge this with $additionalParamters OR come up with an entirely new idea for the ContentDispositionHeader
* @var array(string=>array())
*/
public $additionalParametersMetaData = array();
/**
* Constructs a new ezcMailContentDispositionHeader holding the various values of this
* container.
*
* @param string $disposition
* @param string $fileName
* @param string $creationDate
* @param string $modificationDate
* @param string $readDate
* @param string $size
* @param array(string=>string) $additionalParameters
* @param string $fileNameLanguage
* @param string $fileNameCharSet
*/
public function __construct( $disposition = 'inline',
$fileName = null,
$creationDate = null,
$modificationDate = null,
$readDate = null,
$size = null,
$additionalParameters = array(),
$fileNameLanguage = null,
$fileNameCharSet = null,
$displayFileName = null )
{
$this->disposition = $disposition;
$this->fileName = $fileName;
$this->fileNameLanguage = $fileNameLanguage;
$this->fileNameCharSet = $fileNameCharSet;
$this->displayFileName = $displayFileName;
$this->creationDate = $creationDate;
$this->modificationDate = $modificationDate;
$this->readDate = $readDate;
$this->size = $size;
$this->additionalParameters = $additionalParameters;
}
/**
* Returns a new instance of this class with the data specified by $array.
*
* $array contains all the data members of this class in the form:
* array('member_name'=>value).
*
* __set_state makes this class exportable with var_export.
* var_export() generates code, that calls this method when it
* is parsed with PHP.
*
* @param array(string=>mixed) $array
* @return ezcMailAddress
*/
static public function __set_state( array $array )
{
return new ezcMailContentDispositionHeader( $array['disposition'],
$array['fileName'],
$array['creationDate'],
$array['modificationDate'],
$array['readDate'],
$array['size'],
$array['additionalParameters'],
$array['fileNameLanguage'],
$array['fileNameCharSet'],
$array['displayFileName']
);
}
}
?>

View file

@ -0,0 +1,90 @@
<?php
/**
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @version //autogentag//
* @filesource
* @package Mail
*/
/**
* A container to store a mail address in RFC822 format.
*
* The class ezcMailTools contains methods for transformation between several
* formats.
*
* @package Mail
* @version //autogentag//
* @mainclass
*/
class ezcMailAddress extends ezcBaseStruct
{
/**
* The name of the recipient (optional).
*
* @var string
*/
public $name;
/**
* The email address of the recipient.
*
* @var string
*/
public $email;
/**
* The character set used in the $name property.
*
* The characterset defaults to us-ascii.
*/
public $charset;
/**
* Constructs a new ezcMailAddress with the mail address $email and the optional name $name.
*
* @param string $email
* @param string $name
* @param string $charset
*/
public function __construct( $email, $name = '', $charset = 'us-ascii' )
{
$this->name = $name;
$this->email = $email;
$this->charset = $charset;
}
/**
* Returns a new instance of this class with the data specified by $array.
*
* $array contains all the data members of this class in the form:
* array('member_name'=>value).
*
* __set_state makes this class exportable with var_export.
* var_export() generates code, that calls this method when it
* is parsed with PHP.
*
* @param array(string=>mixed) $array
* @return ezcMailAddress
*/
static public function __set_state( array $array )
{
return new ezcMailAddress( $array['email'], $array['name'] );
}
/**
* Returns string representation of email address on string cast.
*
* Builds a representation in format "Name <email@example.com>", if name
* is present, else only "<email@example.com>", if name is not present. You
* can simply do echo with an object of type ezcMailAddress or (since PHP
* 5.2) explicitly cast it to string using (string) $object.
*
* @return string String representation of the email address.
*/
public function __toString()
{
return ( !empty( $this->name ) ? "{$this->name} " : "" ) . "<{$this->email}>";
}
}
?>

View file

@ -0,0 +1,188 @@
<?php
/**
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @version //autogentag//
* @filesource
* @package Mail
*/
/**
* Use this class to create a context to be passed to the walkParts() method from ezcMail.
*
* Example:
* <code>
* class App
* {
* public static function saveMailPart( $context, $mailPart )
* {
* // code to save the $mailPart object to disk
* }
* }
*
* // use the saveMailPart() function as a callback in walkParts()
* // where $mail is an ezcMail object.
* $context = new ezcMailPartWalkContext( array( 'App', 'saveMailPart' ) );
* $context->includeDigests = true; // if you want to go through the digests in the mail
* $mail->walkParts( $context, $mail );
* </code>
*
* @property array(string) $filter
* Used to restrict processing only to the specified mail part names.
* If empty or null, then ezcMailText, ezcMailFile and ezcMailRfc822Digest
* parts are processed. Usage e.g.: array( 'ezcMailFile' )
* @property callback $callbackFunction
* Name of a function or array( 'class_name', 'function_name' )
* @property bool $includeDigests
* If true then then ezcMailRfc822Digest parts are not processed by
* the callback function, instead the mail parts inside the digests will
* be available for processing.
* @property int $level
* The current level in the mail part walk (0 = first level).
*
* @package Mail
* @version //autogentag//
*/
class ezcMailPartWalkContext
{
/**
* An array of mail parts (retrieved recursively from a mail object).
*
* @var array(ezcMailPart)
*/
protected $parts = array();
/**
* Holds the properties of this class.
*
* @var array(string=>mixed)
*/
private $properties = array();
/**
* Constructs a new ezcMailPartWalkContext object.
*
* The parameter $callbackFunction must be a function name as string or as
* array( 'class_name', 'function_name' ).
*
* @param callback $callbackFunction
*/
public function __construct( $callbackFunction )
{
$this->callbackFunction = $callbackFunction;
$this->level = 0;
$this->filter = array();
$this->includeDigests = false;
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @throws ezcBaseValueException
* if $value is not appropiate for property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'level':
if ( !is_numeric( $value) || $value < 0 )
{
throw new ezcBaseValueException( $name, $value, 'int >= 0' );
}
$this->properties[$name] = (int) $value;
break;
case 'includeDigests':
if ( !is_bool( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'bool' );
}
$this->properties[$name] = (bool) $value;
break;
case 'filter':
$this->properties[$name] = $value;
break;
case 'callbackFunction':
$this->properties[$name] = $value;
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns the value of the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'level':
case 'filter':
case 'callbackFunction':
case 'includeDigests':
return $this->properties[$name];
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'level':
case 'filter':
case 'callbackFunction':
case 'includeDigests':
return isset( $this->properties[$name] );
default:
return false;
}
}
/**
* Appends a part to the list of mail parts.
*
* @param ezcMailPart $part
*/
public function appendPart( ezcMailPart $part )
{
$this->parts[] = $part;
}
/**
* Returns the mail parts.
*
* @return array(ezcMailPart)
*/
public function getParts()
{
return $this->parts;
}
}
?>

View file

@ -0,0 +1,782 @@
<?php
/**
* File containing the ezcMailTools class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* This class contains static convenience methods for composing addresses
* and ensuring correct line-breaks in the mail.
*
* @package Mail
* @version //autogen//
* @mainclass
*/
class ezcMailTools
{
/**
* Reply to sender.
*/
const REPLY_SENDER = 1;
/**
* Reply to all.
*/
const REPLY_ALL = 1;
/**
* Server to use for validateEmailAddressMx(). Change this if this server
* cannot be used with your Internet Service Provider.
*
* Default value: 'smtp.ez.no'.
*
* @var string
*/
public static $mxValidateServer = 'smtp.ez.no';
/**
* Email address to use for validateEmailAddressMx(). Change this if this
* address cannot be used with your Internet Service Provider.
*
* Default value: 'postmaster@ez.no'.
*
* @var string
*/
public static $mxValidateAddress = 'postmaster@ez.no';
/**
* Holds the unique ID's.
*
* @var int
*/
private static $idCounter = 0;
/**
* The characters to use for line-breaks in the mail.
*
* The default is \r\n which is the value specified in RFC822.
*
* @var string
*/
private static $lineBreak = "\r\n";
/**
* Returns ezcMailAddress $item as a RFC822 compliant address string.
*
* Example:
* <code>
* composeEmailAddress( new ezcMailAddress( 'sender@example.com', 'John Doe' ) );
* </code>
*
* Returns:
* <pre>
* John Doe <sender@example.com>
* </pre>
*
* 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:
* <code>
* ezcMailTools::parseEmailAddress( 'John Doe <john@example.com>' );
* </code>
*
* @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:
* <code>
* ezcMailTools::parseEmailAddresses( 'John Doe <john@example.com>' );
* </code>
*
* @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. MX record checking does not work
* on Windows due to the lack of getmxrr() and checkdnsrr() PHP functions.
* The ezcBaseFunctionalityNotSupportedException is thrown in this case.
*
* If checking against MX records, set these values before performing the
* check, to ensure the MX record checks work properly:
* <code>
* ezcMailTools::$mxValidateServer = 'your.mail.server'; // default 'smtp.ez.no'
* ezcMailTools::$mxValidateAddress = 'email@mail.server'; // default 'postmaster@ez.no'
* </code>
*
* 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:
* <code>
* localpart@domainpart
* </code>
*
* The localpart has these rules, and these rules are just an approximation of
* the rules in RFC2822:
* - 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 function
* ezcMailTools::composeEmailAddress will encode it when using it
* in a mail header)
* - 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.
*
* @throws ezcBaseFunctionalityNotSupportedException
* if $checkMxRecords is true and getmxrr() or checkdnsrr() functions
* are missing (e.g. on Windows)
* @param string $address
* @param bool $checkMxRecords
* @return bool
*/
public static function validateEmailAddress( $address, $checkMxRecords = false )
{
$pattern = '/^((\"[^\"\f\n\r\t\v\b]+\")|([A-Za-z0-9_\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[A-Za-z0-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]))\.((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
*
* Set these values before calling this function, to ensure the MX record
* checks work properly:
* <code>
* ezcMailTools::$mxValidateServer = 'your.mail.server'; // default 'smtp.ez.no'
* ezcMailTools::$mxValidateAddress = 'email@mail.server'; // default 'postmaster@ez.no'
* </code>
*
* MX record checking does not work on Windows due to the lack of getmxrr()
* and checkdnsrr() PHP functions. The ezcBaseFunctionalityNotSupportedException
* is thrown in this case.
*
* @throws ezcBaseFunctionalityNotSupportedException
* if getmxrr() or checkdnsrr() functions are missing (e.g. on Windows)
* @param string $address
* @return bool
*/
protected static function validateEmailAddressMx( $address )
{
if ( !ezcBaseFeatures::hasFunction( 'getmxrr' ) || !ezcBaseFeatures::hasFunction( 'checkdnsrr' ) )
{
throw new ezcBaseFunctionalityNotSupportedException( 'Checking DNS records', 'getmxrr() or checkdnsrr() missing' );
}
$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 " . self::$mxValidateServer,
"MAIL FROM: <" . self::$mxValidateAddress . ">",
"RCPT TO: <{$address}>",
"QUIT",
);
for ( $i = 0; $i < $numberOfMx; $i++ )
{
if ( $socket = @fsockopen( $mx[$i], 25, $errno = 0, $errstr = 0, $timeoutOpen ) )
{
$response = fgets( $socket );
stream_set_timeout( $socket, $timeoutConnection );
$meta = stream_get_meta_data( $socket );
if ( !$meta['timed_out'] && !preg_match( '/^2\d\d[ -]/', $response ) )
{
return false;
}
foreach ( $smtp as $command )
{
fputs( $socket, "{$command}\r\n" );
$response = fgets( $socket, 4096 );
if ( !$meta['timed_out'] && preg_match( '/^5\d\d[ -]/', $response ) )
{
return false;
}
}
fclose( $socket );
return true;
}
elseif ( $i === $numberOfMx - 1 )
{
// none of the mail servers could be contacted
return false;
}
}
}
else
{
// no mail servers found
return false;
}
}
}
/**
* Returns an unique message ID to be used for a mail message.
*
* The hostname $hostname will be added to the unique ID as required by RFC822.
* If an e-mail address is provided instead, the hostname is extracted and used.
*
* The formula to generate the message ID is: [time_and_date].[process_id].[counter]
*
* @param string $hostname
* @return string
*/
public static function generateMessageId( $hostname )
{
if ( strpos( $hostname, '@' ) !== false )
{
$hostname = strstr( $hostname, '@' );
}
else
{
$hostname = '@' . $hostname;
}
return date( 'YmdGHjs' ) . '.' . getmypid() . '.' . self::$idCounter++ . $hostname;
}
/**
* Returns an unique ID to be used for Content-ID headers.
*
* The part $partName is default set to "part". Another value can be used to provide,
* for example, a file name of a part. $partName will be encoded with base64 to be
* compliant with the RFCs.
*
* The formula used is [base64( $partName )]."@".[time].[counter]
*
* @param string $partName
* @return string
*/
public static function generateContentId( $partName = "part" )
{
return str_replace( array( '=', '+', '/' ), '', base64_encode( $partName ) ) . '@' . date( 'His' ) . self::$idCounter++;
}
/**
* Sets the endLine $character(s) to use when generating mail.
* The default is to use "\r\n" as specified by RFC 2045.
*
* @param string $characters
*/
public static function setLineBreak( $characters )
{
self::$lineBreak = $characters;
}
/**
* Returns one endLine character.
*
* The default is to use "\n\r" as specified by RFC 2045.
*
* @return string
*/
public static function lineBreak()
{
// Note, this function does deliberately not
// have a $count parameter because of speed issues.
return self::$lineBreak;
}
/**
* Decodes mime encoded fields and tries to recover from errors.
*
* Decodes the $text encoded as a MIME string to the $charset. In case the
* strict conversion fails this method tries to workaround the issues by
* trying to "fix" the original $text before trying to convert it.
*
* @param string $text
* @param string $charset
* @return string
*/
public static function mimeDecode( $text, $charset = 'utf-8' )
{
$origtext = $text;
$text = @iconv_mime_decode( $text, 0, $charset );
if ( $text !== false )
{
return $text;
}
// something went wrong while decoding, let's see if we can fix it
// Try to fix lower case hex digits
$text = preg_replace_callback(
'/=(([a-f][a-f0-9])|([a-f0-9][a-f]))/',
create_function( '$matches', 'return strtoupper($matches[0]);' ),
$origtext
);
$text = @iconv_mime_decode( $text, 0, $charset );
if ( $text !== false )
{
return $text;
}
// Workaround a bug in PHP 5.1.0-5.1.3 where the "b" and "q" methods
// are not understood (but only "B" and "Q")
$text = str_replace( array( '?b?', '?q?' ), array( '?B?', '?Q?' ), $origtext );
$text = @iconv_mime_decode( $text, 0, $charset );
if ( $text !== false )
{
return $text;
}
// Try it as latin 1 string
$text = preg_replace( '/=\?([^?]+)\?/', '=?iso-8859-1?', $origtext );
$text = iconv_mime_decode( $text, 0, $charset );
return $text;
}
/**
* Returns a new mail object that is a reply to the current object.
*
* The new mail will have the correct to, cc, bcc and reference headers set.
* It will not have any body set.
*
* By default the reply will only be sent to the sender of the original mail.
* If $type is set to REPLY_ALL, all the original recipients will be included
* in the reply.
*
* Use $subjectPrefix to set the prefix to the subject of the mail. The default
* is to prefix with 'Re: '.
*
* @param ezcMail $mail
* @param ezcMailAddress $from
* @param int $type REPLY_SENDER or REPLY_ALL
* @param string $subjectPrefix
* @param string $mailClass
* @return ezcMail
*/
static public function replyToMail( ezcMail $mail, ezcMailAddress $from,
$type = self::REPLY_SENDER, $subjectPrefix = "Re: ",
$mailClass = "ezcMail" )
{
$reply = new $mailClass();
$reply->from = $from;
// To = Reply-To if set
if ( $mail->getHeader( 'Reply-To' ) != '' )
{
$reply->to = ezcMailTools::parseEmailAddresses( $mail->getHeader( 'Reply-To' ) );
}
else // Else To = From
{
$reply->to = array( $mail->from );
}
if ( $type == self::REPLY_ALL )
{
// Cc = Cc + To - your own address
$cc = array();
foreach ( $mail->to as $address )
{
if ( $address->email != $from->email )
{
$cc[] = $address;
}
}
foreach ( $mail->cc as $address )
{
if ( $address->email != $from->email )
{
$cc[] = $address;
}
}
$reply->cc = $cc;
}
$reply->subject = $subjectPrefix . $mail->subject;
if ( $mail->getHeader( 'Message-Id' ) )
{
// In-Reply-To = Message-Id
$reply->setHeader( 'In-Reply-To', $mail->getHeader( 'Message-ID' ) );
// References = References . Message-Id
if ( $mail->getHeader( 'References' ) != '' )
{
$reply->setHeader( 'References', $mail->getHeader( 'References' )
. ' ' . $mail->getHeader( 'Message-ID' ) );
}
else
{
$reply->setHeader( 'References', $mail->getHeader( 'Message-ID' ) );
}
}
else // original mail is borked. Let's support it anyway.
{
$reply->setHeader( 'References', $mail->getHeader( 'References' ) );
}
return $reply;
}
/**
* Guesses the content and mime type by using the file extension.
*
* The content and mime types are returned through the $contentType
* and $mimeType arguments.
* For the moment only for image files.
*
* @param string $fileName
* @param string $contentType
* @param string $mimeType
*/
static public function guessContentType( $fileName, &$contentType, &$mimeType )
{
$extension = strtolower( pathinfo( $fileName, PATHINFO_EXTENSION ) );
switch ( $extension )
{
case 'gif':
$contentType = 'image';
$mimeType = 'gif';
break;
case 'jpg':
case 'jpe':
case 'jpeg':
$contentType = 'image';
$mimeType = 'jpeg';
break;
case 'png':
$contentType = 'image';
$mimeType = 'png';
break;
case 'bmp':
$contentType = 'image';
$mimeType = 'bmp';
break;
case 'tif':
case 'tiff':
$contentType = 'image';
$mimeType = 'tiff';
break;
default:
return false;
}
return true;
}
/**
* Replaces HTML embedded "cid:" references with replacements from $contentIdArray.
*
* The method matches all "cid:" references in the $htmlText and then loops
* over each match. For each match the found content ID is looked-up as key
* in the $contentIdArray and the value is then inserted as replacement for
* the "cid:" reference.
*
* <code>
* <?php
* $contentIdArray = array( 'consoletools-table.png@1421450' => 'http://localhost/consoletools-table.jpg' );
* $text = "<html> Embedded image: <img src='cid:consoletools-table.png@1421450'/> </html>";
* $htmlBody = ezcMailTools::replaceContentIdRefs( $text, $contentIdArray );
* // $htmlBody is now:
* // <html> Embedded image: <img src='http://localhost/consoletools-table.jpg'/> </html>
* ?>
* </code>
*
* The $contentIdArray can be build by iterating over all parts in the
* mail, and for each ezcMailFilePart that you find: 1. copy the associated
* file (fileName property of the ezcMailFilePart object) to your webroot;
* 2. add an element to the array with the key created from the contentId
* property from the ezcMailFilePart object. See the tutorial for an
* example of this.
*
* @param string $htmlText
* @param array(string=>string) $contentIdArray
* @return string
*/
static function replaceContentIdRefs( $htmlText, $contentIdArray )
{
preg_match_all( '@src=[\'"](cid:(.*?))[\'"]@', $htmlText, $matches );
for ( $i = 0; $i < count( $matches[0] ); $i++ )
{
if ( isset( $contentIdArray[$matches[2][$i]] ) )
{
$htmlText = str_replace( $matches[1][$i], $contentIdArray[$matches[2][$i]], $htmlText );
}
}
return $htmlText;
}
}
?>

View file

@ -0,0 +1,186 @@
<?php
/**
* File containing the ezcMailFileSet class
*
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @version //autogentag//
* @package Mail
*/
/**
* ezcMailFileSet is an internal class that can be used to parse mail directly
* from files on disk.
*
* Each file should contain only one mail message in RFC822 format. Bad files
* or non-existing files are ignored.
*
* Example:
*
* <code>
* $set = new ezcMailFileSet( array( 'path/to/mail/rfc822message.mail' ) );
* $parser = new ezcMailParser();
* $mail = $parser->parseMail( $set );
* </code>
*
* @package Mail
* @version //autogen//
*/
class ezcMailFileSet implements ezcMailParserSet
{
/**
* Holds the pointer to the file currently being parsed.
*
* @var filepointer
*/
private $fp = null;
/**
* Holds the list of files that the set should serve.
*
* @var array(string)
*/
private $files = array();
/**
* This variable is true if there is more data in the mail that is being fetched.
*
* It is false if there is no mail being fetched currently or if all the data of the current mail
* has been fetched.
*
* @var bool
*/
private $hasMoreMailData = false;
/**
* Constructs a new set that servers the files specified by $files.
*
* The set will start on the first file in the the array.
*
* @param array(string) $files
*/
public function __construct( array $files )
{
$this->files = $files;
reset( $this->files );
$this->hasMoreMailData = false;
}
/**
* Destructs the set.
*
* Closes any open files.
*/
public function __destruct()
{
if ( is_resource( $this->fp ) )
{
fclose( $this->fp );
$this->fp = null;
}
}
/**
* Returns whether the file set contains files
*
* @return bool
*/
public function hasData()
{
if ( count( $this->files ) >= 1 )
{
if ( $this->files[0] === 'php://stdin' || filesize( $this->files[0] ) > 0 )
{
return true;
}
}
return false;
}
/**
* Returns one line of data from the current mail in the set.
*
* Null is returned if there is no current mail in the set or
* the end of the mail is reached,
*
* @return string
*/
public function getNextLine()
{
if ( $this->hasMoreMailData === false )
{
$this->nextMail();
}
// finished?
if ( $this->fp == null || feof( $this->fp ) )
{
if ( $this->fp != null )
{
fclose( $this->fp );
$this->fp = null;
}
return null;
}
// get one line
$next = fgets( $this->fp );
if ( $next == "" && feof( $this->fp ) )
{
return null;
}
return $next;
}
/**
* Moves the set to the next mail and returns true upon success.
*
* False is returned if there are no more mail in the set.
*
* @return bool
*/
public function nextMail()
{
if ( $this->hasMoreMailData === false )
{
$this->hasMoreMailData = true;
return $this->openFile( true );
}
return $this->openFile();
}
/**
* Opens the next file in the set and returns true on success.
*
* @param bool $isFirst
* @return bool
*/
private function openFile( $isFirst = false )
{
// cleanup file pointer if needed
if ( $this->fp != null )
{
fclose( $this->fp );
$this->fp = null;
}
// open the new file
$file = $isFirst ? current( $this->files ) : next( $this->files );
// loop until we can open a file.
while ( $this->fp == null && $file !== false )
{
if ( $file === 'php://stdin' || file_exists( $file ) )
{
$fp = fopen( $file, 'r' );
if ( $fp !== false )
{
$this->fp = $fp;
return true;
}
}
$file = next( $this->files );
}
return false;
}
}
?>

View file

@ -0,0 +1,375 @@
<?php
/**
* File containing the ezcMailImapSet class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailImapSet is an internal class that fetches a series of mail
* from the IMAP server.
*
* The IMAP set works on an existing connection and a list of the messages that
* the user wants to fetch. The user must accept all the data for each mail for
* correct behaviour.
*
* @package Mail
* @version //autogen//
*/
class ezcMailImapSet implements ezcMailParserSet
{
/**
* Holds the list of messages that the user wants to retrieve from the server.
*
* @var array(int)
*/
private $messages;
/**
* Holds the current message the user is fetching.
*
* The variable is null before the first message and false after
* the last message has been fetched.
*
* @var int
*/
private $currentMessage = null;
/**
* Holds the line that will be read-ahead in order to determine the trailing paranthesis.
*
* @var string
*/
private $nextData = null;
/**
* This variable is true if there is more data in the mail that is being fetched.
*
* It is false if there is no mail being fetched currently or if all the data of the current mail
* has been fetched.
*
* @var bool
*/
private $hasMoreMailData = false;
/**
* Holds if mail should be deleted from the server after retrieval.
*
* @var bool
*/
private $deleteFromServer = false;
/**
* Used to generate a tag for sending commands to the IMAP server.
*
* @var string
*/
private $currentTag = 'A0000';
/**
* Holds the mode in which the IMAP commands operate.
*
* @var string
*/
private $uid;
/**
* Holds the options for an IMAP mail set.
*
* @var ezcMailImapSetOptions
*/
private $options;
/**
* Holds the number of bytes to read from the IMAP server.
*
* It is set before starting to read a message from the information
* returned by the IMAP server in this form:
*
* <code>
* * 2 FETCH (FLAGS (\Answered \Seen) RFC822 {377}
* </code>
*
* In this example, $this->bytesToRead will be set to 377.
*
* @var int
*/
private $bytesToRead = false;
/**
* Constructs a new IMAP parser set that will fetch the messages $messages.
*
* $connection must hold a valid connection to a IMAP server that is ready
* to retrieve the messages.
*
* If $deleteFromServer is set to true the messages will be deleted after retrieval.
*
* See {@link ezcMailImapSetOptions} for options you can set to IMAP sets.
*
* @throws ezcMailTransportException
* if the server sent a negative response
* @param ezcMailTransportConnection $connection
* @param array(int) $messages
* @param bool $deleteFromServer
* @param ezcMailImapSetOptions|array(string=>mixed) $options
*/
public function __construct( ezcMailTransportConnection $connection, array $messages, $deleteFromServer = false, $options = array() )
{
if ( $options instanceof ezcMailImapSetOptions )
{
$this->options = $options;
}
else if ( is_array( $options ) )
{
$this->options = new ezcMailImapSetOptions( $options );
}
else
{
throw new ezcBaseValueException( "options", $options, "ezcMailImapSetOptions|array" );
}
$this->connection = $connection;
$this->messages = $messages;
$this->deleteFromServer = $deleteFromServer;
$this->nextData = null;
$this->uid = ( $this->options->uidReferencing ) ? ezcMailImapTransport::UID : ezcMailImapTransport::NO_UID;
}
/**
* Returns true if all the data has been fetched from this set.
*
* @return bool
*/
public function isFinished()
{
return $this->currentMessage === false ? true : false;
}
/**
* Returns one line of data from the current mail in the set.
*
* Null is returned if there is no current mail in the set or
* the end of the mail is reached,
*
* @return string
*/
public function getNextLine()
{
if ( $this->currentMessage === null )
{
// instead of calling $this->nextMail() in the constructor, it is called
// here, to avoid sending commands to the server when creating the set, and
// instead send the server commands when parsing the set (see ezcMailParser).
$this->nextMail();
}
if ( $this->hasMoreMailData )
{
if ( $this->bytesToRead !== false && $this->bytesToRead >= 0 )
{
$data = $this->connection->getLine();
$this->bytesToRead -= strlen( $data );
// modified for issue #13878 (Endless loop in ezcMailParser):
// removed wrong checks (ending in ')' check and ending with tag check (e.g. 'A0002'))
if ( $this->bytesToRead <= 0 )
{
if ( $this->bytesToRead < 0 )
{
$data = substr( $data, 0, strlen( $data ) + $this->bytesToRead ); //trim( $data, ")\r\n" );
}
if ( $this->bytesToRead === 0 )
{
// hack for Microsoft Exchange, which sometimes puts
// FLAGS (\Seen)) at the end of a message instead of before (!)
if ( strlen( trim( $data, ")\r\n" ) !== strlen( $data ) - 3 ) )
{
// if the last line in a mail does not end with ")\r\n"
// then read an extra line and discard it
$extraData = $this->connection->getLine();
}
}
$this->hasMoreMailData = false;
// remove the mail if required by the user.
if ( $this->deleteFromServer === true )
{
$tag = $this->getNextTag();
$this->connection->sendData( "{$tag} {$this->uid}STORE {$this->currentMessage} +FLAGS (\\Deleted)" );
// skip OK response ("{$tag} OK Store completed.")
$response = $this->getResponse( $tag );
}
return $data;
}
}
return $data;
}
return null;
}
/**
* Moves the set to the next mail and returns true upon success.
*
* False is returned if there are no more mail in the set.
*
* @throws ezcMailTransportException
* if the server sent a negative response
* @return bool
*/
public function nextMail()
{
if ( $this->currentMessage === null )
{
$this->currentMessage = reset( $this->messages );
}
else
{
$this->currentMessage = next( $this->messages );
}
$this->nextData = null;
$this->bytesToRead = false;
if ( $this->currentMessage !== false )
{
$tag = $this->getNextTag();
$this->connection->sendData( "{$tag} {$this->uid}FETCH {$this->currentMessage} RFC822" );
$response = $this->connection->getLine();
if ( strpos( $response, ' NO ' ) !== false ||
strpos( $response, ' BAD ') !== false )
{
throw new ezcMailTransportException( "The IMAP server sent a negative reply when requesting mail." );
}
else
{
$response = $this->getResponse( 'FETCH (', $response );
if ( strpos( $response, 'FETCH (' ) !== false )
{
$this->hasMoreMailData = true;
// retrieve the message size from $response, eg. if $response is:
// * 2 FETCH (FLAGS (\Answered \Seen) RFC822 {377}
// then $this->bytesToRead will be 377
preg_match( '/\{(.*)\}/', $response, $matches );
if ( count( $matches ) > 0 )
{
$this->bytesToRead = (int) $matches[1];
}
return true;
}
else
{
$response = $this->getResponse( $tag );
if ( strpos( $response, 'OK ' ) === false )
{
throw new ezcMailTransportException( "The IMAP server sent a negative reply when requesting mail." );
}
}
}
}
return false;
}
/**
* Reads the responses from the server until encountering $tag.
*
* In IMAP, each command sent by the client is prepended with a
* alphanumeric tag like 'A1234'. The server sends the response
* to the client command as lines, and the last line in the response
* is prepended with the same tag, and it contains the status of
* the command completion ('OK', 'NO' or 'BAD').
*
* Sometimes the server sends alerts and response lines from other
* commands before sending the tagged line, so this method just
* reads all the responses until it encounters $tag.
*
* It returns the tagged line to be processed by the calling method.
*
* If $response is specified, then it will not read the response
* from the server before searching for $tag in $response.
*
* Before calling this method, a connection to the IMAP server must be
* established.
*
* @param string $tag
* @param string $response
* @return string
*/
private function getResponse( $tag = null, $response = null )
{
if ( is_null( $response ) )
{
$response = $this->connection->getLine();
}
while ( strpos( $response, $tag ) === false )
{
if ( strpos( $response, ' BAD ' ) !== false ||
strpos( $response, ' NO ' ) !== false )
{
break;
}
$response = $this->connection->getLine();
}
return $response;
}
/**
* Generates the next IMAP tag to prepend to client commands.
*
* The structure of the IMAP tag is Axxxx, where:
* - A is a letter (uppercase for conformity)
* - x is a digit from 0 to 9
*
* example of generated tag: T5439
*
* It uses the class variable $this->currentTag.
*
* Everytime it is called, the tag increases by 1.
*
* If it reaches the last tag, it wraps around to the first tag.
*
* By default, the first generated tag is A0001.
*
* @return string
*/
private function getNextTag()
{
$tagLetter = substr( $this->currentTag, 0, 1 );
$tagNumber = intval( substr( $this->currentTag, 1 ) );
$tagNumber++;
if ( $tagLetter == 'Z' && $tagNumber == 10000 )
{
$tagLetter = 'A';
$tagNumber = 1;
}
if ( $tagNumber == 10000 )
{
$tagLetter++;
$tagNumber = 0;
}
$this->currentTag = $tagLetter . sprintf( "%04s", $tagNumber );
return $this->currentTag;
}
/**
* Returns whether the set has mails.
*
* @return bool
*/
public function hasData()
{
return count( $this->messages );
}
/**
* Returns message numbers from the current set.
*
* @return array(int)
*/
public function getMessageNumbers()
{
return $this->messages;
}
}
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,163 @@
<?php
/**
* File containing the ezcMailMboxSet class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailMboxSet is an internal class that fetches a series of mail
* from an mbox file.
*
* The mbox set is constructed from a file pointer and iterates over all the
* messages in an mbox file.
*
* @package Mail
* @version //autogen//
*/
class ezcMailMboxSet implements ezcMailParserSet
{
/**
* Holds the filepointer to the mbox
*
* @var resource(filepointer)
*/
private $fh;
/**
* This variable is true if there is more data in the mail that is being fetched.
*
* It is false if there is no mail being fetched currently or if all the data of the current mail
* has been fetched.
*
* @var bool
*/
private $hasMoreMailData = false;
/**
* Records whether we initialized the mbox or not
*
* @var bool
*/
private $initialized = false;
/**
* Holds the current message positions.
*
* @var array(int=>int)
*/
private $messagePositions = array();
/**
* Holds the current message position in array $messagePositions.
*
* @var int
*/
private $currentMesssagePosition = 0;
/**
* Constructs a new mbox parser set.
*
* @throws ezcBaseFileIoException
* if $fh is not a filepointer resource.
* @param resource(filepointer) $fh
* @param array(int=>int) $messages
*/
public function __construct( $fh, array $messages )
{
if ( !is_resource( $fh ) || get_resource_type( $fh ) != 'stream' )
{
throw new ezcBaseFileIoException( 'filepointer', ezcBaseFileException::READ, "The passed filepointer is not a stream resource." );
}
$this->fh = $fh;
$this->initialized = false;
$this->hasMoreMailData = true;
$this->messagePositions = $messages;
$this->currentMessagePosition = 0;
}
/**
* Returns true if all the data has been fetched from this set.
*
* @return bool
*/
public function isFinished()
{
return feof( $this->fh ) ? true : false;
}
/**
* Returns one line of data from the current mail in the set
* including the ending linebreak.
*
* Null is returned if there is no current mail in the set or
* the end of the mail is reached.
*
* @return string
*/
public function getNextLine()
{
if ( $this->currentMessagePosition === 0 )
{
$this->nextMail();
}
if ( $this->hasMoreMailData )
{
$data = fgets( $this->fh );
if ( feof( $this->fh ) || substr( $data, 0, 5 ) === "From " )
{
$this->hasMoreMailData = false;
return null;
}
return $data;
}
return null;
}
/**
* Returns whether the set contains mails.
*
* @return bool
*/
public function hasData()
{
return ( $this->hasMoreMailData === true && count( $this->messagePositions ) > 0 );
}
/**
* Moves the set to the next mail and returns true upon success.
*
* False is returned if there are no more mail in the set.
*
* @return bool
*/
public function nextMail()
{
// seek to next message if available
if ( $this->currentMessagePosition > count( $this->messagePositions ) - 1 )
{
$this->hasMoreMailData = false;
return false;
}
fseek( $this->fh, $this->messagePositions[$this->currentMessagePosition] );
$this->currentMessagePosition++;
$this->hasMoreMailData = true;
return true;
}
/**
* Returns message numbers for current set.
*
* @return array(int=>int)
*/
public function getMessageNumbers()
{
return array_keys( $this->messagePositions );
}
}
?>

View file

@ -0,0 +1,195 @@
<?php
/**
* File containing the ezcMailMboxTransport class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailMboxTransport implements mail retrieval from an mbox file.
*
* The mbox set is constructed from a file pointer and iterates over all the
* messages in an mbox file.
*
* @package Mail
* @version //autogen//
* @mainclass
*/
class ezcMailMboxTransport
{
/**
* Holds the filepointer to the mbox
*
* @var resource(filepointer)
*/
public $fh;
/**
* Constructs the ezcMailMboxTransport object
*
* Opens the mbox $fileName.
*
* @throws ezcBaseFileNotFoundException
* if the mbox file could not be found.
* @throws ezcBaseFilePermissionException
* if the mbox file could be opened for reading.
* @param string $fileName
*/
public function __construct( $fileName )
{
if ( !file_exists( $fileName ) )
{
throw new ezcBaseFileNotFoundException( $fileName, 'mbox' );
}
if ( !is_readable( $fileName ) )
{
throw new ezcBaseFilePermissionException( $fileName, ezcBaseFileException::READ );
}
$this->fh = fopen( $fileName, 'rt' );
}
/**
* Finds the position of the first message while skipping a possible header.
*
* Mbox files can contain a header which does not describe an email
* message. This method skips over this optional header by checking for a
* specific From MAILER-DAEMON header.
*
* @return int
*/
private function findFirstMessage()
{
$data = fgets( $this->fh );
fseek( $this->fh, 0 );
if ( substr( $data, 0, 18 ) === 'From MAILER-DAEMON' )
{
return $this->findNextMessage();
}
else
{
return 0;
}
}
/**
* Reads through the Mbox file and stops at the next message.
*
* Messages in Mbox files are separated with lines starting with "From "
* and this function reads to the next "From " marker. It then returns the
* current posistion in the file. If EOF is detected during reading the
* function returns false instead.
*
* @return int
*/
private function findNextMessage()
{
do
{
$data = fgets( $this->fh );
} while ( !feof( $this->fh ) && substr( $data, 0, 5 ) !== "From " );
if ( feof( $this->fh ) )
{
return false;
}
return ftell( $this->fh );
}
/**
* This function reads through the whole mbox and returns starting positions of the messages.
*
* @return array(int=>int)
*/
public function listMessages()
{
$messages = array();
fseek( $this->fh, 0 );
// Skip the first mail as this is the mbox header
$position = $this->findFirstMessage();
if ( $position === false )
{
return $messages;
}
// Continue reading through the rest of the mbox
do
{
$position = $this->findNextMessage();
if ( $position !== false )
{
$messages[] = $position;
}
} while ( $position !== false );
return $messages;
}
/**
* Returns an ezcMailMboxSet containing all the messages in the mbox.
*
* @return ezcMailMboxSet
*/
public function fetchAll()
{
$messages = $this->listMessages();
return new ezcMailMboxSet( $this->fh, $messages );
}
/**
* Returns an ezcMailMboxSet containing only the $number -th message in the mbox.
*
* @throws ezcMailNoSuchMessageException
* if the message $number is out of range.
* @param int $number
* @return ezcMailMboxSet
*/
public function fetchByMessageNr( $number )
{
$messages = $this->listMessages();
if ( !isset( $messages[$number] ) )
{
throw new ezcMailNoSuchMessageException( $number );
}
return new ezcMailMboxSet( $this->fh, array( 0 => $messages[$number] ) );
}
/**
* Returns an ezcMailMboxSet with $count messages starting from $offset.
*
* Fetches $count messages starting from the $offset and returns them as a
* ezcMailMboxSet. If $count is not specified or if it is 0, it fetches
* all messages starting from the $offset.
*
* @throws ezcMailInvalidLimitException
* if $count is negative.
* @throws ezcMailOffsetOutOfRangeException
* if $offset is outside of the existing range of messages.
* @param int $offset
* @param int $count
* @return ezcMailMboxSet
*/
public function fetchFromOffset( $offset, $count = 0 )
{
if ( $count < 0 )
{
throw new ezcMailInvalidLimitException( $offset, $count );
}
$messages = $this->listMessages();
if ( !isset( $messages[$offset] ) )
{
throw new ezcMailOffsetOutOfRangeException( $offset, $count );
}
if ( $count == 0 )
{
$range = array_slice( $messages, $offset );
}
else
{
$range = array_slice( $messages, $offset, $count );
}
return new ezcMailMboxSet( $this->fh, $range );
}
}
?>

View file

@ -0,0 +1,65 @@
<?php
/**
* File containing the ezcMailMtaTransport class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Implementation of the mail transport interface using the system MTA.
*
* The system MTA translates to sendmail on most Linux distributions.
*
* Qmail insists it should only have "\n" linebreaks and will send
* garbled messages with the default "\r\n" setting.
* Use ezcMailTools::setLineBreak( "\n" ) before sending mail to fix this issue.
*
* @package Mail
* @version //autogen//
* @mainclass
*/
class ezcMailMtaTransport implements ezcMailTransport
{
/**
* Constructs a new ezcMailMtaTransport.
*/
public function __construct( )
{
}
/**
* Sends the mail $mail using the PHP mail method.
*
* Note that a message may not arrive at the destination even though
* it was accepted for delivery.
*
* @throws ezcMailTransportException
* if the mail was not accepted for delivery by the MTA.
* @param ezcMail $mail
*/
public function send( ezcMail $mail )
{
$mail->appendExcludeHeaders( array( 'to', 'subject' ) );
$headers = rtrim( $mail->generateHeaders() ); // rtrim removes the linebreak at the end, mail doesn't want it.
if ( ( count( $mail->to ) + count( $mail->cc ) + count( $mail->bcc ) ) < 1 )
{
throw new ezcMailTransportException( 'No recipient addresses found in message header.' );
}
$additionalParameters = "";
if ( isset( $mail->returnPath ) )
{
$additionalParameters = "-f{$mail->returnPath->email}";
}
$success = mail( ezcMailTools::composeEmailAddresses( $mail->to ),
$mail->getHeader( 'Subject' ), $mail->generateBody(), $headers, $additionalParameters );
if ( $success === false )
{
throw new ezcMailTransportException( 'The email could not be sent by sendmail' );
}
}
}
?>

View file

@ -0,0 +1,21 @@
<?php
/**
* File containing the ezcMailTransportMta class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* This class is deprecated. Use ezcMailMtaTransport instead.
*
* @package Mail
* @version //autogen//
* @ignore
*/
class ezcMailTransportMta extends ezcMailMtaTransport
{
}
?>

View file

@ -0,0 +1,182 @@
<?php
/**
* File containing the ezcMailPop3Set class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailPop3Set is an internal class that fetches a series of mail
* from the pop3 server.
*
* The POP3 set works on an existing connection and a list of the messages that
* the user wants to fetch. The user must accept all the data for each mail for
* correct behaviour.
*
* The set can be parsed with ezcMailParser.
*
* @package Mail
* @version //autogen//
*/
class ezcMailPop3Set implements ezcMailParserSet
{
/**
* Holds the list of messages that the user wants to retrieve from the server.
*
* @var array(int)
*/
private $messages;
/**
* Holds the current message the user is fetching.
*
* The variable is null before the first message and false after
* the last message has been fetched.
*
* @var int
*/
private $currentMessage = null;
/**
* This variable is true if there is more data in the mail that is being
* fetched.
*
* It is false if there is no mail being fetched currently or if all the
* data of the current mail has been fetched.
*
* @var bool
*/
private $hasMoreMailData = false;
/**
* Holds if mail should be deleted from the server after retrieval.
*
* @var bool
*/
private $deleteFromServer = false;
/**
* Constructs a new POP3 parser set that will fetch the messages $messages.
*
* $connection must hold a valid connection to a POP3 server that is ready
* to retrieve the messages.
*
* If $deleteFromServer is set to true the messages will be deleted after
* retrieval.
*
* @throws ezcMailTransportException
* if the server sent a negative response
* @param ezcMailTransportConnection $connection
* @param array(ezcMail) $messages
* @param bool $deleteFromServer
*/
public function __construct( ezcMailTransportConnection $connection, array $messages, $deleteFromServer = false )
{
$this->connection = $connection;
$this->messages = $messages;
$this->deleteFromServer = $deleteFromServer;
}
/**
* Returns true if all the data has been fetched from this set.
*
* @return bool
*/
public function isFinished()
{
return $this->currentMessage === false ? true : false;
}
/**
* Returns one line of data from the current mail in the set.
*
* Null is returned if there is no current mail in the set or the end of the
* mail is reached.
*
* @return string
*/
public function getNextLine()
{
if ( $this->currentMessage === null )
{
$this->nextMail();
}
if ( $this->hasMoreMailData )
{
$data = $this->connection->getLine();
if ( rtrim( $data ) === "." )
{
$this->hasMoreMailData = false;
// remove the mail if required by the user.
if ( $this->deleteFromServer == true )
{
$this->connection->sendData( "DELE {$this->currentMessage}" );
$response = $this->connection->getLine(); // ignore response
}
return null;
}
return $data;
}
return null;
}
/**
* Moves the set to the next mail and returns true upon success.
*
* False is returned if there are no more mail in the set.
*
* @throws ezcMailTransportException
* if the server sent a negative response
* @return bool
*/
public function nextMail()
{
if ( $this->currentMessage === null )
{
$this->currentMessage = reset( $this->messages );
}
else
{
$this->currentMessage = next( $this->messages );
}
if ( $this->currentMessage !== false )
{
$this->connection->sendData( "RETR {$this->currentMessage}" );
$response = $this->connection->getLine();
if ( strpos( $response, "+OK" ) === 0 )
{
$this->hasMoreMailData = true;
return true;
}
else
{
throw new ezcMailTransportException( "The POP3 server sent a negative reply when requesting mail." );
}
}
return false;
}
/**
* Returns whether the set has mails.
*
* @return bool
*/
public function hasData()
{
return count( $this->messages );
}
/**
* Returns message numbers from the current set.
*
* @return array(int)
*/
public function getMessageNumbers()
{
return $this->messages;
}
}
?>

View file

@ -0,0 +1,847 @@
<?php
/**
* File containing the ezcMailPop3Transport class.
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* The class ezcMailPop3Transport implements functionality for handling POP3
* mail servers.
*
* The implementation supports most of the commands specified in:
* - {@link http://www.faqs.org/rfcs/rfc1939.html} (POP3)
* - {@link http://www.faqs.org/rfcs/rfc1734.html} (POP3 AUTH)
*
* The POP3 server can be in different states. Most POP3 commands require
* that a connection is established and a user is authenticated.
*
* The POP3 transport class allows developers to interface with a POP3 server.
*
* Basic commands:
* - connect to a POP3 server ({@link __construct()})
* - authenticate a user with a username and password ({@link authenticate()})
* - disconnect from the POP3 server ({@link disconnect()})
*
* Work with message numbers:
* - get the message numbers and sizes of all the messages ({@link listMessages()})
* - get the message numbers and IDs of all the messages ({@link listUniqueIdentifiers()})
* - get the headers of a certain message ({@link top()})
* - delete a message ({@link delete()})
*
* Work with ezcMailPop3Set sets (parseable with ezcMailParser):
* - create a set from all messages ({@link fetchAll()})
* - create a set from a certain message ({@link fetchByMessageNr()})
* - create a set from a range of messages ({@link fetchFromOffset()})
*
* Miscellaneous commands:
* - get the status of messages on the server ({@link status()})
* - issue a NOOP command to keep the connection alive ({@link noop()})
*
* The usual operation with a POP3 server is illustrated by this example:
* <code>
* // create a new POP3 transport object by specifying the server name, optional
* // port and optional SSL mode
* $options = new ezcMailPop3TransportOptions();
* $options->ssl = true;
*
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com', null, $options );
*
* // Authenticate to the POP3 server
* $pop3->authenticate( 'username', 'password' );
*
* // issue commands to the POP3 server
* // for example get the headers of the first message, which can be
* // parsed with ezcMailVariableSet and ezcMailParser
* $headers = $pop3->top( 1 );
*
* // see the above list of commands or consult the online documentation for
* // the full list of commands you can issue to an POP3 server and examples
*
* // disconnect from the POP3 server
* $pop3->disconnect();
* </code>
*
* See {@link ezcMailPop3TransportOptions} for options you can specify for POP3.
*
* @todo ignore messages of a certain size?
* @todo // support for signing?
*
* @property ezcMailPop3TransportOptions $options
* Holds the options you can set to the POP3 transport.
*
* @package Mail
* @version //autogen//
* @mainclass
*/
class ezcMailPop3Transport
{
/**
* Internal state set when the POP3 transport is not connected to a server.
*
* @access private
*/
const STATE_NOT_CONNECTED = 1;
/**
* Internal state set when the POP3 transport is connected to the server
* but no successful authentication has been performed.
*
* @access private
*/
const STATE_AUTHORIZATION = 2;
/**
* Internal state set when the POP3 transport is connected to the server
* and authenticated.
*
* @access private
*/
const STATE_TRANSACTION = 3;
/**
* Internal state set when the QUIT command has been issued to the POP3 server
* but before the disconnect has taken place.
*
* @access private
*/
const STATE_UPDATE = 4;
/**
* Plain text authorization.
*/
const AUTH_PLAIN_TEXT = 1;
/**
* APOP authorization.
*/
const AUTH_APOP = 2;
/**
* Holds the connection state.
*
* $var int {@link STATE_NOT_CONNECTED},
* {@link STATE_AUTHORIZATION},
* {@link STATE_TRANSACTION} or
* {@link STATE_UPDATE}.
*/
protected $state = self::STATE_NOT_CONNECTED;
/**
* The connection to the POP3 server.
*
* @var ezcMailTransportConnection
*/
protected $connection = null;
/**
* Holds the initial greeting from the POP3 server when connecting.
*
* @var string
*/
protected $greeting = null;
/**
* Options for a POP3 transport connection.
*
* @var ezcMailPop3TransportOptions
*/
private $options;
/**
* Creates a new POP3 transport and connects to the $server at $port.
*
* You can specify the $port if the POP3 server is not on the default
* port 995 (for SSL connections) or 110 (for plain connections). Use the
* $options parameter to specify an SSL connection.
*
* For options you can specify for POP3 see {@link ezcMailPop3TransportOptions}.
*
* Example of creating a POP3 transport:
* <code>
* // replace with your POP3 server address
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com' );
*
* // if you want to use SSL:
* $options = new ezcMailPop3TransportOptions();
* $options->ssl = true;
*
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com', null, $options );
* </code>
*
* @throws ezcMailTransportException
* if it was not possible to connect to the server
* @throws ezcBaseExtensionNotFoundException
* if trying to use SSL and the extension openssl is not installed
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param string $server
* @param int $port
* @param ezcMailPop3TransportOptions|array(string=>mixed) $options
*/
public function __construct( $server, $port = null, $options = array() )
{
if ( $options instanceof ezcMailPop3TransportOptions )
{
$this->options = $options;
}
else if ( is_array( $options ) )
{
$this->options = new ezcMailPop3TransportOptions( $options );
}
else
{
throw new ezcBaseValueException( "options", $options, "ezcMailPop3TransportOptions|array" );
}
if ( $port === null )
{
$port = ( $this->options->ssl === true ) ? 995 : 110;
}
$this->connection = new ezcMailTransportConnection( $server, $port, $this->options );
$this->greeting = $this->connection->getLine();
if ( !$this->isPositiveResponse( $this->greeting ) )
{
throw new ezcMailTransportException( "The connection to the POP3 server is ok, but a negative response from server was received: '{$this->greeting}'. Try again later." );
}
$this->state = self::STATE_AUTHORIZATION;
}
/**
* Destructs the POP3 transport object.
*
* If there is an open connection to the POP3 server it is closed.
*/
public function __destruct()
{
if ( $this->state != self::STATE_NOT_CONNECTED )
{
$this->connection->sendData( 'QUIT' );
$this->connection->getLine(); // discard
$this->connection->close();
}
}
/**
* Sets the value of the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @throws ezcBaseValueException
* if $value is not accepted for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'options':
if ( !( $value instanceof ezcMailPop3TransportOptions ) )
{
throw new ezcBaseValueException( 'options', $value, 'instanceof ezcMailPop3TransportOptions' );
}
$this->options = $value;
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns the value of the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @param string $name
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'options':
return $this->options;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'options':
return true;
default:
return false;
}
}
/**
* Disconnects the transport from the POP3 server.
*/
public function disconnect()
{
if ( $this->state != self::STATE_NOT_CONNECTED )
{
$this->connection->sendData( 'QUIT' );
$this->connection->getLine(); // discard
$this->state = self::STATE_UPDATE;
$this->connection->close();
$this->connection = null;
$this->state = self::STATE_NOT_CONNECTED;
}
}
/**
* Authenticates the user to the POP3 server with $user and $password.
*
* You can choose the authentication method with the $method parameter.
* The default is to use plaintext username and password (specified in the
* ezcMailPop3TransportOptions class).
*
* This method should be called directly after the construction of this
* object.
*
* Example:
* <code>
* // replace with your POP3 server address
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com' );
*
* // replace the values with your username and password for the POP3 server
* $pop3->authenticate( 'username', 'password' );
* </code>
*
* @throws ezcMailTransportException
* if there is no connection to the server
* or if already authenticated
* or if the authentication method is not accepted by the server
* or if the provided username/password combination did not work
* @param string $user
* @param string $password
* @param int $method
*/
public function authenticate( $user, $password, $method = null )
{
if ( $this->state != self::STATE_AUTHORIZATION )
{
throw new ezcMailTransportException( "Tried to authenticate when there was no connection or when already authenticated." );
}
if ( is_null( $method ) )
{
$method = $this->options->authenticationMethod;
}
if ( $method == self::AUTH_PLAIN_TEXT ) // normal plain text login
{
// authenticate ourselves
$this->connection->sendData( "USER {$user}" );
$response = $this->connection->getLine();
if ( !$this->isPositiveResponse( $response ) )
{
throw new ezcMailTransportException( "The POP3 server did not accept the username: {$response}." );
}
$this->connection->sendData( "PASS {$password}" );
$response = $this->connection->getLine();
if ( !$this->isPositiveResponse( $response ) )
{
throw new ezcMailTransportException( "The POP3 server did not accept the password: {$response}." );
}
}
else if ( $method == self::AUTH_APOP ) // APOP login
{
// fetch the timestamp from the greeting
$timestamp = '';
preg_match( '/.*(<.*>).*/',
$this->greeting,
$timestamp );
// check if there was a greeting. If not, apop is not supported
if ( count( $timestamp ) < 2 )
{
throw new ezcMailTransportException( "The POP3 server did not accept the APOP login: No greeting." );
}
$hash = md5( $timestamp[1] . $password );
$this->connection->sendData( "APOP {$user} {$hash}" );
$response = $this->connection->getLine();
if ( !$this->isPositiveResponse( $response ) )
{
throw new ezcMailTransportException( "The POP3 server did not accept the APOP login: {$response}." );
}
}
else
{
throw new ezcMailTransportException( "Invalid authentication method provided." );
}
$this->state = self::STATE_TRANSACTION;
}
/**
* Returns an array of the message numbers on the server and the size of the
* messages in bytes.
*
* The format of the returned array is:
* <code>
* array( message_id => message_size );
* </code>
*
* Example:
* <code>
* array( 2 => 1700, 5 => 1450, 6 => 21043 );
* </code>
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @return array(int)
*/
public function listMessages()
{
if ( $this->state != self::STATE_TRANSACTION )
{
throw new ezcMailTransportException( "Can't call listMessages() on the POP3 transport when not successfully logged in." );
}
// send the command
$this->connection->sendData( "LIST" );
$response = $this->connection->getLine();
if ( !$this->isPositiveResponse( $response ) )
{
throw new ezcMailTransportException( "The POP3 server sent a negative response to the LIST command: {$response}." );
}
// fetch the data from the server and prepare it to be returned.
$messages = array();
while ( ( $response = $this->connection->getLine( true ) ) !== "." )
{
list( $num, $size ) = explode( ' ', $response );
$messages[$num] = $size;
}
return $messages;
}
/**
* Returns the unique identifiers for messages on the POP3 server.
*
* You can fetch the unique identifier for a specific message by providing
* the $msgNum parameter.
*
* The unique identifier can be used to recognize mail from servers
* between requests. In contrast to the message numbers the unique numbers
* assigned to an email usually never changes.
*
* Note: POP3 servers are not required to support this command and it may fail.
*
* The format of the returned array is:
* <code>
* array( message_num => unique_id );
* </code>
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* Example:
* <code>
* array( 1 => '000001fc4420e93a', 2 => '000001fd4420e93a' );
* </code>
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @param int $msgNum
* @return array(string)
*/
public function listUniqueIdentifiers( $msgNum = null )
{
if ( $this->state != self::STATE_TRANSACTION )
{
throw new ezcMailTransportException( "Can't call ListUniqueIdentifiers() on the POP3 transport when not successfully logged in." );
}
// send the command
$result = array();
if ( $msgNum !== null )
{
$this->connection->sendData( "UIDL {$msgNum}" );
$response = $this->connection->getLine( true );
if ( $this->isPositiveResponse( $response ) )
{
// get the single response line from the server
list( $dummy, $num, $id ) = explode( ' ', $response );
$result[(int)$num] = $id;
}
else
{
throw new ezcMailTransportException( "The POP3 server sent a negative response to the UIDL command: {$response}." );
}
}
else
{
$this->connection->sendData( "UIDL" );
$response = $this->connection->getLine();
if ( $this->isPositiveResponse( $response ) )
{
// fetch each of the result lines and add it to the result
while ( ( $response = $this->connection->getLine( true ) ) !== "." )
{
list( $num, $id ) = explode( ' ', $response );
$result[(int)$num] = $id;
}
}
else
{
throw new ezcMailTransportException( "The POP3 server sent a negative response to the UIDL command: {$response}." );
}
}
return $result;
}
/**
* Returns information about the messages on the server.
*
* The information returned through the parameters is:
* - $numMessages = number of messages
* - $sizeMessages = sum of the messages sizes
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* Example of returning the status of messages on the server:
* <code>
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com' );
* $pop3->authenticate( 'username', 'password' );
*
* $pop3->status( $numMessages, $sizeMessages );
* </code>
*
* After running the above code, $numMessages and $sizeMessages will be
* populated with values.
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @param int &$numMessages
* @param int &$sizeMessages
*/
public function status( &$numMessages, &$sizeMessages )
{
if ( $this->state != self::STATE_TRANSACTION )
{
throw new ezcMailTransportException( "Can't call status() on the POP3 transport when not successfully logged in." );
}
$this->connection->sendData( "STAT" );
$response = $this->connection->getLine();
if ( $this->isPositiveResponse( $response ) )
{
// get the single response line from the server
list( $dummy, $numMessages, $sizeMessages ) = explode( ' ', $response );
$numMessages = (int)$numMessages;
$sizeMessages = (int)$sizeMessages;
}
else
{
throw new ezcMailTransportException( "The POP3 server did not respond with a status message: {$response}." );
}
}
/**
* Deletes the message with the message number $msgNum from the server.
*
* The message number must be a valid identifier fetched with (example)
* {@link listMessages()}.
*
* Any future reference to the message-number associated with the message
* in a command generates an error.
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @param int $msgNum
*/
public function delete( $msgNum )
{
if ( $this->state != self::STATE_TRANSACTION )
{
throw new ezcMailTransportException( "Can't call delete() on the POP3 transport when not successfully logged in." );
}
$this->connection->sendData( "DELE {$msgNum}" );
$response = $this->connection->getLine();
if ( !$this->isPositiveResponse( $response ) )
{
throw new ezcMailTransportException( "The POP3 server could not delete the message: {$response}." );
}
}
/**
* Returns the headers and the $numLines first lines of the body of the mail with
* the message number $msgNum.
*
* If the command failed or if it was not supported by the server an empty string is
* returned.
*
* Note: POP3 servers are not required to support this command and it may fail.
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* Example of listing the mail headers of all the messages from the server:
* <code>
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com' );
* $pop3->authenticate( 'username', 'password' );
*
* $parser = new ezcMailParser();
* $messages = $pop3->listMessages();
* foreach ( $messages as $messageNr => $size )
* {
* $set = new ezcMailVariableSet( $pop3->top( $messageNr ) );
* $mail = $parser->parseMail( $set );
* $mail = $mail[0];
* echo "From: {$mail->from}, Subject: {$mail->subject}, Size: {$size}\n";
* }
* </code>
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @param int $msgNum
* @param int $numLines
* @return string
*/
public function top( $msgNum, $numLines = 0 )
{
if ( $this->state != self::STATE_TRANSACTION )
{
throw new ezcMailTransportException( "Can't call top() on the POP3 transport when not successfully logged in." );
}
// send the command
$this->connection->sendData( "TOP {$msgNum} {$numLines}" );
$response = $this->connection->getLine();
if ( !$this->isPositiveResponse( $response ) )
{
throw new ezcMailTransportException( "The POP3 server sent a negative response to the TOP command: {$response}." );
}
// fetch the data from the server and prepare it to be returned.
$message = "";
while ( ( $response = $this->connection->getLine( true ) ) !== "." )
{
$message .= $response . "\n";
}
return $message;
}
/**
* Returns an ezcMailPop3Set with all the messages on the server.
*
* If $deleteFromServer is set to true the mail will be removed from the
* server after retrieval. If not it will be left.
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* Example:
* <code>
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com' );
* $pop3->authenticate( 'username', 'password' );
*
* $set = $pop3->fetchAll();
*
* // parse $set with ezcMailParser
* $parser = new ezcMailParser();
* $mails = $parser->parseMail( $set );
* foreach ( $mails as $mail )
* {
* // process $mail which is an ezcMail object
* }
* </code>
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @param bool $deleteFromServer
* @return ezcMailParserSet
*/
public function fetchAll( $deleteFromServer = false )
{
$messages = $this->listMessages();
return new ezcMailPop3Set( $this->connection, array_keys( $messages ), $deleteFromServer );
}
/**
* Returns an ezcMailPop3Set containing only the $number -th message from
* the server.
*
* If $deleteFromServer is set to true the mail will be removed from the
* server after retrieval. If not it will be left.
*
* Note: for POP3 the first message is 1 (so for $number = 0 the exception
* will be thrown).
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* Example:
* <code>
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com' );
* $pop3->authenticate( 'username', 'password' );
*
* $set = $pop3->fetchByMessageNr( 1 );
*
* // $set can be parsed with ezcMailParser
* </code>
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @throws ezcMailNoSuchMessageException
* if the message $number is out of range
* @param int $number
* @param bool $deleteFromServer
* @return ezcMailPop3Set
*/
public function fetchByMessageNr( $number, $deleteFromServer = false )
{
$messages = $this->listMessages();
if ( !isset( $messages[$number] ) )
{
throw new ezcMailNoSuchMessageException( $number );
}
return new ezcMailPop3Set( $this->connection, array( $number ), $deleteFromServer );
}
/**
* Returns an ezcMailPop3Set with $count messages starting from $offset from
* the server.
*
* Fetches $count messages starting from the $offset and returns them as a
* ezcMailPop3Set. If $count is not specified or if it is 0, it fetches
* all messages starting from the $offset.
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* Example:
* <code>
* $pop3 = new ezcMailPop3Transport( 'pop3.example.com' );
* $pop3->authenticate( 'username', 'password' );
*
* $set = $pop3->fetchFromOffset( 1, 10 );
*
* // $set can be parsed with ezcMailParser
* </code>
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
* @throws ezcMailInvalidLimitException
* if $count is negative
* @throws ezcMailOffsetOutOfRangeException
* if $offset is outside of the existing range of messages
* @param int $offset
* @param int $count
* @param bool $deleteFromServer
* @return ezcMailPop3Set
*/
public function fetchFromOffset( $offset, $count = 0, $deleteFromServer = false )
{
if ( $count < 0 )
{
throw new ezcMailInvalidLimitException( $offset, $count );
}
$messages = array_keys( $this->listMessages() );
if ( $count == 0 )
{
$range = array_slice( $messages, $offset - 1, count( $messages ), true );
}
else
{
$range = array_slice( $messages, $offset - 1, $count, true );
}
if ( !isset( $range[$offset - 1] ) )
{
throw new ezcMailOffsetOutOfRangeException( $offset, $count );
}
return new ezcMailPop3Set( $this->connection, $range, $deleteFromServer );
}
/**
* Sends a NOOP command to the server, use it to keep the connection alive.
*
* Before calling this method, a connection to the POP3 server must be
* established and a user must be authenticated successfully.
*
* @throws ezcMailTransportException
* if there was no connection to the server
* or if not authenticated
* or if the server sent a negative response
*/
public function noop()
{
if ( $this->state != self::STATE_TRANSACTION )
{
throw new ezcMailTransportException( "Can't call noop() on the POP3 transport when not successfully logged in." );
}
// send the command
$this->connection->sendData( "NOOP" );
$response = $this->connection->getLine();
if ( !$this->isPositiveResponse( $response ) )
{
throw new ezcMailTransportException( "The POP3 server sent a negative response to the NOOP command: {$response}." );
}
}
/**
* Returns true if the response from the server is a positive one.
*
* @param string $line
* @return bool
*/
protected function isPositiveResponse( $line )
{
if ( strpos( $line, "+OK" ) === 0 )
{
return true;
}
return false;
}
}
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
<?php
/**
* File containing the ezcMailTransportSmtp class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* This class is deprecated. Use ezcMailSmtpTransport instead.
*
* @package Mail
* @version //autogen//
* @ignore
*/
class ezcMailTransportSmtp extends ezcMailSmtpTransport
{
}
?>

View file

@ -0,0 +1,208 @@
<?php
/**
* File containing the ezcMailStorageSet class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* ezcMailStorageSet is a wrapper around other mail sets and provides saving of
* mail sources to files.
*
* Example:
*
* <code>
* // create a new POP3 transport object and a mail parser object
* $transport = new ezcMailPop3Transport( "server" );
* $transport->authenticate( "username", "password" );
* $parser = new ezcMailParser();
*
* // wrap around the set returned by fetchAll()
* // and specify that the sources are to be saved in the folder /tmp/cache
* $set = new ezcMailStorageSet( $transport->fetchAll(), '/tmp/cache' );
*
* // parse the storage set
* $mail = $parser->parseMail( $set );
*
* // get the filenames of the saved mails in the set.
* // The file names are composed of process ID + current time + a counter
* // This array must be saved to be used on a subsequent request
* $files = $set->getSourceFiles();
*
* // get the source of the 4th saved mail.
* // This can be on a subsequent request if the $files array was saved from
* // a previous request
* $source = file_get_contents( $files[3] );
* </code>
*
* @package Mail
* @version //autogen//
*/
class ezcMailStorageSet implements ezcMailParserSet
{
/**
* Holds the pointer to the current file which holds the mail source.
*
* @var filepointer
*/
private $writer = null;
/**
* Holds the temporary file name where contents are being initially written
* (until set is parsed and Message-ID is extracted).
*
* @var string
*/
private $file = null;
/**
* Holds the path where the files are written (specified in the constructor).
*
* @var string
*/
private $path = null;
/**
* This variable is true if there is more data in the mail that is being fetched.
*
* @var bool
*/
private $hasMoreMailData = false;
/**
* Holds the location where to store the message sources.
*
* @var string
*/
private $location;
/**
* Holds the filenames holding the sources of the mails in this set.
*
* @var array(string)
*/
private $files = null;
/**
* Holds the current email number being parsed.
*
* @var int
*/
private $counter;
/**
* Constructs a new storage set around the provided set.
*
* $location specifies where to save the message sources. This directory MUST
* exist and must be writable.
*
* @param ezcMailParserSet $set
* @param string $location
*/
public function __construct( ezcMailParserSet $set, $location )
{
$this->set = $set;
$this->location = $location;
$this->path = rtrim( $this->location, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
$this->hasMoreMailData = false;
$this->counter = 0;
}
/**
* Destructs the set.
*
* Closes any open files.
*/
public function __destruct()
{
if ( is_resource( $this->writer ) )
{
fclose( $this->writer );
$this->writer = null;
}
}
/**
* Returns one line of data from the current mail in the set.
*
* Null is returned if there is no current mail in the set or
* the end of the mail is reached,
*
* It also writes the line of data to the current file. If the line contains
* a Message-ID header then the value in the header will be used to rename the
* file.
*
* @return string
*/
public function getNextLine()
{
if ( $this->hasMoreMailData === false )
{
$this->nextMail();
$this->hasMoreMailData = true;
}
$line = $this->set->getNextLine();
fputs( $this->writer, $line );
return $line;
}
/**
* Moves the set to the next mail and returns true upon success.
*
* False is returned if there are no more mail in the set.
*
* @return bool
*/
public function nextMail()
{
if ( $this->writer !== null )
{
fclose( $this->writer );
$this->files[] = $this->path . $this->file;
$this->writer = null;
}
$mail = $this->set->nextMail();
if ( $mail === true || $this->hasMoreMailData === false )
{
$this->counter++;
// Temporary file name for the mail source
$this->file = getmypid() . '-' . time() . '-' . $this->counter;
$writer = fopen( $this->path . $this->file, 'w' );
if ( $writer !== false )
{
$this->writer = $writer;
}
return $mail;
}
return false;
}
/**
* Returns whether the set has mails.
*
* @return bool
*/
public function hasData()
{
return $this->set->hasData();
}
/**
* Returns an array of the filenames holding the sources of the mails in this set.
*
* The format of the returned array is:
* array( 0 => 'location/filename1', 1 => 'location/filename2',...)
*
* @return array(string)
*/
public function getSourceFiles()
{
return $this->files;
}
}
?>

View file

@ -0,0 +1,257 @@
<?php
/**
* File containing the ezcMailTransportConnection class
*
* @package Mail
* @version //autogen//
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @access private
*/
/**
* ezcMailTransportConnection is an internal class used to connect to
* a server and have line based communication with.
*
* @property ezcMailTransportOptions $options
* Holds the options you can set to the transport connection.
*
* @package Mail
* @version //autogen//
* @access private
*/
class ezcMailTransportConnection
{
/**
* The line-break characters to send to the server.
*/
const CRLF = "\r\n";
/**
* The connection to the server or null if there is none.
*
* @var resource
*/
private $connection = null;
/**
* Options for a transport connection.
*
* @var ezcMailTransportOptions
*/
private $options;
/**
* Constructs a new connection to the $server using the port $port.
*
* {@link ezcMailTransportOptions} for options you can specify for a
* transport connection.
*
* @todo The @ should be removed when PHP doesn't throw warnings for connect problems.
*
* @throws ezcMailTransportException
* if a connection to the server could not be made
* @throws ezcBaseExtensionNotFoundException
* if trying to use SSL and the extension openssl is not installed
* @throws ezcBasePropertyNotFoundException
* if $options contains a property not defined
* @throws ezcBaseValueException
* if $options contains a property with a value not allowed
* @param string $server
* @param int $port
* @param ezcMailTransportOptions $options
*/
public function __construct( $server, $port, ezcMailTransportOptions $options = null )
{
$errno = null;
$errstr = null;
if ( $options === null )
{
$this->options = new ezcMailTransportOptions();
}
else
{
$this->options = $options;
}
if ( $this->options->ssl )
{
if ( ezcBaseFeatures::hasExtensionSupport( 'openssl' ) !== true )
{
throw new ezcBaseExtensionNotFoundException( 'openssl', null, "PHP not configured --with-openssl." );
}
$this->connection = @stream_socket_client( "ssl://{$server}:{$port}",
$errno, $errstr, $this->options->timeout );
}
else
{
$this->connection = @stream_socket_client( "tcp://{$server}:{$port}",
$errno, $errstr, $this->options->timeout );
}
if ( is_resource( $this->connection ) )
{
stream_set_timeout( $this->connection, $this->options->timeout );
}
else
{
throw new ezcMailTransportException( "Failed to connect to the server: {$server}:{$port}." );
}
}
/**
* Sets the property $name to $value.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @throws ezcBaseValueException
* if $value is not accepted for the property $name
* @param string $name
* @param mixed $value
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'options':
if ( !( $value instanceof ezcMailTransportOptions ) )
{
throw new ezcBaseValueException( 'options', $value, 'instanceof ezcMailTransportOptions' );
}
$this->options = $value;
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns the value of the property $name.
*
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @param string $name
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'options':
return $this->options;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* Returns true if the property $name is set, otherwise false.
*
* @param string $name
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'options':
return true;
default:
return false;
}
}
/**
* Send $data to the server through the connection.
*
* This method appends one line-break at the end of $data.
*
* @throws ezcMailTransportException
* if there is no valid connection.
* @param string $data
*/
public function sendData( $data )
{
if ( is_resource( $this->connection ) )
{
if ( fwrite( $this->connection, $data . self::CRLF,
strlen( $data ) + strlen( self::CRLF ) ) === false )
{
throw new ezcMailTransportException( 'Could not write to the stream. It was probably terminated by the host.' );
}
}
}
/**
* Returns one line of data from the stream.
*
* The returned line will have linebreaks removed if the $trim option is set.
*
* @throws ezcMailTransportConnection
* if there is no valid connection
* @param bool $trim
* @return string
*/
public function getLine( $trim = false )
{
$data = '';
$line = '';
if ( is_resource( $this->connection ) )
{
// in case there is a problem with the connection fgets() returns false
while ( strpos( $data, self::CRLF ) === false )
{
$line = fgets( $this->connection, 512 );
/* If the mail server aborts the connection, fgets() will
* return false. We need to throw an exception here to prevent
* the calling code from looping indefinitely. */
if ( $line === false )
{
$this->connection = null;
throw new ezcMailTransportException( 'Could not read from the stream. It was probably terminated by the host.' );
}
$data .= $line;
}
if ( $trim == false )
{
return $data;
}
else
{
return rtrim( $data, "\r\n" );
}
}
throw new ezcMailTransportException( 'Could not read from the stream. It was probably terminated by the host.' );
}
/**
* Returns if the connection is open.
*
* @return bool
*/
public function isConnected()
{
return is_resource( $this->connection );
}
/**
* Closes the connection to the server if it is open.
*/
public function close()
{
if ( is_resource( $this->connection ) )
{
fclose( $this->connection );
$this->connection = null;
}
}
}
?>

View file

@ -0,0 +1,92 @@
<?php
/**
* File containing the ezcMailVariableSet class
*
* @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @version //autogen//
* @package Mail
*/
/**
* ezcMailVariableSet is an internal class that can be used to parse mail directly from
* a string variable in your script.
*
* The variable should contain the complete mail message in RFC822 format.
*
* Example:
*
* <code>
* $mail = "To: user@example.com\r\nSubject: Test mail .....";
* $set = new ezcMailVariableSet( $mail ) );
* $parser = new ezcMailParser();
* $mail = $parser->parseMail( $set );
* </code>
*
* @package Mail
* @version //autogen//
*/
class ezcMailVariableSet implements ezcMailParserSet
{
/**
* Holds the mail split by linebreaks.
*
* @var array(string)
*/
private $mail = array();
/**
* Constructs a new set that contains one mail from $mail.
*
* @param string $mail
*/
public function __construct( $mail )
{
$this->mail = preg_split( "/\r\n|\n/", $mail );
reset( $this->mail );
}
/**
* Returns one line of data from the current mail in the set.
*
* Null is returned if there is no current mail in the set or
* the end of the mail is reached.
*
* @return string
*/
public function getNextLine()
{
$line = current( $this->mail );
next( $this->mail );
if ( $line === false )
{
return null;
}
return $line . "\n";
}
/**
* Moves the set to the next mail and returns true upon success.
*
* False is returned if there are no more mail in the set (always).
*
* @return bool
*/
public function nextMail()
{
return false;
}
/**
* Returns whether the variable set contains mails.
*
* @return bool
*/
public function hasData()
{
return ( count( $this->mail ) > 1 );
}
}
?>