PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/ImapMailbox.php

https://github.com/ychoucha/php-imap
PHP | 626 lines | 352 code | 37 blank | 237 comment | 26 complexity | b94d8b9ec7eb15f9218caac314d8eba5 MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /**
  3. * @see https://github.com/barbushin/php-imap
  4. * @author Barbushin Sergey http://linkedin.com/in/barbushin
  5. *
  6. */
  7. class ImapMailbox {
  8. protected $imapPath;
  9. protected $login;
  10. protected $password;
  11. protected $mbox;
  12. protected $serverEncoding;
  13. protected $attachmentsDir;
  14. public function __construct($imapPath, $login, $password, $attachmentsDir = false, $serverEncoding = 'utf-8') {
  15. $this->imapPath = $imapPath;
  16. $this->login = $login;
  17. $this->password = $password;
  18. $this->serverEncoding = $serverEncoding;
  19. if ($attachmentsDir) {
  20. if (!is_dir($attachmentsDir)) {
  21. throw new Exception('Directory "' . $attachmentsDir . '" not found');
  22. }
  23. $this->attachmentsDir = realpath($attachmentsDir);
  24. }
  25. $this->connect();
  26. }
  27. /*
  28. * Connect to IMAP mailbox
  29. */
  30. public function connect() {
  31. $this->mbox = @imap_open($this->imapPath, $this->login, $this->password);
  32. if (!$this->mbox) {
  33. throw new ImapMailboxException('Connection error: ' . imap_last_error());
  34. }
  35. return true;
  36. }
  37. /*
  38. * CLose IMAP connection
  39. */
  40. public function disconnect() {
  41. if ($this->mbox) {
  42. $this->expungeDeletedMessages();
  43. $errors = imap_errors();
  44. if ($errors) {
  45. foreach ($errors as $error) {
  46. trigger_error($error);
  47. }
  48. }
  49. imap_close($this->mbox);
  50. $this->mbox = null;
  51. }
  52. }
  53. /*
  54. * Pings the stream to see if it's still active. It may discover new mail;
  55. * this is the preferred method for a periodic "new mail check" as well as a "keep alive"
  56. * for servers which have inactivity timeout.
  57. *
  58. * Returns TRUE if the stream is still alive, attempts to reconnect otherwise.
  59. */
  60. public function pingMailbox() {
  61. if (!imap_ping($this->mbox)) {
  62. return $this->reconnect();
  63. }
  64. return true;
  65. }
  66. /*
  67. * Re-connect to IMAP mailbox
  68. */
  69. public function reconnect() {
  70. $this->disconnect();
  71. return $this->connect();
  72. }
  73. /*
  74. * object checkMailbox ( )
  75. *
  76. * Checks information about the current mailbox.
  77. *
  78. * Returns the information in an object with following properties:
  79. * Date - current system time formatted according to ť RFC2822
  80. * Driver - protocol used to access this mailbox: POP3, IMAP, NNTP
  81. * Mailbox - the mailbox name
  82. * Nmsgs - number of messages in the mailbox
  83. * Recent - number of recent messages in the mailbox
  84. * Returns FALSE on failure.
  85. */
  86. public function checkMailbox() {
  87. $this->pingMailbox();
  88. return imap_check($this->mbox);
  89. }
  90. /*
  91. * array searchMailbox ( string $criteria )
  92. *
  93. * This function performs a search on the mailbox currently opened in the given IMAP stream.
  94. * For example, to match all unanswered messages sent by Mom, you'd use: "UNANSWERED FROM mom".
  95. * Searches appear to be case insensitive. This list of criteria is from a reading of the UW
  96. * c-client source code and may be incomplete or inaccurate (see also ť RFC2060, section 6.4.4).
  97. *
  98. * criteria
  99. * A 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.
  100. * ALL - return all messages matching the rest of the criteria
  101. * ANSWERED - match messages with the \\ANSWERED flag set
  102. * BCC "string" - match messages with "string" in the Bcc: field
  103. * BEFORE "date" - match messages with Date: before "date"
  104. * BODY "string" - match messages with "string" in the body of the message
  105. * CC "string" - match messages with "string" in the Cc: field
  106. * DELETED - match deleted messages
  107. * FLAGGED - match messages with the \\FLAGGED (sometimes referred to as Important or Urgent) flag set
  108. * FROM "string" - match messages with "string" in the From: field
  109. * KEYWORD "string" - match messages with "string" as a keyword
  110. * NEW - match new messages
  111. * OLD - match old messages
  112. * ON "date" - match messages with Date: matching "date"
  113. * RECENT - match messages with the \\RECENT flag set
  114. * SEEN - match messages that have been read (the \\SEEN flag is set)
  115. * SINCE "date" - match messages with Date: after "date"
  116. * SUBJECT "string" - match messages with "string" in the Subject:
  117. * TEXT "string" - match messages with text "string"
  118. * TO "string" - match messages with "string" in the To:
  119. * UNANSWERED - match messages that have not been answered
  120. * UNDELETED - match messages that are not deleted
  121. * UNFLAGGED - match messages that are not flagged
  122. * UNKEYWORD "string" - match messages that do not have the keyword "string"
  123. * UNSEEN - match messages which have not been read yet
  124. *
  125. * Returns an array of message numbers or UIDs.
  126. .* Return FALSE if it does not understand the search criteria or no messages have been found.
  127. */
  128. public function searchMailbox($criteria = 'ALL') {
  129. $this->pingMailbox();
  130. $mailsIds = imap_search($this->mbox, $criteria, SE_UID, $this->serverEncoding);
  131. return $mailsIds ? $mailsIds : array();
  132. }
  133. /*
  134. * bool undeleteMessage (int $msg_number )
  135. *
  136. * Removes the deletion flag for a specified message, which is set by imap_delete() or imap_mail_move().
  137. *
  138. * msg_number
  139. * The message number
  140. */
  141. public function undeleteMessage($msg_number) {
  142. $this->pingMailbox();
  143. return imap_delete($this->mbox, $msg_number, FT_UID);
  144. }
  145. /*
  146. * bool deleteMessage (int $msg_number )
  147. *
  148. * Marks messages listed in msg_number for deletion.
  149. *
  150. * msg_number
  151. * The message number
  152. */
  153. public function deleteMessage($msg_number, $purge_deleted = false) {
  154. $this->pingMailbox();
  155. return imap_delete($this->mbox, $msg_number, FT_UID);
  156. }
  157. /*
  158. * bool expungeDeletedMessages ( )
  159. *
  160. * Deletes all the messages marked for deletion by imap_delete(), imap_mail_move(), or imap_setflag_full().
  161. */
  162. public function expungeDeletedMessages() {
  163. $this->pingMailbox();
  164. return imap_expunge($this->mbox);
  165. }
  166. // Mark e-mail as seen
  167. public function markMessageAsRead($mId) {
  168. $this->pingMailbox();
  169. $this->setFlag($mId, '\\Seen');
  170. }
  171. // Mark e-mail as unseen
  172. public function markMessageAsUnread($mId) {
  173. $this->pingMailbox();
  174. $this->clearFlag($mId, '\\Seen');
  175. }
  176. // Mark e-mail as flagged
  177. public function markMessageAsImportant($mId) {
  178. $this->pingMailbox();
  179. $this->setFlag($mId, '\\Flagged');
  180. }
  181. /*
  182. * bool setFlag ( string $sequence , string $flag )
  183. *
  184. * Causes a store to add the specified flag to the flags set for the messages in the specified sequence.
  185. *
  186. * sequence
  187. * A sequence of message numbers. You can enumerate desired messages with the X,Y syntax, or retrieve all messages within an interval with the X:Y syntax
  188. * flag
  189. * The flags which you can set are \Seen, \Answered, \Flagged, \Deleted, and \Draft as defined by ť RFC2060.
  190. *
  191. * Returns TRUE on success or FALSE on failure.
  192. */
  193. public function setFlag($sequence, $flag) {
  194. $this->pingMailbox();
  195. return imap_setflag_full($this->mbox, $sequence, $flag, ST_UID);
  196. }
  197. /*
  198. * bool clearFlag ( string $sequence , string $flag )
  199. *
  200. * This function causes a store to delete the specified flag to the flags set for the messages in the specified sequence.
  201. *
  202. * sequence
  203. * A sequence of message numbers. You can enumerate desired messages with the X,Y syntax, or retrieve all messages within an interval with the X:Y syntax
  204. *
  205. * flag
  206. * The flags which you can unset are "\\Seen", "\\Answered", "\\Flagged", "\\Deleted", and "\\Draft" (as defined by ť RFC2060)
  207. *
  208. * Returns TRUE on success or FALSE on failure.
  209. */
  210. public function clearFlag($sequence, $flag) {
  211. $this->pingMailbox();
  212. return imap_clearflag_full($this->mbox, $sequence, $flag, ST_UID);
  213. }
  214. /*
  215. * string fetchHeader ( int $msg_number )
  216. *
  217. * This function causes a fetch of the complete, unfiltered ť RFC2822 format header of the specified message.
  218. *
  219. * msg_number
  220. * The message number
  221. *
  222. * Returns the header of the specified message as a text string.
  223. */
  224. public function fetchHeader($msg_number) {
  225. $this->pingMailbox();
  226. /*
  227. if (!$headers) {
  228. throw new ImapMailboxException('Message with UID "' . $msg_number . '" not found');
  229. }
  230. */
  231. return imap_fetchheader($this->mbox, $msg_number, FT_UID);
  232. }
  233. /*
  234. * array fetchOverview ( string $sequence, bool $asCSVString )
  235. *
  236. * This function fetches mail headers for the given sequence and returns an overview of their contents.
  237. *
  238. * sequence
  239. * A message sequence description. You can enumerate desired messages with the X,Y syntax, or retrieve all messages within an interval with the X:Y syntax
  240. *
  241. * Returns an array of objects describing one message header each. The object will only define a property if it exists. The possible properties are:
  242. * subject - the messages subject
  243. * from - who sent it
  244. * to - recipient
  245. * date - when was it sent
  246. * message_id - Message-ID
  247. * references - is a reference to this message id
  248. * in_reply_to - is a reply to this message id
  249. * size - size in bytes
  250. * uid - UID the message has in the mailbox
  251. * msgno - message sequence number in the mailbox
  252. * recent - this message is flagged as recent
  253. * flagged - this message is flagged
  254. * answered - this message is flagged as answered
  255. * deleted - this message is flagged for deletion
  256. * seen - this message is flagged as already read
  257. * draft - this message is flagged as being a draft
  258. */
  259. public function fetchOverview($sequence) {
  260. $this->pingMailbox();
  261. return imap_fetch_overview($this->mbox, $sequence, FT_UID);
  262. }
  263. /*
  264. * array imap_sort ( rint $criteria , int $reverse )
  265. *
  266. * Criteria can be one (and only one) of the following:
  267. * SORTDATE - message Date
  268. * SORTARRIVAL - arrival date (default)
  269. * SORTFROM - mailbox in first From address
  270. * SORTSUBJECT - message subject
  271. * SORTTO - mailbox in first To address
  272. * SORTCC - mailbox in first cc address
  273. * SORTSIZE - size of message in octets
  274. *
  275. * reverse
  276. * Set this to 1 for reverse sorting (default)
  277. *
  278. * asString
  279. * Boolean value, return array in comma separated value format
  280. *
  281. * Returns an array of message numbers sorted by the given parameters.
  282. */
  283. public function sortMessages($criteria = SORTARRIVAL, $reverse = 1, $asString = false) {
  284. $this->pingMailbox();
  285. $list = imap_sort($this->mbox, $criteria, $reverse, SE_UID);
  286. if ($asString) {
  287. $list = rtrim(implode(',', $list), ',');
  288. }
  289. return $list;
  290. }
  291. /*
  292. * int countMessages ( )
  293. *
  294. * Gets the number of messages in the current mailbox.
  295. *
  296. * Return the number of messages in the current mailbox, as an integer.
  297. */
  298. public function countMessages() {
  299. $this->pingMailbox();
  300. return imap_num_msg($this->mbox);
  301. }
  302. public function getMail($mId) {
  303. $this->pingMailbox();
  304. $head = imap_rfc822_parse_headers($this->fetchHeader($mId));
  305. $mail = new IncomingMail();
  306. $mail->mId = $mId;
  307. $mail->date = date('Y-m-d H:i:s', isset($head->date) ? strtotime($head->date) : time());
  308. $mail->subject = $this->decodeMimeStr($head->subject);
  309. $mail->fromName = isset($head->from[0]->personal) ? $this->decodeMimeStr($head->from[0]->personal) : null;
  310. $mail->fromAddress = strtolower($head->from[0]->mailbox . '@' . $head->from[0]->host);
  311. $toStrings = array();
  312. foreach ($head->to as $to) {
  313. $toEmail = strtolower($to->mailbox . '@' . $to->host);
  314. $toName = isset($to->personal) ? $this->decodeMimeStr($to->personal) : null;
  315. $toStrings[] = $toName ? "$toName <$toEmail>" : $toEmail;
  316. $mail->to[$toEmail] = $toName;
  317. }
  318. $mail->toString = implode(', ', $toStrings);
  319. if (isset($head->cc)) {
  320. foreach ($head->cc as $cc) {
  321. $mail->cc[strtolower($cc->mailbox . '@' . $cc->host)] = isset($cc->personal) ? $this->decodeMimeStr($cc->personal) : null;
  322. }
  323. }
  324. if (isset($head->reply_to)) {
  325. foreach ($head->reply_to as $replyTo) {
  326. $mail->replyTo[strtolower($replyTo->mailbox . '@' . $replyTo->host)] = isset($replyTo->personal) ? $this->decodeMimeStr($replyTo->personal) : null;
  327. }
  328. }
  329. // object imap_fetchstructure ( resource $imap_stream , int $msg_number [, int $options = 0 ] )
  330. // Fetches all the structured information for a given message.
  331. // msg_number
  332. // The message number
  333. // options
  334. // This optional parameter only has a single option, FT_UID, which tells the function to treat the msg_number argument as a UID.
  335. //
  336. // Returns an object includes the envelope, internal date, size, flags and body structure along with a similar object for each mime attachment. The structure of the returned objects is as follows:
  337. // type Primary body type
  338. // encoding Body transfer encoding
  339. // ifsubtype TRUE if there is a subtype string
  340. // subtype MIME subtype
  341. // ifdescription TRUE if there is a description string
  342. // description Content description string
  343. // ifid TRUE if there is an identification string
  344. // id Identification string
  345. // lines Number of lines
  346. // bytes Number of bytes
  347. // ifdisposition TRUE if there is a disposition string
  348. // disposition Disposition string
  349. // ifdparameters TRUE if the dparameters array exists
  350. // dparameters An array of objects where each object has an "attribute" and a "value" property corresponding to the parameters on the Content-disposition MIME header.
  351. // ifparameters TRUE if the parameters array exists
  352. // parameters An array of objects where each object has an "attribute" and a "value" property.
  353. // parts An array of objects identical in structure to the top-level object, each of which corresponds to a MIME body part.
  354. //
  355. // Primary body type (may vary with used library)
  356. // 0 text
  357. // 1 multipart
  358. // 2 message
  359. // 3 application
  360. // 4 audio
  361. // 5 image
  362. // 6 video
  363. // 7 other
  364. //
  365. // Transfer encodings (may vary with used library)
  366. // 0 7BIT
  367. // 1 8BIT
  368. // 2 BINARY
  369. // 3 BASE64
  370. // 4 QUOTED-PRINTABLE
  371. // 5 OTHER
  372. //
  373. // See Also
  374. // imap_fetchbody() - Fetch a particular section of the body of the message
  375. // imap_bodystruct() - Read the structure of a specified body section of a specific message
  376. $struct = imap_fetchstructure($this->mbox, $mId, FT_UID);
  377. if (empty($struct->parts)) {
  378. $this->initMailPart($mail, $struct, 0);
  379. } else {
  380. foreach ($struct->parts as $partNum => $partStruct) {
  381. $this->initMailPart($mail, $partStruct, $partNum + 1);
  382. }
  383. }
  384. $mail->textHtmlOriginal = $mail->textHtml;
  385. return $mail;
  386. }
  387. public function quoteAttachmentFilename($filename) {
  388. $replace = array('/\s/' => '_', '/[^0-9a-zA-Z_\.]/' => '', '/_+/' => '_', '/(^_)|(_$)/' => '');
  389. return preg_replace(array_keys($replace), $replace, $filename);
  390. }
  391. public function initMailPart(IncomingMail $mail, $partStruct, $partNum) {
  392. $data = $partNum ? imap_fetchbody($this->mbox, $mail->mId, $partNum, FT_UID) : imap_body($this->mbox, $mail->mId, FT_UID);
  393. if ($partStruct->encoding == 1) {
  394. $data = imap_utf8($data);
  395. } elseif ($partStruct->encoding == 2) {
  396. $data = imap_binary($data);
  397. } elseif ($partStruct->encoding == 3) {
  398. $data = imap_base64($data);
  399. } elseif ($partStruct->encoding == 4) {
  400. $data = imap_qprint($data);
  401. }
  402. $params = array();
  403. if (!empty($partStruct->parameters)) {
  404. foreach ($partStruct->parameters as $param) {
  405. $params[strtolower($param->attribute)] = $param->value;
  406. }
  407. }
  408. if (!empty($partStruct->dparameters)) {
  409. foreach ($partStruct->dparameters as $param) {
  410. $params[strtolower($param->attribute)] = $param->value;
  411. }
  412. }
  413. if (!empty($params['charset'])) {
  414. $data = iconv($params['charset'], $this->serverEncoding, $data);
  415. }
  416. // attachments
  417. if ($this->attachmentsDir) {
  418. $filename = false;
  419. $attachmentId = $partStruct->ifid ? trim($partStruct->id, " <>") : null;
  420. if (empty($params['filename']) && empty($params['name']) && $attachmentId) {
  421. $filename = $attachmentId . '.' . strtolower($partStruct->subtype);
  422. } elseif (!empty($params['filename']) || !empty($params['name'])) {
  423. $filename = !empty($params['filename']) ? $params['filename'] : $params['name'];
  424. $filename = $this->decodeMimeStr($filename);
  425. $filename = $this->quoteAttachmentFilename($filename);
  426. }
  427. if ($filename) {
  428. if ($this->attachmentsDir) {
  429. $filepath = rtrim($this->attachmentsDir, '/\\') . DIRECTORY_SEPARATOR . $filename;
  430. file_put_contents($filepath, $data);
  431. $mail->attachments[$filename] = $filepath;
  432. } else {
  433. $mail->attachments[$filename] = $filename;
  434. }
  435. if ($attachmentId) {
  436. $mail->attachmentsIds[$filename] = $attachmentId;
  437. }
  438. }
  439. }
  440. if ($partStruct->type == 0 && $data) {
  441. if (strtolower($partStruct->subtype) == 'plain') {
  442. $mail->textPlain .= $data;
  443. } else {
  444. $mail->textHtml .= $data;
  445. }
  446. } elseif ($partStruct->type == 2 && $data) {
  447. $mail->textPlain .= trim($data);
  448. }
  449. if (!empty($partStruct->parts)) {
  450. foreach ($partStruct->parts as $subpartNum => $subpartStruct) {
  451. $this->initMailPart($mail, $subpartStruct, $partNum . '.' . ($subpartNum + 1));
  452. }
  453. }
  454. }
  455. public function decodeMimeStr($string, $charset = 'UTF-8') {
  456. $newString = '';
  457. $elements = imap_mime_header_decode($string);
  458. for ($i = 0; $i < count($elements); $i++) {
  459. if ($elements[$i]->charset == 'default') {
  460. $elements[$i]->charset = 'iso-8859-1';
  461. }
  462. $newString .= iconv($elements[$i]->charset, $charset, $elements[$i]->text);
  463. }
  464. return $newString;
  465. }
  466. public function __call($imapFunction, $args) {
  467. $result = call_user_func_array($imapFunction, $args);
  468. $errors = imap_errors();
  469. if ($errors) {
  470. foreach ($errors as $error) {
  471. trigger_error($error);
  472. }
  473. }
  474. return $result;
  475. }
  476. public function __destruct() {
  477. $this->disconnect();
  478. }
  479. /*
  480. * Un-Implemented IMAP Connection Functions
  481. */
  482. // imap_alerts — Returns all IMAP alert messages that have occurred
  483. // imap_errors — Returns all of the IMAP errors that have occured
  484. // imap_gc — Clears IMAP cache
  485. // imap_last_error — Gets the last IMAP error that occurred during this page request
  486. // imap_timeout — Set or fetch imap timeout
  487. /*
  488. * Un-Implemented IMAP General Functions
  489. */
  490. // imap_mail_compose — Create a MIME message based on given envelope and body sections
  491. // imap_get_quotaroot — Retrieve the quota settings per user
  492. // imap_mail — Send an email message
  493. // imap_thread — Returns a tree of threaded message
  494. /*
  495. * Un-Implemented IMAP Mailbox Functions
  496. */
  497. // imap_append — Append a string message to a specified mailbox
  498. // imap_createmailbox — Create a new mailbox
  499. // imap_deletemailbox — Delete a mailbox
  500. // imap_get_quota — Retrieve the quota level settings, and usage statics per mailbox
  501. // imap_getacl — Gets the ACL for a given mailbox
  502. // imap_getmailboxes — Read the list of mailboxes, returning detailed information on each one
  503. // imap_getsubscribed — List all the subscribed mailboxes
  504. // imap_list — Read the list of mailboxes
  505. // imap_listscan — Returns the list of mailboxes that matches the given text
  506. // imap_lsub — List all the subscribed mailboxes
  507. // imap_mail_copy — Copy specified messages to a mailbox
  508. // imap_mail_move — Move specified messages to a mailbox
  509. // imap_mailboxmsginfo — Get information about the current mailbox
  510. // imap_num_recent — Gets the number of recent messages in current mailbox
  511. // imap_renamemailbox — Rename an old mailbox to new mailbox
  512. // imap_reopen — Reopen IMAP stream to new mailbox
  513. // imap_set_quota — Sets a quota for a given mailbox
  514. // imap_setacl — Sets the ACL for a giving mailbox
  515. // imap_status — Returns status information on a mailbox
  516. // imap_subscribe — Subscribe to a mailbox
  517. // imap_unsubscribe — Unsubscribe from a mailbox
  518. /*
  519. * Un-Implemented IMAP Message Functions
  520. */
  521. // imap_bodystruct — Read the structure of a specified body section of a specific message
  522. // imap_fetchmime — Fetch MIME headers for a particular section of the message
  523. // imap_headerinfo — Read the header of the message
  524. // imap_headers — Returns headers for all messages in a mailbox
  525. // imap_msgno — Gets the message sequence number for the given UID
  526. // imap_savebody — Save a specific body section to a file
  527. // imap_uid — This function returns the UID for the given message sequence number
  528. /*
  529. * Un-Implemented IMAP Encoding Functions
  530. */
  531. // imap_8bit — Convert an 8bit string to a quoted-printable string
  532. // imap_rfc822_parse_adrlist — Parses an address string
  533. // imap_rfc822_write_address — Returns a properly formatted email address given the mailbox, host, and personal info
  534. // imap_utf7_decode — Decodes a modified UTF-7 encoded string
  535. // imap_utf7_encode — Converts ISO-8859-1 string to modified UTF-7 text
  536. }
  537. class IncomingMail {
  538. public $mId;
  539. public $date;
  540. public $subject;
  541. public $fromName;
  542. public $fromAddress;
  543. public $to = array();
  544. public $toString;
  545. public $cc = array();
  546. public $replyTo = array();
  547. public $textPlain;
  548. public $textHtml;
  549. public $textHtmlOriginal;
  550. public $attachments = array();
  551. public $attachmentsIds = array();
  552. public function fetchMessageInternalLinks($baseUrl) {
  553. if ($this->textHtml) {
  554. foreach ($this->attachments as $filepath) {
  555. $filename = basename($filepath);
  556. if (isset($this->attachmentsIds[$filename])) {
  557. $this->textHtml = preg_replace('/(<img[^>]*?)src=["\']?ci?d:' . preg_quote($this->attachmentsIds[$filename]) . '["\']?/is', '\\1 src="' . $baseUrl . $filename . '"', $this->textHtml);
  558. }
  559. }
  560. }
  561. }
  562. public function fetchMessageHtmlTags($stripTags = array('html', 'body', 'head', 'meta')) {
  563. if ($this->textHtml) {
  564. foreach ($stripTags as $tag) {
  565. $this->textHtml = preg_replace('/<\/?' . $tag . '.*?>/is', '', $this->textHtml);
  566. }
  567. $this->textHtml = trim($this->textHtml, " \r\n");
  568. }
  569. }
  570. }
  571. class ImapMailboxException extends Exception {
  572. }