├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── index.php └── src └── phpImapReader ├── Email.php ├── EmailAttachment.php └── Reader.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 benhall14 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP IMAP Reader 2 | A PHP class that makes working with IMAP as easy as possible. 3 | 4 | This class is written to be chain-able so to create a logically fluent and easily readable way to access an IMAP mailbox. 5 | 6 | It simplifies the PHP IMAP_* library into a set of easy to read methods that do the heavy lifting for you. 7 | 8 | It has been fully tested to work with PHP 5.3+, including **PHP 8.1.** 9 | 10 | # Installation via Composer 11 | You can now install this class via composer. 12 | 13 | $ composer require benhall14/php-imap-reader 14 | 15 | **Remember** to add the composer autoloader before using the class and use the correct namespace. 16 | 17 | require 'vendor/autoload.php'; 18 | 19 | use benhall14\phpImapReader\Email as Email; 20 | use benhall14\phpImapReader\EmailAttachment as EmailAttachment; 21 | use benhall14\phpImapReader\Reader as Reader; 22 | 23 | # Usage 24 | Please make sure you have added the required classes. 25 | 26 | In its simplest form, use the following to connect: 27 | 28 | ```php 29 | define('IMAP_USERNAME', ''); # your imap user name 30 | define('IMAP_PASSWORD', ''); # your imap password 31 | define('IMAP_MAILBOX', ''); # your imap address EG. {mail.example.com:993/novalidate-cert/ssl} 32 | define('ATTACHMENT_PATH', __DIR__ . '/attachments'); # the path to save attachments to or false to skip attachments 33 | 34 | try{ 35 | 36 | # set the mark as read flag (true by default). If you don't want emails to be marked as read/seen, set this to false. 37 | $mark_as_read = true; 38 | 39 | # You can ommit this to use UTF-8 by default. 40 | $encoding = 'UTF-8' 41 | 42 | # create a new Reader object 43 | $imap = new Reader(IMAP_MAILBOX, IMAP_USERNAME, IMAP_PASSWORD, ATTACHMENT_PATH, $mark_as_read, $encoding); 44 | 45 | # use one or more of the following chain-able methods to filter your email selection 46 | $imap 47 | ->folder($folder) # alias for mailbox($mailbox) 48 | ->mailbox($mailbox) # sets the mailbox to return emails from. Default = INBOX 49 | ->id($id) # retrieve a specific email by id 50 | ->recent() # get all RECENT emails 51 | ->flagged() # get all FLAGGED emails 52 | ->unflagged() # get all UNFLAGGED emails 53 | ->unanswered() # get all UNANSWERED emails 54 | ->deleted() # get all DELETED emails 55 | ->unread() # alias for UNSEEN() 56 | ->unseen() # get all UNSEEN emails 57 | ->from($email) # get all emails from $email 58 | ->searchSubject($string) # get all emails with $string in the subject line 59 | ->searchBody($string) # get all emails with $string in the body 60 | ->searchText($string) # get all emails with $string TEXT 61 | ->seen() # get all SEEN emails 62 | ->read() # alias for SEEN() 63 | ->newMessages() # get all NEW emails 64 | ->oldMessages() # get all OLD emails 65 | ->keyword($keyword) # get all emails with $keyword KEYWORD 66 | ->unkeyword($keyword) # get all emails without $keyword KEYWORD 67 | ->beforeDate($date) # get all emails received before $date. *Date should be in a format that can be parsed by strtotime.* 68 | ->sinceDate($date) # get all emails received since $date. *Date should be in a format that can be parsed by strtotime.* 69 | ->sentTo($to) # get all emails sent to $to 70 | ->searchBCC($string) # get all emails with $string in the BCC field 71 | ->searchCC($string) # get all emails with $string in the CC field 72 | ->onDate($date) # get all emails received on $date. *Date should be in a format that can be parsed by strtotime.* 73 | ->limit($limit) # limit the number of emails returned to $limit for pagination 74 | ->page($page) # used with limit to create pagination 75 | ->orderASC() # order the emails returned in ASCending order 76 | ->orderDESC() # order the emails returned in DESCendeing order 77 | ->reset() # resets the current reader to be able to reconnect to another folder/mailbox. 78 | ->all() # get all emails (default) 79 | ->get(); # finally make the connection and retrieve the emails. 80 | 81 | # You can then loop through $imap->emails() for each email. 82 | foreach($imap->emails() as $email){ 83 | 84 | # The email has been clean and formated. 85 | # see below. 86 | 87 | } 88 | 89 | # Reset the reader and connect to another folder. 90 | $imap->reset()->folder('Sent')->get(); 91 | 92 | # You can also create a folder/mailbox on the IMAP stream. 93 | $imap->createFolder('New Folder Name'); 94 | #or 95 | $imap->createMailbox('New Folder Name'); 96 | 97 | # You can also check if a mailbox/folder exists on the IMAP stream using: 98 | if ($imap->doesMailboxExists('INBOX')) { 99 | return "Yes, it exsits"; 100 | } else { 101 | return "No, it doesn't exist."; 102 | } 103 | 104 | # ... your code here ... 105 | 106 | } catch (Exception $e){ 107 | 108 | echo $e->getMessage(); 109 | 110 | } 111 | ``` 112 | 113 | While looping through the returned emails, each email object can be used as below: 114 | ```php 115 | 116 | $email->isTo('mail@example.com'); # Return true if the email is to $email, else returns false 117 | 118 | $email->replyTo(); # Returns an array of Reply To email addresses (and names) 119 | 120 | $email->cc(); # Returns an array of CC email addresses (and names) 121 | 122 | $email->to(); # Returns the recipient email address 123 | 124 | $email->id(); # Returns the id of the email 125 | 126 | $email->size(); # Returns the size of the email 127 | 128 | $email->date($format); # Returns the date in the $format specified. Default Y-m-d H:i:s 129 | 130 | $email->subject(); # Returns the email subject 131 | 132 | $email->fromName(); # Returns the sender's name, if set. 133 | 134 | $email->fromEmail(); # Returns the sender's email address 135 | 136 | $email->plain(); # Returns the plain text body of the email, if present 137 | 138 | $email->html(); # Returns the html body of the email, if present 139 | 140 | $email->hasAttachments(); # Returns true/false based on if the email has attachments 141 | 142 | $email->attachments(); # Returns an array of EmailAttachment objects 143 | 144 | $email->attachment($id); # Returns an attachment based on the given attachment $id 145 | 146 | $email->isRecent(); # Returns true/false based on the recent flag 147 | 148 | $email->isUnseen(); # Returns true/false based on the unseen flag 149 | 150 | $email->isFlagged(); # Returns true/false based on the flagged flag 151 | 152 | $email->isAnswered(); # Returns true/false based on the answered flag 153 | 154 | $email->isDeleted(); # Returns true/false based on the deleted flag 155 | 156 | $email->isDraft(); # Returns true/false based on the draft flag 157 | 158 | $email->eml(); # Returns the email in .eml format 159 | 160 | $email->saveEml($filename); # Saves the email in .eml format 161 | 162 | $email->count(); # Returns number of emails in folder 163 | 164 | ``` 165 | 166 | The **$email->attachments();** method returns an array of attachments belonging to the email in a **benhall14\phpImapReader\EmailAttachment** object. The following methods are available for each attachment. 167 | 168 | ```php 169 | 170 | # check if the current $email has any attachments. 171 | if($email->hasAttachments()){ 172 | 173 | # get the attachments for the current $email in the loop. 174 | $attachments = $email->attachments(); 175 | 176 | # loop through the found attachments. 177 | foreach($attachments as $attachment){ 178 | 179 | $attachment->id(); # Returns the attachments ID. 180 | 181 | $attachment->name(); # Returns the attachments name. 182 | 183 | $attachment->filePath(); # Returns the local file path for the attachment. This is based on the ATTACHMENT_PATH constant set in the imap config. 184 | 185 | $attachment->content(); # Returns the attachments content data. 186 | 187 | $attachment->type(); # Returns either 'attachment' or 'inline'. 188 | 189 | } 190 | 191 | } 192 | ``` 193 | 194 | # Requirements 195 | 196 | **Works with PHP 5.3+ (including PHP 8.1)** 197 | 198 | **PHP IMAP Extension** 199 | 200 | # License 201 | Copyright (c) 2016-2021 Benjamin Hall, ben@conobe.co.uk 202 | https://conobe.co.uk 203 | 204 | Licensed under the MIT license 205 | 206 | # Donate? 207 | 208 | If you find this project helpful or useful in anyway, please consider getting me a cup of coffee - It's really appreciated :) 209 | 210 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/benhall14) 211 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benhall14/php-imap-reader", 3 | "description": "A PHP class that makes working with IMAP in PHP simple.", 4 | "homepage": "https://github.com/benhall14/php-imap-reader", 5 | "keywords": ["imap", "mail", "php", "pop3", "mailbox", "email"], 6 | "license": "MIT", 7 | "type": "library", 8 | "authors": [ 9 | { 10 | "name": "Benjamin Hall", 11 | "email": "ben@conobe.co.uk", 12 | "homepage": "https://conobe.co.uk", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.3", 18 | "ext-imap": "*" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "benhall14\\": "src/" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "074f9787659c58fd1437a1af846f0e0d", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=5.3" 17 | }, 18 | "platform-dev": [] 19 | } 20 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | limit(10)->get(); 18 | 19 | foreach ($imap->emails() as $email) { 20 | echo '
'; 21 | 22 | echo '
' . $email->fromEmail() . '
'; 23 | 24 | echo '
' . $email->subject() . '
'; 25 | 26 | echo '
' . $email->date('Y-m-d H:i:s') . '
'; 27 | 28 | if ($email->hasAttachments()) { 29 | foreach ($email->attachments() as $attachment) { 30 | echo '
' . $attachment->filePath() . '
'; 31 | } 32 | } 33 | 34 | #print_r($email->plain()); 35 | #print_r($email->html()); 36 | 37 | echo '



