Removed variables that were initialized, but never actually used
[squirrelmail.git] / functions / rfc822address.php
1 <?php
2 /**
3 * rfc822address.php
4 *
5 * Copyright (c) 2004 The SquirrelMail Project Team
6 * Licensed under the GNU GPL. For full terms see the file COPYING.
7 *
8 * Contains rfc822 email address function parsing functions.
9 *
10 *
11 * @version $Id$
12 * @package squirrelmail
13 */
14
15 /**
16 * Undocumented defines
17 */
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);
22
23 /**
24 * parseRFC822Address: function for parsing RFC822 email address strings and store
25 * them in an address array
26 *
27 * @param string $address The email address string to parse
28 * @param array $aProps associative array with properties
29 * @public
30 * @author Marc Groot Koerkamp
31 *
32 **/
33
34 function parseRFC822Address($sAddress,$aProps) {
35 // $aPropsDefault = array (
36 // 'domain' => '', //
37 // 'limit' => 0, // limits returned addresses
38 // 'abooklookup' => false); // callback function for addressbook lookup
39 //
40 // $aProps = is_array($aProps) ? array_merge($aPropsDefault,$aProps) : $aPropsDefault;
41
42 // $cbLookup = $aProps['abooklookup'];
43 // $sDomain = $aProps['domain'];
44 $iLimit = $aProps['limit'];
45
46 $aTokens = _getAddressTokens($sAddress);
47 $sEmail = $sGroup = '';
48 $aStack = $aComment = $aAddress = array();
49 foreach ($aTokens as $sToken) {
50 if ($iLimit && $iLimit == count($aAddress)) {
51 return $aAddress;
52 }
53 $cChar = $sToken{0};
54 switch ($cChar)
55 {
56 case '=':
57 case '"':
58 case ' ':
59 $aStack[] = $sToken;
60 break;
61 case '(':
62 $aComment[] = substr($sToken,1,-1);
63 break;
64 case ';':
65 if ($sGroup) {
66 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
67 $oAddr = end($aAddress);
68 if(!$oAddr || ((isset($oAddr)) && !$oAddr->mailbox && !$oAddr->personal)) {
69 $sEmail = $sGroup . ':;';
70 }
71 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
72 $sGroup = '';
73 $aStack = $aComment = array();
74 break;
75 }
76 case ',':
77 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
78 break;
79 case ':':
80 $sGroup = trim(implode(' ',$aStack));
81 $sGroup = preg_replace('/\s+/',' ',$sGroup);
82 $aStack = array();
83 break;
84 case '<':
85 $sEmail = trim(substr($sToken,1,-1));
86 break;
87 case '>':
88 /* skip */
89 break;
90 default: $aStack[] = $sToken; break;
91 }
92 }
93 /* now do the action again for the last address */
94 $aAddress[] = _createAddressElement($aStack,$aComment,$sEmail);
95 return $aAddress;
96 }
97
98 /**
99 * Do the address array to string translation
100 *
101 * @param array $aAddressList list with email address arrays
102 * @param array $aProps associative array with properties
103 * @return string
104 * @public
105 * @see parseRFC822Address
106 * @author Marc Groot Koerkamp
107 *
108 **/
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
120 );
121
122 $aProps = is_array($aProps) ? array_merge($aPropsDefault,$aProps) : $aPropsDefault;
123
124 $aNewAddressList = array();
125 $aEmailUnique = array();
126 foreach ($aAddressList as $aAddr) {
127 if ($aProps['limit'] && count($aNewAddressList) == $aProps['limit']) {
128 break;
129 }
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] : '';
133
134 $sEmail = ($sHost) ? "$sMailbox@$sHost": $sMailbox;
135
136 if (in_array($sEmail,$aProps['exclude'],true)) {
137 continue;
138 }
139
140 if ($aProps['unique']) {
141 if (in_array($sEmail,$aEmailUnique,true)) {
142 continue;
143 } else {
144 $aEmailUnique[] = $sEmail;
145 }
146 }
147
148 $s = '';
149 if ($aProps['best']) {
150 $s .= ($sPersonal) ? $sPersonal : $sEmail;
151 } else {
152 if ($aProps['personal'] && $sPersonal) {
153 if ($aProps['encode']) {
154 $sPersonal = encodeHeader($sPersonal);
155 }
156 $s .= $sPersonal;
157 }
158 if ($aProps['email'] && $sEmail) {
159 $s.= ($s) ? ' <'.$sEmail.'>': '<'.$sEmail.'>';
160 }
161 }
162 if ($s) {
163 $aNewAddressList[] = $s;
164 }
165 }
166 return explode($aProps['seperator'],$aNewAddressList);
167 }
168
169
170 /**
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.
177 *
178 * @param array $aAddressList list with email address arrays
179 * @param array $aProps associative array with properties
180 * @return string
181 * @public
182 * @see parseRFC822Address
183 * $see Rfc822Header
184 * @author Marc Groot Koerkamp
185 *
186 **/
187 function processAddressArray($aAddresses,$aProps) {
188 $aPropsDefault = array (
189 'domain' => '',
190 'limit' => 0,
191 'abooklookup' => false);
192
193 $aProps = is_array($aProps) ? array_merge($aPropsDefault,$aProps) : $aPropsDefault;
194 $aProcessedAddress = array();
195
196 foreach ($aAddresses as $aEntry) {
197 /*
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
202 */
203 if (!$aEntry[SQM_ADDR_HOST]) {
204 if ($cbLookup) {
205 $aAddr = call_user_func_array($cbLookup,array($aEntry[SQM_ADDR_MAILBOX]));
206 if (isset($aAddr['email'])) {
207 /*
208 * if the returned email address concerns multiple email
209 * addresses we have to process those as well
210 */
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 */
215 continue;
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'];
222 } else {
223 $aEntry[SQM_ADDR_PERSONAL] = encodeHeader($sPersonal);
224 }
225 }
226 }
227 }
228 /*
229 * append the domain
230 *
231 */
232 if (!$aEntry[SQM_ADDR_MAILBOX]) {
233 $aEntry[SQM_ADDR_MAILBOX] = trim($sEmail);
234 }
235 if ($sDomain && !$aEntry[SQM_ADDR_HOST]) {
236 $aEntry[SQM_ADDR_HOST] = $sDomain;
237 }
238 }
239 if ($aEntry[SQM_ADDR_MAILBOX]) {
240 $aProcessedAddress[] = $aEntry;
241 }
242 }
243 return $aProcessedAddress;
244 }
245
246 /**
247 * Internal function for creating an address array
248 *
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
253 * @private
254 * @author Marc Groot Koerkamp
255 *
256 **/
257
258 function _createAddressElement(&$aStack,&$aComment,&$sEmail) {
259 if (!$sEmail) {
260 while (count($aStack) && !$sEmail) {
261 $sEmail = trim(array_pop($aStack));
262 }
263 }
264 if (count($aStack)) {
265 $sPersonal = trim(implode('',$aStack));
266 } else {
267 $sPersonal = '';
268 }
269 if (!$sPersonal && count($aComment)) {
270 $sComment = trim(implode(' ',$aComment));
271 $sPersonal .= $sComment;
272 }
273 $aAddr = array();
274 // if ($sPersonal && substr($sPersonal,0,2) == '=?') {
275 // $aAddr[SQM_ADDR_PERSONAL] = encodeHeader($sPersonal);
276 // } else {
277 $aAddr[SQM_ADDR_PERSONAL] = $sPersonal;
278 // }
279
280 $iPosAt = strpos($sEmail,'@');
281 if ($iPosAt) {
282 $aAddr[SQM_ADDR_MAILBOX] = substr($sEmail, 0, $iPosAt);
283 $aAddr[SQM_ADDR_HOST] = substr($sEmail, $iPosAt+1);
284 } else {
285 $aAddr[SQM_ADDR_MAILBOX] = $sEmail;
286 $aAddr[SQM_ADDR_HOST] = false;
287 }
288 $sEmail = '';
289 $aStack = $aComment = array();
290 return $aAddr;
291 }
292
293 /**
294 * Tokenizer function for parsing the RFC822 email address string
295 *
296 * @param string $address The email address string to parse
297 * @return array $aTokens
298 * @private
299 * @author Marc Groot Koerkamp
300 *
301 **/
302
303 function _getAddressTokens($address) {
304 $aTokens = array();
305 $aSpecials = array('(' ,'<' ,',' ,';' ,':');
306 $aReplace = array(' (',' <',' ,',' ;',' :');
307 $address = str_replace($aSpecials,$aReplace,$address);
308 $iCnt = strlen($address);
309 $i = 0;
310 while ($i < $iCnt) {
311 $cChar = $address{$i};
312 switch($cChar)
313 {
314 case '<':
315 $iEnd = strpos($address,'>',$i+1);
316 if (!$iEnd) {
317 $sToken = substr($address,$i);
318 $i = $iCnt;
319 } else {
320 $sToken = substr($address,$i,$iEnd - $i +1);
321 $i = $iEnd;
322 }
323 $sToken = str_replace($aReplace, $aSpecials,$sToken);
324 if ($sToken) $aTokens[] = $sToken;
325 break;
326 case '"':
327 $iEnd = strpos($address,$cChar,$i+1);
328 if ($iEnd) {
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);
333 if ($iEnd) {
334 $prev_char = $address{$iEnd-1};
335 } else {
336 $prev_char = false;
337 }
338 }
339 }
340 if (!$iEnd) {
341 $sToken = substr($address,$i);
342 $i = $iCnt;
343 } else {
344 // also remove the surrounding quotes
345 $sToken = substr($address,$i+1,$iEnd - $i -1);
346 $i = $iEnd;
347 }
348 $sToken = str_replace($aReplace, $aSpecials,$sToken);
349 if ($sToken) $aTokens[] = $sToken;
350 break;
351 case '(':
352 array_pop($aTokens); //remove inserted space
353 $iEnd = strpos($address,')',$i);
354 if (!$iEnd) {
355 $sToken = substr($address,$i);
356 $i = $iCnt;
357 } else {
358 $iDepth = 1;
359 $iComment = $i;
360 while (($iDepth > 0) && (++$iComment < $iCnt)) {
361 $cCharComment = $address{$iComment};
362 switch($cCharComment) {
363 case '\\':
364 ++$iComment;
365 break;
366 case '(':
367 ++$iDepth;
368 break;
369 case ')':
370 --$iDepth;
371 break;
372 default:
373 break;
374 }
375 }
376 if ($iDepth == 0) {
377 $sToken = substr($address,$i,$iComment - $i +1);
378 $i = $iComment;
379 } else {
380 $sToken = substr($address,$i,$iEnd - $i + 1);
381 $i = $iEnd;
382 }
383 }
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);
389 if ($iEnd) {
390 $sNextToken = trim(substr($address,$i+1,$iEnd - $i -1));
391 $i = $iEnd-1;
392 } else {
393 $sNextToken = trim(substr($address,$i+1));
394 $i = $iCnt;
395 }
396 // remove the token
397 array_pop($aTokens);
398 // create token and add it again
399 $sNewToken = $prevToken . $sNextToken;
400 if($sNewToken) $aTokens[] = $sNewToken;
401 }
402 }
403 $sToken = str_replace($aReplace, $aSpecials,$sToken);
404 if ($sToken) $aTokens[] = $sToken;
405 break;
406 case ',':
407 case ':':
408 case ';':
409 case ' ':
410 $aTokens[] = $cChar;
411 break;
412 default:
413 $iEnd = strpos($address,' ',$i+1);
414 if ($iEnd) {
415 $sToken = trim(substr($address,$i,$iEnd - $i));
416 $i = $iEnd-1;
417 } else {
418 $sToken = trim(substr($address,$i));
419 $i = $iCnt;
420 }
421 if ($sToken) $aTokens[] = $sToken;
422 }
423 ++$i;
424 }
425 return $aTokens;
426 }
427 ?>