<?php
/**
* Author: Ashish Kumar Srivastava
* Email: hide@address.com
* Purpose: The class is written for parsing IMAP supported mailbox
*
* This class is used for parsing IMAP supported mailboxes.
* It is used for accessing plain text, html, headers and attachements of the messages
* by message id, by subject, by received date, by sender address of the messages. It can also
* filter messages by unread status. If more than one message have same subject or received date
* or from address then it accesses all the messages and provides information for each.
*/
class MailParser {
const MESSAGE_ID = 0;
const RECEIVED_DATE = 1;
const SUBJECT = 2;
const FROM_ADDRESS = 3;
const UNREAD = 4;
const TRY_TO_CONNECT = 3; // Try to connect mail server three times
/**
* This array is resultant array which further contains an associative array as an element.
* And the associative array contain attachments, htmlBody, plainBody as its keys.
*
* @var Array
*/
private $result = array();
/**
* This associative array contains attached file name as a key
* and corresponding binary file as a value.
*
* @var Array
*/
private $attachments = array();
/**
* This contains standard object of header information
*
* @var Object
*/
private $headers;
/**
* This variable conatains the html part of a message.
*
* @var String
*/
private $htmlBody = '';
/**
* This variable conatains the plain text part of a message.
*
* @var String
*/
private $plainBody = "";
/**
* It consists a mail server name
*
* @var string
*/
private $mailServerName = '';
/**
* User name for the mail account
*
* @var string
*/
private $userName = '';
/**
* Password for the mail account
*
* @var string
*/
private $password = '';
/**
* Parameterised constructor
*/
function __construct($mailServerName, $userName, $password) {
$this->mailServerName = $mailServerName;
$this->userName = $userName;
$this->password = $password;
}
/**
* This is the funtion for settion connection to the given mail server.
*
* @param String $mailbox
* @param String $user
* @param String $pass
* @param IMAP stream as a connection
* @return IMAP stream as a connection
*/
public function setConnection($mailbox, $user, $pass, $conn = null) {
// If connection is not exist then make a connection
if(!$conn) {
// If folder name for processing is not present
if (substr(trim($mailbox), -1) == '}') {
$mailbox = $mailbox . 'INBOX';
}
// Try to connect three times
for($i = 0; $i < self::TRY_TO_CONNECT; $i++) {
$conn = imap_open($mailbox, $user, $pass);
if($conn) {
return $conn;
}
}
echo 'Unable to connect mail server';
exit(); // User can throw an exception here
}
return $conn;
}
/**
* This is the funtion for getting message
*
* @param String $conn
* @param Integer $messageId
*/
public function getMessage($conn, $messageId) {
$structure = imap_fetchstructure($conn, $messageId);
// If message is not multipart
if (!$structure->parts) {
$this->getMessagePart($conn, $messageId, $structure, 0);
}
else {
foreach ($structure->parts as $partno => $part) {
$this->getMessagePart($conn, $messageId, $part, $partno+1);
}
}
}
/**
* This is the funtion for parsing messge parts
*
* @param String $conn
* @param Integer $messageId
* @param Object $partObj
* @param Integer $partno
*/
public function getMessagePart($conn, $messageId, $partObj, $partno) {
// If partno is 0 then fetch body as a single part message
$data = ($partno) ? imap_fetchbody($conn, $messageId, $partno) : imap_body($conn, $messageId);
// Any part may be encoded, even plain text messages, so decoding it
if ($partObj->encoding == 4) {
$data = quoted_printable_decode($data);
}
elseif ($partObj->encoding == 3) {
$data = base64_decode($data);
}
// Collection all parameters, like name, filenames of attachments, etc.
$params = array();
if ($partObj->parameters) {
foreach ((array) $partObj->parameters as $x) {
$params[strtolower($x->attribute)] = $x->value;
}
}
if ($partObj->dparameters) {
foreach ((array) $partObj->dparameters as $x) {
$params[strtolower($x->attribute)] = $x->value;
}
}
// Any part with a filename is an attachment,
if ($params['filename'] || $params['name']) {
// Filename may be given as 'Filename' or 'Name' or both
$filename = ($params['filename'])? $params['filename'] : $params['name'];
$this->attachments[$filename] = $data;
}
// Processing plain text message
if ($partObj->type == 0 && $data) {
// Messages may be split in different parts because of inline attachments,
// so append parts together with blank row.
if (strtolower($partObj->subtype) == 'plain') {
$this->plainBody .= trim($data);
}
else {
$this->htmlBody .= $data;
}
}
// Some times it happens that one message embeded in another.
// This is used to appends the raw source to the main message.
elseif ($partObj->type == 2 && $data) {
$this->plainBody .= $data;
}
// Here is recursive call for subpart of the message
if ($partObj->parts) {
foreach ((array) $partObj->parts as $partno2 => $part2) {
$this->getMessagePart($conn, $messageId, $part2, $partno.'.'.($partno2+1));
}
}
}
/**
* This is the funtion for parsing message
*
* @param String $conn
* @param Integer $messageId
*/
public function parseMessage($conn, $messageId) {
$this->getMessage($conn, $messageId);
$this->makeResult();
}
/**
* Return Received date in to mm-dd-yyyy format
*
* @param String $date format Thu, 20 Aug 2009 15:55:52 +0530
* @return String $date format 08-20-2009
*/
private function getRecDate($date) {
$date = substr($date, 5, 20);
$timestamp = strtotime($date);
return date('m-d-Y', $timestamp);
}
/**
* This function is used for checking the particular email id is present
* in the from addresses of headder information. It returns true if present,
* otherwise returns false.
*
* @param String $fromAddresses
* @param String $userInput
*/
private function containsFromAddress($fromAddresses, $userInput) {
if(strpos($fromAddresses, $userInput) != false) {
return true;
}
return false;
}
/**
* Used for parsing the message by message id.
* If flag is false then fuction parse the message with given message id,
* otherwise parse only if message with given id is unread.
*
* @param Integer $messageId
* @param Boolean $flag
*/
public function pasrseMessageById($messageId, $flag = false) {
$this->parseMessageByFilter($messageId, self::MESSAGE_ID, $flag);
}
/**
* Used for parsing unread messages.
*
*/
public function pasrseUnreadMessages() {
$this->parseMessageByFilter('', self::UNREAD, true);
}
/**
* Used for parsing the message by subject of the message.
* If flag is false then fuction parse all the messages with given subject,
* otherwise parse only unread messages with the given subject.
*
* @param Integer $subject
* @param Boolean $flag
*/
public function pasrseMessagesBySubject($subject, $flag = false) {
$this->parseMessageByFilter($subject, self::SUBJECT, $flag);
}
/**
* Used for parsing the message by the from address of the messages.
* If flag is false then fuction parse all the messages with given from address,
* otherwise parse only unread messages with the given from address.
*
* @param Integer $fromAddress
* @param Boolean $flag
*/
public function pasrseMessagesByFromAddress($fromAddress, $flag = false) {
$this->parseMessageByFilter($fromAddress, self::FROM_ADDRESS, $flag);
}
/**
* Used for parsing the message by received date of the message.
* $rdate should be in mm-dd-yyyy format.
* If flag is false then fuction parse all the messages with given date,
* otherwise parse only unread messages with the given date.
*
* @param Integer $rdate
* @param Boolean $flag
*/
public function pasrseMessagesByRecDate($rdate, $flag = false) {
$this->parseMessageByFilter($rdate, self::RECEIVED_DATE, $flag);
}
/**
* Used for parsing the message by filter given by user
*
* @param Integer $userInput
* @param Integer $filterType
* @param Boolean $flag
*/
public function parseMessageByFilter($userInput, $filterType, $flag) {
$conn = $this->setConnection($this->mailServerName, $this->userName, $this->password);
$countMsg = imap_num_msg($conn);
// Iteration on mailbox messages
for ($i = 1; $i <= $countMsg; $i++) {
$this->headers = @imap_headerinfo($conn, $i);
// Preparing filter by given filter type
if($filterType == 0) {
$filter = $this->headers && ($this->headers->Msgno == trim($userInput));
}
else if($filterType == 1) {
$filter = $this->headers && ($this->getRecDate($this->headers->date) == trim($userInput));
}
else if($filterType == 2) {
$filter = $this->headers && ($this->headers->Subject == trim($userInput));
}
else if($filterType == 3) {
$filter = $this->headers && ($this->containsFromAddress($this->headers->fromaddress, trim($userInput)));
}
else if($filterType == 4) {
$filter = $this->headers && true;
}
// Process only filtered messages by received date
if ($filter) {
if($flag) {
if($this->headers->Unseen == 'U')
$this->parseMessage($conn, $i);
}
else {
$this->parseMessage($conn, $i);
}
}
}
imap_close($conn);
}
/**
* Used for preparing resultant array.
*
*/
private function makeResult() {
$temp = array();
$temp['attachments'] = $this->attachments;
$temp['htmlBody'] = $this->htmlBody;
$temp['plainBody'] = $this->plainBody;
$temp['headers'] = $this->headers;
$this->result[] = $temp;
// Unsetting the variables for next message
unset($this->attachments);
unset($this->htmlBody);
unset($this->plainBody);
unset($this->headers);
}
/**
* This method returns the resultant array.
*
* @retuen Array result
*/
public function getResult() {
return $this->result;
}
}
?>