'; 38 | } 39 | } catch (Exception $e) { 40 | die($e->getMessage()); 41 | } 42 | -------------------------------------------------------------------------------- /src/phpImapReader/Email.php: -------------------------------------------------------------------------------- 1 | 14 | * @copyright 2022 Copyright (c) Benjamin Hall 15 | * @license MIT https://github.com/benhall14/php-imap-reader 16 | * @link https://conobe.co.uk/projects/php-imap-reader/ 17 | */ 18 | class Email 19 | { 20 | /** 21 | * The ID of this email. 22 | * 23 | * @var int 24 | */ 25 | public $id; 26 | 27 | /** 28 | * The msgno of this email. 29 | * 30 | * @var int 31 | */ 32 | public $msgno; 33 | 34 | /** 35 | * The date this email was received. 36 | * 37 | * @var DateTime 38 | */ 39 | public $date; 40 | 41 | /** 42 | * The UNIX time stamp when this email was received. 43 | * 44 | * @var int 45 | */ 46 | public $udate; 47 | 48 | /** 49 | * An array containing the custom headers of the email. 50 | * 51 | * @var array 52 | */ 53 | public $custom_headers = array(); 54 | 55 | /** 56 | * The subject line of this email. 57 | * 58 | * @var string 59 | */ 60 | public $subject; 61 | 62 | /** 63 | * The recipient of this email. 64 | * 65 | * @var array 66 | */ 67 | public $to = array(); 68 | 69 | /** 70 | * The sender of this email. 71 | * 72 | * @var string 73 | */ 74 | public $from; 75 | 76 | /** 77 | * An array of Reply To email addresses. 78 | * 79 | * @var array 80 | */ 81 | public $reply_to = array(); 82 | 83 | /** 84 | * An array of Carbon Copy recipients. 85 | * 86 | * @var array 87 | */ 88 | public $cc = array(); 89 | 90 | /** 91 | * The 'recent' flag. 92 | * 93 | * @var boolean 94 | */ 95 | public $recent; 96 | 97 | /** 98 | * The 'unseen' flag. 99 | * 100 | * @var boolean 101 | */ 102 | public $unseen; 103 | 104 | /** 105 | * The 'flagged' flag. 106 | * 107 | * @var boolean 108 | */ 109 | public $flagged; 110 | 111 | /** 112 | * The 'answered' flag. 113 | * 114 | * @var boolean 115 | */ 116 | public $answered; 117 | 118 | /** 119 | * The 'deleted' flag. 120 | * 121 | * @var boolean 122 | */ 123 | public $deleted; 124 | 125 | /** 126 | * The 'draft' flag. 127 | * 128 | * @var boolean 129 | */ 130 | public $draft; 131 | 132 | /** 133 | * The integer size of this email. 134 | * 135 | * @var int 136 | */ 137 | public $size; 138 | 139 | /** 140 | * The plain text body of this email. 141 | * 142 | * @var string 143 | */ 144 | public $text_plain; 145 | 146 | /** 147 | * The HTML body of this email. 148 | * 149 | * @var string 150 | */ 151 | public $text_html; 152 | 153 | /** 154 | * An array of attachments, including inline. 155 | * 156 | * @var array 157 | */ 158 | public $attachments = array(); 159 | 160 | /** 161 | * Raw Body 162 | * 163 | * @var string 164 | */ 165 | public $raw_body; 166 | 167 | /** 168 | * Checks if the email recipient matches the given email address. 169 | * 170 | * @param string $email The email address to match against the recipient. 171 | * 172 | * @return boolean 173 | */ 174 | public function isTo($email) 175 | { 176 | foreach ($this->to as $to) { 177 | if ($email == $to->email) { 178 | return true; 179 | } 180 | } 181 | 182 | return false; 183 | } 184 | 185 | /** 186 | * Get the 'Reply To' email addresses for this email. 187 | * 188 | * @return array 189 | */ 190 | public function replyTo() 191 | { 192 | return $this->reply_to; 193 | } 194 | 195 | /** 196 | * Get the 'Carbon Copied' email addresses for this email. 197 | * 198 | * @return array An array of email addresses in the cc field. 199 | */ 200 | public function cc() 201 | { 202 | return $this->cc; 203 | } 204 | 205 | /** 206 | * Get the recipient of this email. 207 | * 208 | * @return string 209 | */ 210 | public function to() 211 | { 212 | return $this->to; 213 | } 214 | 215 | /** 216 | * Get the ID of this email. 217 | * 218 | * @return integer The ID of the email. 219 | */ 220 | public function id() 221 | { 222 | return $this->id; 223 | } 224 | 225 | /** 226 | * Get the msgno of this email. 227 | * 228 | * @return integer The msgno of the email. 229 | */ 230 | public function msgno() 231 | { 232 | return $this->msgno; 233 | } 234 | 235 | /** 236 | * Get the custom headers of this email. 237 | * 238 | * @return array The custom headers of the email. 239 | */ 240 | public function customHeaders() 241 | { 242 | return $this->custom_headers; 243 | } 244 | 245 | /** 246 | * Get the size in bytes of this email. 247 | * 248 | * @return integer 249 | */ 250 | public function size() 251 | { 252 | return (int) $this->size; 253 | } 254 | 255 | /** 256 | * Get the date that this email was received. 257 | * 258 | * @param string $format The format in which to return the date. 259 | * 260 | * @return string 261 | */ 262 | public function date($format = 'Y-m-d H:i:s') 263 | { 264 | return $this->date->format($format); 265 | } 266 | 267 | /** 268 | * Get the subject line of this email. 269 | * 270 | * @return string 271 | */ 272 | public function subject() 273 | { 274 | return $this->subject; 275 | } 276 | 277 | /** 278 | * Get the sender name of this email. 279 | * 280 | * @return string 281 | */ 282 | public function fromName() 283 | { 284 | return $this->from && $this->from->name ? $this->from->name : null; 285 | } 286 | 287 | /** 288 | * Get the sender email address of this email. 289 | * 290 | * @return string 291 | */ 292 | public function fromEmail() 293 | { 294 | return $this->from && $this->from->email ? $this->from->email : null; 295 | } 296 | 297 | /** 298 | * Get the plain text body of this email. 299 | * 300 | * @return string 301 | */ 302 | public function plain() 303 | { 304 | return $this->text_plain; 305 | } 306 | 307 | /** 308 | * Get the HTML body of this email. 309 | * 310 | * @return string 311 | */ 312 | public function html() 313 | { 314 | return $this->text_html ? $this->injectInline($this->text_html) : false; 315 | } 316 | 317 | /** 318 | * Return a boolean based on whether this email has attachments. 319 | * 320 | * @return boolean 321 | */ 322 | public function hasAttachments() 323 | { 324 | return (count($this->attachments)) ? true : false; 325 | } 326 | 327 | /** 328 | * Return an array of the attachments for this email. 329 | * 330 | * @return array 331 | */ 332 | public function attachments() 333 | { 334 | return $this->attachments; 335 | } 336 | 337 | /** 338 | * Return a specific attachment based on the attachment id. 339 | * 340 | * @param integer $attachment_id The attachment to return. 341 | * 342 | * @return EmailAttachment 343 | */ 344 | public function attachment($attachment_id) 345 | { 346 | return isset($this->attachments[$attachment_id]) ? $this->attachments[$attachment_id] : false; 347 | } 348 | 349 | /** 350 | * Return the status of the recent flag. 351 | * 352 | * @return boolean 353 | */ 354 | public function isRecent() 355 | { 356 | return $this->recent; 357 | } 358 | 359 | /** 360 | * Return the status of the unseen flag. 361 | * 362 | * @return boolean 363 | */ 364 | public function isUnseen() 365 | { 366 | return $this->unseen; 367 | } 368 | 369 | /** 370 | * Return the status of the flagged flag. 371 | * 372 | * @return boolean 373 | */ 374 | public function isFlagged() 375 | { 376 | return $this->flagged; 377 | } 378 | 379 | /** 380 | * Return the status of the answered flag. 381 | * 382 | * @return boolean 383 | */ 384 | public function isAnswered() 385 | { 386 | return $this->answered; 387 | } 388 | 389 | /** 390 | * Return the status of the deleted flag. 391 | * 392 | * @return boolean 393 | */ 394 | public function isDeleted() 395 | { 396 | return $this->deleted; 397 | } 398 | 399 | /** 400 | * Return the status of the draft flag. 401 | * 402 | * @return boolean 403 | */ 404 | public function isDraft() 405 | { 406 | return $this->draft; 407 | } 408 | 409 | /** 410 | * Set the subject line for this email. 411 | * 412 | * @param string $subject The subject line string. 413 | * 414 | * @return Email 415 | */ 416 | public function setSubject($subject) 417 | { 418 | $this->subject = $subject; 419 | 420 | return $this; 421 | } 422 | 423 | /** 424 | * Set the unique id for this email. 425 | * 426 | * @param integer $id The id. 427 | * 428 | * @return Email 429 | */ 430 | public function setId($id) 431 | { 432 | $this->id = $id; 433 | 434 | return $this; 435 | } 436 | 437 | /** 438 | * Set the msgno for this email. 439 | * 440 | * @param integer $msgno The msgno. 441 | * 442 | * @return Email 443 | */ 444 | public function setMsgno($msgno) 445 | { 446 | $this->msgno = $msgno; 447 | 448 | return $this; 449 | } 450 | 451 | /** 452 | * Sets the raw body. 453 | * 454 | * @param string $body 455 | * 456 | * @return Email 457 | */ 458 | public function setRawBody($body) 459 | { 460 | $this->raw_body = $body; 461 | 462 | return $this; 463 | } 464 | 465 | /** 466 | * Set the date for this email. 467 | * 468 | * @param string $date The Date string. 469 | * 470 | * @return Email 471 | */ 472 | public function setDate($date) 473 | { 474 | $this->date = new DateTime($date); 475 | 476 | return $this; 477 | } 478 | 479 | /** 480 | * Set the UNIX time stamp for this email. 481 | * 482 | * @param integer $date A UNIX time stamp. 483 | * 484 | * @return Email 485 | */ 486 | public function setUdate($date) 487 | { 488 | $this->udate = $date; 489 | 490 | return $this; 491 | } 492 | 493 | /** 494 | * Set the size of this email. 495 | * 496 | * @param integer $size Size in bytes. 497 | * 498 | * @return Email 499 | */ 500 | public function setSize($size) 501 | { 502 | $this->size = (int) $size; 503 | 504 | return $this; 505 | } 506 | 507 | /** 508 | * Sets the unseen flag based on the given boolean state. 509 | * 510 | * @param boolean $boolean The flag status. 511 | * 512 | * @return Email 513 | */ 514 | public function setUnseen($boolean) 515 | { 516 | $this->unseen = (bool) $boolean; 517 | 518 | return $this; 519 | } 520 | 521 | /** 522 | * Sets the answered flag based on the given boolean state. 523 | * 524 | * @param boolean $boolean The flag status. 525 | * 526 | * @return Email 527 | */ 528 | public function setAnswered($boolean) 529 | { 530 | $this->answered = (bool) $boolean; 531 | 532 | return $this; 533 | } 534 | 535 | /** 536 | * Sets the draft flag based on the given boolean state. 537 | * 538 | * @param boolean $boolean The flag status. 539 | * 540 | * @return Email 541 | */ 542 | public function setDraft($boolean) 543 | { 544 | $this->draft = (bool) $boolean; 545 | 546 | return $this; 547 | } 548 | 549 | /** 550 | * Sets the recent flag based on the given boolean state. 551 | * 552 | * @param boolean $boolean The flag status. 553 | * 554 | * @return Email 555 | */ 556 | public function setRecent($boolean) 557 | { 558 | $this->recent = (bool) $boolean; 559 | 560 | return $this; 561 | } 562 | 563 | /** 564 | * Sets the flagged flag based on the given boolean state. 565 | * 566 | * @param boolean $boolean The flag status. 567 | * 568 | * @return Email 569 | */ 570 | public function setFlagged($boolean) 571 | { 572 | $this->flagged = (bool) $boolean; 573 | 574 | return $this; 575 | } 576 | 577 | /** 578 | * Sets the deleted flag based on the given boolean state. 579 | * 580 | * @param boolean $boolean The flag status. 581 | * 582 | * @return Email 583 | */ 584 | public function setDeleted($boolean) 585 | { 586 | $this->deleted = (bool) $boolean; 587 | 588 | return $this; 589 | } 590 | 591 | /** 592 | * Adds a recipient to the 'To' array. 593 | * 594 | * @param string $mailbox The mailbox. 595 | * @param string $host The host name. 596 | * @param string $name (optional) The recipient name. 597 | * 598 | * @return Email 599 | */ 600 | public function addTo($mailbox, $host, $name = false) 601 | { 602 | if (!$mailbox || !$host) { 603 | return false; 604 | } 605 | 606 | $to = new stdClass(); 607 | 608 | $to->name = ($name) ? $name : false; 609 | 610 | $to->mailbox = $mailbox; 611 | 612 | $to->host = $host; 613 | 614 | $to->email = $to->mailbox . '@' . $to->host; 615 | 616 | $this->to[] = $to; 617 | 618 | return $this; 619 | } 620 | 621 | /** 622 | * Adds a 'Reply To' to 'Reply To' array. 623 | * 624 | * @param string $mailbox The mailbox. 625 | * @param string $host The host name. 626 | * @param string $name (optional) The reply to name. 627 | * 628 | * @return Email 629 | */ 630 | public function addReplyTo($mailbox, $host, $name = false) 631 | { 632 | if (!$mailbox || !$host) { 633 | return false; 634 | } 635 | 636 | $reply_to = new stdClass(); 637 | 638 | $reply_to->name = ($name) ? $name : false; 639 | 640 | $reply_to->mailbox = $mailbox; 641 | 642 | $reply_to->host = $host; 643 | 644 | $reply_to->email = $reply_to->mailbox . '@' . $reply_to->host; 645 | 646 | $this->reply_to[] = $reply_to; 647 | 648 | return $this; 649 | } 650 | 651 | /** 652 | * Adds a custom header to this email. 653 | * 654 | * @param string $custom_header The custom header to append to the array. 655 | * 656 | * @return Email 657 | */ 658 | public function addCustomHeader($custom_header) 659 | { 660 | if (!$custom_header) { 661 | return false; 662 | } 663 | 664 | $header_info = explode(":", $custom_header); 665 | 666 | if (is_array($header_info) && isset($header_info[0]) && isset($header_info[1])) { 667 | $this->custom_headers[$header_info[0]] = (string) trim($header_info[1]); 668 | } 669 | 670 | return $this; 671 | } 672 | 673 | /** 674 | * Returns a specific header (if it exists), or returns null. 675 | * 676 | * @param string $header_name 677 | * 678 | * @return string 679 | */ 680 | public function getCustomHeader($header_name) 681 | { 682 | return isset($this->custom_headers) && isset($this->custom_headers[$header_name]) ? $this->custom_headers[$header_name] : null; 683 | } 684 | 685 | /** 686 | * An alias for getCustomHeader 687 | * 688 | * @param string $header_name 689 | * 690 | * @return string 691 | */ 692 | public function getHeader($header_name) 693 | { 694 | return $this->getCustomHeader($header_name); 695 | } 696 | 697 | /** 698 | * Returns, or saves, the .eml version of the email. 699 | * 700 | * @param mixed $filename 701 | * 702 | * @return string 703 | */ 704 | public function eml($filename = null) 705 | { 706 | if ($filename && !file_exists($filename)) { 707 | file_put_contents($filename, $this->raw_body); 708 | } 709 | 710 | return $this->raw_body; 711 | } 712 | 713 | /** 714 | * Save the Email as an .eml file. 715 | * 716 | * @param string $filename 717 | * 718 | * @return string 719 | */ 720 | public function saveEml($filename) 721 | { 722 | return $this->eml($filename); 723 | } 724 | 725 | /** 726 | * Adds a carbon copy entry to this email. 727 | * 728 | * @param string $mailbox The mailbox. 729 | * @param string $host The host name. 730 | * @param string $name (optional) The name of the CC. 731 | * 732 | * @return Email 733 | */ 734 | public function addCC($mailbox, $host, $name = false) 735 | { 736 | if (!$mailbox || !$host) { 737 | return false; 738 | } 739 | 740 | $cc = new stdClass(); 741 | 742 | $cc->name = $name ?: false; 743 | 744 | $cc->mailbox = $mailbox; 745 | 746 | $cc->host = $host; 747 | 748 | $cc->email = $cc->mailbox . '@' . $cc->host; 749 | 750 | $this->cc[] = $cc; 751 | 752 | return $this; 753 | } 754 | 755 | /** 756 | * Set the 'from' email address for this email. 757 | * 758 | * @param string $mailbox The mailbox. 759 | * @param string $host The host name. 760 | * @param string $name (optional) The senders name. 761 | * 762 | * @return Email 763 | */ 764 | public function setFrom($mailbox, $host, $name = false) 765 | { 766 | $this->from = new stdClass(); 767 | 768 | $this->from->name = $name ?: false; 769 | 770 | $this->from->mailbox = $mailbox; 771 | 772 | $this->from->host = $host; 773 | 774 | $this->from->email = $this->from->mailbox . '@' . $this->from->host; 775 | 776 | return $this; 777 | } 778 | 779 | /** 780 | * Updates the HTML text body by concatenating the given 781 | * string to the current HTML body. 782 | * 783 | * @param string $html The HTML string to be added to the HTML text body. 784 | * 785 | * @return Email 786 | */ 787 | public function setHTML($html) 788 | { 789 | $this->text_html .= trim($html); 790 | 791 | return $this; 792 | } 793 | 794 | /** 795 | * Updates the plain text body by concatenating the given 796 | * string to the current plain text body. 797 | * 798 | * @param string $plain The text string to be added to the plain text body. 799 | * 800 | * @return Email 801 | */ 802 | public function setPlain($plain) 803 | { 804 | $this->text_plain .= trim($plain); 805 | 806 | return $this; 807 | } 808 | 809 | /** 810 | * Adds an attachment to this email. 811 | * 812 | * @param EmailAttachment $attachment An attachment object. 813 | * 814 | * @return Email 815 | */ 816 | public function addAttachment(EmailAttachment $attachment) 817 | { 818 | $this->attachments[$attachment->id()] = $attachment; 819 | 820 | return $this; 821 | } 822 | 823 | /** 824 | * Inject in-line attachments by replacing the attachment ids 825 | * with the attachment file path. 826 | * 827 | * @param string $body The email body to have attachments injected. 828 | * 829 | * @return string 830 | */ 831 | public function injectInline($body) 832 | { 833 | if ($this->attachments) { 834 | foreach ($this->attachments as $attachment) { 835 | if ($attachment->isInline()) { 836 | if ($attachment->id() && $attachment->filePath()) { 837 | $body = str_replace('cid:' . $attachment->id(), $attachment->filePath(), $body); 838 | } 839 | } 840 | } 841 | } 842 | 843 | return $body; 844 | } 845 | } 846 | -------------------------------------------------------------------------------- /src/phpImapReader/EmailAttachment.php: -------------------------------------------------------------------------------- 1 | 11 | * @copyright 2022 Copyright (c) Benjamin Hall 12 | * @license MIT https://github.com/benhall14/php-imap-reader 13 | * @link https://conobe.co.uk/projects/php-imap-reader/ 14 | */ 15 | class EmailAttachment 16 | { 17 | /** 18 | * The attachment id 19 | * 20 | * @var int 21 | */ 22 | public $id; 23 | 24 | /** 25 | * The attachment name. 26 | * 27 | * @var string 28 | */ 29 | public $name; 30 | 31 | /** 32 | * The attachment file path. 33 | * 34 | * @var string 35 | */ 36 | public $file_path; 37 | 38 | /** 39 | * The attachment type. 40 | * 41 | * @var string 42 | */ 43 | public $type; 44 | 45 | /** 46 | * The attachment mime type. 47 | * 48 | * @var string 49 | */ 50 | public $mime; 51 | 52 | /** 53 | * The attachment data. 54 | * 55 | * @var string 56 | */ 57 | public $attachment_data; 58 | 59 | /** 60 | * Sets the attachments id. 61 | * 62 | * @param int $id The attachment id. 63 | * 64 | * @return EmailAttachment 65 | */ 66 | public function setID($id) 67 | { 68 | $this->id = $id; 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * Sets the attachments name. 75 | * 76 | * @param string $name The attachment name. 77 | * 78 | * @return EmailAttachment 79 | */ 80 | public function setName($name) 81 | { 82 | $this->name = $name; 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * Sets the attachment type. 89 | * 90 | * @param string $type The attachment type. 91 | * 92 | * @return EmailAttachment 93 | */ 94 | public function setType($type) 95 | { 96 | $this->type = $type; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Sets the attachment mime type. 103 | * 104 | * @param string $mime_type The attachment mime type. 105 | * 106 | * @return EmailAttachment 107 | */ 108 | public function setMime($mime_type) 109 | { 110 | $this->mime = $mime_type; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Sets the attachments file path. 117 | * 118 | * @param string $file_path The attachment file path. 119 | * 120 | * @return EmailAttachment 121 | */ 122 | public function setFilePath($file_path) 123 | { 124 | $this->file_path = $file_path; 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Sets the attachments data 131 | * 132 | * @param string $data The attachment data. 133 | * 134 | * @return EmailAttachment 135 | */ 136 | public function setAttachmentData($data) 137 | { 138 | $this->attachment_data = $data; 139 | 140 | return $this; 141 | } 142 | 143 | /** 144 | * Get the attachments id. 145 | * 146 | * @return integer 147 | */ 148 | public function id() 149 | { 150 | return $this->id; 151 | } 152 | 153 | /** 154 | * Get the attachments name. 155 | * 156 | * @return string 157 | */ 158 | public function name() 159 | { 160 | return $this->name; 161 | } 162 | 163 | /** 164 | * Get the attachments file path. 165 | * 166 | * @return string 167 | */ 168 | public function filePath() 169 | { 170 | return $this->file_path; 171 | } 172 | 173 | /** 174 | * Get the attachments content. 175 | * 176 | * @return string 177 | */ 178 | public function content() 179 | { 180 | return $this->attachment_data; 181 | } 182 | 183 | /** 184 | * Get the attachments type. 185 | * 186 | * @return string 187 | */ 188 | public function type() 189 | { 190 | return $this->type; 191 | } 192 | 193 | /** 194 | * Gets the inline status of the attachment. 195 | * 196 | * @return boolean 197 | */ 198 | public function isInline() 199 | { 200 | return $this->type() == 'inline' ? true : false; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/phpImapReader/Reader.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2022 Copyright (c) Benjamin Hall 16 | * @license MIT https://github.com/benhall14/php-imap-reader 17 | * @link https://conobe.co.uk/projects/php-imap-reader/ 18 | */ 19 | class Reader 20 | { 21 | /** 22 | * The IMAP host name. 23 | * 24 | * @var string 25 | */ 26 | public $hostname; 27 | 28 | /** 29 | * The IMAP user name. 30 | * 31 | * @var string 32 | */ 33 | public $user_name; 34 | 35 | /** 36 | * The IMAP password. 37 | * 38 | * @var string 39 | */ 40 | public $password; 41 | 42 | /** 43 | * The IMAP Encoding 44 | * 45 | * @var string 46 | */ 47 | public $encoding; 48 | 49 | /** 50 | * Save attachment status - should we save attachments. 51 | * 52 | * @var boolean 53 | */ 54 | public $save_attachments; 55 | 56 | /** 57 | * Retry status 58 | * 59 | * @var boolean 60 | */ 61 | public $retry; 62 | 63 | /** 64 | * The IMAP handler. 65 | * 66 | * @var resource 67 | */ 68 | public $imap; 69 | 70 | /** 71 | * The index of email ids. 72 | * 73 | * @var array 74 | */ 75 | public $email_index; 76 | 77 | /** 78 | * The array of previously fetched emails 79 | * 80 | * @var array 81 | */ 82 | public $emails = array(); 83 | 84 | /** 85 | * Modes - such as NEW or UNSEEN. 86 | * 87 | * @var array 88 | */ 89 | public $modes = array(); 90 | 91 | /** 92 | * The id of an specific id. 93 | * 94 | * @var int 95 | */ 96 | public $id = 0; 97 | 98 | /** 99 | * Limit the number of emails fetch. Can be used with page for pagination. 100 | * 101 | * @var int 102 | */ 103 | public $limit = 0; 104 | 105 | /** 106 | * Page number. Can be used with limit for pagination. 107 | * 108 | * @var int 109 | */ 110 | public $page = 0; 111 | 112 | /** 113 | * The email offset. 114 | * 115 | * @var int 116 | */ 117 | public $offset = 0; 118 | 119 | /** 120 | * The sorting order direction. 121 | * 122 | * @var string 123 | */ 124 | public $order = 'DESC'; 125 | 126 | /** 127 | * The mailbox name. 128 | * 129 | * @var string 130 | */ 131 | public $mailbox = 'INBOX'; 132 | 133 | /** 134 | * Defines whether the email should be auto-marked as read. 135 | * 136 | * @var boolean 137 | */ 138 | public $mark_as_read = false; 139 | 140 | /** 141 | * Defines the attachment directory on disk. 142 | * 143 | * @var string 144 | */ 145 | public $attachment_dir = null; 146 | 147 | /** 148 | * Sets the IMAP Reader 149 | * 150 | * @param string $hostname The IMAP host name. 151 | * @param string $user_name The IMAP user name. 152 | * @param string $password The IMAP password. 153 | * @param mixed $attachment_dir The directory path to store attachments or false to turn off saving attachments. 154 | * @param bool $mark_as_read Whether we should mark as read. 155 | * 156 | * @return boolean 157 | */ 158 | public function __construct($hostname, $user_name, $password, $attachment_dir = false, $mark_as_read = true, $encoding = 'UTF-8') 159 | { 160 | $this->hostname = $hostname; 161 | 162 | $this->user_name = $user_name; 163 | 164 | $this->password = $password; 165 | 166 | $this->encoding = $encoding; 167 | 168 | $this->retry = 0; 169 | 170 | $this->mark_as_read = $mark_as_read; 171 | 172 | $this->save_attachments = false; 173 | 174 | if ($attachment_dir) { 175 | if (!is_dir($attachment_dir)) { 176 | throw new Exception( 177 | 'ERROR: Directory "' . $attachment_dir . '" could not be found.' 178 | ); 179 | } 180 | 181 | if (!is_writable($attachment_dir)) { 182 | throw new Exception( 183 | 'ERROR: Directory "' . $attachment_dir . '" is not writable.' 184 | ); 185 | } 186 | 187 | $this->save_attachments = true; 188 | $this->attachment_dir = $attachment_dir; 189 | } 190 | 191 | return true; 192 | } 193 | 194 | /** 195 | * Returns true/false based on if $imap is an imap resource. 196 | * 197 | * @param mixed $imap 198 | * 199 | * @return boolean 200 | */ 201 | public function isImapResource($imap) 202 | { 203 | return is_resource($imap) && 'imap' == \get_resource_type($imap); 204 | } 205 | 206 | /** 207 | * Fetch the active IMAP stream. If no stream is active, try a connection. 208 | * 209 | * @param boolean $reconnect Whether to reconnect connection. 210 | * 211 | * @return resource 212 | */ 213 | public function stream($reconnect = false) 214 | { 215 | if ($this->imap && (!$this->isImapResource($this->imap) || !imap_ping($this->imap))) { 216 | $this->close(); 217 | 218 | $this->imap = false; 219 | } 220 | 221 | if (!$this->imap || $reconnect) { 222 | $this->imap = $this->connect(); 223 | } 224 | 225 | return $this->imap; 226 | } 227 | 228 | /** 229 | * Connect to an IMAP stream. 230 | * 231 | * @return resource 232 | */ 233 | public function connect() 234 | { 235 | $stream = imap_open( 236 | $this->hostname . $this->mailbox, 237 | $this->user_name, 238 | $this->password, 239 | false, 240 | $this->retry 241 | ); 242 | 243 | if (!$stream) { 244 | $last_error = imap_last_error(); 245 | 246 | imap_errors(); 247 | 248 | throw new Exception('ERROR: Could Not Connect (' . $last_error . ')'); 249 | } 250 | 251 | return $stream; 252 | } 253 | 254 | /** 255 | * Close the current IMAP stream. 256 | * 257 | * @return Reader 258 | */ 259 | public function close() 260 | { 261 | if ($this->isImapResource($this->imap)) { 262 | imap_close($this->imap, CL_EXPUNGE); 263 | } 264 | 265 | return $this; 266 | } 267 | 268 | /** 269 | * Resets the reader to be able to connect to another folder. 270 | * 271 | * @return Reader 272 | */ 273 | public function reset() 274 | { 275 | $this->close(); 276 | 277 | $this->emails = []; 278 | 279 | return $this; 280 | } 281 | 282 | /** 283 | * Close connection on destruct. 284 | * 285 | * @return Reader 286 | */ 287 | public function __destruct() 288 | { 289 | return $this->close(); 290 | } 291 | 292 | /** 293 | * The number of emails found. 294 | * 295 | * @return int 296 | */ 297 | public function count() 298 | { 299 | return imap_num_msg($this->stream()); 300 | } 301 | 302 | /** 303 | * Get the last error. 304 | * 305 | * @return string The error message. 306 | */ 307 | public function getError() 308 | { 309 | return imap_last_error(); 310 | } 311 | 312 | /** 313 | * Alias for doesMailboxExist - Returns true/false based on if the specified folder/mailbox exists on the IMAP stream. 314 | * 315 | * @param string $folder_name 316 | * 317 | * @return boolean. 318 | */ 319 | public function doesFolderExist($folder_name = null) 320 | { 321 | return $this->doesMailboxExist($folder_name); 322 | } 323 | 324 | /** 325 | * Returns true/false based on if the specified folder/mailbox exists on the IMAP stream. 326 | * 327 | * @param string $mailbox 328 | * 329 | * @return boolean 330 | */ 331 | public function doesMailboxExist($mailbox) 332 | { 333 | if (!$mailbox) { 334 | return false; 335 | } 336 | 337 | $mailboxes = imap_list($this->stream(), $this->hostname, "*"); 338 | 339 | if (!in_array($this->hostname . $mailbox, $mailboxes)) { 340 | return false; 341 | } 342 | 343 | return true; 344 | } 345 | 346 | /** 347 | * Create a new folder/mailbox on the IMAP stream. 348 | * 349 | * @param string $folder_name 350 | * 351 | * @return boolean 352 | */ 353 | public function makeFolder($folder_name = null) 354 | { 355 | if (!$folder_name) { 356 | return false; 357 | } 358 | 359 | if ($this->doesFolderExist($folder_name)) { 360 | return false; 361 | } 362 | 363 | return imap_createmailbox($this->stream(), imap_utf7_encode($this->hostname . $folder_name)); 364 | } 365 | 366 | /** 367 | * Alias for makeFolder. Creates a new folder/mailbox on the IMAP stream. 368 | * 369 | * @param string $folder_name 370 | * 371 | * @return boolean 372 | */ 373 | public function createFolder($folder_name) 374 | { 375 | return $this->makeFolder($folder_name); 376 | } 377 | 378 | /** 379 | * Alias for makeFolder. Create a new folder/mailbox on the IMAP stream. 380 | * 381 | * @param string $mailbox 382 | * 383 | * @return boolean 384 | */ 385 | public function createMailbox($mailbox = null) 386 | { 387 | return $this->makeFolder($mailbox); 388 | } 389 | 390 | /** 391 | * Alias for makeFolder. Create a new folder/mailbox on the IMAP stream. 392 | * 393 | * @param string $mailbox 394 | * 395 | * @return boolean 396 | */ 397 | public function makeMailbox($mailbox = null) 398 | { 399 | return $this->makeFolder($mailbox); 400 | } 401 | 402 | /** 403 | * Delete an email by given email id. 404 | * 405 | * @param int $email_id The id of the email to delete. 406 | * 407 | * @return boolean 408 | */ 409 | public function deleteEmail($email_id) 410 | { 411 | return imap_delete($this->stream(), $email_id, FT_UID); 412 | } 413 | 414 | /** 415 | * Expunge all emails that are marked for deletion on the connected inbox. 416 | * 417 | * @return boolean 418 | */ 419 | public function expunge() 420 | { 421 | return imap_expunge($this->stream()); 422 | } 423 | 424 | /** 425 | * Mark an email as read by given email id. 426 | * 427 | * @param int $email_id The id of the email to mark as read. 428 | * 429 | * @return boolean 430 | */ 431 | public function markAsRead($email_id) 432 | { 433 | return imap_setflag_full($this->stream(), $email_id, '\\Seen', ST_UID); 434 | } 435 | 436 | /** 437 | * Move mail to specific folder 438 | * @param int $email_id The id of the email to move. 439 | * @param string $folder Destination folder 440 | * @return boolean The result of the action. 441 | */ 442 | public function moveEmailToFolder($email_id, $folder) 443 | { 444 | if ($this->mailbox == $folder) { 445 | return false; 446 | } 447 | 448 | return imap_mail_move($this->stream(), (string) $email_id, $folder, CP_UID); 449 | } 450 | 451 | /** 452 | * Get the list of emails from the last get call. 453 | * 454 | * @return array An array of returned emails. 455 | */ 456 | public function emails() 457 | { 458 | return $this->emails; 459 | } 460 | 461 | /** 462 | * Get the first email from the list of returned emails. This is a shortcut for $this->emails[0]; 463 | * 464 | * @return Email The email. 465 | */ 466 | public function email() 467 | { 468 | return $this->emails && isset($this->emails[0]) 469 | ? $this->emails[0] : null; 470 | } 471 | 472 | /** 473 | * Get the email based on the given id. 474 | * 475 | * @param int $id The Email Id. 476 | * 477 | * @return Reader 478 | */ 479 | public function id($id) 480 | { 481 | $this->id = (int) $id; 482 | 483 | return $this; 484 | } 485 | 486 | /** 487 | * Add 'all' to the mode selection. 488 | * This allows for matching 'all' emails. 489 | * 490 | * @return Reader 491 | */ 492 | public function all() 493 | { 494 | $this->modes[] = 'ALL'; 495 | 496 | return $this; 497 | } 498 | 499 | /** 500 | * Add 'flagged' to the mode selection. 501 | * This allows for matching 'flagged' emails. 502 | * 503 | * @return Reader 504 | */ 505 | public function flagged() 506 | { 507 | $this->modes[] = 'FLAGGED'; 508 | 509 | return $this; 510 | } 511 | 512 | /** 513 | * Add 'unanswered' to the mode selection. 514 | * This allows for matching 'unanswered' emails. 515 | * 516 | * @return Reader 517 | */ 518 | public function unanswered() 519 | { 520 | $this->modes[] = 'UNANSWERED'; 521 | 522 | return $this; 523 | } 524 | 525 | /** 526 | * Add 'deleted' to the mode selection. 527 | * This allows for matching 'deleted' emails. 528 | * 529 | * @return Reader 530 | */ 531 | public function deleted() 532 | { 533 | $this->modes[] = 'DELETED'; 534 | 535 | return $this; 536 | } 537 | 538 | /** 539 | * Add 'unseen' to the mode selection. 540 | * This allows for matching 'unseen' emails. 541 | * 542 | * @return Reader 543 | */ 544 | public function unseen() 545 | { 546 | $this->modes[] = 'UNSEEN'; 547 | 548 | return $this; 549 | } 550 | 551 | /** 552 | * An alias of unseen. See unseen(). 553 | * 554 | * @return void 555 | */ 556 | public function unread() 557 | { 558 | return $this->unseen(); 559 | } 560 | 561 | /** 562 | * Add 'from' to the mode selection. 563 | * This allows for matching emails 'from' the given email address. 564 | * 565 | * @param string $from The sender email address. 566 | * 567 | * @return Reader 568 | */ 569 | public function from($from) 570 | { 571 | $this->modes[] = 'FROM "' . $from . '"'; 572 | 573 | return $this; 574 | } 575 | 576 | /** 577 | * Add the body search to the mode selection. 578 | * This allows for searching for a string within a body 579 | * 580 | * @param string $string The search keywords. 581 | * 582 | * @return Reader 583 | */ 584 | public function searchBody($string) 585 | { 586 | if ($string) { 587 | $this->modes[] = 'BODY "' . $string . '"'; 588 | } 589 | 590 | return $this; 591 | } 592 | 593 | /** 594 | * Add the subject search to the mode selection. 595 | * This allows for searching for a string within a subject line. 596 | * 597 | * @param string $string The string to search for. 598 | * 599 | * @return Reader 600 | */ 601 | public function searchSubject($string) 602 | { 603 | if ($string) { 604 | $this->modes[] = 'SUBJECT "' . $string . '"'; 605 | } 606 | 607 | return $this; 608 | } 609 | 610 | /** 611 | * Add the recent flag to the mode selection. 612 | * This allows for matching recent emails. 613 | * 614 | * @return Reader 615 | */ 616 | public function recent() 617 | { 618 | $this->modes[] = 'RECENT'; 619 | 620 | return $this; 621 | } 622 | 623 | /** 624 | * Add the unflagged flag to the mode selection. 625 | * This allows for matching unflagged emails. 626 | * 627 | * @return Reader 628 | */ 629 | public function unflagged() 630 | { 631 | $this->modes[] = 'UNFLAGGED'; 632 | 633 | return $this; 634 | } 635 | 636 | /** 637 | * Add the seen flag to the mode selection. 638 | * This allows for matching seen emails. 639 | * 640 | * @return Reader 641 | */ 642 | public function seen() 643 | { 644 | $this->modes[] = 'SEEN'; 645 | 646 | return $this; 647 | } 648 | 649 | /** 650 | * Alias of seen(). See Seen(). 651 | * 652 | * @return void 653 | */ 654 | public function read() 655 | { 656 | $this->seen(); 657 | } 658 | 659 | /** 660 | * Add the new flag to the mode selection. This allows for matching new emails. 661 | * 662 | * @return Reader 663 | */ 664 | public function newMessages() 665 | { 666 | $this->modes[] = 'NEW'; 667 | 668 | return $this; 669 | } 670 | 671 | /** 672 | * Add the old flag to the mode selection. 673 | * This allows for matching old emails. 674 | * 675 | * @return Reader 676 | */ 677 | public function oldMessages() 678 | { 679 | $this->modes[] = 'OLD'; 680 | 681 | return $this; 682 | } 683 | 684 | /** 685 | * Add the keyword flag to the mode selection. 686 | * This allows for matching emails with the given keyword. 687 | * 688 | * @param string $keyword The keyword to search for. 689 | * 690 | * @return Reader 691 | */ 692 | public function keyword($keyword) 693 | { 694 | $this->modes[] = 'KEYWORD "' . $keyword . '"'; 695 | 696 | return $this; 697 | } 698 | 699 | /** 700 | * Add the unkeyword flag to the mode selection. 701 | * This allows for matching emails without the given keyword. 702 | * 703 | * @param string $keyword The keyword to avoid. 704 | * 705 | * @return Reader 706 | */ 707 | public function unkeyword($keyword) 708 | { 709 | $this->modes[] = 'UNKEYWORD "' . $keyword . '"'; 710 | 711 | return $this; 712 | } 713 | 714 | /** 715 | * Add the before date flag to the mode selection. 716 | * This allows for matching emails received before the given date. 717 | * 718 | * @param string $date The date to match. 719 | * 720 | * @return Reader 721 | */ 722 | public function beforeDate($date) 723 | { 724 | $date = date('d-M-Y', strtotime($date)); 725 | 726 | $this->modes[] = 'BEFORE "' . $date . '"'; 727 | 728 | return $this; 729 | } 730 | 731 | /** 732 | * Add the since date flag to the mode selection. 733 | * This allows for matching emails received since the given date. 734 | * 735 | * @param string $date The date to match. 736 | * 737 | * @return Reader 738 | */ 739 | public function sinceDate($date) 740 | { 741 | $date = date('d-M-Y', strtotime($date)); 742 | 743 | $this->modes[] = 'SINCE "' . $date . '"'; 744 | 745 | return $this; 746 | } 747 | 748 | /** 749 | * Add the sent to flag to the mode selection. 750 | * This allows for matching emails sent to the given email address string. 751 | * 752 | * @param string $to The email address to match. 753 | * 754 | * @return Reader 755 | */ 756 | public function sentTo($to) 757 | { 758 | $this->modes[] = 'TO "' . $to . '"'; 759 | 760 | return $this; 761 | } 762 | 763 | /** 764 | * Add the BCC flag to the mode selection. 765 | * This allows for matching emails with the string present in the BCC field. 766 | * 767 | * @param string $to The email address to match. 768 | * 769 | * @return Reader 770 | */ 771 | public function searchBCC($to) 772 | { 773 | $this->modes[] = 'BCC "' . $to . '"'; 774 | 775 | return $this; 776 | } 777 | 778 | /** 779 | * Add the CC flag to the mode selection. 780 | * This allows for matching emails with the string present in the CC field. 781 | * 782 | * @param string $to The email address to match. 783 | * 784 | * @return Reader 785 | */ 786 | public function searchCC($to) 787 | { 788 | $this->modes[] = 'CC "' . $to . '"'; 789 | 790 | return $this; 791 | } 792 | 793 | /** 794 | * Add the on date flag to the mode selection. 795 | * This allows for matching emails received on the given date. 796 | * 797 | * @param string $date The date to match. 798 | * 799 | * @return Reader 800 | */ 801 | public function onDate($date) 802 | { 803 | $date = date('d-M-Y', strtotime($date)); 804 | 805 | $this->modes[] = 'ON "' . $date . '"'; 806 | 807 | return $this; 808 | } 809 | 810 | /** 811 | * Add the text flag to the mode selection. 812 | * This allows for matching emails with the given text string. 813 | * 814 | * @param string $string The string to search for. 815 | * 816 | * @return Reader 817 | */ 818 | public function searchText($string) 819 | { 820 | $this->modes[] = 'TEXT "' . $string . '"'; 821 | 822 | return $this; 823 | } 824 | 825 | /** 826 | * Set the limit. 827 | * This can be used with page() for pagination of emails. 828 | * 829 | * @param int $limit The total number of emails to return. 830 | * 831 | * @return Reader 832 | */ 833 | public function limit($limit) 834 | { 835 | $this->limit = (int) $limit; 836 | 837 | return $this; 838 | } 839 | 840 | /** 841 | * Set the page number. 842 | * This is used with limit() for pagination of emails. 843 | * 844 | * @param int $page The page number. 845 | * 846 | * @return Reader 847 | */ 848 | public function page($page) 849 | { 850 | $this->page = $page; 851 | 852 | $this->offset = ($page - 1) * $this->limit; 853 | 854 | return $this; 855 | } 856 | 857 | /** 858 | * Set the email fetching order to ASCending. 859 | * 860 | * @return Reader 861 | */ 862 | public function orderASC() 863 | { 864 | $this->order = 'ASC'; 865 | 866 | return $this; 867 | } 868 | 869 | /** 870 | * Set the email fetching order to DESCending. 871 | * 872 | * @return Reader 873 | */ 874 | public function orderDESC() 875 | { 876 | $this->order = 'DESC'; 877 | 878 | return $this; 879 | } 880 | 881 | /** 882 | * Sets the folder to retrieve emails from. Alias for mailbox(). 883 | * 884 | * @param string $folder The name of the folder. IE. INBOX. 885 | * 886 | * @return Reader 887 | */ 888 | public function folder($folder) 889 | { 890 | return $this->mailbox($folder); 891 | } 892 | 893 | /** 894 | * Sets the mailbox to retrieve emails from. 895 | * 896 | * @param string $mailbox The name of the mailbox, IE. INBOX. 897 | * 898 | * @return Reader 899 | */ 900 | public function mailbox($mailbox) 901 | { 902 | $this->mailbox = $mailbox; 903 | 904 | return $this; 905 | } 906 | 907 | /** 908 | * Get a formatted list of selected modes for imap_search. 909 | * 910 | * @return string 911 | */ 912 | public function modes() 913 | { 914 | if (!$this->modes) { 915 | $this->modes[] = 'ALL'; 916 | } 917 | 918 | return implode(' ', $this->modes); 919 | } 920 | 921 | /** 922 | * Fetch emails based on the previously set parameters. 923 | * 924 | * @return array 925 | */ 926 | public function get() 927 | { 928 | if (!$this->connect()) { 929 | throw new Exception('ERROR: Could not connect.'); 930 | } 931 | 932 | if ($this->id) { 933 | $this->emails = array(); 934 | 935 | $this->emails[] = $this->getEmail($this->id); 936 | 937 | return $this->emails; 938 | } 939 | 940 | $this->emails = array(); 941 | 942 | $this->email_index = imap_search( 943 | $this->stream(), 944 | $this->modes(), 945 | false, 946 | $this->encoding 947 | ); 948 | 949 | if (!$this->limit) { 950 | $this->limit = isset($this->email_index) && is_array($this->email_index) ? count($this->email_index) : 0; 951 | } 952 | 953 | if ($this->email_index) { 954 | if ($this->order == 'DESC') { 955 | rsort($this->email_index); 956 | } else { 957 | sort($this->email_index); 958 | } 959 | 960 | if ($this->limit || ($this->limit && $this->offset)) { 961 | $this->email_index = array_slice( 962 | $this->email_index, 963 | $this->offset, 964 | $this->limit 965 | ); 966 | } 967 | 968 | $this->emails = array(); 969 | 970 | foreach ($this->email_index as $id) { 971 | $this->emails[] = $this->getEmailByMessageSequence($id); 972 | 973 | if ($this->mark_as_read) { 974 | $this->markAsRead($id); 975 | } 976 | } 977 | } 978 | 979 | return $this->emails; 980 | } 981 | 982 | /** 983 | * Fetches an email by its UID. 984 | * 985 | * @param integer $uid UID Number 986 | * 987 | * @return Email 988 | */ 989 | public function getEmailByUID($uid) 990 | { 991 | return $this->getEmail($uid); 992 | } 993 | 994 | /** 995 | * Fetches an email by its message sequence id 996 | * 997 | * @param integer $id ID 998 | * 999 | * @return Email 1000 | */ 1001 | public function getEmailByMessageSequence($id) 1002 | { 1003 | $uid = imap_uid($this->stream(), $id); 1004 | 1005 | return $this->getEmail($uid); 1006 | } 1007 | 1008 | /** 1009 | * Fetch an email by id. 1010 | * 1011 | * @param integer $uid The message UID. 1012 | * 1013 | * @return Email 1014 | */ 1015 | public function getEmail($uid) 1016 | { 1017 | $email = new Email(); 1018 | 1019 | // imap_headerinfo doesn't work with the uid, so we use imap_fetchbody instead. 1020 | //$header = imap_headerinfo($this->stream(), $uid); 1021 | 1022 | $options = ($this->mark_as_read) ? FT_UID : FT_UID | FT_PEEK; 1023 | 1024 | $header_from_body = imap_fetchbody($this->stream(), $uid, '0', $options); 1025 | 1026 | $header = imap_rfc822_parse_headers($header_from_body); 1027 | 1028 | if (!$header) { 1029 | return null; 1030 | } 1031 | 1032 | $email->setId($uid); 1033 | 1034 | $header->subject = isset($header->subject) 1035 | ? $this->decodeMimeHeader($header->subject) 1036 | : false; 1037 | 1038 | $email->setSubject($header->subject); 1039 | 1040 | $email->setDate(isset($header->date) ? $header->date : null); 1041 | 1042 | if (isset($header->to)) { 1043 | foreach ($header->to as $to) { 1044 | $to_name = isset($to->personal) 1045 | ? $this->decodeMimeHeader($to->personal) 1046 | : false; 1047 | $email->addTo($to->mailbox, $to->host, $to_name); 1048 | } 1049 | } 1050 | 1051 | if (isset($header->from)) { 1052 | $from_name = isset($header->from[0]->personal) 1053 | ? $this->decodeMimeHeader($header->from[0]->personal) 1054 | : false; 1055 | $email->setFrom( 1056 | $header->from[0]->mailbox, 1057 | $header->from[0]->host, 1058 | $from_name 1059 | ); 1060 | } 1061 | 1062 | if (isset($header->reply_to)) { 1063 | foreach ($header->reply_to as $reply_to) { 1064 | $reply_to_name = isset($reply_to->personal) 1065 | ? $this->decodeMimeHeader($reply_to->personal) 1066 | : false; 1067 | $email->addReplyTo( 1068 | $reply_to->mailbox, 1069 | $reply_to->host, 1070 | $reply_to_name 1071 | ); 1072 | } 1073 | } 1074 | 1075 | if (isset($header->cc)) { 1076 | foreach ($header->cc as $cc) { 1077 | $cc_name = isset($cc->personal) 1078 | ? $this->decodeMimeHeader($cc->personal) 1079 | : false; 1080 | $email->addCC($cc->mailbox, $cc->host, $cc_name); 1081 | } 1082 | } 1083 | 1084 | $email->setRawBody(imap_fetchbody($this->stream(), $uid, '', $options)); 1085 | 1086 | $body = imap_fetchstructure($this->stream(), $uid, FT_UID); 1087 | 1088 | if (isset($body->parts) && count($body->parts)) { 1089 | foreach ($body->parts as $part_number => $part) { 1090 | $this->decodePart($email, $part, $part_number + 1); 1091 | } 1092 | } else { 1093 | $this->decodePart($email, $body); 1094 | } 1095 | 1096 | $msgno = imap_msgno($this->stream(), $uid); 1097 | 1098 | $email->setMsgno($msgno); 1099 | 1100 | $header = imap_headerinfo($this->imap, $msgno, 20, 20); 1101 | 1102 | $email->setSize(isset($header->Size) ? $header->Size : 0); 1103 | 1104 | $email->setUdate(isset($header->udate) ? $header->udate : null); 1105 | 1106 | $recent = isset($header->Recent) 1107 | && ($header->Recent == 'R' || $header->Recent == 'N') 1108 | ? true 1109 | : false; 1110 | $email->setRecent($recent); 1111 | 1112 | $unseen = isset($header->Unseen) && $header->Unseen == 'U' 1113 | ? true : false; 1114 | $email->setUnseen($unseen); 1115 | 1116 | $flagged = isset($header->Flagged) && $header->Flagged == 'F' 1117 | ? true : false; 1118 | $email->setFlagged($flagged); 1119 | 1120 | $answered = isset($header->Answered) && $header->Answered == 'A' 1121 | ? true : false; 1122 | $email->setAnswered($answered); 1123 | 1124 | $deleted = isset($header->Deleted) && $header->Deleted == 'D' 1125 | ? true : false; 1126 | $email->setDeleted($deleted); 1127 | 1128 | $draft = isset($header->Draft) && $header->Draft == 'X' 1129 | ? true : false; 1130 | $email->setDraft($draft); 1131 | 1132 | $headers = imap_fetchheader($this->stream(), $email->msgno()); 1133 | 1134 | if ($headers) { 1135 | 1136 | $headers_array = explode("\n", imap_fetchheader($this->stream(), $email->msgno())); 1137 | 1138 | foreach ($headers_array as $header) { 1139 | if (strpos($header, "X-") !== false) { 1140 | $email->addCustomHeader($header); 1141 | } 1142 | } 1143 | } 1144 | 1145 | return $email; 1146 | } 1147 | 1148 | /** 1149 | * Decode an email part. 1150 | * 1151 | * @param Email $email The email object to update. 1152 | * @param object $part The part data to decode. 1153 | * @param boolean $part_number The part number. 1154 | * 1155 | * @return string 1156 | */ 1157 | public function decodePart(Email $email, $part, $part_number = false) 1158 | { 1159 | $options = ($this->mark_as_read) ? FT_UID : FT_UID | FT_PEEK; 1160 | 1161 | if ($part_number) { 1162 | $data = imap_fetchbody( 1163 | $this->stream(), 1164 | $email->id(), 1165 | $part_number, 1166 | $options 1167 | ); 1168 | } else { 1169 | $data = imap_body($this->stream(), $email->id(), $options); 1170 | } 1171 | 1172 | switch ($part->encoding) { 1173 | case 1: 1174 | $data = imap_utf8($data); 1175 | break; 1176 | 1177 | case 2: 1178 | $data = imap_binary($data); 1179 | break; 1180 | 1181 | case 3: 1182 | $data = imap_base64($data); 1183 | break; 1184 | 1185 | case 4: 1186 | $data = quoted_printable_decode($data); 1187 | break; 1188 | } 1189 | 1190 | $params = array(); 1191 | if (isset($part->parameters)) { 1192 | foreach ($part->parameters as $param) { 1193 | $params[strtolower($param->attribute)] = $param->value; 1194 | } 1195 | } 1196 | 1197 | if (isset($part->dparameters)) { 1198 | foreach ($part->dparameters as $param) { 1199 | $params[strtolower($param->attribute)] = $param->value; 1200 | } 1201 | } 1202 | 1203 | // is this part an attachment 1204 | $attachment_id = false; 1205 | $is_attachment = false; 1206 | 1207 | if ( 1208 | isset($part->disposition) 1209 | && in_array(strtolower($part->disposition), array('attachment', 'inline')) 1210 | && $part->subtype != 'PLAIN' 1211 | ) { 1212 | $is_attachment = true; 1213 | $attachment_type = strtolower($part->disposition); 1214 | 1215 | if ($attachment_type == 'inline') { 1216 | $is_inline_attachment = true; 1217 | $attachment_id = isset($part->id) ? trim($part->id, " <>") : false; 1218 | } else { 1219 | $is_inline_attachment = false; 1220 | $attachment_id = rand(); 1221 | } 1222 | } 1223 | 1224 | // if there is an attachment 1225 | if ($is_attachment) { 1226 | $file_name = false; 1227 | 1228 | if (isset($params['filename'])) { 1229 | $file_name = $params['filename']; 1230 | } elseif (isset($params['name'])) { 1231 | $file_name = $params['name']; 1232 | } 1233 | 1234 | if ($file_name) { 1235 | $file_name = $attachment_id . '-' . $file_name; 1236 | 1237 | $attachment = new EmailAttachment(); 1238 | $attachment->setId($attachment_id); 1239 | $attachment->setName($file_name); 1240 | 1241 | if ($is_inline_attachment) { 1242 | $attachment->setType('inline'); 1243 | } else { 1244 | $attachment->setType('attachment'); 1245 | } 1246 | 1247 | if ($this->save_attachments) { 1248 | $attachment->setFilePath( 1249 | $this->attachment_dir . DIRECTORY_SEPARATOR . $attachment->name() 1250 | ); 1251 | 1252 | if ($this->attachment_dir && $attachment->filePath()) { 1253 | if (!file_exists($attachment->filePath())) { 1254 | file_put_contents($attachment->filePath(), $data); 1255 | } 1256 | } 1257 | } else { 1258 | $attachment->setAttachmentData($data); 1259 | } 1260 | 1261 | $email->addAttachment($attachment); 1262 | } 1263 | } else { 1264 | // if the charset is set, convert to our encoding UTF-8 1265 | if (!empty($params['charset'])) { 1266 | $data = $this->convertEncoding($data, $params['charset']); 1267 | } 1268 | 1269 | // part->type = 0 is TEXT or TYPETEXT 1270 | if (isset($part->type)) { 1271 | if ($part->type == 0) { 1272 | // subpart is either plain text or html version 1273 | if (strtoupper($part->subtype) == 'PLAIN') { 1274 | $email->setPlain($data); 1275 | } else { 1276 | $email->setHTML($data); 1277 | } 1278 | 1279 | // part->type = 2 is MESSAGE 1280 | } elseif ($part->type == 2) { 1281 | $email->setPlain($data); 1282 | } 1283 | } 1284 | } 1285 | 1286 | // rerun for additional parts 1287 | if (!empty($part->parts)) { 1288 | foreach ($part->parts as $subpart_number => $subpart) { 1289 | if ($part->type == 2 && $part->subtype == 'RFC822') { 1290 | $this->decodePart($email, $subpart, $part_number); 1291 | } else { 1292 | $this->decodePart($email, $subpart, $part_number . '.' . ($subpart_number + 1)); 1293 | } 1294 | } 1295 | } 1296 | 1297 | return trim($data); 1298 | } 1299 | 1300 | /** 1301 | * Decode Mime Header. 1302 | * 1303 | * @param string $encoded_header The encoded header string. 1304 | * 1305 | * @return string 1306 | */ 1307 | public function decodeMimeHeader($encoded_header) 1308 | { 1309 | $decoded_header = ''; 1310 | 1311 | $elements = imap_mime_header_decode($encoded_header); 1312 | 1313 | for ($i = 0; $i < count($elements); $i++) { 1314 | if ($elements[$i]->charset == 'default') { 1315 | $elements[$i]->charset = 'iso-8859-1'; 1316 | } 1317 | 1318 | $decoded_header .= $this->convertEncoding($elements[$i]->text, $elements[$i]->charset); 1319 | } 1320 | 1321 | return $decoded_header; 1322 | } 1323 | 1324 | /** 1325 | * Convert a string encoding to the encoding set. 1326 | * 1327 | * @param string $string The string to re-encode. 1328 | * @param string $current_encoding_type The encoding type of the original string. 1329 | * 1330 | * @return string 1331 | */ 1332 | public function convertEncoding($string, $current_encoding_type) 1333 | { 1334 | $converted_string = false; 1335 | 1336 | if (!$string) { 1337 | return $string; 1338 | } 1339 | 1340 | if ($current_encoding_type == $this->encoding) { 1341 | return $string; 1342 | } 1343 | 1344 | if (extension_loaded('mbstring')) { 1345 | $converted_string = @mb_convert_encoding($string, $this->encoding, $current_encoding_type); 1346 | } else { 1347 | $converted_string = @iconv($current_encoding_type, $this->encoding . '//IGNORE', $string); 1348 | } 1349 | 1350 | return $converted_string ?: $string; 1351 | } 1352 | } 1353 | --------------------------------------------------------------------------------