|
@@ -0,0 +1,425 @@
|
|
|
|
+<?php
|
|
|
|
+/**
|
|
|
|
+ * rfc822address.php
|
|
|
|
+ *
|
|
|
|
+ * Copyright (c) 2004 The SquirrelMail Project Team
|
|
|
|
+ * Licensed under the GNU GPL. For full terms see the file COPYING.
|
|
|
|
+ *
|
|
|
|
+ * Contains rfc822 email address function parsing functions.
|
|
|
|
+ *
|
|
|
|
+ *
|
|
|
|
+ * @version $Id$
|
|
|
|
+ * @package squirrelmail
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+if (!defined('SQM_ADDR_PERSONAL')) define('SQM_ADDR_PERSONAL', 0);
|
|
|
|
+if (!defined('SQM_ADDR_ADLL')) define('SQM_ADDR_ADL', 1);
|
|
|
|
+if (!defined('SQM_ADDR_MAILBOX')) define('SQM_ADDR_MAILBOX', 2);
|
|
|
|
+if (!defined('SQM_ADDR_HOST')) define('SQM_ADDR_HOST', 3);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * parseRFC822Address: function for parsing RFC822 email address strings and store
|
|
|
|
+ * them in an address array
|
|
|
|
+ *
|
|
|
|
+ * @param string $address The email address string to parse
|
|
|
|
+ * @param array $aProps associative array with properties
|
|
|
|
+ * @public
|
|
|
|
+ * @author Marc Groot Koerkamp
|
|
|
|
+ *
|
|
|
|
+ **/
|
|
|
|
+
|
|
|
|
+function parseRFC822Address($sAddress,$aProps) {
|
|
|
|
+ $aPropsDefault = array (
|
|
|
|
+ 'domain' => '', //
|
|
|
|
+ 'limit' => 0, // limits returned addresses
|
|
|
|
+ 'abooklookup' => false); // callback function for addressbook lookup
|
|
|
|
+
|
|
|
|
+ $aProps = is_array($aProps) ? array_merge($aPropsDefault,$aProps) : $aPropsDefault;
|
|
|
|
+
|
|
|
|
+ $cbLookup = $aProps['abooklookup'];
|
|
|
|
+ $sDomain = $aProps['domain'];
|
|
|
|
+ $iLimit = $aProps['limit'];
|
|
|
|
+
|
|
|
|
+ $aTokens = _getAddressTokens($sAddress);
|
|
|
|
+ $sPersonal = $sEmail = $sComment = $sGroup = '';
|
|
|
|
+ $aStack = $aComment = $aAddress = array();
|
|
|
|
+ foreach ($aTokens as $sToken) {
|
|
|
|
+ if ($iLimit && $iLimit == count($aAddress)) {
|
|
|
|
+ return $aAddress;
|
|
|
|
+ }
|
|
|
|
+ $cChar = $sToken{0};
|
|
|
|
+ switch ($cChar)
|
|
|
|
+ {
|
|
|
|
+ case '=':
|
|
|
|
+ case '"':
|
|
|
|
+ case ' ':
|
|
|
|
+ $aStack[] = $sToken;
|
|
|
|
+ break;
|
|
|
|
+ case '(':
|
|
|
|
+ $aComment[] = substr($sToken,1,-1);
|
|
|
|
+ break;
|
|
|
|
+ case ';':
|
|
|
|
+ if ($sGroup) {
|
|
|
|
+ $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
|
|
|
|
+ $oAddr = end($aAddress);
|
|
|
|
+ if(!$oAddr || ((isset($oAddr)) && !$oAddr->mailbox && !$oAddr->personal)) {
|
|
|
|
+ $sEmail = $sGroup . ':;';
|
|
|
|
+ }
|
|
|
|
+ $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
|
|
|
|
+ $sGroup = '';
|
|
|
|
+ $aStack = $aComment = array();
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case ',':
|
|
|
|
+ $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
|
|
|
|
+ break;
|
|
|
|
+ case ':':
|
|
|
|
+ $sGroup = trim(implode(' ',$aStack));
|
|
|
|
+ $sGroup = preg_replace('/\s+/',' ',$sGroup);
|
|
|
|
+ $aStack = array();
|
|
|
|
+ break;
|
|
|
|
+ case '<':
|
|
|
|
+ $sEmail = trim(substr($sToken,1,-1));
|
|
|
|
+ break;
|
|
|
|
+ case '>':
|
|
|
|
+ /* skip */
|
|
|
|
+ break;
|
|
|
|
+ default: $aStack[] = $sToken; break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ /* now do the action again for the last address */
|
|
|
|
+ $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
|
|
|
|
+ return $aAddress;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Do the address array to string translation
|
|
|
|
+ *
|
|
|
|
+ * @param array $aAddressList list with email address arrays
|
|
|
|
+ * @param array $aProps associative array with properties
|
|
|
|
+ * @return string
|
|
|
|
+ * @public
|
|
|
|
+ * @see parseRFC822Address
|
|
|
|
+ * @author Marc Groot Koerkamp
|
|
|
|
+ *
|
|
|
|
+ **/
|
|
|
|
+function getAddressString($aAddressList,$aProps) {
|
|
|
|
+ $aPropsDefault = array (
|
|
|
|
+ 'separator' => ',', // address separator
|
|
|
|
+ 'limit' => 0, // limits returned addresses
|
|
|
|
+ 'personal' => true, // show persnal part
|
|
|
|
+ 'email' => true, // show email part
|
|
|
|
+ 'best' => false, // show personal if available
|
|
|
|
+ 'encode' => false, // encode the personal part
|
|
|
|
+ 'unique' => false, // make email addresses unique.
|
|
|
|
+ 'exclude' => array() // array with exclude addresses
|
|
|
|
+ // format of address: mailbox@host
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ $aProps = is_array($aProps) ? array_merge($aPropsDefault,$aProps) : $aPropsDefault;
|
|
|
|
+
|
|
|
|
+ $aNewAddressList = array();
|
|
|
|
+ $aEmailUnique = array();
|
|
|
|
+ foreach ($aAddressList as $aAddr) {
|
|
|
|
+ if ($aProps['limit'] && count($aNewAddressList) == $aProps['limit']) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ $sPersonal = (isset($aAddr[SQM_ADDR_PERSONAL])) ? $aAddr[SQM_ADDR_PERSONAL] : '';
|
|
|
|
+ $sMailbox = (isset($aAddr[SQM_ADDR_MAILBOX])) ? $aAddr[SQM_ADDR_MAILBOX] : '';
|
|
|
|
+ $sHost = (isset($aAddr[SQM_ADDR_HOST])) ? $aAddr[SQM_ADDR_HOST] : '';
|
|
|
|
+
|
|
|
|
+ $sEmail = ($sHost) ? "$sMailbox@$sHost": $sMailbox;
|
|
|
|
+
|
|
|
|
+ if (in_array($sEmail,$aProps['exclude'],true)) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ($aProps['unique']) {
|
|
|
|
+ if (in_array($sEmail,$aEmailUnique,true)) {
|
|
|
|
+ continue;
|
|
|
|
+ } else {
|
|
|
|
+ $aEmailUnique[] = $sEmail;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $s = '';
|
|
|
|
+ if ($aProps['best']) {
|
|
|
|
+ $s .= ($sPersonal) ? $sPersonal : $sEmail;
|
|
|
|
+ } else {
|
|
|
|
+ if ($aProps['personal'] && $sPersonal) {
|
|
|
|
+ if ($aProps['encode']) {
|
|
|
|
+ $sPersonal = encodeHeader($sPersonal);
|
|
|
|
+ }
|
|
|
|
+ $s .= $sPersonal;
|
|
|
|
+ }
|
|
|
|
+ if ($aProps['email'] && $sEmail) {
|
|
|
|
+ $s.= ($s) ? ' <'.$sEmail.'>': '<'.$sEmail.'>';
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if ($s) {
|
|
|
|
+ $aNewAddressList[] = $s;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return explode($aProps['seperator'],$aNewAddressList);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Do after address parsing handling. This is used by compose.php and should
|
|
|
|
+ * be moved to compose.php.
|
|
|
|
+ * The AddressStructure objetc is now obsolete and dependent parts of that will
|
|
|
|
+ * be adapted so that it can make use of this function
|
|
|
|
+ * After that we can remove the parseAddress method from the Rfc822Header class completely
|
|
|
|
+ * so we achieved 1 single instance of parseAddress instead of two like we have now.
|
|
|
|
+ *
|
|
|
|
+ * @param array $aAddressList list with email address arrays
|
|
|
|
+ * @param array $aProps associative array with properties
|
|
|
|
+ * @return string
|
|
|
|
+ * @public
|
|
|
|
+ * @see parseRFC822Address
|
|
|
|
+ * $see Rfc822Header
|
|
|
|
+ * @author Marc Groot Koerkamp
|
|
|
|
+ *
|
|
|
|
+ **/
|
|
|
|
+function processAddressArray($aAddresses,$aProps) {
|
|
|
|
+ $aPropsDefault = array (
|
|
|
|
+ 'domain' => '',
|
|
|
|
+ 'limit' => 0,
|
|
|
|
+ 'abooklookup' => false);
|
|
|
|
+
|
|
|
|
+ $aProps = is_array($aProps) ? array_merge($aPropsDefault,$aProps) : $aPropsDefault;
|
|
|
|
+ $aProcessedAddress = array();
|
|
|
|
+
|
|
|
|
+ foreach ($aAddresses as $aEntry) {
|
|
|
|
+ /*
|
|
|
|
+ * if the emailaddress does not contain the domainpart it can concern
|
|
|
|
+ * an alias or local (in the same domain as the user is) email
|
|
|
|
+ * address. In that case we try to look it up in the addressbook or add
|
|
|
|
+ * the local domain part
|
|
|
|
+ */
|
|
|
|
+ if (!$aEntry[SQM_ADDR_HOST]) {
|
|
|
|
+ if ($cbLookup) {
|
|
|
|
+ $aAddr = call_user_func_array($cbLookup,array($aEntry[SQM_ADDR_MAILBOX]));
|
|
|
|
+ if (isset($aAddr['email'])) {
|
|
|
|
+ /*
|
|
|
|
+ * if the returned email address concerns multiple email
|
|
|
|
+ * addresses we have to process those as well
|
|
|
|
+ */
|
|
|
|
+ if (strpos($aAddr['email'],',')) { /* multiple addresses */
|
|
|
|
+ /* add the parsed addresses to the processed address array */
|
|
|
|
+ $aProcessedAddress = array_merge($aProcessedAddress,parseAddress($aAddr['email']));
|
|
|
|
+ /* skip to next address, all processing is done */
|
|
|
|
+ continue;
|
|
|
|
+ } else { /* single address */
|
|
|
|
+ $iPosAt = strpos($aAddr['email'], '@');
|
|
|
|
+ $aEntry[SQM_ADDR_MAILBOX] = substr($aAddr['email'], 0, $iPosAt);
|
|
|
|
+ $aEntry[SQM_ADDR_HOST] = substr($aAddr['email'], $iPosAt+1);
|
|
|
|
+ if (isset($aAddr['name'])) {
|
|
|
|
+ $aEntry[SQM_ADDR_PERSONAL] = $aAddr['name'];
|
|
|
|
+ } else {
|
|
|
|
+ $aEntry[SQM_ADDR_PERSONAL] = encodeHeader($sPersonal);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ /*
|
|
|
|
+ * append the domain
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+ if (!$aEntry[SQM_ADDR_MAILBOX]) {
|
|
|
|
+ $aEntry[SQM_ADDR_MAILBOX] = trim($sEmail);
|
|
|
|
+ }
|
|
|
|
+ if ($sDomain && !$aEntry[SQM_ADDR_HOST]) {
|
|
|
|
+ $aEntry[SQM_ADDR_HOST] = $sDomain;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if ($aEntry[SQM_ADDR_MAILBOX]) {
|
|
|
|
+ $aProcessedAddress[] = $aEntry;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return $aProcessedAddress;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Internal function for creating an address array
|
|
|
|
+ *
|
|
|
|
+ * @param array $aStack
|
|
|
|
+ * @param array $aComment
|
|
|
|
+ * @param string $sEmail
|
|
|
|
+ * @return array $aAddr array with personal (0), adl(1), mailbox(2) and host(3) info
|
|
|
|
+ * @private
|
|
|
|
+ * @author Marc Groot Koerkamp
|
|
|
|
+ *
|
|
|
|
+ **/
|
|
|
|
+
|
|
|
|
+function _createAddressElement(&$aStack,&$aComment,&$sEmail) {
|
|
|
|
+ if (!$sEmail) {
|
|
|
|
+ while (count($aStack) && !$sEmail) {
|
|
|
|
+ $sEmail = trim(array_pop($aStack));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (count($aStack)) {
|
|
|
|
+ $sPersonal = trim(implode('',$aStack));
|
|
|
|
+ } else {
|
|
|
|
+ $sPersonal = '';
|
|
|
|
+ }
|
|
|
|
+ if (!$sPersonal && count($aComment)) {
|
|
|
|
+ $sComment = trim(implode(' ',$aComment));
|
|
|
|
+ $sPersonal .= $sComment;
|
|
|
|
+ }
|
|
|
|
+ $aAddr = array();
|
|
|
|
+// if ($sPersonal && substr($sPersonal,0,2) == '=?') {
|
|
|
|
+// $aAddr[SQM_ADDR_PERSONAL] = encodeHeader($sPersonal);
|
|
|
|
+// } else {
|
|
|
|
+ $aAddr[SQM_ADDR_PERSONAL] = $sPersonal;
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+ $iPosAt = strpos($sEmail,'@');
|
|
|
|
+ if ($iPosAt) {
|
|
|
|
+ $aAddr[SQM_ADDR_MAILBOX] = substr($sEmail, 0, $iPosAt);
|
|
|
|
+ $aAddr[SQM_ADDR_HOST] = substr($sEmail, $iPosAt+1);
|
|
|
|
+ } else {
|
|
|
|
+ $aAddr[SQM_ADDR_MAILBOX] = $sEmail;
|
|
|
|
+ $aAddr[SQM_ADDR_HOST] = false;
|
|
|
|
+ }
|
|
|
|
+ $sEmail = '';
|
|
|
|
+ $aStack = $aComment = array();
|
|
|
|
+ return $aAddr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Tokenizer function for parsing the RFC822 email address string
|
|
|
|
+ *
|
|
|
|
+ * @param string $address The email address string to parse
|
|
|
|
+ * @return array $aTokens
|
|
|
|
+ * @private
|
|
|
|
+ * @author Marc Groot Koerkamp
|
|
|
|
+ *
|
|
|
|
+ **/
|
|
|
|
+
|
|
|
|
+function _getAddressTokens($address) {
|
|
|
|
+ $aTokens = array();
|
|
|
|
+ $aAddress = array();
|
|
|
|
+ $aSpecials = array('(' ,'<' ,',' ,';' ,':');
|
|
|
|
+ $aReplace = array(' (',' <',' ,',' ;',' :');
|
|
|
|
+ $address = str_replace($aSpecials,$aReplace,$address);
|
|
|
|
+ $iCnt = strlen($address);
|
|
|
|
+ $i = 0;
|
|
|
|
+ while ($i < $iCnt) {
|
|
|
|
+ $cChar = $address{$i};
|
|
|
|
+ switch($cChar)
|
|
|
|
+ {
|
|
|
|
+ case '<':
|
|
|
|
+ $iEnd = strpos($address,'>',$i+1);
|
|
|
|
+ if (!$iEnd) {
|
|
|
|
+ $sToken = substr($address,$i);
|
|
|
|
+ $i = $iCnt;
|
|
|
|
+ } else {
|
|
|
|
+ $sToken = substr($address,$i,$iEnd - $i +1);
|
|
|
|
+ $i = $iEnd;
|
|
|
|
+ }
|
|
|
|
+ $sToken = str_replace($aReplace, $aSpecials,$sToken);
|
|
|
|
+ if ($sToken) $aTokens[] = $sToken;
|
|
|
|
+ break;
|
|
|
|
+ case '"':
|
|
|
|
+ $iEnd = strpos($address,$cChar,$i+1);
|
|
|
|
+ if ($iEnd) {
|
|
|
|
+ // skip escaped quotes
|
|
|
|
+ $prev_char = $address{$iEnd-1};
|
|
|
|
+ while ($prev_char === '\\' && substr($address,$iEnd-2,2) !== '\\\\') {
|
|
|
|
+ $iEnd = strpos($address,$cChar,$iEnd+1);
|
|
|
|
+ if ($iEnd) {
|
|
|
|
+ $prev_char = $address{$iEnd-1};
|
|
|
|
+ } else {
|
|
|
|
+ $prev_char = false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!$iEnd) {
|
|
|
|
+ $sToken = substr($address,$i);
|
|
|
|
+ $i = $iCnt;
|
|
|
|
+ } else {
|
|
|
|
+ // also remove the surrounding quotes
|
|
|
|
+ $sToken = substr($address,$i+1,$iEnd - $i -1);
|
|
|
|
+ $i = $iEnd;
|
|
|
|
+ }
|
|
|
|
+ $sToken = str_replace($aReplace, $aSpecials,$sToken);
|
|
|
|
+ if ($sToken) $aTokens[] = $sToken;
|
|
|
|
+ break;
|
|
|
|
+ case '(':
|
|
|
|
+ array_pop($aTokens); //remove inserted space
|
|
|
|
+ $iEnd = strpos($address,')',$i);
|
|
|
|
+ if (!$iEnd) {
|
|
|
|
+ $sToken = substr($address,$i);
|
|
|
|
+ $i = $iCnt;
|
|
|
|
+ } else {
|
|
|
|
+ $iDepth = 1;
|
|
|
|
+ $iComment = $i;
|
|
|
|
+ while (($iDepth > 0) && (++$iComment < $iCnt)) {
|
|
|
|
+ $cCharComment = $address{$iComment};
|
|
|
|
+ switch($cCharComment) {
|
|
|
|
+ case '\\':
|
|
|
|
+ ++$iComment;
|
|
|
|
+ break;
|
|
|
|
+ case '(':
|
|
|
|
+ ++$iDepth;
|
|
|
|
+ break;
|
|
|
|
+ case ')':
|
|
|
|
+ --$iDepth;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if ($iDepth == 0) {
|
|
|
|
+ $sToken = substr($address,$i,$iComment - $i +1);
|
|
|
|
+ $i = $iComment;
|
|
|
|
+ } else {
|
|
|
|
+ $sToken = substr($address,$i,$iEnd - $i + 1);
|
|
|
|
+ $i = $iEnd;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // check the next token in case comments appear in the middle of email addresses
|
|
|
|
+ $prevToken = end($aTokens);
|
|
|
|
+ if (!in_array($prevToken,$aSpecials,true)) {
|
|
|
|
+ if ($i+1<strlen($address) && !in_array($address{$i+1},$aSpecials,true)) {
|
|
|
|
+ $iEnd = strpos($address,' ',$i+1);
|
|
|
|
+ if ($iEnd) {
|
|
|
|
+ $sNextToken = trim(substr($address,$i+1,$iEnd - $i -1));
|
|
|
|
+ $i = $iEnd-1;
|
|
|
|
+ } else {
|
|
|
|
+ $sNextToken = trim(substr($address,$i+1));
|
|
|
|
+ $i = $iCnt;
|
|
|
|
+ }
|
|
|
|
+ // remove the token
|
|
|
|
+ array_pop($aTokens);
|
|
|
|
+ // create token and add it again
|
|
|
|
+ $sNewToken = $prevToken . $sNextToken;
|
|
|
|
+ if($sNewToken) $aTokens[] = $sNewToken;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ $sToken = str_replace($aReplace, $aSpecials,$sToken);
|
|
|
|
+ if ($sToken) $aTokens[] = $sToken;
|
|
|
|
+ break;
|
|
|
|
+ case ',':
|
|
|
|
+ case ':':
|
|
|
|
+ case ';':
|
|
|
|
+ case ' ':
|
|
|
|
+ $aTokens[] = $cChar;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ $iEnd = strpos($address,' ',$i+1);
|
|
|
|
+ if ($iEnd) {
|
|
|
|
+ $sToken = trim(substr($address,$i,$iEnd - $i));
|
|
|
|
+ $i = $iEnd-1;
|
|
|
|
+ } else {
|
|
|
|
+ $sToken = trim(substr($address,$i));
|
|
|
|
+ $i = $iCnt;
|
|
|
|
+ }
|
|
|
|
+ if ($sToken) $aTokens[] = $sToken;
|
|
|
|
+ }
|
|
|
|
+ ++$i;
|
|
|
|
+ }
|
|
|
|
+ return $aTokens;
|
|
|
|
+}
|
|
|
|
+?>
|