Files
DomFramework/imap.php
2016-02-23 10:27:18 +00:00

759 lines
28 KiB
PHP

<?php
/** DomFramework
@package domframework
@author Dominique Fournier <dominique@fournier38.fr> */
/** 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 */
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 */
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 */
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 */
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
*/
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 */
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 */
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 */
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 */
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 **/
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 */
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 */
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 */
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 */
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)."\n".
@imap_body (self::$instance[$this->mailbox], $msgno);
$errors = imap_errors ();
if ($errors !== false)
throw new Exception ("Mail not found", 404);
return $content;
}
/** Get the headers of the email (in raw format) */
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)
throw new Exception ("Mail not found", 404);
return $content;
}
/** Get all the body (and attached files) of an email in raw format */
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 */
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 */
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 */
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 */
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 */
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 */
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). */
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). */
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 */
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 */
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;
}
}