diff --git a/mail.php b/mail.php index 5fd4b8c..a5cee0a 100644 --- a/mail.php +++ b/mail.php @@ -33,6 +33,7 @@ class mail * returns */ private function printSections () + // {{{ { foreach ($this->sections as $sectionID=>$vals) { @@ -66,22 +67,26 @@ class mail echo ")\n\n"; } } + // }}} /** Add a new section * @param array $param The parameters to store * @return the sectionID */ private function sectionAdd ($param) + // {{{ { $sectionID = md5 (microtime(true).rand()); $this->sections[$sectionID] = $param; return $sectionID; } + // }}} /** Add a new section with the default parameters * @return array The sectionID stored with the default parameters */ private function sectionAddDefault () + // {{{ { return $this->sectionAdd ( array ("_headerBodySeparator"=>$this->headerBodySeparator, @@ -91,6 +96,7 @@ class mail "_contentEML"=>"", "_contentUTF"=>"")); } + // }}} /** Del an existing section * If there is one child, and the section was multiple, remove it and @@ -98,6 +104,7 @@ class mail * @param string $sectionID The section to delete */ private function sectionDel ($sectionID) + // {{{ { // TODO ! } @@ -107,6 +114,7 @@ class mail * @param string $sectionIDchild The sectionID of the child to add */ private function sectionAddChild ($sectionIDParent, $sectionIDchild) + // {{{ { if (! array_key_exists ($sectionIDParent, $this->sections)) throw new \Exception (dgettext ("domframework", @@ -114,12 +122,14 @@ class mail $this->sections[$sectionIDParent]["_partsIDchild"][] = $sectionIDchild; $this->sections[$sectionIDchild]["_parentID"] = $sectionIDParent; } + // }}} /** Add a newChild to an existing section at the beginning of the list * @param string $sectionIDParent The parent modified by adding a child * @param string $sectionIDchild The sectionID of the child to add */ private function sectionAddChildFirst ($sectionIDParent, $sectionIDchild) + // {{{ { if (! array_key_exists ($sectionIDParent, $this->sections)) throw new \Exception (dgettext ("domframework", @@ -128,24 +138,28 @@ class mail $sectionIDchild); $this->sections[$sectionIDchild]["_parentID"] = $sectionIDParent; } + // }}} /** Remove all the defined Childs in the section. Do not remove really the * childs ! * @param string $sectionID the section to clean */ private function sectionDelChilds ($sectionID) + // {{{ { if (! array_key_exists ($sectionID, $this->sections)) throw new \Exception (dgettext ("domframework", "Section not found"), 404); unset ($this->sections[$sectionID]["_partsIDchild"]); } + // }}} /** Update the content of an existing section * @param string $sectionID The section to modify * @param array $param The parameters to update */ private function sectionUpdate ($sectionID, $param) + // {{{ { if (! array_key_exists ($sectionID, $this->sections)) throw new \Exception (dgettext ("domframework", "Section not found"), @@ -158,19 +172,23 @@ class mail $this->sections[$sectionID][$key] = $val; } } + // }}} /** Get the list of sections ID * @return array the defined sectionsID */ private function sectionList () + // {{{ { return array_keys ($this->sections); } + // }}} /** Get the section ID List with parents ID * @return array the defined sectionsID with the parent ID as value */ private function sectionListParent () + // {{{ { $res = array (); foreach ($this->sections as $sectionID=>$content) @@ -182,23 +200,27 @@ class mail } return $res; } + // }}} /** Return the content array of the section * @param string $sectionID The section ID to get * @return array The content of the section */ private function sectionGet ($sectionID) + // {{{ { if (! array_key_exists ($sectionID, $this->sections)) throw new \Exception (dgettext ("domframework", "Section not found"), 404); return $this->sections[$sectionID]; } + // }}} /** Get the section ID of the main part * @return bool|string the section ID of the main part or FALSE if not found */ public function sectionMainID () + // {{{ { foreach ($this->sectionListParent () as $sectionID=>$parentID) { @@ -207,11 +229,13 @@ class mail } return false; } + // }}} /** Refresh the _headersEML from the _headersArray * @param string $sectionID the section to refresh */ private function sectionRefreshHeadersEML ($sectionID) + // {{{ { $section = $this->sectionGet ($sectionID); $headersEML = ""; @@ -223,12 +247,14 @@ class mail } $this->sections[$sectionID]["_headersEML"] = $headersEML; } + // }}} /** Read the complete mail to analyze * Destroy all the previous definitions of mail * @param string $content The complete mail to read */ public function readMail ($content) + // {{{ { $this->sections = array (); $this->completeEmailEML = $content; @@ -236,6 +262,7 @@ class mail if ($partinfo !== null) $this->readMailContentRecurse ($partinfo); } + // }}} /** Read the content of the mail and allow the content to be also multipart. * Then the method is recursively called to generate the sections @@ -243,6 +270,7 @@ class mail * @param string|null $sectionIDParent The parent sectionID to link with */ private function readMailContentRecurse ($partinfo, $sectionIDParent=false) + // {{{ { if (key_exists ("_contentType", $partinfo) && substr ($partinfo["_contentType"], 0, 10) === "multipart/") @@ -300,12 +328,14 @@ class mail $sectionIDParent = $this->sectionAdd ($partinfo); } } + // }}} /** Return the data for a part of the mail * @param string $content The content of the mail to parse * @return array The data content in the mail */ private function parseMessagePart ($content) + // {{{ { // Get the HeaderBodySeparator $pos = strpos ($content, "\r\n\r\n"); @@ -428,10 +458,12 @@ class mail $res["Content-ID"] = $contentID; return $res; } + // }}} /** The constuctor verify if the external libraries are available */ public function __construct () + // {{{ { if (! function_exists ("finfo_buffer")) throw new \Exception (dgettext ("domframework", @@ -446,6 +478,7 @@ class mail $this->addHeader ("From", $user["name"]."@".php_uname('n')); $this->addHeader ("MIME-Version", "1.0"); } + // }}} /** Define a HTML body. If the HTML body already exists, overwrite it * If there is an text body, manage the boundary in alternative mode @@ -455,6 +488,7 @@ class mail */ public function setBodyHTML ($htmlContent, $charset="utf-8", $encoding="quoted-printable") + // {{{ { // Look if there is an existing section with text (main or // multipart/alternative) @@ -550,6 +584,7 @@ class mail $this->sectionUpdate ($sectionIDtoChange, $part); $this->createMailEML (); } + // }}} /** Add a Text body. If the text body already exists, overwrite it * If there is an HTML body, manage the boundary in alternative mode @@ -559,6 +594,7 @@ class mail */ public function setBodyText ($textContent, $charset="utf-8", $encoding="quoted-printable") + // {{{ { // Look if there is an existing section with text (main or // multipart/alternative) @@ -636,6 +672,7 @@ class mail $this->sectionUpdate ($sectionIDtoChange, $part); $this->createMailEML (); } + // }}} /** Return the HTML body if exists in UTF-8. If the body is not in UTF-8, it * is converted @@ -644,6 +681,7 @@ class mail * no HTML part in the mail */ public function getBodyHTML () + // {{{ { $sectionList = $this->sectionList (); $sectionIDtoChange = ""; @@ -662,6 +700,7 @@ class mail } return false; } + // }}} /** Get the text body if exists in UTF-8. If the body is not in UTF-8, it is * converted @@ -670,6 +709,7 @@ class mail * no Text part in the mail */ public function getBodyText () + // {{{ { $sectionList = $this->sectionList (); $sectionIDtoChange = ""; @@ -688,6 +728,7 @@ class mail } return false; } + // }}} /** Move the sections where the parent is defined to $oldParentID to the * $newParentID. @@ -697,6 +738,7 @@ class mail * @param string|null $oldParentID The oldParent to look for */ private function moveChilds ($newParentID, $oldParentID=false) + // {{{ { if ($newParentID === $oldParentID) throw new \Exception ("moveChilds : old and new ParentID are the same", @@ -756,6 +798,7 @@ class mail } } } + // }}} /** Add an attachment to the mail. * The allowed encodings are "quoted-printable" or "base64" @@ -769,6 +812,7 @@ class mail */ public function addAttachment ($name, $fileContent, $encoding="base64", $inline=false) + // {{{ { if ($this->getBodyHTML() === false && $inline !== false) $this->setBodyHTML ("No HTML provided by inline file added"); @@ -851,6 +895,7 @@ class mail if ($inline === true) return substr ($contentID, 1, -1); } + // }}} /** Add an inline attachment to the mail. * The allowed encodings are "quoted-printable" or "base64" @@ -863,9 +908,11 @@ class mail * @return string The content ID created */ public function addAttachmentInline ($name, $fileContent, $encoding="base64") + // {{{ { return $this->addAttachment ($name, $fileContent, $encoding, true); } + // }}} /** Get an attachment of the mail * @param integer $number the number of attach to get starting to 0 @@ -873,6 +920,7 @@ class mail * @return the content of the attachment. Can be binary */ public function getAttachment ($number, $inline = false) + // {{{ { $attachmentIDs = $this->getAttachmentID ($inline); if (! array_key_exists ($number, $attachmentIDs)) @@ -883,6 +931,7 @@ class mail $part["_headersArray"]); return $this->encodingDecode ($part["_contentEML"], $encoding); } + // }}} /** Get the attachment details * @param integer $number the number of attach to get starting to 0 @@ -890,6 +939,7 @@ class mail * @return array containing the information of the attachment */ public function getAttachmentDetails ($number, $inline = false) + // {{{ { $attachmentIDs = $this->getAttachmentID ($inline); if (! array_key_exists ($number, $attachmentIDs)) @@ -911,6 +961,7 @@ class mail } return $res; } + // }}} /** Return the list of the sectionID containing a attachment. Contains the * inline attachments too. @@ -918,6 +969,7 @@ class mail * @return array The sectionIDs */ private function getAttachmentID ($inline = false) + // {{{ { $res = array (); foreach ($this->sections as $sectionID=>$section) @@ -946,12 +998,14 @@ class mail } return $res; } + // }}} /** Add a To: header. If it already exists, add a new recipient * @param string $toMail The mail to add * @param string|null $toName The name of the recipient */ public function addTo ($toMail, $toName = "") + // {{{ { if (strspn ($toName, "abcdefghijklmnopqrstuvwxyz". "ABCDEFGHIJKLMNOPQRSTUVWXYZ". @@ -967,20 +1021,24 @@ class mail else $this->setHeader ("To", $toField); } + // }}} /** Get the To Header as it is written in the mail * @return string The To Header defined in the mail */ public function getTo () + // {{{ { return $this->getHeader ("To"); } + // }}} /** Add a From: header. If it already exists, overwrite the existing one * @param string $fromMail The from Mail to define * @param string|null $fromName The from Name to define */ public function setFrom ($fromMail, $fromName= "") + // {{{ { if (strspn ($fromName, "abcdefghijklmnopqrstuvwxyz". "ABCDEFGHIJKLMNOPQRSTUVWXYZ". @@ -991,19 +1049,23 @@ class mail $fromName .= " "; $this->setHeader ("From", "$fromName<$fromMail>"); } + // }}} /** Return the From header as it is written in the mail * @return string The From Header defined in the mail */ public function getFrom () + // {{{ { return $this->getHeader ("From"); } + // }}} /** Return the From header converted to array with mail and name keys * @return array The From details */ public function getFromArray () + // {{{ { $from = $this->getHeader ("From"); $res = array (); @@ -1011,49 +1073,59 @@ class mail $from = $this->convertPeopleToArray ($from); return reset ($from); } + // }}} /** Set the subject * @param string $subject In UTF8 */ public function setSubject ($subject) + // {{{ { $this->setHeader ("Subject", $this->encodeHeaders ("Subject", $subject, "quoted-printable")); } + // }}} /** Set the Date * @param string $date In RFC 2822 format */ public function setDate ($date) + // {{{ { // TODO : Check if the date format is valid $this->setHeader ("Date", $date); } + // }}} /** Set the Date * @param string $timestamp In Timestamp format */ public function setDateTimestamp ($timestamp) + // {{{ { // TODO : Check if the timestamp is valid $this->setHeader ("Date", date ("r", $timestamp)); } + // }}} /** Get the Date header if defined. * Return false if not defined * @return string|bool The date Header if defined or false if not defined */ public function getDate () + // {{{ { return $this->getHeader ("Date"); } + // }}} /** Return the Date header (if defined) in timestamp * Return false if not defined * @return integer|bool The date Header if defined or false if not defined */ public function getDateTimestamp () + // {{{ { $datetimestamp = false; $date = rtrim ($this->getDate ()); @@ -1069,6 +1141,7 @@ class mail } return $dateTimestamp; } + // }}} /** Set a generic header * @param string $header The name of the Header (without colon) @@ -1076,6 +1149,7 @@ class mail * @param string|null $sectionID The section to modify. If null, use the main */ public function setHeader ($header, $value, $sectionID=null) + // {{{ { if (substr ($value, -1) !== "\n" && substr ($value, -1) !== "\r" && @@ -1118,6 +1192,7 @@ class mail $this->sections[$sectionMainID]["_headersEML"] = $_headerEML; $this->createMailEML (); } + // }}} /** Add a generic header * @param string $header The name of the Header (without colon) @@ -1125,6 +1200,7 @@ class mail * @param string|null $sectionID The section to modify. If null, use the main */ public function addHeader ($header, $value, $sectionID=null) + // {{{ { if (substr ($value, -1) !== "\n" && substr ($value, -1) !== "\r" && @@ -1170,12 +1246,14 @@ class mail } $this->createMailEML (); } + // }}} /** Delete a specific header * @param string $header The header to remove * @param string|null $sectionID The section to modify. If null, use the main */ public function delHeader ($header, $sectionID=null) + // {{{ { if ($sectionID === null) { @@ -1213,6 +1291,7 @@ class mail $this->sections[$sectionMainID]["_headersEML"] = $_headerEML; $this->createMailEML (); } + // }}} /** Get a generic header * If there is multiple headers with the same name, return the first @@ -1221,6 +1300,7 @@ class mail * @return string|bool the literal value or false if it doesn't exist */ public function getHeader ($header, $headers=null) + // {{{ { if ($headers === null) { @@ -1240,6 +1320,7 @@ class mail } return false; } + // }}} /** Get a generic header with removing the carriage return * If there is multiple headers with the same name, return the first @@ -1248,6 +1329,7 @@ class mail * @return string|bool the literal value or false if it doesn't exist */ public function getHeaderValue ($header, $headers=null) + // {{{ { if ($headers === null) { @@ -1272,10 +1354,12 @@ class mail } return false; } + // }}} /** Create the complete mail structure */ public function createMailEML () + // {{{ { $complete = ""; $this->recurse = 0; @@ -1292,11 +1376,13 @@ class mail } $this->completeEmailEML = $complete; } + // }}} /** Recursive email EML creation for childs * @param array $parent The parent array */ private function createMailEMLSub ($parent) + // {{{ { $this->recurse++; if ($this->recurse > 120) @@ -1322,17 +1408,20 @@ class mail $complete .= "--".$parent["_boundary"]."--".$parent["_boundaryCR"]; return $complete; } + // }}} /** Return the complete mail * @return string The complete mail */ public function getMail () + // {{{ { if (trim ($this->getBodyHTML()) === "No HTML provided by inline file added") throw new \Exception ("No HTML provided by inline file added", 500); return $this->completeEmailEML; } + // }}} /** Return an array with the details of the mail : * the number of attachments, the from, to, subject in UTF-8, if there is @@ -1340,6 +1429,7 @@ class mail * @return array The details of the mail */ public function getDetails () + // {{{ { $bodyTextExists = false; $bodyHTMLExists = false; @@ -1381,11 +1471,13 @@ class mail $subject = $this->decodeHeaders ("Subject", $this->getHeader ("Subject")); return get_defined_vars(); } + // }}} /** Create a boundary * @return string the textual boundary */ private function getBoundary () + // {{{ { $data = openssl_random_pseudo_bytes (16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0010 @@ -1393,11 +1485,13 @@ class mail return "-----".vsprintf ('%s%s%s%s%s%s%s%s', str_split (bin2hex ($data), 4)); } + // }}} /** Create a messageID * @return string the textual MessageID */ public function provideMessageID () + // {{{ { $data = openssl_random_pseudo_bytes (16); $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0010 @@ -1406,6 +1500,7 @@ class mail str_split (bin2hex ($data), 4))."@". php_uname('n').">"; } + // }}} /** Convert the content to correct encoding. * The allowed encodings are "quoted-printable" or "base64" or "flowed" @@ -1415,6 +1510,7 @@ class mail * @return string the content encoded by $encoding */ private function encodingEncode ($content, $encoding) + // {{{ { if ($encoding === "quoted-printable") { @@ -1434,6 +1530,7 @@ class mail dgettext ("domframework", "Invalid encoding provided to encodingEncode : %s"), $encoding), 500); } + // }}} /** Decode the content with correct encoding. * The allowed encodings are "quoted-printable" or "base64" or "8bit" @@ -1442,6 +1539,7 @@ class mail * @return the content decoded by $encoding */ private function encodingDecode ($content, $encoding) + // {{{ { if ($encoding === "quoted-printable") return quoted_printable_decode ($content); @@ -1453,6 +1551,7 @@ class mail dgettext ("domframework", "Invalid encoding provided to encodingDecode : '%s'"), $encoding), 500); } + // }}} /** Encode a string to be compliant with MIME (used in headers) * @param string $header The header to be used. Will not be returned, but the @@ -1463,6 +1562,7 @@ class mail * @return string the content encoded by $encoding */ private function encodeHeaders ($header, $content, $encoding) + // {{{ { $prefs = array ("input-charset" => "utf-8", "output-charset" => "utf-8"); @@ -1476,6 +1576,7 @@ class mail return substr (iconv_mime_encode ($header, $content, $prefs), strlen ($header)+2); } + // }}} /** Convert the header to text * @param string $header The header to decode @@ -1483,10 +1584,12 @@ class mail * @return string the header converted */ private function decodeHeaders ($header, $content) + // {{{ { return substr (iconv_mime_decode ("$header: $content", 0, "utf-8"), strlen ($header)+2); } + // }}} /** Encode a header string not starting on first column. The number of chars * need to be skipped is passed as argument. The function will correctely @@ -1498,6 +1601,7 @@ class mail * @return string the content encoded by $encoding */ private function encodeHeaderStringWithPosition ($content, $encoding, $blanks) + // {{{ { $prefs = array ("input-charset" => "utf-8", "output-charset" => "utf-8"); @@ -1512,6 +1616,7 @@ class mail $prefs), $blanks+2); } + // }}} /** Convert a From/To string to array. Manage multiple recipients * Ex. : toto toto , titi @@ -1521,6 +1626,7 @@ class mail * @return array The array with the converted data */ public function convertPeopleToArray ($data) + // {{{ { $elements = explode (",", $data); $res = array (); @@ -1542,12 +1648,14 @@ class mail } return $res; } + // }}} /** Analyze the Content-Type line and return an array with the values * @param string $contentType The content Type to analyze * @return array The analyzed Content-Type */ public function contentTypeAnalyze ($contentType) + // {{{ { $contentType = preg_replace ("#[\r\n]+[ \t]+#", " ", $contentType); $elements = explode (";", $contentType); @@ -1566,4 +1674,5 @@ class mail } return $res; } + // }}} }