6 * Contains rfc822 email address function parsing functions.
8 * @copyright © 2004-2007 The SquirrelMail Project Team
9 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
11 * @package squirrelmail
16 * parseRFC822Address: function for parsing RFC822 email address strings and store
17 * them in an address array
19 * @param string $address The email address string to parse
20 * @param integer $iLimit stop on $iLimit parsed addresses
22 * @author Marc Groot Koerkamp
25 function parseRFC822Address($sAddress,$iLimit = 0) {
27 $aTokens = _getAddressTokens($sAddress);
28 $sPersonal = $sEmail = $sComment = $sGroup = '';
29 $aStack = $aComment = $aAddress = array();
30 foreach ($aTokens as $sToken) {
31 if ($iLimit && $iLimit == count($aAddress)) {
43 $aComment[] = substr($sToken,1,-1);
47 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
48 $aAddr = end($aAddress);
49 if(!$aAddr ||
((isset($aAddr)) && !$aAddr[SQM_ADDR_MAILBOX
] && !$aAddr[SQM_ADDR_PERSONAL
])) {
50 $sEmail = $sGroup . ':;';
52 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
54 $aStack = $aComment = array();
58 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
61 $sGroup = trim(implode(' ',$aStack));
62 $sGroup = preg_replace('/\s+/',' ',$sGroup);
66 $sEmail = trim(substr($sToken,1,-1));
71 default: $aStack[] = $sToken; break;
74 /* now do the action again for the last address */
75 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
81 * Do the address array to string translation
83 * @param array $aAddressList list with email address arrays
84 * @param array $aProps associative array with properties
87 * @see parseRFC822Address
88 * @author Marc Groot Koerkamp
91 function getAddressString($aAddressList,$aProps) {
92 $aPropsDefault = array (
93 'separator' => ', ', // address separator
94 'limit' => 0, // limits returned addresses
95 'personal' => true, // show persnal part
96 'email' => true, // show email part
97 'best' => false, // show personal if available
98 'encode' => false, // encode the personal part
99 'unique' => false, // make email addresses unique.
100 'exclude' => array() // array with exclude addresses
101 // format of address: mailbox@host
104 $aProps = is_array($aProps) ?
array_merge($aPropsDefault,$aProps) : $aPropsDefault;
106 $aNewAddressList = array();
107 $aEmailUnique = array();
108 foreach ($aAddressList as $aAddr) {
109 if ($aProps['limit'] && count($aNewAddressList) == $aProps['limit']) {
112 $sPersonal = (isset($aAddr[SQM_ADDR_PERSONAL
])) ?
$aAddr[SQM_ADDR_PERSONAL
] : '';
113 $sMailbox = (isset($aAddr[SQM_ADDR_MAILBOX
])) ?
$aAddr[SQM_ADDR_MAILBOX
] : '';
114 $sHost = (isset($aAddr[SQM_ADDR_HOST
])) ?
$aAddr[SQM_ADDR_HOST
] : '';
116 $sEmail = ($sHost) ?
"$sMailbox@$sHost": $sMailbox;
118 if (in_array($sEmail,$aProps['exclude'],true)) {
122 if ($aProps['unique']) {
123 if (in_array($sEmail,$aEmailUnique,true)) {
126 $aEmailUnique[] = $sEmail;
131 if ($aProps['best']) {
132 $s .= ($sPersonal) ?
$sPersonal : $sEmail;
134 if ($aProps['personal'] && $sPersonal) {
135 if ($aProps['encode']) {
136 $sPersonal = encodeHeader($sPersonal);
140 if ($aProps['email'] && $sEmail) {
141 $s.= ($s) ?
' <'.$sEmail.'>': '<'.$sEmail.'>';
145 $aNewAddressList[] = $s;
148 return implode($aProps['separator'],$aNewAddressList);
153 * Do after address parsing handling. This is used by compose.php and should
154 * be moved to compose.php.
155 * The AddressStructure objetc is now obsolete and dependent parts of that will
156 * be adapted so that it can make use of this function
157 * After that we can remove the parseAddress method from the Rfc822Header class completely
158 * so we achieved 1 single instance of parseAddress instead of two like we have now.
160 * @param array $aAddressList list with email address arrays
161 * @param array $aProps associative array with properties
164 * @see parseRFC822Address
166 * @author Marc Groot Koerkamp
169 function processAddressArray($aAddresses,$aProps) {
170 $aPropsDefault = array (
173 'abooklookup' => false);
175 $aProps = is_array($aProps) ?
array_merge($aPropsDefault,$aProps) : $aPropsDefault;
176 $aProcessedAddress = array();
178 foreach ($aAddresses as $aEntry) {
180 * if the emailaddress does not contain the domainpart it can concern
181 * an alias or local (in the same domain as the user is) email
182 * address. In that case we try to look it up in the addressbook or add
183 * the local domain part
185 if (!$aEntry[SQM_ADDR_HOST
]) {
187 $aAddr = call_user_func_array($cbLookup,array($aEntry[SQM_ADDR_MAILBOX
]));
188 if (isset($aAddr['email'])) {
190 * if the returned email address concerns multiple email
191 * addresses we have to process those as well
193 if (strpos($aAddr['email'],',')) { /* multiple addresses */
194 /* add the parsed addresses to the processed address array */
195 $aProcessedAddress = array_merge($aProcessedAddress,parseAddress($aAddr['email']));
196 /* skip to next address, all processing is done */
198 } else { /* single address */
199 $iPosAt = strpos($aAddr['email'], '@');
200 $aEntry[SQM_ADDR_MAILBOX
] = substr($aAddr['email'], 0, $iPosAt);
201 $aEntry[SQM_ADDR_HOST
] = substr($aAddr['email'], $iPosAt+
1);
202 if (isset($aAddr['name'])) {
203 $aEntry[SQM_ADDR_PERSONAL
] = $aAddr['name'];
205 $aEntry[SQM_ADDR_PERSONAL
] = encodeHeader($sPersonal);
214 if (!$aEntry[SQM_ADDR_MAILBOX
]) {
215 $aEntry[SQM_ADDR_MAILBOX
] = trim($sEmail);
217 if ($sDomain && !$aEntry[SQM_ADDR_HOST
]) {
218 $aEntry[SQM_ADDR_HOST
] = $sDomain;
221 if ($aEntry[SQM_ADDR_MAILBOX
]) {
222 $aProcessedAddress[] = $aEntry;
225 return $aProcessedAddress;
229 * Internal function for creating an address array
231 * @param array $aStack
232 * @param array $aComment
233 * @param string $sEmail
234 * @return array $aAddr array with personal (0), adl(1), mailbox(2) and host(3) info
236 * @author Marc Groot Koerkamp
240 function _createAddressElement(&$aStack,&$aComment,&$sEmail) {
242 while (count($aStack) && !$sEmail) {
243 $sEmail = trim(array_pop($aStack));
246 if (count($aStack)) {
247 $sPersonal = trim(implode('',$aStack));
251 if (!$sPersonal && count($aComment)) {
252 $sComment = trim(implode(' ',$aComment));
253 $sPersonal .= $sComment;
256 // if ($sPersonal && substr($sPersonal,0,2) == '=?') {
257 // $aAddr[SQM_ADDR_PERSONAL] = encodeHeader($sPersonal);
259 $aAddr[SQM_ADDR_PERSONAL
] = $sPersonal;
262 $iPosAt = strpos($sEmail,'@');
264 $aAddr[SQM_ADDR_MAILBOX
] = substr($sEmail, 0, $iPosAt);
265 $aAddr[SQM_ADDR_HOST
] = substr($sEmail, $iPosAt+
1);
267 $aAddr[SQM_ADDR_MAILBOX
] = $sEmail;
268 $aAddr[SQM_ADDR_HOST
] = false;
271 $aStack = $aComment = array();
276 * Tokenizer function for parsing the RFC822 email address string
278 * @param string $address The email address string to parse
279 * @return array $aTokens
281 * @author Marc Groot Koerkamp
285 function _getAddressTokens($address) {
287 $aSpecials = array('(' ,'<' ,',' ,';' ,':');
288 $aReplace = array(' (',' <',' ,',' ;',' :');
289 $address = str_replace($aSpecials,$aReplace,$address);
290 $iCnt = strlen($address);
293 $cChar = $address{$i};
297 $iEnd = strpos($address,'>',$i+
1);
299 $sToken = substr($address,$i);
302 $sToken = substr($address,$i,$iEnd - $i +
1);
305 $sToken = str_replace($aReplace, $aSpecials,$sToken);
306 if ($sToken) $aTokens[] = $sToken;
309 $iEnd = strpos($address,$cChar,$i+
1);
311 // skip escaped quotes
312 $prev_char = $address{$iEnd-1};
313 while ($prev_char === '\\' && substr($address,$iEnd-2,2) !== '\\\\') {
314 $iEnd = strpos($address,$cChar,$iEnd+
1);
316 $prev_char = $address{$iEnd-1};
323 $sToken = substr($address,$i);
326 // also remove the surrounding quotes
327 $sToken = substr($address,$i+
1,$iEnd - $i -1);
330 $sToken = str_replace($aReplace, $aSpecials,$sToken);
331 if ($sToken) $aTokens[] = $sToken;
334 array_pop($aTokens); //remove inserted space
335 $iEnd = strpos($address,')',$i);
337 $sToken = substr($address,$i);
342 while (($iDepth > 0) && (++
$iComment < $iCnt)) {
343 $cCharComment = $address{$iComment};
344 switch($cCharComment) {
359 $sToken = substr($address,$i,$iComment - $i +
1);
362 $sToken = substr($address,$i,$iEnd - $i +
1);
366 // check the next token in case comments appear in the middle of email addresses
367 $prevToken = end($aTokens);
368 if (!in_array($prevToken,$aSpecials,true)) {
369 if ($i+
1<strlen($address) && !in_array($address{$i+
1},$aSpecials,true)) {
370 $iEnd = strpos($address,' ',$i+
1);
372 $sNextToken = trim(substr($address,$i+
1,$iEnd - $i -1));
375 $sNextToken = trim(substr($address,$i+
1));
380 // create token and add it again
381 $sNewToken = $prevToken . $sNextToken;
382 if($sNewToken) $aTokens[] = $sNewToken;
385 $sToken = str_replace($aReplace, $aSpecials,$sToken);
386 if ($sToken) $aTokens[] = $sToken;
395 $iEnd = strpos($address,' ',$i+
1);
397 $sToken = trim(substr($address,$i,$iEnd - $i));
400 $sToken = trim(substr($address,$i));
403 if ($sToken) $aTokens[] = $sToken;