/**
* Rfc822Header.class.php
*
- * Copyright (c) 2003 The SquirrelMail Project Team
+ * Copyright (c) 2003-2004 The SquirrelMail Project Team
* Licensed under the GNU GPL. For full terms see the file COPYING.
*
* This contains functions needed to handle mime messages.
*
* $Id$
+ * @package squirrelmail
*/
-/*
- * rdc822_header class
+/**
* input: header_string or array
+ * @package squirrelmail
*/
class Rfc822Header {
- var $date = '',
+ var $date = -1,
$subject = '',
$from = array(),
$sender = '',
$reply_to = array(),
+ $mail_followup_to = array(),
$to = array(),
$cc = array(),
$bcc = array(),
$priority = 3,
$dnt = '',
$encoding = '',
+ $content_id = '',
+ $content_desc = '',
$mlist = array(),
$more_headers = array(); /* only needed for constructing headers
in smtp.php */
if (is_array($hdr)) {
$hdr = implode('', $hdr);
}
- /* First we unfold the header */
- $hdr = trim(str_replace(array("\r\n\t", "\r\n "),array('', ''), $hdr));
+ /* First we replace \r\n by \n and unfold the header */
+ $hdr = trim(str_replace(array("\r\n", "\n\t", "\n "),array("\n", ' ', ' '), $hdr));
/* Now we can make a new header array with */
/* each element representing a headerline */
- $hdr = explode("\r\n" , $hdr);
+ $hdr = explode("\n" , $hdr);
foreach ($hdr as $line) {
$pos = strpos($line, ':');
if ($pos > 0) {
case 'reply-to':
$this->reply_to = $this->parseAddress($value, true);
break;
+ case 'mail-followup-to':
+ $this->mail_followup_to = $this->parseAddress($value, true);
+ break;
case 'to':
$this->to = $this->parseAddress($value, true);
break;
$value = $this->stripComments($value);
$this->parseDisposition($value);
break;
+ case 'content-transfer-encoding':
+ $this->encoding = $value;
+ break;
+ case 'content-description':
+ $this->content_desc = $value;
+ break;
+ case 'content-id':
+ $value = $this->stripComments($value);
+ $this->content_id = $value;
+ break;
case 'user-agent':
case 'x-mailer':
$this->xmailer = $value;
$this->mlist('post', $value);
break;
case 'list-reply':
- $value = $this->stripComments($value);
+ $value = $this->stripComments($value);
$this->mlist('reply', $value);
break;
case 'list-subscribe':
- $value = $this->stripComments($value);
+ $value = $this->stripComments($value);
$this->mlist('subscribe', $value);
break;
case 'list-unsubscribe':
$i = $iEnd;
}
$sToken = str_replace($aReplace, $aSpecials,$sToken);
- $aTokens[] = $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;
if ($sToken) $aTokens[] = $sToken;
break;
case '(':
+ array_pop($aTokens); //remove inserted space
$iEnd = strpos($address,')',$i);
if (!$iEnd) {
$sToken = substr($address,$i);
$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);
- $aTokens[] = $sToken;
+ if ($sToken) $aTokens[] = $sToken;
break;
case ',':
- case ';':
+ case ':':
case ';':
case ' ':
$aTokens[] = $cChar;
return $aTokens;
}
function createAddressObject(&$aStack,&$aComment,&$sEmail,$sGroup='') {
+ //$aStack=explode(' ',implode('',$aStack));
if (!$sEmail) {
while (count($aStack) && !$sEmail) {
$sEmail = trim(array_pop($aStack));
}
if (count($aStack)) {
$sPersonal = trim(implode('',$aStack));
- } else {
+ } else {
$sPersonal = '';
}
if (!$sPersonal && count($aComment)) {
} else {
$oAddr->personal = $sPersonal;
}
- $oAddr->group = $sGroup;
+ // $oAddr->group = $sGroup;
$iPosAt = strpos($sEmail,'@');
if ($iPosAt) {
$oAddr->mailbox = substr($sEmail, 0, $iPosAt);
$oAddr->mailbox = $sEmail;
$oAddr->host = false;
}
- $oAddr->group = $sGroup;
$sEmail = '';
$aStack = $aComment = array();
return $oAddr;
}
/*
- * parseAddress: recursive function for parsing address strings and store
+ * parseAddress: recursive function for parsing address strings and store
* them in an address stucture object.
* input: $address = string
* $ar = boolean (return array instead of only the
* first element)
* $addr_ar = array with parsed addresses // obsolete
* $group = string // obsolete
- * $host = string (default domainname in case of
+ * $host = string (default domainname in case of
* addresses without a domainname)
* $lookup = callback function (for lookup address
* strings which are probably nicks
- * (without @ ) )
+ * (without @ ) )
* output: array with addressstructure objects or only one
* address_structure object.
* personal name: encoded: =?charset?Q|B?string?=
case '=':
case '"':
case ' ':
- $aStack[] = $sToken;
+ $aStack[] = $sToken;
break;
case '(':
$aComment[] = substr($sToken,1,-1);
break;
case ';':
if ($sGroup) {
+ $aAddress[] = $this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
$oAddr = end($aAddress);
- if ($oAddr && $oAddr->group == $sGroup) {
- $aAddress[] = $this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
- } else {
- /* group is empty */
- $aAddress[] = $this->createAddressObject(array(),array(),$sGroup,'');
+ if(!$oAddr || ((isset($oAddr)) && !$oAddr->mailbox && !$oAddr->personal)) {
+ $sEmail = $sGroup . ':;';
}
+ $aAddress[] = $this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
$sGroup = '';
$aStack = $aComment = array();
break;
case ',':
$aAddress[] = $this->createAddressObject($aStack,$aComment,$sEmail,$sGroup);
break;
- case ':':
- $sGroup = implode(' ',$aStack); break;
+ case ':':
+ $sGroup = trim(implode(' ',$aStack));
+ $sGroup = preg_replace('/\s+/',' ',$sGroup);
$aStack = array();
break;
case '<':
break;
case '>':
/* skip */
- break;
+ break;
default: $aStack[] = $sToken; break;
}
}
if ($sHost && $oAddr->mailbox) {
$oAddr->host = $sHost;
}
+ } else if (!$grouplookup && !$oAddr->host) {
+ if ($sHost && $oAddr->mailbox) {
+ $oAddr->host = $sHost;
+ }
}
}
if (!$aAddrBookAddress && $oAddr->mailbox) {
$aProcessedAddress[] = $oAddr;
} else {
- $aProcessedAddress = array_merge($aProcessedAddress,$aAddrBookAddress);
+ $aProcessedAddress = array_merge($aProcessedAddress,$aAddrBookAddress);
}
}
- if ($ar) {
+ if ($ar) {
return $aProcessedAddress;
} else {
return $aProcessedAddress[0];
}
- }
+ }
function parseContentType($value) {
$pos = strpos($value, ';');
$this->content_type = $content_type;
}
+ /* RFC2184 */
+ function processParameters($aParameters) {
+ $aResults = array();
+ $aCharset = array();
+ // handle multiline parameters
+ foreach($aParameters as $key => $value) {
+ if ($iPos = strpos($key,'*')) {
+ $sKey = substr($key,0,$iPos);
+ if (!isset($aResults[$sKey])) {
+ $aResults[$sKey] = $value;
+ if (substr($key,-1) == '*') { // parameter contains language/charset info
+ $aCharset[] = $sKey;
+ }
+ } else {
+ $aResults[$sKey] .= $value;
+ }
+ } else {
+ $aResults[$key] = $value;
+ }
+ }
+ foreach ($aCharset as $key) {
+ $value = $aResults[$key];
+ // extract the charset & language
+ $charset = substr($value,0,strpos($value,"'"));
+ $value = substr($value,strlen($charset)+1);
+ $language = substr($value,0,strpos($value,"'"));
+ $value = substr($value,strlen($charset)+1);
+ // FIX ME What's the status of charset decode with language information ????
+ $value = charset_decode($charset,$value);
+ $aResults[$key] = $value;
+ }
+ return $aResults;
+ }
+
function parseProperties($value) {
$propArray = explode(';', $value);
$propResultArray = array();
$propResultArray[$key] = $val;
}
}
- return $propResultArray;
+ return $this->processParameters($propResultArray);
}
function parseDisposition($value) {
}
return $arr;
}
-
+
function findAddress($address, $recurs = false) {
$result = false;
if (is_array($address)) {
$result = $i;
}
}
- ++$i;
+ ++$i;
}
} else {
if (!is_array($this->cc)) $this->cc = array();
return true;
} else {
return false;
- }
+ }
}
//exit;
return $result;