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
16 * Undocumented defines
18 if (!defined('SQM_ADDR_PERSONAL')) define('SQM_ADDR_PERSONAL', 0);
19 if (!defined('SQM_ADDR_ADLL')) define('SQM_ADDR_ADL', 1);
20 if (!defined('SQM_ADDR_MAILBOX')) define('SQM_ADDR_MAILBOX', 2);
21 if (!defined('SQM_ADDR_HOST')) define('SQM_ADDR_HOST', 3);
24 * parseRFC822Address: function for parsing RFC822 email address strings and store
25 * them in an address array
27 * @param string $address The email address string to parse
28 * @param array $aProps associative array with properties
30 * @author Marc Groot Koerkamp
34 function parseRFC822Address($sAddress,$aProps) {
35 // $aPropsDefault = array (
37 // 'limit' => 0, // limits returned addresses
38 // 'abooklookup' => false); // callback function for addressbook lookup
40 // $aProps = is_array($aProps) ? array_merge($aPropsDefault,$aProps) : $aPropsDefault;
42 // $cbLookup = $aProps['abooklookup'];
43 // $sDomain = $aProps['domain'];
44 $iLimit = $aProps['limit'];
46 $aTokens = _getAddressTokens($sAddress);
47 $sEmail = $sGroup = '';
48 $aStack = $aComment = $aAddress = array();
49 foreach ($aTokens as $sToken) {
50 if ($iLimit && $iLimit == count($aAddress)) {
62 $aComment[] = substr($sToken,1,-1);
66 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
67 $oAddr = end($aAddress);
68 if(!$oAddr ||
((isset($oAddr)) && !$oAddr->mailbox
&& !$oAddr->personal
)) {
69 $sEmail = $sGroup . ':;';
71 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
73 $aStack = $aComment = array();
77 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
80 $sGroup = trim(implode(' ',$aStack));
81 $sGroup = preg_replace('/\s+/',' ',$sGroup);
85 $sEmail = trim(substr($sToken,1,-1));
90 default: $aStack[] = $sToken; break;
93 /* now do the action again for the last address */
94 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
99 * Do the address array to string translation
101 * @param array $aAddressList list with email address arrays
102 * @param array $aProps associative array with properties
105 * @see parseRFC822Address
106 * @author Marc Groot Koerkamp
109 function getAddressString($aAddressList,$aProps) {
110 $aPropsDefault = array (
111 'separator' => ',', // address separator
112 'limit' => 0, // limits returned addresses
113 'personal' => true, // show persnal part
114 'email' => true, // show email part
115 'best' => false, // show personal if available
116 'encode' => false, // encode the personal part
117 'unique' => false, // make email addresses unique.
118 'exclude' => array() // array with exclude addresses
119 // format of address: mailbox@host
122 $aProps = is_array($aProps) ?
array_merge($aPropsDefault,$aProps) : $aPropsDefault;
124 $aNewAddressList = array();
125 $aEmailUnique = array();
126 foreach ($aAddressList as $aAddr) {
127 if ($aProps['limit'] && count($aNewAddressList) == $aProps['limit']) {
130 $sPersonal = (isset($aAddr[SQM_ADDR_PERSONAL
])) ?
$aAddr[SQM_ADDR_PERSONAL
] : '';
131 $sMailbox = (isset($aAddr[SQM_ADDR_MAILBOX
])) ?
$aAddr[SQM_ADDR_MAILBOX
] : '';
132 $sHost = (isset($aAddr[SQM_ADDR_HOST
])) ?
$aAddr[SQM_ADDR_HOST
] : '';
134 $sEmail = ($sHost) ?
"$sMailbox@$sHost": $sMailbox;
136 if (in_array($sEmail,$aProps['exclude'],true)) {
140 if ($aProps['unique']) {
141 if (in_array($sEmail,$aEmailUnique,true)) {
144 $aEmailUnique[] = $sEmail;
149 if ($aProps['best']) {
150 $s .= ($sPersonal) ?
$sPersonal : $sEmail;
152 if ($aProps['personal'] && $sPersonal) {
153 if ($aProps['encode']) {
154 $sPersonal = encodeHeader($sPersonal);
158 if ($aProps['email'] && $sEmail) {
159 $s.= ($s) ?
' <'.$sEmail.'>': '<'.$sEmail.'>';
163 $aNewAddressList[] = $s;
166 return explode($aProps['seperator'],$aNewAddressList);
171 * Do after address parsing handling. This is used by compose.php and should
172 * be moved to compose.php.
173 * The AddressStructure objetc is now obsolete and dependent parts of that will
174 * be adapted so that it can make use of this function
175 * After that we can remove the parseAddress method from the Rfc822Header class completely
176 * so we achieved 1 single instance of parseAddress instead of two like we have now.
178 * @param array $aAddressList list with email address arrays
179 * @param array $aProps associative array with properties
182 * @see parseRFC822Address
184 * @author Marc Groot Koerkamp
187 function processAddressArray($aAddresses,$aProps) {
188 $aPropsDefault = array (
191 'abooklookup' => false);
193 $aProps = is_array($aProps) ?
array_merge($aPropsDefault,$aProps) : $aPropsDefault;
194 $aProcessedAddress = array();
196 foreach ($aAddresses as $aEntry) {
198 * if the emailaddress does not contain the domainpart it can concern
199 * an alias or local (in the same domain as the user is) email
200 * address. In that case we try to look it up in the addressbook or add
201 * the local domain part
203 if (!$aEntry[SQM_ADDR_HOST
]) {
205 $aAddr = call_user_func_array($cbLookup,array($aEntry[SQM_ADDR_MAILBOX
]));
206 if (isset($aAddr['email'])) {
208 * if the returned email address concerns multiple email
209 * addresses we have to process those as well
211 if (strpos($aAddr['email'],',')) { /* multiple addresses */
212 /* add the parsed addresses to the processed address array */
213 $aProcessedAddress = array_merge($aProcessedAddress,parseAddress($aAddr['email']));
214 /* skip to next address, all processing is done */
216 } else { /* single address */
217 $iPosAt = strpos($aAddr['email'], '@');
218 $aEntry[SQM_ADDR_MAILBOX
] = substr($aAddr['email'], 0, $iPosAt);
219 $aEntry[SQM_ADDR_HOST
] = substr($aAddr['email'], $iPosAt+
1);
220 if (isset($aAddr['name'])) {
221 $aEntry[SQM_ADDR_PERSONAL
] = $aAddr['name'];
223 $aEntry[SQM_ADDR_PERSONAL
] = encodeHeader($sPersonal);
232 if (!$aEntry[SQM_ADDR_MAILBOX
]) {
233 $aEntry[SQM_ADDR_MAILBOX
] = trim($sEmail);
235 if ($sDomain && !$aEntry[SQM_ADDR_HOST
]) {
236 $aEntry[SQM_ADDR_HOST
] = $sDomain;
239 if ($aEntry[SQM_ADDR_MAILBOX
]) {
240 $aProcessedAddress[] = $aEntry;
243 return $aProcessedAddress;
247 * Internal function for creating an address array
249 * @param array $aStack
250 * @param array $aComment
251 * @param string $sEmail
252 * @return array $aAddr array with personal (0), adl(1), mailbox(2) and host(3) info
254 * @author Marc Groot Koerkamp
258 function _createAddressElement(&$aStack,&$aComment,&$sEmail) {
260 while (count($aStack) && !$sEmail) {
261 $sEmail = trim(array_pop($aStack));
264 if (count($aStack)) {
265 $sPersonal = trim(implode('',$aStack));
269 if (!$sPersonal && count($aComment)) {
270 $sComment = trim(implode(' ',$aComment));
271 $sPersonal .= $sComment;
274 // if ($sPersonal && substr($sPersonal,0,2) == '=?') {
275 // $aAddr[SQM_ADDR_PERSONAL] = encodeHeader($sPersonal);
277 $aAddr[SQM_ADDR_PERSONAL
] = $sPersonal;
280 $iPosAt = strpos($sEmail,'@');
282 $aAddr[SQM_ADDR_MAILBOX
] = substr($sEmail, 0, $iPosAt);
283 $aAddr[SQM_ADDR_HOST
] = substr($sEmail, $iPosAt+
1);
285 $aAddr[SQM_ADDR_MAILBOX
] = $sEmail;
286 $aAddr[SQM_ADDR_HOST
] = false;
289 $aStack = $aComment = array();
294 * Tokenizer function for parsing the RFC822 email address string
296 * @param string $address The email address string to parse
297 * @return array $aTokens
299 * @author Marc Groot Koerkamp
303 function _getAddressTokens($address) {
305 $aSpecials = array('(' ,'<' ,',' ,';' ,':');
306 $aReplace = array(' (',' <',' ,',' ;',' :');
307 $address = str_replace($aSpecials,$aReplace,$address);
308 $iCnt = strlen($address);
311 $cChar = $address{$i};
315 $iEnd = strpos($address,'>',$i+
1);
317 $sToken = substr($address,$i);
320 $sToken = substr($address,$i,$iEnd - $i +
1);
323 $sToken = str_replace($aReplace, $aSpecials,$sToken);
324 if ($sToken) $aTokens[] = $sToken;
327 $iEnd = strpos($address,$cChar,$i+
1);
329 // skip escaped quotes
330 $prev_char = $address{$iEnd-1};
331 while ($prev_char === '\\' && substr($address,$iEnd-2,2) !== '\\\\') {
332 $iEnd = strpos($address,$cChar,$iEnd+
1);
334 $prev_char = $address{$iEnd-1};
341 $sToken = substr($address,$i);
344 // also remove the surrounding quotes
345 $sToken = substr($address,$i+
1,$iEnd - $i -1);
348 $sToken = str_replace($aReplace, $aSpecials,$sToken);
349 if ($sToken) $aTokens[] = $sToken;
352 array_pop($aTokens); //remove inserted space
353 $iEnd = strpos($address,')',$i);
355 $sToken = substr($address,$i);
360 while (($iDepth > 0) && (++
$iComment < $iCnt)) {
361 $cCharComment = $address{$iComment};
362 switch($cCharComment) {
377 $sToken = substr($address,$i,$iComment - $i +
1);
380 $sToken = substr($address,$i,$iEnd - $i +
1);
384 // check the next token in case comments appear in the middle of email addresses
385 $prevToken = end($aTokens);
386 if (!in_array($prevToken,$aSpecials,true)) {
387 if ($i+
1<strlen($address) && !in_array($address{$i+
1},$aSpecials,true)) {
388 $iEnd = strpos($address,' ',$i+
1);
390 $sNextToken = trim(substr($address,$i+
1,$iEnd - $i -1));
393 $sNextToken = trim(substr($address,$i+
1));
398 // create token and add it again
399 $sNewToken = $prevToken . $sNextToken;
400 if($sNewToken) $aTokens[] = $sNewToken;
403 $sToken = str_replace($aReplace, $aSpecials,$sToken);
404 if ($sToken) $aTokens[] = $sToken;
413 $iEnd = strpos($address,' ',$i+
1);
415 $sToken = trim(substr($address,$i,$iEnd - $i));
418 $sToken = trim(substr($address,$i));
421 if ($sToken) $aTokens[] = $sToken;