Code cleanup, intented to remove the parseAddress routines from
authorstekkel <stekkel@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Wed, 26 May 2004 00:35:03 +0000 (00:35 +0000)
committerstekkel <stekkel@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Wed, 26 May 2004 00:35:03 +0000 (00:35 +0000)
class/Rfc822Header.class.php and from imap_general.php

git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@7551 7612ce4b-ef26-0410-bec9-ea0150e637f0

functions/rfc822address.php [new file with mode: 0644]

diff --git a/functions/rfc822address.php b/functions/rfc822address.php
new file mode 100644 (file)
index 0000000..c57af13
--- /dev/null
@@ -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;
+}
+?>
\ No newline at end of file