4623fb42eef72e3a0e187ac2d153ee106fa12580
5 * Copyright (c) 2004-2005 The SquirrelMail Project Team
6 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 * Contains rfc822 email address function parsing functions.
12 * @package squirrelmail
17 * parseRFC822Address: function for parsing RFC822 email address strings and store
18 * them in an address array
20 * @param string $address The email address string to parse
21 * @param integer $iLimit stop on $iLimit parsed addresses
23 * @author Marc Groot Koerkamp
26 function parseRFC822Address($sAddress,$iLimit = 0) {
28 $aTokens = _getAddressTokens($sAddress);
29 $sPersonal = $sEmail = $sComment = $sGroup = '';
30 $aStack = $aComment = $aAddress = array();
31 foreach ($aTokens as $sToken) {
32 if ($iLimit && $iLimit == count($aAddress)) {
44 $aComment[] = substr($sToken,1,-1);
48 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
49 $oAddr = end($aAddress);
50 if(!$oAddr ||
((isset($oAddr)) && !$oAddr->mailbox
&& !$oAddr->personal
)) {
51 $sEmail = $sGroup . ':;';
53 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
55 $aStack = $aComment = array();
59 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
62 $sGroup = trim(implode(' ',$aStack));
63 $sGroup = preg_replace('/\s+/',' ',$sGroup);
67 $sEmail = trim(substr($sToken,1,-1));
72 default: $aStack[] = $sToken; break;
75 /* now do the action again for the last address */
76 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
82 * Do the address array to string translation
84 * @param array $aAddressList list with email address arrays
85 * @param array $aProps associative array with properties
88 * @see parseRFC822Address
89 * @author Marc Groot Koerkamp
92 function getAddressString($aAddressList,$aProps) {
93 $aPropsDefault = array (
94 'separator' => ',', // address separator
95 'limit' => 0, // limits returned addresses
96 'personal' => true, // show persnal part
97 'email' => true, // show email part
98 'best' => false, // show personal if available
99 'encode' => false, // encode the personal part
100 'unique' => false, // make email addresses unique.
101 'exclude' => array() // array with exclude addresses
102 // format of address: mailbox@host
105 $aProps = is_array($aProps) ?
array_merge($aPropsDefault,$aProps) : $aPropsDefault;
107 $aNewAddressList = array();
108 $aEmailUnique = array();
109 foreach ($aAddressList as $aAddr) {
110 if ($aProps['limit'] && count($aNewAddressList) == $aProps['limit']) {
113 $sPersonal = (isset($aAddr[SQM_ADDR_PERSONAL
])) ?
$aAddr[SQM_ADDR_PERSONAL
] : '';
114 $sMailbox = (isset($aAddr[SQM_ADDR_MAILBOX
])) ?
$aAddr[SQM_ADDR_MAILBOX
] : '';
115 $sHost = (isset($aAddr[SQM_ADDR_HOST
])) ?
$aAddr[SQM_ADDR_HOST
] : '';
117 $sEmail = ($sHost) ?
"$sMailbox@$sHost": $sMailbox;
119 if (in_array($sEmail,$aProps['exclude'],true)) {
123 if ($aProps['unique']) {
124 if (in_array($sEmail,$aEmailUnique,true)) {
127 $aEmailUnique[] = $sEmail;
132 if ($aProps['best']) {
133 $s .= ($sPersonal) ?
$sPersonal : $sEmail;
135 if ($aProps['personal'] && $sPersonal) {
136 if ($aProps['encode']) {
137 $sPersonal = encodeHeader($sPersonal);
141 if ($aProps['email'] && $sEmail) {
142 $s.= ($s) ?
' <'.$sEmail.'>': '<'.$sEmail.'>';
146 $aNewAddressList[] = $s;
149 return implode($aProps['separator'],$aNewAddressList);
154 * Do after address parsing handling. This is used by compose.php and should
155 * be moved to compose.php.
156 * The AddressStructure objetc is now obsolete and dependent parts of that will
157 * be adapted so that it can make use of this function
158 * After that we can remove the parseAddress method from the Rfc822Header class completely
159 * so we achieved 1 single instance of parseAddress instead of two like we have now.
161 * @param array $aAddressList list with email address arrays
162 * @param array $aProps associative array with properties
165 * @see parseRFC822Address
167 * @author Marc Groot Koerkamp
170 function processAddressArray($aAddresses,$aProps) {
171 $aPropsDefault = array (
174 'abooklookup' => false);
176 $aProps = is_array($aProps) ?
array_merge($aPropsDefault,$aProps) : $aPropsDefault;
177 $aProcessedAddress = array();
179 foreach ($aAddresses as $aEntry) {
181 * if the emailaddress does not contain the domainpart it can concern
182 * an alias or local (in the same domain as the user is) email
183 * address. In that case we try to look it up in the addressbook or add
184 * the local domain part
186 if (!$aEntry[SQM_ADDR_HOST
]) {
188 $aAddr = call_user_func_array($cbLookup,array($aEntry[SQM_ADDR_MAILBOX
]));
189 if (isset($aAddr['email'])) {
191 * if the returned email address concerns multiple email
192 * addresses we have to process those as well
194 if (strpos($aAddr['email'],',')) { /* multiple addresses */
195 /* add the parsed addresses to the processed address array */
196 $aProcessedAddress = array_merge($aProcessedAddress,parseAddress($aAddr['email']));
197 /* skip to next address, all processing is done */
199 } else { /* single address */
200 $iPosAt = strpos($aAddr['email'], '@');
201 $aEntry[SQM_ADDR_MAILBOX
] = substr($aAddr['email'], 0, $iPosAt);
202 $aEntry[SQM_ADDR_HOST
] = substr($aAddr['email'], $iPosAt+
1);
203 if (isset($aAddr['name'])) {
204 $aEntry[SQM_ADDR_PERSONAL
] = $aAddr['name'];
206 $aEntry[SQM_ADDR_PERSONAL
] = encodeHeader($sPersonal);
215 if (!$aEntry[SQM_ADDR_MAILBOX
]) {
216 $aEntry[SQM_ADDR_MAILBOX
] = trim($sEmail);
218 if ($sDomain && !$aEntry[SQM_ADDR_HOST
]) {
219 $aEntry[SQM_ADDR_HOST
] = $sDomain;
222 if ($aEntry[SQM_ADDR_MAILBOX
]) {
223 $aProcessedAddress[] = $aEntry;
226 return $aProcessedAddress;
230 * Internal function for creating an address array
232 * @param array $aStack
233 * @param array $aComment
234 * @param string $sEmail
235 * @return array $aAddr array with personal (0), adl(1), mailbox(2) and host(3) info
237 * @author Marc Groot Koerkamp
241 function _createAddressElement(&$aStack,&$aComment,&$sEmail) {
243 while (count($aStack) && !$sEmail) {
244 $sEmail = trim(array_pop($aStack));
247 if (count($aStack)) {
248 $sPersonal = trim(implode('',$aStack));
252 if (!$sPersonal && count($aComment)) {
253 $sComment = trim(implode(' ',$aComment));
254 $sPersonal .= $sComment;
257 // if ($sPersonal && substr($sPersonal,0,2) == '=?') {
258 // $aAddr[SQM_ADDR_PERSONAL] = encodeHeader($sPersonal);
260 $aAddr[SQM_ADDR_PERSONAL
] = $sPersonal;
263 $iPosAt = strpos($sEmail,'@');
265 $aAddr[SQM_ADDR_MAILBOX
] = substr($sEmail, 0, $iPosAt);
266 $aAddr[SQM_ADDR_HOST
] = substr($sEmail, $iPosAt+
1);
268 $aAddr[SQM_ADDR_MAILBOX
] = $sEmail;
269 $aAddr[SQM_ADDR_HOST
] = false;
272 $aStack = $aComment = array();
277 * Tokenizer function for parsing the RFC822 email address string
279 * @param string $address The email address string to parse
280 * @return array $aTokens
282 * @author Marc Groot Koerkamp
286 function _getAddressTokens($address) {
288 $aSpecials = array('(' ,'<' ,',' ,';' ,':');
289 $aReplace = array(' (',' <',' ,',' ;',' :');
290 $address = str_replace($aSpecials,$aReplace,$address);
291 $iCnt = strlen($address);
294 $cChar = $address{$i};
298 $iEnd = strpos($address,'>',$i+
1);
300 $sToken = substr($address,$i);
303 $sToken = substr($address,$i,$iEnd - $i +
1);
306 $sToken = str_replace($aReplace, $aSpecials,$sToken);
307 if ($sToken) $aTokens[] = $sToken;
310 $iEnd = strpos($address,$cChar,$i+
1);
312 // skip escaped quotes
313 $prev_char = $address{$iEnd-1};
314 while ($prev_char === '\\' && substr($address,$iEnd-2,2) !== '\\\\') {
315 $iEnd = strpos($address,$cChar,$iEnd+
1);
317 $prev_char = $address{$iEnd-1};
324 $sToken = substr($address,$i);
327 // also remove the surrounding quotes
328 $sToken = substr($address,$i+
1,$iEnd - $i -1);
331 $sToken = str_replace($aReplace, $aSpecials,$sToken);
332 if ($sToken) $aTokens[] = $sToken;
335 array_pop($aTokens); //remove inserted space
336 $iEnd = strpos($address,')',$i);
338 $sToken = substr($address,$i);
343 while (($iDepth > 0) && (++
$iComment < $iCnt)) {
344 $cCharComment = $address{$iComment};
345 switch($cCharComment) {
360 $sToken = substr($address,$i,$iComment - $i +
1);
363 $sToken = substr($address,$i,$iEnd - $i +
1);
367 // check the next token in case comments appear in the middle of email addresses
368 $prevToken = end($aTokens);
369 if (!in_array($prevToken,$aSpecials,true)) {
370 if ($i+
1<strlen($address) && !in_array($address{$i+
1},$aSpecials,true)) {
371 $iEnd = strpos($address,' ',$i+
1);
373 $sNextToken = trim(substr($address,$i+
1,$iEnd - $i -1));
376 $sNextToken = trim(substr($address,$i+
1));
381 // create token and add it again
382 $sNewToken = $prevToken . $sNextToken;
383 if($sNewToken) $aTokens[] = $sNewToken;
386 $sToken = str_replace($aReplace, $aSpecials,$sToken);
387 if ($sToken) $aTokens[] = $sToken;
396 $iEnd = strpos($address,' ',$i+
1);
398 $sToken = trim(substr($address,$i,$iEnd - $i));
401 $sToken = trim(substr($address,$i));
404 if ($sToken) $aTokens[] = $sToken;