Current Path : /home/bitrix/ext_www/klimatlend.ua/ajax/esolutions.importexcel/lib/PhpImap/ |
Current File : /home/bitrix/ext_www/klimatlend.ua/ajax/esolutions.importexcel/lib/PhpImap/Mailbox.php |
<?php namespace PhpImap; use stdClass; /** * @see https://github.com/barbushin/php-imap * @author Barbushin Sergey http://linkedin.com/in/barbushin */ class Mailbox { protected $imapPath; protected $imapLogin; protected $imapPassword; protected $imapOptions = 0; protected $imapRetriesNum = 0; protected $imapParams = array(); protected $serverEncoding; protected $attachmentsDir = null; protected $expungeOnDisconnect = true; private $imapStream; /** * @param string $imapPath * @param string $login * @param string $password * @param string $attachmentsDir * @param string $serverEncoding * @throws Exception */ public function __construct($imapPath, $login, $password, $attachmentsDir = null, $serverEncoding = 'UTF-8') { $this->setImapPath($imapPath); $this->imapLogin = $login; $this->imapPassword = $password; $this->serverEncoding = strtoupper($serverEncoding); if($attachmentsDir) { if(!is_dir($attachmentsDir)) { throw new Exception('Directory "' . $attachmentsDir . '" not found'); } $this->attachmentsDir = rtrim(realpath($attachmentsDir), '\\/'); } } /** * Set custom connection arguments of imap_open method. See http://php.net/imap_open * @param int $options * @param int $retriesNum * @param array $params */ public function setConnectionArgs($options = 0, $retriesNum = 0, array $params = null) { $this->imapOptions = $options; $this->imapRetriesNum = $retriesNum; $this->imapParams = $params; } /** * Set custom folder for attachments in case you want to have tree of folders for each email * i.e. a/1 b/1 c/1 where a,b,c - senders, i.e. john@smith.com * @param string $dir folder where to save attachments * * @return void */ public function setAttachmentsDir($dir) { $this->attachmentsDir = $dir; } /** * Get IMAP mailbox connection stream * @param bool $forceConnection Initialize connection if it's not initialized * @return null|resource */ public function getImapStream($forceConnection = true) { if($forceConnection) { if($this->imapStream && (!is_resource($this->imapStream) || !imap_ping($this->imapStream))) { $this->disconnect(); $this->imapStream = null; } if(!$this->imapStream) { $this->imapStream = $this->initImapStream(); } } return $this->imapStream; } /** * Switch mailbox without opening a new connection * * @param string $imapPath */ public function switchMailbox($imapPath = '') { $this->imapPath = $imapPath; $imapStream = @imap_reopen($this->getImapStream(), $imapPath, $this->imapOptions); if(!$imapStream) { throw new Exception("Couldn't switch mailbox: " . imap_last_error()); } } protected function initImapStream() { $imapStream = @imap_open($this->imapPath, $this->imapLogin, $this->imapPassword, $this->imapOptions, $this->imapRetriesNum, $this->imapParams); if(!$imapStream) { $lastError = imap_last_error(); imap_errors(); //throw new Exception('Connection error: ' . $lastError); return null; } return $imapStream; } protected function disconnect() { $imapStream = $this->getImapStream(false); if($imapStream && is_resource($imapStream)) { imap_close($imapStream, $this->expungeOnDisconnect ? CL_EXPUNGE : 0); } } /** * Sets 'expunge on disconnect' parameter * @param bool $isEnabled */ public function setExpungeOnDisconnect($isEnabled) { $this->expungeOnDisconnect = $isEnabled; } /** * Get information about the current mailbox. * * Returns the information in an object with following properties: * Date - current system time formatted according to RFC2822 * Driver - protocol used to access this mailbox: POP3, IMAP, NNTP * Mailbox - the mailbox name * Nmsgs - number of mails in the mailbox * Recent - number of recent mails in the mailbox * * @return stdClass */ public function checkMailbox() { return imap_check($this->getImapStream()); } /** * Creates a new mailbox specified by mailbox. * * @return bool */ public function createMailbox() { return imap_createmailbox($this->getImapStream(), imap_utf7_encode($this->imapPath)); } /** * Gets status information about the given mailbox. * * This function returns an object containing status information. * The object has the following properties: messages, recent, unseen, uidnext, and uidvalidity. * * @return stdClass if the box doesn't exist */ public function statusMailbox() { return imap_status($this->getImapStream(), $this->imapPath, SA_ALL); } /** * Gets listing the folders * * This function returns an object containing listing the folders. * The object has the following properties: messages, recent, unseen, uidnext, and uidvalidity. * * @return array listing the folders */ public function getListingFolders() { $folders = imap_list($this->getImapStream(), $this->imapPath, "*"); if(!is_array($folders)) $folders = array(); $inboxFolder = preg_grep('/\}INBOX/i', $folders); if(count($inboxFolder) > 0) { $inboxFolder = current($inboxFolder); $folders = array_diff($folders, array($inboxFolder)); array_unshift($folders, $inboxFolder); } $arFolders = array(); foreach ($folders as $key => $folder) { $origFolder = str_replace($this->imapPath, "", $folder); if (function_exists('mb_convert_encoding')) { $folder = str_replace($this->imapPath, "", mb_convert_encoding($folder, "UTF-8", "UTF7-IMAP")); } else { $folder = str_replace($this->imapPath, "", imap_utf7_decode($folder)); } $folders[$key] = $folder; $arFolders[$origFolder] = $folder; } return $arFolders; } /** * This function uses imap_search() to perform a search on the mailbox currently opened in the given IMAP stream. * For example, to match all unanswered mails sent by Mom, you'd use: "UNANSWERED FROM mom". * * @param string $criteria See http://php.net/imap_search for a complete list of available criteria * @return array mailsIds (or empty array) */ public function searchMailbox($criteria = 'ALL') { $mailsIds = imap_search($this->getImapStream(), $criteria, SE_UID, $this->serverEncoding); return $mailsIds ? $mailsIds : array(); } /** * Save mail body. * @return bool */ public function saveMail($mailId, $filename = 'email.eml') { return imap_savebody($this->getImapStream(), $filename, $mailId, "", FT_UID); } /** * Marks mails listed in mailId for deletion. * @return bool */ public function deleteMail($mailId) { return imap_delete($this->getImapStream(), $mailId, FT_UID); } /** * Moves mails listed in mailId into new mailbox * @return bool */ public function moveMail($mailId, $mailBox) { return imap_mail_move($this->getImapStream(), $mailId, $mailBox, CP_UID) && $this->expungeDeletedMails(); } /** * Copys mails listed in mailId into new mailbox * @return bool */ public function copyMail($mailId, $mailBox) { return imap_mail_copy($this->getImapStream(), $mailId, $mailBox, CP_UID) && $this->expungeDeletedMails(); } /** * Deletes all the mails marked for deletion by imap_delete(), imap_mail_move(), or imap_setflag_full(). * @return bool */ public function expungeDeletedMails() { return imap_expunge($this->getImapStream()); } /** * Add the flag \Seen to a mail. * @return bool */ public function markMailAsRead($mailId) { return $this->setFlag(array($mailId), '\\Seen'); } /** * Remove the flag \Seen from a mail. * @return bool */ public function markMailAsUnread($mailId) { return $this->clearFlag(array($mailId), '\\Seen'); } /** * Add the flag \Flagged to a mail. * @return bool */ public function markMailAsImportant($mailId) { return $this->setFlag(array($mailId), '\\Flagged'); } /** * Add the flag \Seen to a mails. * @return bool */ public function markMailsAsRead(array $mailId) { return $this->setFlag($mailId, '\\Seen'); } /** * Remove the flag \Seen from some mails. * @return bool */ public function markMailsAsUnread(array $mailId) { return $this->clearFlag($mailId, '\\Seen'); } /** * Add the flag \Flagged to some mails. * @return bool */ public function markMailsAsImportant(array $mailId) { return $this->setFlag($mailId, '\\Flagged'); } /** * Causes a store to add the specified flag to the flags set for the mails in the specified sequence. * * @param array $mailsIds * @param string $flag which you can set are \Seen, \Answered, \Flagged, \Deleted, and \Draft as defined by RFC2060. * @return bool */ public function setFlag(array $mailsIds, $flag) { return imap_setflag_full($this->getImapStream(), implode(',', $mailsIds), $flag, ST_UID); } /** * Cause a store to delete the specified flag to the flags set for the mails in the specified sequence. * * @param array $mailsIds * @param string $flag which you can set are \Seen, \Answered, \Flagged, \Deleted, and \Draft as defined by RFC2060. * @return bool */ public function clearFlag(array $mailsIds, $flag) { return imap_clearflag_full($this->getImapStream(), implode(',', $mailsIds), $flag, ST_UID); } /** * Fetch mail headers for listed mails ids * * Returns an array of objects describing one mail header each. The object will only define a property if it exists. The possible properties are: * subject - the mails subject * from - who sent it * to - recipient * date - when was it sent * message_id - Mail-ID * references - is a reference to this mail id * in_reply_to - is a reply to this mail id * size - size in bytes * uid - UID the mail has in the mailbox * msgno - mail sequence number in the mailbox * recent - this mail is flagged as recent * flagged - this mail is flagged * answered - this mail is flagged as answered * deleted - this mail is flagged for deletion * seen - this mail is flagged as already read * draft - this mail is flagged as being a draft * * @param array $mailsIds * @return array */ public function getMailsInfo(array $mailsIds) { $mails = imap_fetch_overview($this->getImapStream(), implode(',', $mailsIds), FT_UID); if(is_array($mails) && count($mails)) { foreach($mails as &$mail) { if(isset($mail->subject)) { $mail->subject = $this->decodeMimeStr($mail->subject, $this->serverEncoding); } if(isset($mail->from)) { $mail->from = $this->decodeMimeStr($mail->from, $this->serverEncoding); } if(isset($mail->to)) { $mail->to = $this->decodeMimeStr($mail->to, $this->serverEncoding); } } } return $mails; } /** * Get information about the current mailbox. * * Returns an object with following properties: * Date - last change (current datetime) * Driver - driver * Mailbox - name of the mailbox * Nmsgs - number of messages * Recent - number of recent messages * Unread - number of unread messages * Deleted - number of deleted messages * Size - mailbox size * * @return object Object with info | FALSE on failure */ public function getMailboxInfo() { return imap_mailboxmsginfo($this->getImapStream()); } /** * Gets mails ids sorted by some criteria * * Criteria can be one (and only one) of the following constants: * SORTDATE - mail Date * SORTARRIVAL - arrival date (default) * SORTFROM - mailbox in first From address * SORTSUBJECT - mail subject * SORTTO - mailbox in first To address * SORTCC - mailbox in first cc address * SORTSIZE - size of mail in octets * * @param int $criteria * @param bool $reverse * @return array Mails ids */ public function sortMails($criteria = SORTARRIVAL, $reverse = true) { return imap_sort($this->getImapStream(), $criteria, $reverse, SE_UID); } /** * Get mails count in mail box * @return int */ public function countMails() { return imap_num_msg($this->getImapStream()); } /** * Retrieve the quota settings per user * @return array - FALSE in the case of call failure */ protected function getQuota() { return imap_get_quotaroot($this->getImapStream(), 'INBOX'); } /** * Return quota limit in KB * @return int - FALSE in the case of call failure */ public function getQuotaLimit() { $quota = $this->getQuota(); if(is_array($quota)) { $quota = $quota['STORAGE']['limit']; } return $quota; } /** * Return quota usage in KB * @return int - FALSE in the case of call failure */ public function getQuotaUsage() { $quota = $this->getQuota(); if(is_array($quota)) { $quota = $quota['STORAGE']['usage']; } return $quota; } /** * Get raw mail data * * @param $msgId * @param bool $markAsSeen * @return mixed */ public function getRawMail($msgId, $markAsSeen = true){ $options = FT_UID; if(!$markAsSeen) { $options |= FT_PEEK; } return imap_fetchbody($this->getImapStream(), $msgId, '', $options); } /** * Get mail data * * @param $mailId * @param bool $markAsSeen * @return IncomingMail */ public function getMail($mailId, $markAsSeen = true) { $headersRaw = imap_fetchheader($this->getImapStream(), $mailId, FT_UID); $head = imap_rfc822_parse_headers($headersRaw); $mail = new IncomingMail(); $mail->headersRaw = $headersRaw; $mail->headers = $head; $mail->id = $mailId; $mail->date = date('Y-m-d H:i:s', isset($head->date) ? strtotime(preg_replace('/\(.*?\)/', '', $head->date)) : time()); $mail->subject = isset($head->subject) ? $this->decodeMimeStr($head->subject, $this->serverEncoding) : null; $mail->fromName = isset($head->from[0]->personal) ? $this->decodeMimeStr($head->from[0]->personal, $this->serverEncoding) : null; $mail->fromAddress = strtolower($head->from[0]->mailbox . '@' . $head->from[0]->host); if(isset($head->to)) { $toStrings = array(); foreach($head->to as $to) { if(!empty($to->mailbox) && !empty($to->host)) { $toEmail = strtolower($to->mailbox . '@' . $to->host); $toName = isset($to->personal) ? $this->decodeMimeStr($to->personal, $this->serverEncoding) : null; $toStrings[] = $toName ? "$toName <$toEmail>" : $toEmail; $mail->to[$toEmail] = $toName; } } $mail->toString = implode(', ', $toStrings); } if(isset($head->cc)) { foreach($head->cc as $cc) { $mail->cc[strtolower($cc->mailbox . '@' . $cc->host)] = isset($cc->personal) ? $this->decodeMimeStr($cc->personal, $this->serverEncoding) : null; } } if(isset($head->bcc)) { foreach($head->bcc as $bcc) { $mail->bcc[strtolower($bcc->mailbox . '@' . $bcc->host)] = isset($bcc->personal) ? $this->decodeMimeStr($bcc->personal, $this->serverEncoding) : null; } } if(isset($head->reply_to)) { foreach($head->reply_to as $replyTo) { $mail->replyTo[strtolower($replyTo->mailbox . '@' . $replyTo->host)] = isset($replyTo->personal) ? $this->decodeMimeStr($replyTo->personal, $this->serverEncoding) : null; } } if(isset($head->message_id)) { $mail->messageId = $head->message_id; } $mailStructure = imap_fetchstructure($this->getImapStream(), $mailId, FT_UID); if(empty($mailStructure->parts)) { $this->initMailPart($mail, $mailStructure, 0, $markAsSeen); } else { foreach($mailStructure->parts as $partNum => $partStructure) { $this->initMailPart($mail, $partStructure, $partNum + 1, $markAsSeen); } } return $mail; } protected function initMailPart(IncomingMail $mail, $partStructure, $partNum, $markAsSeen = true) { $options = FT_UID; if(!$markAsSeen) { $options |= FT_PEEK; } $data = $partNum ? imap_fetchbody($this->getImapStream(), $mail->id, $partNum, $options) : imap_body($this->getImapStream(), $mail->id, $options); if($partStructure->encoding == 1) { $data = imap_utf8($data); } elseif($partStructure->encoding == 2) { $data = imap_binary($data); } elseif($partStructure->encoding == 3) { $data = preg_replace('~[^a-zA-Z0-9+=/]+~s', '', $data); // https://github.com/barbushin/php-imap/issues/88 $data = imap_base64($data); } elseif($partStructure->encoding == 4) { $data = quoted_printable_decode($data); } $params = array(); if(!empty($partStructure->parameters)) { foreach($partStructure->parameters as $param) { $params[strtolower($param->attribute)] = $param->value; } } if(!empty($partStructure->dparameters)) { foreach($partStructure->dparameters as $param) { $paramName = strtolower(preg_match('~^(.*?)\*~', $param->attribute, $matches) ? $matches[1] : $param->attribute); if(isset($params[$paramName])) { $params[$paramName] .= $param->value; } else { $params[$paramName] = $param->value; } } } // attachments $attachmentId = $partStructure->ifid ? trim($partStructure->id, " <>") : (isset($params['filename']) || isset($params['name']) ? mt_rand() . mt_rand() : null); // ignore contentId on body when mail isn't multipart (https://github.com/barbushin/php-imap/issues/71) if (!$partNum && TYPETEXT === $partStructure->type) { $attachmentId = null; } if($attachmentId) { if(empty($params['filename']) && empty($params['name'])) { $fileName = $attachmentId . '.' . strtolower($partStructure->subtype); } else { $fileName = !empty($params['filename']) ? $params['filename'] : $params['name']; $fileName = $this->decodeMimeStr($fileName, $this->serverEncoding); $fileName = $this->decodeRFC2231($fileName, $this->serverEncoding); } $attachment = new IncomingMailAttachment(); $attachment->id = $attachmentId; $attachment->name = $fileName; $attachment->disposition = (isset($partStructure->disposition) ? $partStructure->disposition : null); if($this->attachmentsDir) { $replace = array( '/\s/' => '_', '/[^0-9a-zа-яіїє_\.]/iu' => '', '/_+/' => '_', '/(^_)|(_$)/' => '', ); $fileSysName = preg_replace('~[\\\\/]~', '', $mail->id . '_' . $attachmentId . '_' . preg_replace(array_keys($replace), $replace, $fileName)); $attachment->filePath = $this->attachmentsDir . DIRECTORY_SEPARATOR . $fileSysName; if(strlen($attachment->filePath) > 255) { $ext = pathinfo($attachment->filePath, PATHINFO_EXTENSION); $attachment->filePath = substr($attachment->filePath, 0, 255 -1 -strlen($ext)).".".$ext; } file_put_contents($attachment->filePath, $data); } $mail->addAttachment($attachment); } else { if(!empty($params['charset'])) { $data = $this->convertStringEncoding($data, $params['charset'], $this->serverEncoding); } if($partStructure->type == 0 && $data) { if(strtolower($partStructure->subtype) == 'plain') { $mail->textPlain .= $data; } else { $mail->textHtml .= $data; } } elseif($partStructure->type == 2 && $data) { $mail->textPlain .= trim($data); } } if(!empty($partStructure->parts)) { foreach($partStructure->parts as $subPartNum => $subPartStructure) { if($partStructure->type == 2 && $partStructure->subtype == 'RFC822') { $this->initMailPart($mail, $subPartStructure, $partNum, $markAsSeen); } else { $this->initMailPart($mail, $subPartStructure, $partNum . '.' . ($subPartNum + 1), $markAsSeen); } } } } protected function decodeMimeStr($string, $charset = 'utf-8') { $newString = ''; $elements = imap_mime_header_decode($string); for($i = 0; $i < count($elements); $i++) { if($elements[$i]->charset == 'default') { $elements[$i]->charset = 'iso-8859-1'; } $newString .= $this->convertStringEncoding($elements[$i]->text, $elements[$i]->charset, $charset); } return $newString; } function isUrlEncoded($string) { $hasInvalidChars = preg_match( '#[^%a-zA-Z0-9\-_\.\+]#', $string ); $hasEscapedChars = preg_match( '#%[a-zA-Z0-9]{2}#', $string ); return !$hasInvalidChars && $hasEscapedChars; } protected function decodeRFC2231($string, $charset = 'utf-8') { if(preg_match("/^(.*?)'.*?'(.*?)$/", $string, $matches)) { $encoding = $matches[1]; $data = $matches[2]; if($this->isUrlEncoded($data)) { $string = $this->convertStringEncoding(urldecode($data), $encoding, $charset); } } return $string; } /** * Converts a string from one encoding to another. * @param string $string * @param string $fromEncoding * @param string $toEncoding * @return string Converted string if conversion was successful, or the original string if not */ protected function convertStringEncoding($string, $fromEncoding, $toEncoding) { $convertedString = null; if($string && $fromEncoding != $toEncoding) { $convertedString = @iconv($fromEncoding, $toEncoding . '//IGNORE', $string); if(!$convertedString && extension_loaded('mbstring')) { $convertedString = @mb_convert_encoding($string, $toEncoding, $fromEncoding); } } return $convertedString ?: $string; } public function __destruct() { $this->disconnect(); } /** * @param $imapPath * @return void */ protected function setImapPath($imapPath) { if (function_exists('mb_convert_encoding')) { $this->imapPath = mb_convert_encoding($imapPath, "UTF7-IMAP", "UTF-8"); } else { $this->imapPath = imap_utf7_encode($imapPath); } } } class Exception extends \Exception { }