* @license BSD */ namespace Domframework; /** IMAP connection abstraction In the IMAP terminology, "mailbox" is a folder in the mailbox of the user */ class Imap { /** The mailbox string */ private $mailbox = null; /** The current folder in UTF-8 */ private $curDir = "INBOX"; /** The auto expunge feature, after deleting/moving an email */ public $autoexpunge = true; /** Limit to one instance of the connection to the same database */ // Based on an idea of http://tonylandis.com/php/php5-pdo-singleton-class/ private static $instance = array(); /** The constructor * The IMAP standard port is 143, but SSL tunnelled is 993 * @param string|null $imapserver The IMAP server to connect. Localhost is * used if not defined * @param integer|null $imapport The IMAP port to connect. 143 is used if not * defined * @param string|null $username The username to connect * @param string|null $password The password to connect * @param boolean|null $imapssl Use the SSL connection layer. Not used by * default * @param boolean|null $imapcertvalidate Check the certificates if using the * SSL connection. True by default */ public function __construct( $imapserver = "localhost", $imapport = 143, $username = null, $password = null, $imapssl = false, $imapcertvalidate = true ) { $this->connect( $imapserver, $imapport, $username, $password, $imapssl, $imapcertvalidate ); } /** The connect can be used when extends the imap class. The constructor can * be override by the child class. * The IMAP standard port is 143, but SSL tunnelled is 993 * @param string|null $imapserver The IMAP server to connect. Localhost is * used if not defined * @param integer|null $imapport The IMAP port to connect. 143 is used if not * defined * @param string|null $username The username to connect * @param string|null $password The password to connect * @param boolean|null $imapssl Use the SSL connection layer. Not used by * default * @param boolean|null $imapcertvalidate Check the certificates if using the * SSL connection. True by default */ public function connect( $imapserver = "localhost", $imapport = 143, $username = null, $password = null, $imapssl = false, $imapcertvalidate = true ) { if (! function_exists("imap_open")) { throw new \Exception("PHP don't support IMAP. Please add it !", 500); } if (! function_exists("mb_convert_encoding")) { throw new \Exception("PHP don't support MBString. Please add it !", 500); } if ($username === null) { throw new \Exception("No username provided for IMAP server", 500); } if ($password === null) { throw new \Exception("No password provided for IMAP server", 500); } $imapssl = ($imapssl !== false) ? "/ssl" : ""; $imapcertvalidate = ($imapcertvalidate === false) ? "/novalidate-cert" : ""; $this->mailbox = "{" . "$imapserver:$imapport/imap$imapssl$imapcertvalidate" . "/user=$username}"; if (! array_key_exists($this->mailbox, self::$instance)) { try { // Timeout authentication error to 1s (can't be less). By default, IMAP // wait 10s before returning an auth error imap_timeout(IMAP_READTIMEOUT, 1); self::$instance[$this->mailbox] = @imap_open( $this->mailbox, $username, $password ); if (self::$instance[$this->mailbox] === false) { throw new \Exception(imap_last_error()); } } catch (\Exception $e) { // imap_errors() takes the errors and clear the error stack $errors = imap_errors(); if ( substr($e->getMessage(), 0, 35) === "Can not authenticate to IMAP server" ) { throw new \Exception("IMAP error : " . $e->getMessage(), 401); } throw new \Exception("IMAP error : " . $e->getMessage(), 500); } } } /////////////////// /// FOLDERS /// /////////////////// /** Return an array of the existing folders. The sub-folders are with slash * separator * The names of folders are converted in UTF-8 */ public function foldersList() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $list = array_keys($this->foldersListWithAttr()); sort($list); return $list; } /** Return an array with folder name in key and attributes in value. The * attributes allow to see if there is new mails in folders */ public function foldersListWithAttr() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $list = imap_getmailboxes( self::$instance[$this->mailbox], $this->mailbox, "*" ); $res = array(); foreach ($list as $val) { $dir = substr($val->name, strlen($this->mailbox)); $dir = mb_convert_encoding($dir, "UTF8", "UTF7-IMAP"); if (isset($val->delimiter)) { $dir = str_replace($val->delimiter, "/", $dir); } $res[$dir] = $val->attributes; } return ($res); } /** Change to provided folder * The folder name must be in UTF-8. The folder must be absolute * @param string $folder Change to the provided folder */ public function changeFolder($folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } if (! in_array($folder, $this->foldersList())) { throw new \Exception("Folder not found", 404); } $folderUTF7 = mb_convert_encoding($folder, "UTF7-IMAP", "UTF-8"); $rc = @imap_reopen( self::$instance[$this->mailbox], $this->mailbox . $folderUTF7 ); if ($rc === true) { $this->curDir = $folder; } else { throw new \Exception("Can't go in provided folder", 500); } return $rc; } /** Return the current folder in UTF-8 */ public function getFolder() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } return $this->curDir; } /** Create a new folder, provided in UTF-8. The folder must be absolute * @param string $folder Create the provided folder */ public function createFolder($folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } if (in_array($folder, $this->foldersList())) { throw new \Exception("Folder already exists", 406); } $folderUTF7 = mb_convert_encoding($folder, "UTF7-IMAP", "UTF-8"); return imap_createmailbox( self::$instance[$this->mailbox], $this->mailbox . $folderUTF7 ); } /** Delete an existing folder provided in UTF-8. The folder must be absolute * @param string $folder The folder to delete */ public function deleteFolder($folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } if (! in_array($folder, $this->foldersList())) { throw new \Exception("Folder not found", 404); } $folderUTF7 = mb_convert_encoding($folder, "UTF7-IMAP", "UTF-8"); return imap_deletemailbox( self::$instance[$this->mailbox], $this->mailbox . $folderUTF7 ); } /** Return the list of the folders substcribed by the user. The folders are * in UTF-8 */ public function getSubscribe() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $subs = imap_getsubscribed( self::$instance[$this->mailbox], $this->mailbox, "*" ); $res = array(); foreach ($subs as $sub) { $res [] = str_replace( $sub->delimiter, "/", substr($sub->name, strlen($this->mailbox)) ); } $res = array_map(function ($folder) { return mb_convert_encoding($folder, "UTF-8", "UTF7-IMAP"); }, $res); sort($res); return $res; } /** Add a subscription for a folder. The folder must be in UTF-8 * @param string $folder Add the provided folder to the subscription file of * the user */ public function addSubscribe($folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $folderUTF7 = mb_convert_encoding($folder, "UTF7-IMAP", "UTF-8"); return imap_subscribe( self::$instance[$this->mailbox], $this->mailbox . $folder ); } /** Remove a subscription for a folder. The folder must be in UTF-8 * @param string $folder Remove the provided folder to the subscription file * of the user */ public function delSubscribe($folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $folderUTF7 = mb_convert_encoding($folder, "UTF7-IMAP", "UTF-8"); return imap_unsubscribe( self::$instance[$this->mailbox], $this->mailbox . $folder ); } /** Return the information concerning a folder. It return an object with the * following properties : * Date date of last change (current datetime) * Driver driver * Mailbox name of the mailbox * Nmsgs number of messages * Recent number of recent messages * Unread number of unread messages * Deleted number of deleted messages * Size mailbox size * @param string $folder The folder to get the informations */ public function getFolderInfo($folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $oldFolder = $this->curDir; $this->changeFolder($folder); $rc = imap_mailboxmsginfo(self::$instance[$this->mailbox]); $this->changeFolder($oldFolder); if ($rc === false) { throw new \Exception("Can't read information for folder $folder", 500); } return $rc; } ////////////////////// /// LIST MAILS /// ////////////////////// /** Return an array of mailHeaders order by $field and by order ASC or DESC * @param array $mailHeaders The headers to sort * @param string $field The field to examine * @param boolean|null $orderAsc The order of sorting. Asc if not defined */ public function imapSortMail($mailHeaders, $field, $orderAsc = true) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); $sortList = array(); $sortInc = array(); foreach ($mailHeaders as $mail) { // Permit to have to mails with the same comparator field. Add an // increment at the end of field if (!isset($sortInc[$mail->$field])) { $inc = 1; } else { $inc = $sortInc[$mail->$field] + 1; } $sortInc[$mail->$field] = $inc; $sortList[$mail->$field . $inc] = $mail; } ksort($sortList, SORT_NATURAL); return array_values($sortList); } /** Fetch the headers for all messages in the current folder sorted by date * Return an array of mail object containing information like the subject, * the date, if the message is already read (recent), answered... * (see http://www.php.net/manual/en/function.imap-fetch-overview.php) * If the $from is negative, take the LAST $from mails * If from is zero, it's value is override to 1 * For information, takes 0.4s to select 30 mails on 1552 * @param integer|null $from The selector of the mails. 1 if not defined * @param integer|null $nbmails The number of mails returned by the method * 30 if not defined **/ public function mailsDate($from = 1, $nbmails = 30) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if ($from === null) { $from = 1; } $MC = imap_check(self::$instance[$this->mailbox]); if ($MC->Nmsgs === 0) { return array(); } if ($nbmails > $MC->Nmsgs) { $nbmails = $MC->Nmsgs; } if ($from < 0) { $from = abs($from); if ($from < 1) { $from = 1; } if ($from > $MC->Nmsgs) { throw new \Exception( "Mail start is higher than the number of mails", 500 ); } $from = $MC->Nmsgs - $from + 1; $to = $from + $nbmails - 1; if ($to > $MC->Nmsgs) { $to = $MC->Nmsgs; } } else { if ($from > $MC->Nmsgs) { throw new \Exception( "Mail start is higher than the number of mails", 500 ); } if ($from < 1) { $from = 1; } $to = $from + $nbmails - 1; if ($to > $MC->Nmsgs) { $to = $MC->Nmsgs; } } $headers = array(); // Adding the FT_UID options cost 1.1s $result = imap_fetch_overview( self::$instance[$this->mailbox], "$from:$to", 0 ); // imap_errors() takes the errors and clear the error stack $errors = imap_errors(); return $result; } /** Return all the mails numbers order by thread in an array. * [] => array ("msgno"=>msgno, "depth"=>depth) */ public function mailsThread() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); $threads = @imap_thread(self::$instance[$this->mailbox]); // imap_errors() takes the errors and clear the error stack $errors = imap_errors(); $thread = array(); $depth = 0; foreach ($threads as $key => $val) { $tree = explode('.', $key); if ($tree[1] == 'num') { // If the mail unknown (the mails starts at 1), skip the thread record if ($val === 0) { continue; } $thread[] = array("msgno" => $val, "depth" => $depth); $depth++; } elseif ($tree[1] == 'branch' && $depth > 0) { $depth--; } } return $thread; } /** Send back the number of mails in the mailbox */ public function mailsNumber() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); $MC = imap_check(self::$instance[$this->mailbox]); return $MC->Nmsgs; } /** Return an array containing the msgno corresponding to the criteria * @param string $criteria The criteria to use for the IMAP search */ public function mailsSearch($criteria) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); return imap_search(self::$instance[$this->mailbox], $criteria); } /** Move the mail provided in the $folder in UTF-8. * If $msgno is an array, all the mails with the contain msgno are deleted * Expunge automatically the current folder to remove the old emails * @param integer|array $msgno The message number(s) to copy * @param string $folder The destination folder of the move. Must exists */ public function mailMove($msgno, $folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if (is_array($msgno)) { $msgno = implode(",", $msgno); } $folderUTF7 = mb_convert_encoding($folder, "UTF7-IMAP", "UTF-8"); $rc = imap_mail_move(self::$instance[$this->mailbox], $msgno, $folderUTF7); if ($rc !== true) { return false; } if ($this->autoexpunge) { return imap_expunge(self::$instance[$this->mailbox]); } return true; } /** Copy the mail provided in the $folder in UTF-8. * If $msgno is an array, all the mails with the contain msgno are copied * @param integer|array $msgno The message number(s) to copy * @param string $folder The destination folder of the copy. Must exists */ public function mailCopy($msgno, $folder) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if (is_array($msgno)) { $msgno = implode(",", $msgno); } $folderUTF7 = mb_convert_encoding($folder, "UTF7-IMAP", "UTF-8"); $rc = imap_mail_copy(self::$instance[$this->mailbox], $msgno, $folderUTF7); if ($rc !== true) { return false; } return true; } /** Expunge the mailbox. If the autoexpunge is activated, it is normally not * needed */ public function expunge() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); return imap_expunge(self::$instance[$this->mailbox]); } ///////////////////////////// /// GET/SET/DEL EMAIL /// ///////////////////////////// /** Get an existing email in the current folder in raw format * @param integer $msgno The message number to examine */ public function getEmailRaw($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); // Clear the errors imap_errors(); $content = @imap_fetchheader(self::$instance[$this->mailbox], $msgno) . "\r\n" . @imap_body(self::$instance[$this->mailbox], $msgno); $errors = imap_errors(); if ($errors !== false || $content === "\n") { throw new \Exception("Mail not found", 404); } return $content; } /** Get the headers of the email (in raw format) * @param integer $msgno The message number to examine */ public function getEmailHeadersRaw($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); // Clear the errors imap_errors(); $content = @imap_fetchheader(self::$instance[$this->mailbox], $msgno); $errors = imap_errors(); if ($errors !== false || $content === "") { throw new \Exception("Mail not found", 404); } return $content; } /** Get all the body (and attached files) of an email in raw format * @param integer $msgno The message number to examine */ public function getEmailBodyRaw($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); // Clear the errors imap_errors(); $content = @imap_body(self::$instance[$this->mailbox], $msgno); $errors = imap_errors(); if ($errors !== false) { throw new \Exception("Mail not found", 404); } return $content; } /** Return email structure of the body * @param integer $msgno The message number to examine */ public function getStructure($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); // Clear the errors imap_errors(); $structure = @imap_fetchstructure(self::$instance[$this->mailbox], $msgno); $errors = imap_errors(); if ($errors !== false) { throw new \Exception("Mail not found", 404); } return $structure; } /** Return the structure of the mail body with the associated content * @param integer $msgno The message number to examine */ public function getStructureWithContent($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); // Clear the errors imap_errors(); $structure = @imap_fetchstructure(self::$instance[$this->mailbox], $msgno); $errors = imap_errors(); if ($errors !== false) { throw new \Exception("Mail not found", 404); } if (! isset($structure->parts)) { // In case of PLAIN text, there is no parts $content = imap_fetchbody(self::$instance[$this->mailbox], $msgno, 1); if ($structure->encoding === 4) { $content = quoted_printable_decode($content); } elseif ($structure->encoding === 3) { $content = base64_decode($content); } foreach ($structure->parameters as $param) { if ($param->attribute === "charset") { $content = iconv($param->value, "utf-8", $content); } } $structure->content = $content; return $structure; } foreach ($structure->parts as $part1 => $struct1) { if (isset($struct1->parts)) { foreach ($struct1->parts as $part2 => $struct2) { $content = imap_fetchbody( self::$instance[$this->mailbox], $msgno, ($part1 + 1) . "." . ($part2 + 1) ); if ($struct2->encoding === 4) { $content = quoted_printable_decode($content); } elseif ($struct2->encoding === 3) { $content = base64_decode($content); } foreach ($struct2->parameters as $param) { if ($param->attribute === "charset") { $content = iconv($param->value, "utf-8", $content); } } $structure->parts[$part1]->parts[$part2]->content = $content; // Add the MIME type if ($struct2->type === 0) { $structure->parts[$part1]->parts[$part2]->mimetype = "text/" . strtolower($struct2->subtype); } elseif ($struct2->type === 1) { $structure->parts[$part1]->parts[$part2]->mimetype = "multipart/" . strtolower($struct2->subtype); } elseif ($struct2->type === 2) { $structure->parts[$part1]->parts[$part2]->mimetype = "message/" . strtolower($struct2->subtype); } elseif ($struct2->type === 3) { $structure->parts[$part1]->parts[$part2]->mimetype = "application/" . strtolower($struct2->subtype); } elseif ($struct2->type === 4) { $structure->parts[$part1]->parts[$part2]->mimetype = "audio/" . strtolower($struct2->subtype); } elseif ($struct2->type === 5) { $structure->parts[$part1]->parts[$part2]->mimetype = "image/" . strtolower($struct2->subtype); } elseif ($struct2->type === 6) { $structure->parts[$part1]->parts[$part2]->mimetype = "video/" . strtolower($struct2->subtype); } elseif ($struct2->type === 7) { $structure->parts[$part1]->parts[$part2]->mimetype = "other/" . strtolower($struct2->subtype); } else { throw new \Exception(sprintf( dgettext( "domframework", "Unknown type in imap_fetchstructure : %s" ), $struct2->type ), 500); } } } else { $content = imap_fetchbody( self::$instance[$this->mailbox], $msgno, $part1 + 1 ); if ($struct1->encoding === 4) { $content = quoted_printable_decode($content); } elseif ($struct1->encoding === 3) { $content = base64_decode($content); } foreach ($struct1->parameters as $param) { if ($param->attribute === "charset") { $content = iconv($param->value, "utf-8", $content); } } $structure->parts[$part1]->content = $content; // Add the MIME type if ($struct1->type === 0) { $structure->parts[$part1]->mimetype = "text/" . strtolower($struct1->subtype); } elseif ($struct1->type === 1) { $structure->parts[$part1]->mimetype = "multipart/" . strtolower($struct1->subtype); } elseif ($struct1->type === 2) { $structure->parts[$part1]->mimetype = "message/" . strtolower($struct1->subtype); } elseif ($struct1->type === 3) { $structure->parts[$part1]->mimetype = "application/" . strtolower($struct1->subtype); } elseif ($struct1->type === 4) { $structure->parts[$part1]->mimetype = "audio/" . strtolower($struct1->subtype); } elseif ($struct1->type === 5) { $structure->parts[$part1]->mimetype = "image/" . strtolower($struct1->subtype); } elseif ($struct1->type === 6) { $structure->parts[$part1]->mimetype = "video/" . strtolower($struct1->subtype); } elseif ($struct1->type === 7) { $structure->parts[$part1]->mimetype = "other/" . strtolower($struct1->subtype); } else { throw new \Exception(sprintf( dgettext( "domframework", "Unknown type in imap_fetchstructure : %s" ), $struct1->type ), 500); } } } return $structure; } /** Return the content of a part of the mail body defined in the structure in * an object, with the associated mimetype, the parameters like the charset * if they are defined, the number of lines associated to this part in the * mail and some other info * @param integer $msgno The message number to examine * @param integer $part The message part to get */ public function getStructureContent($msgno, $part) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $structure = $this->getStructureWithContent($msgno); if (isset($structure->parts[$part])) { return $structure->parts[$part]; } throw new \Exception("Part not found in the mail", 404); } /** Return the part identifiers of the structure of the mail body. To be used * in getStructureContent * @param integer $msgno The message number to examine */ public function getStructureParts($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $structure = $this->getStructure($msgno); if (! isset($structure->parts)) { return array(); } return array_keys($structure->parts); } /** Delete all the mailIDs (msgno) provided in an array or a single mail if * $msgno is not an array * DO NOT MOVE THE MAIL IN TRASH, DESTROY THE MAIL REALLY * Expunge the mails at the end of the operation * @param array|integer $msgno The message number(s) to remove */ public function mailsDel($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if (is_array($msgno)) { $msgno = implode(",", $msgno); } $rc = @imap_delete(self::$instance[$this->mailbox], $msgno); imap_errors(); if ($rc === false) { throw new \Exception("No mailID provided can be found : ABORT"); } if ($this->autoexpunge) { return imap_expunge(self::$instance[$this->mailbox]); } return $rc; } /** Add a new mail in the current folder. The content must be a string * containing all the mail (header and body). If the content is invalid, the * directory listing can provide erroneous data * @param string $content the content of the mail to add */ public function mailAdd($content) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $folderUTF7 = mb_convert_encoding($this->curDir, "UTF7-IMAP", "UTF-8"); $rc = imap_append( self::$instance[$this->mailbox], $this->mailbox . $folderUTF7, $content ); $errors = imap_errors(); if ($rc === false) { throw new \Exception("Error when saving the mail in folder : " . implode(" ", $errors), 500); } return true; } ///////////////// /// QUOTA /// ///////////////// /** Return the quota used by the user in Mo */ public function getQuota() { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $quota = @imap_get_quotaroot(self::$instance[$this->mailbox], "INBOX"); imap_errors(); if (! isset($quota["STORAGE"])) { return array(); } return array_map( function ($n) { return intval($n / 1000); }, $quota["STORAGE"] ); } ///////////////// /// FLAGS /// ///////////////// /** Set the flags of the msgno. If msgno is an array, the flags will be write * on the list of mails. The others flags of the email are not modified. * The flags must be an array containing : * \Seen Message has been read * \Answered Message has been answered * \Flagged Message is "flagged" for urgent/special attention * \Deleted Message is "deleted" for removal by later EXPUNGE * \Draft Message has not completed composition (marked as a draft). * @param integer|array $msgno The messages number(s) to add the flags * @param array $flags The flags to add */ public function setFlag($msgno, $flags) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if (is_array($msgno)) { $msgno = implode(",", $msgno); } $rc = @imap_setflag_full( self::$instance[$this->mailbox], $msgno, implode(" ", $flags) ); imap_errors(); if ($rc === false) { throw new \Exception("Can't define the flags", 500); } return true; } /** Unset the flags of the msgno. If msgno is an array, the flags will be * write on the list of mails. The others flags of the email are not * modified. * The flags must be an array containing : * \Seen Message has been read * \Answered Message has been answered * \Flagged Message is "flagged" for urgent/special attention * \Deleted Message is "deleted" for removal by later EXPUNGE * \Draft Message has not completed composition (marked as a draft). * @param integer|array $msgno The messages number(s) to remove the flags * @param array $flags The flags to remove */ public function unsetFlag($msgno, $flags) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if (is_array($msgno)) { $msgno = implode(",", $msgno); } $rc = @imap_clearflag_full( self::$instance[$this->mailbox], $msgno, implode(" ", $flags) ); imap_errors(); if ($rc === false) { throw new \Exception("Can't define the flags", 500); } return true; } /** Mark mail(s) as read. * If msgno is an array, a list of mails will be modified. * If msgno is an integer, only one mail will be modified * @param integer|array $msgno The messages number(s) to mark as read */ public function markMailAsRead($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if (is_array($msgno)) { $msgno = implode(",", $msgno); } $rc = @imap_setflag_full( self::$instance[$this->mailbox], $msgno, "\\Seen" ); imap_errors(); if ($rc === false) { throw new \Exception("Can't mark mail as read", 500); } return true; } /** Mark mail(s) as unread. * If msgno is an array, a list of mails will be modified. * If msgno is an integer, only one mail will be modified * @param integer|array $msgno The messages number(s) to mark as unread */ public function markMailAsUnread($msgno) { if ($this->mailbox === null) { throw new \Exception("IMAP server not connected", 500); } $this->changeFolder($this->curDir); if (is_array($msgno)) { $msgno = implode(",", $msgno); } $rc = @imap_clearflag_full( self::$instance[$this->mailbox], $msgno, "\\Seen" ); imap_errors(); if ($rc === false) { throw new \Exception("Can't mark mail as read", 500); } return true; } }