├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── example ├── ImapToDB.sql └── index.php └── src └── PhpImap ├── IncomingMail.php └── Mailbox.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Example 3 | ################# 4 | example/attachments/* 5 | 6 | ################# 7 | ## Eclipse 8 | ################# 9 | 10 | *.pydevproject 11 | .project 12 | .metadata 13 | bin/ 14 | tmp/ 15 | *.tmp 16 | *.bak 17 | *.swp 18 | *~.nib 19 | local.properties 20 | .classpath 21 | .settings/ 22 | .loadpath 23 | 24 | # External tool builders 25 | .externalToolBuilders/ 26 | 27 | # Locally stored "Eclipse launch configurations" 28 | *.launch 29 | 30 | # CDT-specific 31 | .cproject 32 | 33 | # PDT-specific 34 | .buildpath 35 | 36 | 37 | ################# 38 | ## Visual Studio 39 | ################# 40 | 41 | ## Ignore Visual Studio temporary files, build results, and 42 | ## files generated by popular Visual Studio add-ons. 43 | 44 | # User-specific files 45 | *.suo 46 | *.user 47 | *.sln.docstates 48 | 49 | # Build results 50 | [Dd]ebug/ 51 | [Rr]elease/ 52 | *_i.c 53 | *_p.c 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.vspscc 68 | .builds 69 | *.dotCover 70 | 71 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 72 | #packages/ 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opensdf 79 | *.sdf 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | 85 | # ReSharper is a .NET coding add-in 86 | _ReSharper* 87 | 88 | # Installshield output folder 89 | [Ee]xpress 90 | 91 | # DocProject is a documentation generator add-in 92 | DocProject/buildhelp/ 93 | DocProject/Help/*.HxT 94 | DocProject/Help/*.HxC 95 | DocProject/Help/*.hhc 96 | DocProject/Help/*.hhk 97 | DocProject/Help/*.hhp 98 | DocProject/Help/Html2 99 | DocProject/Help/html 100 | 101 | # Click-Once directory 102 | publish 103 | 104 | # Others 105 | [Bb]in 106 | [Oo]bj 107 | sql 108 | TestResults 109 | *.Cache 110 | ClientBin 111 | stylecop.* 112 | ~$* 113 | *.dbmdl 114 | Generated_Code #added for RIA/Silverlight projects 115 | 116 | # Backup & report files from converting an old project file to a newer 117 | # Visual Studio version. Backup files are not needed, because we have git ;-) 118 | _UpgradeReport_Files/ 119 | Backup*/ 120 | UpgradeLog*.XML 121 | 122 | 123 | 124 | ############ 125 | ## Windows 126 | ############ 127 | 128 | # Windows image file caches 129 | Thumbs.db 130 | 131 | # Folder config file 132 | Desktop.ini 133 | 134 | 135 | ############# 136 | ## Python 137 | ############# 138 | 139 | *.py[co] 140 | 141 | # Packages 142 | *.egg 143 | *.egg-info 144 | dist 145 | build 146 | eggs 147 | parts 148 | bin 149 | var 150 | sdist 151 | develop-eggs 152 | .installed.cfg 153 | 154 | # Installer logs 155 | pip-log.txt 156 | 157 | # Unit test / coverage reports 158 | .coverage 159 | .tox 160 | 161 | #Translations 162 | *.mo 163 | 164 | #Mr Developer 165 | .mr.developer.cfg 166 | 167 | # Mac crap 168 | .DS_Store 169 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ImapMailbox 2 | 3 | Copyright (c) 2012 by Barbushin Sergey . 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the following 15 | disclaimer in the documentation and/or other materials provided 16 | with the distribution. 17 | 18 | * The names of the contributors may not be used to endorse or 19 | promote products derived from this software without specific 20 | prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ImapMailbox is PHP class to access mailbox by POP3/IMAP/NNTP using IMAP extension 3 | 4 | Based on main ImapMailbox class: 5 | 6 | Added functionality for viewing multiple versions of headers 7 | 8 | Added Functionality to add to database 9 | 10 | Can work with PHPMailer to parse email and resent 11 | 12 | ### Features 13 | 14 | * Connect to mailbox by POP3/IMAP/NNTP (see [imap_open](http://php.net/imap_open)) 15 | * Get mailbox status (see [imap_check](http://php.net/imap_check)) 16 | * Receive emails (+attachments, +html body images) 17 | * Search emails by custom criteria (see [imap_search](http://php.net/imap_search)) 18 | * Change email status (see [imap_setflag_full](http://php.net/imap_setflag_full)) 19 | * Delete email 20 | 21 | ### Installation by Composer 22 | 23 | { 24 | "require": { 25 | "php-imap/php-imap": "2.*" 26 | } 27 | } 28 | 29 | ### Migration from `v1.*` to `v2.*` 30 | 31 | Just add following code in the head of your script: 32 | 33 | use PhpImap\Mailbox as ImapMailbox; 34 | use PhpImap\IncomingMail; 35 | use PhpImap\IncomingMailAttachment; 36 | 37 | ### [Usage example](https://github.com/escobar022/php-imap/blob/master/example/index.php) 38 | ```php 39 | 40 | $mailbox = new PhpImap\Mailbox('{imap.gmail.com:993/imap/ssl}INBOX', 'some@gmail.com', '*********', __DIR__); 41 | $mails = array(); 42 | 43 | $mailsIds = $mailbox->searchMailBox('ALL'); 44 | if(!$mailsIds) { 45 | die('Mailbox is empty'); 46 | } 47 | 48 | $mailId = reset($mailsIds); 49 | $mail = $mailbox->getMail($mailId); 50 | 51 | var_dump($mail); 52 | var_dump($mail->getAttachments()); 53 | ``` 54 | 55 | 56 | ### Trouble shooting 57 | 58 | Attachments wont download 59 | Check directory config, make sure that current user/and or server has persmision 60 | 61 | sudo chown -R apache:apache . 62 | -or- 63 | sudo chown -R nginx:nginx . 64 | sudo find . -type d -exec chmod 755 {} \; 65 | sudo find . -type f -exec chmod 664 {} \; 66 | 67 | ### Recommended 68 | 69 | * Google Chrome extension [PHP Console](https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef) 70 | * Google Chrome extension [JavaScript Errors Notifier](https://chrome.google.com/webstore/detail/javascript-errors-notifie/jafmfknfnkoekkdocjiaipcnmkklaajd) 71 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-imap/php-imap", 3 | "description": "PHP class to access mailbox by POP3/IMAP/NNTP using IMAP extension", 4 | "keywords": [ 5 | "PHP", 6 | "IMAP", 7 | "mail" 8 | ], 9 | "homepage": "https://github.com/barbushin/php-imap", 10 | "license": "BSD 3-Clause", 11 | "type": "library", 12 | "authors": [ 13 | { 14 | "name": "Sergey Barbushin", 15 | "homepage": "http://linkedin.com/in/barbushin", 16 | "email": "barbushin@gmail.com" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=5.3.0" 21 | }, 22 | "autoload": { 23 | "psr-0": { 24 | "PhpImap": "src/" 25 | } 26 | }, 27 | "minimum-stability": "stable" 28 | } 29 | -------------------------------------------------------------------------------- /example/ImapToDB.sql: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Andres Escobar 3 | * Based on code by: Ernest Wojciuk 4 | * https://www.linkedin.com/in/apescobar 5 | */ 6 | 7 | 8 | CREATE TABLE `emailtodb_email` ( 9 | `ID` int(11) NOT NULL auto_increment, 10 | `IDEmail` varchar(255) NOT NULL default '0', 11 | `EmailFrom` varchar(255) NOT NULL default '', 12 | `EmailFromP` varchar(255) NOT NULL default '', 13 | `EmailTo` varchar(255) NOT NULL default '', 14 | `DateE` datetime NOT NULL default '0000-00-00 00:00:00', 15 | `DateDb` datetime NOT NULL default '0000-00-00 00:00:00', 16 | `DateRead` datetime NOT NULL default '0000-00-00 00:00:00', 17 | `DateRe` datetime NOT NULL default '0000-00-00 00:00:00', 18 | `Status` tinyint(3) NOT NULL default '0', 19 | `Type` tinyint(3) NOT NULL default '0', 20 | `Del` tinyint(3) NOT NULL default '0', 21 | `Subject` varchar(255) default NULL, 22 | `Message` text NOT NULL, 23 | `Message_html` text NOT NULL, 24 | `MsgSize` int(11) NOT NULL default '0', 25 | `Kind` tinyint(2) NOT NULL default '0', 26 | `IDre` int(11) NOT NULL default '0', 27 | PRIMARY KEY (`ID`), 28 | KEY `IDEmail` (`IDEmail`), 29 | KEY `EmailFrom` (`EmailFrom`) 30 | ) ENGINE=MyISAM; 31 | 32 | 33 | CREATE TABLE `emailtodb_attach` ( 34 | `ID` int(11) NOT NULL auto_increment, 35 | `IDEmail` int(11) NOT NULL default '0', 36 | `FileNameOrg` varchar(255) NOT NULL default '', 37 | `Filedir` varchar(255) NOT NULL default '', 38 | `AttachType` varchar(255) NOT NULL default '', 39 | PRIMARY KEY (`ID`), 40 | KEY `IDEmail` (`IDEmail`) 41 | ) ENGINE=MyISAM; 42 | -------------------------------------------------------------------------------- /example/index.php: -------------------------------------------------------------------------------- 1 | searchMailBox( 'ALL' ); 49 | 50 | if ( ! $mailsIds ) { 51 | die( 'Mailbox is empty' ); 52 | } 53 | 54 | //get only from first email in array 55 | //$mailId = reset($mailsIds); 56 | 57 | //get only from latest email in array 58 | $mailId = end( $mailsIds ); 59 | 60 | 61 | //Get Header Only, See Option in function 62 | //$header = $mailbox->getHeader( $mailId ); 63 | //var_dump( $header ); 64 | 65 | //Get all mail parts 66 | $mail = $mailbox->getMail( $mailId ); 67 | //var_dump( $mail ); 68 | 69 | //Display Mail with relative URI, Dependent on getMail() 70 | var_dump($mail->replaceInternalLinks($baseUri)); 71 | 72 | //Insert Into DB with Base URI 73 | //$db_insert = $mail->db_add_message( $baseUri ); 74 | //var_dump( $db_insert ); 75 | 76 | 77 | /*Use PHPMailer to Send email Received 78 | * 79 | * Need to add PHPMailer to root folder of project, https://github.com/PHPMailer/PHPMailer 80 | require_once( '../PHPMailer-master/PHPMailerAutoload.php' ); 81 | * (OR reference it to current location, ei.Worpdress as below) 82 | global $phpmailer; 83 | if ( ! is_object( $phpmailer ) || ! is_a( $phpmailer, 'PHPMailer' ) ) { 84 | require_once ABSPATH . WPINC . '/class-phpmailer.php'; 85 | require_once ABSPATH . WPINC . '/class-smtp.php'; 86 | $phpmailer = new PHPMailer(); 87 | } 88 | */ 89 | 90 | /* 91 | //optional for POP3 92 | $pop = POP3::popBeforeSmtp(GMAIL_IMAP_PATH, 110, 30, GMAIL_EMAIL, GMAIL_PASSWORD, 1); 93 | 94 | //Create a new PHPMailer instance 95 | //Passing true to the constructor enables the use of exceptions for error handling 96 | $mail2 = new PHPMailer(); 97 | try { 98 | $mail2->isSMTP(); 99 | $mail2->SMTPDebug = 0; 100 | $mail2->Debugoutput = 'html'; 101 | $mail2->Host = ''; 102 | $mail2->Port = 25; 103 | $mail2->SMTPAuth = false; 104 | $mail2->From = GMAIL_EMAIL; 105 | $mail2->FromName = 'Sender'; 106 | $mail2->addAddress('receiver@example.com','Receiver'); 107 | 108 | //Optional to add different Reply to 109 | // $mail2->addReplyTo('sender@example.com', 'Sender'); 110 | 111 | //Optional See getHeader 112 | // $mail2->addCustomHeader('ParentID','420'); 113 | 114 | $mail2->Subject = $mail->subject; 115 | $mail2->msgHTML($mail->replaceInternalLinks($baseUri)); 116 | 117 | //get attachments 118 | foreach($mail->getAttachments() as $ATTACHMENT){ 119 | if($ATTACHMENT->disposition != 'INLINE'){ 120 | $mail2->addAttachment($ATTACHMENT->filePath, $ATTACHMENT->name); 121 | } 122 | } 123 | $mail2->send(); 124 | echo "Message sent!"; 125 | } catch (phpmailerException $e) { 126 | echo $e->errorMessage(); //Pretty error messages from PHPMailer 127 | } catch (Exception $e) { 128 | echo $e->getMessage(); //Boring error messages from anything else! 129 | } 130 | */ 131 | -------------------------------------------------------------------------------- /src/PhpImap/IncomingMail.php: -------------------------------------------------------------------------------- 1 | attachments[$attachment->id] = $attachment; 29 | } 30 | 31 | /** 32 | * @return IncomingMailAttachment[] 33 | */ 34 | public function getAttachments() { 35 | return $this->attachments; 36 | } 37 | 38 | /** 39 | * Get array of internal HTML links placeholders 40 | * @return array attachmentId => link placeholder 41 | */ 42 | public function getInternalLinksPlaceholders() { 43 | return preg_match_all( '/=["\'](cid:([\w\.%*@-]+))["\']/i', $this->textHtml, $matches ) ? array_combine( $matches[2], $matches[1] ) : array(); 44 | } 45 | 46 | public function replaceInternalLinks($baseUri) { 47 | $baseUri = rtrim($baseUri, '\\/') . '/'; 48 | $fetchedHtml = $this->textHtml; 49 | foreach($this->getInternalLinksPlaceholders() as $attachmentId => $placeholder) { 50 | $fetchedHtml = str_replace($placeholder, $baseUri . basename($this->attachments[$attachmentId]->filePath), $fetchedHtml); 51 | } 52 | return $fetchedHtml; 53 | } 54 | 55 | /** 56 | * Get array of internal HTML links placeholders and add info to Database 57 | * @return array attachmentId => link placeholder 58 | */ 59 | 60 | function db_add_message( $baseUri ) { 61 | 62 | $baseUri = rtrim( $baseUri, '\\/' ) . '/'; 63 | $fetchedHtml = $this->textHtml; 64 | 65 | foreach ( $this->getInternalLinksPlaceholders() as $attachmentId => $placeholder ) { 66 | if ( isset( $this->attachments[ $attachmentId ] ) ) { 67 | $fetchedHtml = str_replace( $placeholder, $baseUri . basename( $this->attachments[ $attachmentId ]->filePath ), $fetchedHtml ); 68 | } 69 | } 70 | 71 | $execute = mysql_query( "INSERT INTO emailtodb_email (IDEmail, EmailFrom, EmailFromP, EmailTo, DateE, DateDb, Subject, Message, Message_html, MsgSize) VALUES 72 | ('" . $this->id . "', 73 | '" . $this->fromAddress . "', 74 | '" . addslashes( strip_tags( $this->fromName ) ) . "', 75 | '" . addslashes( strip_tags( $this->toString ) ) . "', 76 | '" . $this->date . "', 77 | '" . date( "Y-m-d H:i:s" ) . "', 78 | '" . addslashes( $this->subject ) . "', 79 | '" . addslashes( '' ) . "', 80 | '" . addslashes( $fetchedHtml ) . "', 81 | '')" ); 82 | $execute = mysql_query( "select LAST_INSERT_ID() as UID" ); 83 | $row = mysql_fetch_array( $execute ); 84 | $this->newid = $row["UID"]; 85 | 86 | 87 | $attachments = $this->getAttachments(); 88 | 89 | 90 | foreach ( $attachments as $attachment ) { 91 | $this->db_add_attach( $this->newid, $attachment->name, $attachment->filePath, $attachment->disposition ); 92 | } 93 | 94 | return $fetchedHtml; 95 | } 96 | 97 | function db_add_attach( $newid, $file_orig, $filedir,$attachmenttype ) { 98 | 99 | mysql_query( "INSERT INTO emailtodb_attach (IDEmail, FileNameOrg, Filedir, AttachType) VALUES ('" . $newid . "','" . addslashes( $file_orig ) . "','" . addslashes( $filedir ) ."','" . addslashes( $attachmenttype ) . "')" ); 100 | } 101 | 102 | } 103 | 104 | class IncomingMailAttachment { 105 | 106 | public $id; 107 | public $name; 108 | public $filePath; 109 | public $disposition; 110 | } 111 | -------------------------------------------------------------------------------- /src/PhpImap/Mailbox.php: -------------------------------------------------------------------------------- 1 | imapPath = $imapPath; 22 | $this->imapLogin = $login; 23 | $this->imapPassword = $password; 24 | $this->serverEncoding = strtoupper($serverEncoding); 25 | if($attachmentsDir) { 26 | if(!is_dir($attachmentsDir)) { 27 | throw new Exception('Directory "' . $attachmentsDir . '" not found'); 28 | } 29 | $this->attachmentsDir = rtrim(realpath($attachmentsDir), '\\/'); 30 | } 31 | } 32 | 33 | /** 34 | * Set custom connection arguments of imap_open method. See http://php.net/imap_open 35 | * @param int $options 36 | * @param int $retriesNum 37 | * @param array $params 38 | */ 39 | public function setConnectionArgs($options = 0, $retriesNum = 0, array $params = null) { 40 | $this->imapOptions = $options; 41 | $this->imapRetriesNum = $retriesNum; 42 | $this->imapParams = $params; 43 | } 44 | 45 | /** 46 | * Get IMAP mailbox connection stream 47 | * @param bool $forceConnection Initialize connection if it's not initialized 48 | * @return null|resource 49 | */ 50 | public function getImapStream($forceConnection = true) { 51 | static $imapStream; 52 | if($forceConnection) { 53 | if($imapStream && (!is_resource($imapStream) || !imap_ping($imapStream))) { 54 | $this->disconnect(); 55 | $imapStream = null; 56 | } 57 | if(!$imapStream) { 58 | $imapStream = $this->initImapStream(); 59 | } 60 | } 61 | return $imapStream; 62 | } 63 | 64 | protected function initImapStream() { 65 | $imapStream = @imap_open($this->imapPath, $this->imapLogin, $this->imapPassword, $this->imapOptions, $this->imapRetriesNum, $this->imapParams); 66 | if(!$imapStream) { 67 | throw new Exception('Connection error: ' . imap_last_error()); 68 | } 69 | return $imapStream; 70 | } 71 | 72 | protected function disconnect() { 73 | $imapStream = $this->getImapStream(false); 74 | if($imapStream && is_resource($imapStream)) { 75 | imap_close($imapStream, CL_EXPUNGE); 76 | } 77 | } 78 | 79 | /** 80 | * Get information about the current mailbox. 81 | * 82 | * Returns the information in an object with following properties: 83 | * Date - current system time formatted according to RFC2822 84 | * Driver - protocol used to access this mailbox: POP3, IMAP, NNTP 85 | * Mailbox - the mailbox name 86 | * Nmsgs - number of mails in the mailbox 87 | * Recent - number of recent mails in the mailbox 88 | * 89 | * @return stdClass 90 | */ 91 | public function checkMailbox() { 92 | return imap_check($this->getImapStream()); 93 | } 94 | 95 | /** 96 | * Creates a new mailbox specified by mailbox. 97 | * 98 | * @return bool 99 | */ 100 | 101 | public function createMailbox() { 102 | return imap_createmailbox($this->getImapStream(), imap_utf7_encode($this->imapPath)); 103 | } 104 | 105 | /** 106 | * Gets status information about the given mailbox. 107 | * 108 | * This function returns an object containing status information. 109 | * The object has the following properties: messages, recent, unseen, uidnext, and uidvalidity. 110 | * 111 | * @return stdClass if the box doesn't exist 112 | */ 113 | 114 | public function statusMailbox() { 115 | return imap_status($this->getImapStream(), $this->imapPath, SA_ALL); 116 | } 117 | 118 | 119 | /** 120 | * Gets listing the folders 121 | * 122 | * This function returns an object containing listing the folders. 123 | * The object has the following properties: messages, recent, unseen, uidnext, and uidvalidity. 124 | * 125 | * @return array listing the folders 126 | */ 127 | 128 | public function getListingFolders() { 129 | $folders = imap_list($this->getImapStream(), $this->imapPath, "*"); 130 | foreach ($folders as $key => $folder) 131 | { 132 | $folder = str_replace($this->imapPath, "", imap_utf7_decode($folder)); 133 | $folders[ $key ] = $folder; 134 | } 135 | return $folders; 136 | } 137 | 138 | 139 | /** 140 | * This function performs a search on the mailbox currently opened in the given IMAP stream. 141 | * For example, to match all unanswered mails sent by Mom, you'd use: "UNANSWERED FROM mom". 142 | * Searches appear to be case insensitive. This list of criteria is from a reading of the UW 143 | * c-client source code and may be incomplete or inaccurate (see also RFC2060, section 6.4.4). 144 | * 145 | * @param string $criteria String, delimited by spaces, in which the following keywords are allowed. Any multi-word arguments (e.g. FROM "joey smith") must be quoted. Results will match all criteria entries. 146 | * ALL - return all mails matching the rest of the criteria 147 | * ANSWERED - match mails with the \\ANSWERED flag set 148 | * BCC "string" - match mails with "string" in the Bcc: field 149 | * BEFORE "date" - match mails with Date: before "date" 150 | * BODY "string" - match mails with "string" in the body of the mail 151 | * CC "string" - match mails with "string" in the Cc: field 152 | * DELETED - match deleted mails 153 | * FLAGGED - match mails with the \\FLAGGED (sometimes referred to as Important or Urgent) flag set 154 | * FROM "string" - match mails with "string" in the From: field 155 | * KEYWORD "string" - match mails with "string" as a keyword 156 | * NEW - match new mails 157 | * OLD - match old mails 158 | * ON "date" - match mails with Date: matching "date" 159 | * RECENT - match mails with the \\RECENT flag set 160 | * SEEN - match mails that have been read (the \\SEEN flag is set) 161 | * SINCE "date" - match mails with Date: after "date" 162 | * SUBJECT "string" - match mails with "string" in the Subject: 163 | * TEXT "string" - match mails with text "string" 164 | * TO "string" - match mails with "string" in the To: 165 | * UNANSWERED - match mails that have not been answered 166 | * UNDELETED - match mails that are not deleted 167 | * UNFLAGGED - match mails that are not flagged 168 | * UNKEYWORD "string" - match mails that do not have the keyword "string" 169 | * UNSEEN - match mails which have not been read yet 170 | * 171 | * @return array Mails ids 172 | */ 173 | public function searchMailbox($criteria = 'ALL') { 174 | $mailsIds = imap_search($this->getImapStream(), $criteria, SE_UID, $this->serverEncoding); 175 | return $mailsIds ? $mailsIds : array(); 176 | } 177 | 178 | /** 179 | * Save mail body. 180 | * @return bool 181 | */ 182 | public function saveMail($mailId, $filename = 'email.eml') { 183 | return imap_savebody($this->getImapStream(), $filename, $mailId, "", FT_UID); 184 | } 185 | 186 | /** 187 | * Marks mails listed in mailId for deletion. 188 | * @return bool 189 | */ 190 | public function deleteMail($mailId) { 191 | return imap_delete($this->getImapStream(), $mailId, FT_UID); 192 | } 193 | 194 | public function moveMail($mailId, $mailBox) { 195 | return imap_mail_move($this->getImapStream(), $mailId, $mailBox, CP_UID) && $this->expungeDeletedMails(); 196 | } 197 | 198 | /** 199 | * Deletes all the mails marked for deletion by imap_delete(), imap_mail_move(), or imap_setflag_full(). 200 | * @return bool 201 | */ 202 | public function expungeDeletedMails() { 203 | return imap_expunge($this->getImapStream()); 204 | } 205 | 206 | /** 207 | * Add the flag \Seen to a mail. 208 | * @return bool 209 | */ 210 | public function markMailAsRead($mailId) { 211 | return $this->setFlag(array($mailId), '\\Seen'); 212 | } 213 | 214 | /** 215 | * Remove the flag \Seen from a mail. 216 | * @return bool 217 | */ 218 | public function markMailAsUnread($mailId) { 219 | return $this->clearFlag(array($mailId), '\\Seen'); 220 | } 221 | 222 | /** 223 | * Add the flag \Flagged to a mail. 224 | * @return bool 225 | */ 226 | public function markMailAsImportant($mailId) { 227 | return $this->setFlag(array($mailId), '\\Flagged'); 228 | } 229 | 230 | /** 231 | * Add the flag \Seen to a mails. 232 | * @return bool 233 | */ 234 | public function markMailsAsRead(array $mailId) { 235 | return $this->setFlag($mailId, '\\Seen'); 236 | } 237 | 238 | /** 239 | * Remove the flag \Seen from some mails. 240 | * @return bool 241 | */ 242 | public function markMailsAsUnread(array $mailId) { 243 | return $this->clearFlag($mailId, '\\Seen'); 244 | } 245 | 246 | /** 247 | * Add the flag \Flagged to some mails. 248 | * @return bool 249 | */ 250 | public function markMailsAsImportant(array $mailId) { 251 | return $this->setFlag($mailId, '\\Flagged'); 252 | } 253 | 254 | /** 255 | * Causes a store to add the specified flag to the flags set for the mails in the specified sequence. 256 | * 257 | * @param array $mailsIds 258 | * @param string $flag which you can set are \Seen, \Answered, \Flagged, \Deleted, and \Draft as defined by RFC2060. 259 | * @return bool 260 | */ 261 | public function setFlag(array $mailsIds, $flag) { 262 | return imap_setflag_full($this->getImapStream(), implode(',', $mailsIds), $flag, ST_UID); 263 | } 264 | 265 | /** 266 | * Cause a store to delete the specified flag to the flags set for the mails in the specified sequence. 267 | * 268 | * @param array $mailsIds 269 | * @param string $flag which you can set are \Seen, \Answered, \Flagged, \Deleted, and \Draft as defined by RFC2060. 270 | * @return bool 271 | */ 272 | public function clearFlag(array $mailsIds, $flag) { 273 | return imap_clearflag_full($this->getImapStream(), implode(',', $mailsIds), $flag, ST_UID); 274 | } 275 | 276 | /** 277 | * Fetch mail headers for listed mails ids 278 | * 279 | * Returns an array of objects describing one mail header each. The object will only define a property if it exists. The possible properties are: 280 | * subject - the mails subject 281 | * from - who sent it 282 | * to - recipient 283 | * date - when was it sent 284 | * message_id - Mail-ID 285 | * references - is a reference to this mail id 286 | * in_reply_to - is a reply to this mail id 287 | * size - size in bytes 288 | * uid - UID the mail has in the mailbox 289 | * msgno - mail sequence number in the mailbox 290 | * recent - this mail is flagged as recent 291 | * flagged - this mail is flagged 292 | * answered - this mail is flagged as answered 293 | * deleted - this mail is flagged for deletion 294 | * seen - this mail is flagged as already read 295 | * draft - this mail is flagged as being a draft 296 | * 297 | * @param array $mailsIds 298 | * @return array 299 | */ 300 | public function getMailsInfo(array $mailsIds) { 301 | $mails = imap_fetch_overview($this->getImapStream(), implode(',', $mailsIds), FT_UID); 302 | if(is_array($mails) && count($mails)) 303 | { 304 | foreach($mails as &$mail) 305 | { 306 | if(isset($mail->subject)) { 307 | $mail->subject = $this->decodeMimeStr($mail->subject, $this->serverEncoding); 308 | } 309 | if(isset($mail->from)) { 310 | $mail->from = $this->decodeMimeStr($mail->from, $this->serverEncoding); 311 | } 312 | if(isset($mail->to)) { 313 | $mail->to = $this->decodeMimeStr($mail->to, $this->serverEncoding); 314 | } 315 | } 316 | } 317 | return $mails; 318 | } 319 | 320 | /** 321 | * Get information about the current mailbox. 322 | * 323 | * Returns an object with following properties: 324 | * Date - last change (current datetime) 325 | * Driver - driver 326 | * Mailbox - name of the mailbox 327 | * Nmsgs - number of messages 328 | * Recent - number of recent messages 329 | * Unread - number of unread messages 330 | * Deleted - number of deleted messages 331 | * Size - mailbox size 332 | * 333 | * @return object Object with info | FALSE on failure 334 | */ 335 | 336 | public function getMailboxInfo() { 337 | return imap_mailboxmsginfo($this->getImapStream()); 338 | } 339 | 340 | /** 341 | * Gets mails ids sorted by some criteria 342 | * 343 | * Criteria can be one (and only one) of the following constants: 344 | * SORTDATE - mail Date 345 | * SORTARRIVAL - arrival date (default) 346 | * SORTFROM - mailbox in first From address 347 | * SORTSUBJECT - mail subject 348 | * SORTTO - mailbox in first To address 349 | * SORTCC - mailbox in first cc address 350 | * SORTSIZE - size of mail in octets 351 | * 352 | * @param int $criteria 353 | * @param bool $reverse 354 | * @return array Mails ids 355 | */ 356 | public function sortMails($criteria = SORTARRIVAL, $reverse = true) { 357 | return imap_sort($this->getImapStream(), $criteria, $reverse, SE_UID); 358 | } 359 | 360 | /** 361 | * Get mails count in mail box 362 | * @return int 363 | */ 364 | public function countMails() { 365 | return imap_num_msg($this->getImapStream()); 366 | } 367 | 368 | /** 369 | * Retrieve the quota settings per user 370 | * @return array - FALSE in the case of call failure 371 | */ 372 | protected function getQuota() { 373 | return imap_get_quotaroot($this->getImapStream(), 'INBOX'); 374 | } 375 | 376 | /** 377 | * Return quota limit in KB 378 | * @return int - FALSE in the case of call failure 379 | */ 380 | public function getQuotaLimit() { 381 | $quota = $this->getQuota(); 382 | if(is_array($quota)) { 383 | $quota = $quota['STORAGE']['limit']; 384 | } 385 | return $quota; 386 | } 387 | 388 | /** 389 | * Return quota usage in KB 390 | * @return int - FALSE in the case of call failure 391 | */ 392 | public function getQuotaUsage() { 393 | $quota = $this->getQuota(); 394 | if(is_array($quota)) { 395 | $quota = $quota['STORAGE']['usage']; 396 | } 397 | return $quota; 398 | } 399 | 400 | /** 401 | * Get email header of specific id 402 | * @return header(object/string) 403 | */ 404 | 405 | public function getHeader($mailId){ 406 | $head = imap_rfc822_parse_headers( imap_fetchheader( $this->getImapStream(), $mailId, FT_UID ) ); 407 | 408 | //Other Options for getting Header Info 409 | //$head = imap_header( $this->getImapStream(), $mailId, FT_UID ); 410 | //$head = imap_headerinfo( $this->getImapStream(), $mailId, FT_UID ); 411 | 412 | //Use when Looking for custom headers 413 | /*$head = imap_fetchheader( $this->getImapStream(), $mailId, FT_UID ) ; 414 | preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)\r\n/m',$head, $matches); 415 | $headers = array_combine($matches[1], $matches[2]); 416 | $head = $headers['ParentID'];*/ 417 | 418 | return $head; 419 | } 420 | 421 | /** 422 | * Get mail data 423 | * 424 | * @param $mailId 425 | * @return IncomingMail 426 | */ 427 | public function getMail($mailId) { 428 | $head = imap_rfc822_parse_headers(imap_fetchheader($this->getImapStream(), $mailId, FT_UID)); 429 | 430 | $mail = new IncomingMail(); 431 | $mail->id = $mailId; 432 | $mail->date = date('Y-m-d H:i:s', isset($head->date) ? strtotime($head->date) : time()); 433 | $mail->subject = isset($head->subject) ? $this->decodeMimeStr($head->subject, $this->serverEncoding) : null; 434 | $mail->fromName = isset($head->from[0]->personal) ? $this->decodeMimeStr($head->from[0]->personal, $this->serverEncoding) : null; 435 | $mail->fromAddress = strtolower($head->from[0]->mailbox . '@' . $head->from[0]->host); 436 | 437 | if(isset($head->to)) { 438 | $toStrings = array(); 439 | foreach($head->to as $to) { 440 | if(!empty($to->mailbox) && !empty($to->host)) { 441 | $toEmail = strtolower($to->mailbox . '@' . $to->host); 442 | $toName = isset($to->personal) ? $this->decodeMimeStr($to->personal, $this->serverEncoding) : null; 443 | $toStrings[] = $toName ? "$toName <$toEmail>" : $toEmail; 444 | $mail->to[$toEmail] = $toName; 445 | } 446 | } 447 | $mail->toString = implode(', ', $toStrings); 448 | } 449 | 450 | if(isset($head->cc)) { 451 | foreach($head->cc as $cc) { 452 | $mail->cc[strtolower($cc->mailbox . '@' . $cc->host)] = isset($cc->personal) ? $this->decodeMimeStr($cc->personal, $this->serverEncoding) : null; 453 | } 454 | } 455 | 456 | if(isset($head->reply_to)) { 457 | foreach($head->reply_to as $replyTo) { 458 | $mail->replyTo[strtolower($replyTo->mailbox . '@' . $replyTo->host)] = isset($replyTo->personal) ? $this->decodeMimeStr($replyTo->personal, $this->serverEncoding) : null; 459 | } 460 | } 461 | 462 | $mailStructure = imap_fetchstructure($this->getImapStream(), $mailId, FT_UID); 463 | 464 | if(empty($mailStructure->parts)) { 465 | $this->initMailPart($mail, $mailStructure, 0); 466 | } 467 | else { 468 | foreach($mailStructure->parts as $partNum => $partStructure) { 469 | $this->initMailPart($mail, $partStructure, $partNum + 1); 470 | } 471 | } 472 | 473 | return $mail; 474 | } 475 | 476 | protected function initMailPart(IncomingMail $mail, $partStructure, $partNum) { 477 | $data = $partNum ? imap_fetchbody($this->getImapStream(), $mail->id, $partNum, FT_UID) : imap_body($this->getImapStream(), $mail->id, FT_UID); 478 | 479 | if($partStructure->encoding == 1) { 480 | $data = imap_utf8($data); 481 | } 482 | elseif($partStructure->encoding == 2) { 483 | $data = imap_binary($data); 484 | } 485 | elseif($partStructure->encoding == 3) { 486 | $data = imap_base64($data); 487 | } 488 | elseif($partStructure->encoding == 4) { 489 | $data = imap_qprint($data); 490 | } 491 | 492 | $params = array(); 493 | if(!empty($partStructure->parameters)) { 494 | foreach($partStructure->parameters as $param) { 495 | $params[strtolower($param->attribute)] = $param->value; 496 | } 497 | } 498 | if(!empty($partStructure->dparameters)) { 499 | foreach($partStructure->dparameters as $param) { 500 | $paramName = strtolower(preg_match('~^(.*?)\*~', $param->attribute, $matches) ? $matches[1] : $param->attribute); 501 | if(isset($params[$paramName])) { 502 | $params[$paramName] .= $param->value; 503 | } 504 | else { 505 | $params[$paramName] = $param->value; 506 | } 507 | } 508 | } 509 | if(!empty($params['charset'])) { 510 | $data = $this->convertStringEncoding($data, $params['charset'], $this->serverEncoding); 511 | } 512 | 513 | // attachments 514 | $attachmentId = $partStructure->ifid 515 | ? trim($partStructure->id, " <>") 516 | : (isset($params['filename']) || isset($params['name']) ? mt_rand() . mt_rand() : null); 517 | if($attachmentId) { 518 | if(empty($params['filename']) && empty($params['name'])) { 519 | $fileName = $attachmentId . '.' . strtolower($partStructure->subtype); 520 | } 521 | else { 522 | $fileName = !empty($params['filename']) ? $params['filename'] : $params['name']; 523 | $fileName = $this->decodeMimeStr($fileName, $this->serverEncoding); 524 | $fileName = $this->decodeRFC2231($fileName, $this->serverEncoding); 525 | } 526 | $attachment = new IncomingMailAttachment(); 527 | $attachment->id = $attachmentId; 528 | $attachment->name = $fileName; 529 | if($this->attachmentsDir) { 530 | $replace = array( 531 | '/\s/' => '_', 532 | '/[^0-9a-zа-яіїє_\.]/iu' => '', 533 | '/_+/' => '_', 534 | '/(^_)|(_$)/' => '', 535 | ); 536 | $fileSysName = preg_replace('~[\\\\/]~', '', $mail->id . '_' . $attachmentId . '_' . preg_replace(array_keys($replace), $replace, $fileName)); 537 | $attachment->filePath = $this->attachmentsDir . DIRECTORY_SEPARATOR . $fileSysName; 538 | file_put_contents($attachment->filePath, $data); 539 | } 540 | 541 | $attachment->disposition = $partStructure->disposition; 542 | 543 | $mail->addAttachment($attachment); 544 | } 545 | elseif($partStructure->type == 0 && $data) { 546 | if(strtolower($partStructure->subtype) == 'plain') { 547 | $mail->textPlain .= $data; 548 | } 549 | else { 550 | $mail->textHtml .= $data; 551 | } 552 | } 553 | elseif($partStructure->type == 2 && $data) { 554 | $mail->textPlain .= trim($data); 555 | } 556 | if(!empty($partStructure->parts)) { 557 | foreach($partStructure->parts as $subPartNum => $subPartStructure) { 558 | if($partStructure->type == 2 && $partStructure->subtype == 'RFC822') { 559 | $this->initMailPart($mail, $subPartStructure, $partNum); 560 | } 561 | else { 562 | $this->initMailPart($mail, $subPartStructure, $partNum . '.' . ($subPartNum + 1)); 563 | } 564 | } 565 | } 566 | } 567 | 568 | protected function decodeMimeStr($string, $charset = 'utf-8') { 569 | $newString = ''; 570 | $elements = imap_mime_header_decode($string); 571 | for($i = 0; $i < count($elements); $i++) { 572 | if($elements[$i]->charset == 'default') { 573 | $elements[$i]->charset = 'iso-8859-1'; 574 | } 575 | $newString .= $this->convertStringEncoding($elements[$i]->text, $elements[$i]->charset, $charset); 576 | } 577 | return $newString; 578 | } 579 | 580 | function isUrlEncoded($string) { 581 | $hasInvalidChars = preg_match( '#[^%a-zA-Z0-9\-_\.\+]#', $string ); 582 | $hasEscapedChars = preg_match( '#%[a-zA-Z0-9]{2}#', $string ); 583 | return !$hasInvalidChars && $hasEscapedChars; 584 | } 585 | 586 | protected function decodeRFC2231($string, $charset = 'utf-8') { 587 | if(preg_match("/^(.*?)'.*?'(.*?)$/", $string, $matches)) { 588 | $encoding = $matches[1]; 589 | $data = $matches[2]; 590 | if($this->isUrlEncoded($data)) { 591 | $string = $this->convertStringEncoding(urldecode($data), $encoding, $charset); 592 | } 593 | } 594 | return $string; 595 | } 596 | 597 | /** 598 | * Converts a string from one encoding to another. 599 | * @param string $string 600 | * @param string $fromEncoding 601 | * @param string $toEncoding 602 | * @return string Converted string if conversion was successful, or the original string if not 603 | */ 604 | protected function convertStringEncoding($string, $fromEncoding, $toEncoding) { 605 | $convertedString = null; 606 | if($string && $fromEncoding != $toEncoding) { 607 | $convertedString = @iconv($fromEncoding, $toEncoding . '//IGNORE', $string); 608 | if(!$convertedString && extension_loaded('mbstring')) { 609 | $convertedString = @mb_convert_encoding($string, $toEncoding, $fromEncoding); 610 | } 611 | } 612 | return $convertedString ?: $string; 613 | } 614 | 615 | public function __destruct() { 616 | $this->disconnect(); 617 | } 618 | } 619 | 620 | class Exception extends \Exception { 621 | 622 | } 623 | --------------------------------------------------------------------------------