implementing fallback for wrong headerdata encoding; try to detect encoding from headerdata to improve message header data display, due to wrong encoding

This commit is contained in:
Klaus Leithoff 2010-11-05 09:06:00 +00:00
parent 42a814a7e5
commit 0ba6657812
4 changed files with 226 additions and 82 deletions

View File

@ -65,60 +65,21 @@
*/
function addAttachment($_formData)
{
#echo "addattachment<br>";
#_debug_array($_formData);
$attachfailed = false;
// to gard against exploits the file must be either uploaded or be in the temp_dir
if ($_formData['size'] != 0 && (is_uploaded_file($_formData['file']) ||
realpath(dirname($_formData['file'])) == realpath($GLOBALS['egw_info']['server']['temp_dir'])) ||
parse_url($_formData['file'],PHP_URL_SCHEME) == 'vfs')
// check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.)
try
{
// ensure existance of eGW temp dir
// note: this is different from apache temp dir,
// and different from any other temp file location set in php.ini
if (!file_exists($GLOBALS['egw_info']['server']['temp_dir']))
{
@mkdir($GLOBALS['egw_info']['server']['temp_dir'],0700);
}
$tmpFileName = bofelamimail::checkFileBasics($_formData,$this->composeID,false);
}
catch (egw_exception_wrong_userinput $e)
{
$attachfailed = true;
$alert_msg = $e->getMessage();
}
// if we were NOT able to create this temp directory, then make an ERROR report
if (!file_exists($GLOBALS['egw_info']['server']['temp_dir']))
{
$alert_msg .= 'Error:'.'<br>'
.'Server is unable to access phpgw tmp directory'.'<br>'
.$GLOBALS['egw_info']['server']['temp_dir'].'<br>'
.'Please check your configuration'.'<br>'
.'<br>';
}
// sometimes PHP is very clue-less about MIME types, and gives NO file_type
// rfc default for unknown MIME type is:
$mime_type_default = 'application/octet-stream';
// so if PHP did not pass any file_type info, then substitute the rfc default value
if (trim($_formData['type']) == '')
{
$_formData['type'] = $mime_type_default;
}
$tmpFileName = $GLOBALS['egw_info']['server']['temp_dir'].
SEP.
md5(time().$GLOBALS['egw_info']['user']['account_id'].$this->composeID.basename($_formData['file']));
if (parse_url($_formData['file'],PHP_URL_SCHEME) == 'vfs')
{
$tmpFileName = $_formData['file']; // no need to store it somewhere
}
elseif (is_uploaded_file($_formData['file']))
{
//error_log(__METHOD__." Uploaded File:".$_formData['file']." with filesize:".filesize($_formData['file']));
move_uploaded_file($_formData['file'],$tmpFileName); // requirement for safe_mode!
//error_log(__METHOD__." copy to :".$tmpFileName." with filesize:".filesize($tmpFileName));
}
else
{
rename($_formData['file'],$tmpFileName);
}
//$attachmentID = $this->getRandomString();
//error_log(__METHOD__." add Attachment with ID (random String):".$attachmentID);
if ($attachfailed === false)
{
$buffer = array(
'name' => $_formData['name'],
'type' => $_formData['type'],
@ -131,6 +92,10 @@
$this->sessionData['attachments'][$attachmentID] = $buffer;
unset($buffer);
}
else
{
error_log(__METHOD__.__LINE__.array2string($alert_msg));
}
$this->saveSessionData();
#print"<pre>";
@ -582,24 +547,33 @@
$bodyParts = $bofelamimail->getMessageBody($_uid, $this->preferencesArray['always_display'], $_partID);
//_debug_array($bodyParts);
$fromAddress = ($headers['FROM'][0]['PERSONAL_NAME'] != 'NIL') ? $headers['FROM'][0]['RFC822_EMAIL'] : $headers['FROM'][0]['EMAIL'];
$fromAddress = bofelamimail::htmlspecialchars($bofelamimail->decode_header(($headers['FROM'][0]['PERSONAL_NAME'] != 'NIL') ? $headers['FROM'][0]['RFC822_EMAIL'] : $headers['FROM'][0]['EMAIL']));
$toAddressA = array();
$toAddress = '';
foreach ($headers['TO'] as $mailheader) {
$toAddressA[] = ($mailheader['PERSONAL_NAME'] != 'NIL') ? $mailheader['RFC822_EMAIL'] : $mailheader['EMAIL'];
}
if (count($toAddressA)>0) $toAddress = @htmlspecialchars(lang("to").": ".$bofelamimail->decode_header(implode(', ', $toAddressA)),ENT_QUOTES).($bodyParts['0']['mimeType'] == 'text/html'?"\r\n<br>":"\r\n");
if (count($toAddressA)>0)
{
$toAddress = bofelamimail::htmlspecialchars($bofelamimail->decode_header(implode(', ', $toAddressA)));
$toAddress = @htmlspecialchars(lang("to")).": ".$toAddress.($bodyParts['0']['mimeType'] == 'text/html'?"\r\n<br>":"\r\n");;
}
$ccAddressA = array();
$ccAddress = '';
foreach ($headers['CC'] as $mailheader) {
$ccAddressA[] = ($mailheader['PERSONAL_NAME'] != 'NIL') ? $mailheader['RFC822_EMAIL'] : $mailheader['EMAIL'];
}
if (count($ccAddressA)>0) $ccAddress = @htmlspecialchars(lang("cc").": ".$bofelamimail->decode_header(implode(', ', $ccAddressA)),ENT_QUOTES).($bodyParts['0']['mimeType'] == 'text/html'?"\r\n<br>":"\r\n");
if (count($ccAddressA)>0)
{
$ccAddress = bofelamimail::htmlspecialchars($bofelamimail->decode_header(implode(', ', $ccAddressA)));
$ccAddress = @htmlspecialchars(lang("cc")).": ".$ccAddressA.($bodyParts['0']['mimeType'] == 'text/html'?"\r\n<br>":"\r\n");
}
if($bodyParts['0']['mimeType'] == 'text/html') {
$this->sessionData['body'] = "<br>&nbsp;\r\n<p>".'----------------'.lang("original message").'-----------------'."\r\n".'<br>'.
@htmlspecialchars(lang("from").": ".$bofelamimail->decode_header($fromAddress),ENT_QUOTES)."\r\n<br>".
@htmlspecialchars(lang("from")).": ".$fromAddress."\r\n<br>".
$toAddress.$ccAddress.
@htmlspecialchars(lang("date").": ".$headers['DATE'],ENT_QUOTES)."\r\n<br>".
@htmlspecialchars(lang("date").": ".$headers['DATE'],ENT_QUOTES | ENT_IGNORE,bofelamimail::$displayCharset, false)."\r\n<br>".
'----------------------------------------------------------'."\r\n</p>";
$this->sessionData['mimeType'] = 'html';
$this->sessionData['body'] .= '<blockquote type="cite">';
@ -622,9 +596,9 @@
} else {
#$this->sessionData['body'] = @htmlspecialchars(lang("on")." ".$headers['DATE']." ".$bofelamimail->decode_header($fromAddress), ENT_QUOTES) . " ".lang("wrote").":\r\n";
$this->sessionData['body'] = " \r\n \r\n".'----------------'.lang("original message").'-----------------'."\r\n".
@htmlspecialchars(lang("from").": ".$bofelamimail->decode_header($fromAddress),ENT_QUOTES)."\r\n".
@htmlspecialchars(lang("from")).": ".$fromAddress."\r\n".
$toAddress.$ccAddress.
@htmlspecialchars(lang("date").": ".$headers['DATE'], ENT_QUOTES)."\r\n".
@htmlspecialchars(lang("date").": ".$headers['DATE'], ENT_QUOTES | ENT_IGNORE,bofelamimail::$displayCharset, false)."\r\n".
'-------------------------------------------------'."\r\n \r\n ";
$this->sessionData['mimeType'] = 'plain';

View File

@ -3125,6 +3125,38 @@
return preg_match("$needle",$string);
}
/**
* htmlspecialchars
* helperfunction to cope with wrong encoding in strings
* @param string $_string input to be converted
* @return string
*/
static function htmlspecialchars($_string, $_charset=false)
{
//setting the charset (if not given)
if ($_charset===false) $_charset = bofelamimail::$displayCharset;
$_stringORG = $_string;
$_string = @htmlspecialchars($_string,ENT_QUOTES,$_charset, false);
if (empty($_string) && !empty($_stringORG)) $_string = @htmlspecialchars($GLOBALS['egw']->translation->convert($_stringORG,bofelamimail::detect_encoding($_stringORG),$_charset),ENT_QUOTES | ENT_IGNORE,$_charset, false);
return $_string;
}
/**
* htmlentities
* helperfunction to cope with wrong encoding in strings
* @param string $_string input to be converted
* @return string
*/
static function htmlentities($_string, $_charset=false)
{
//setting the charset (if not given)
if ($_charset===false) $_charset = bofelamimail::$displayCharset;
$_stringORG = $_string;
$_string = @htmlentities($_string,ENT_QUOTES,$_charset, false);
if (empty($_string) && !empty($_stringORG)) $_string = @htmlentities($GLOBALS['egw']->translation->convert($_stringORG,bofelamimail::detect_encoding($_stringORG),$_charset),ENT_QUOTES | ENT_IGNORE,$_charset, false);
return $_string;
}
/**
* detect_encoding - try to detect the encoding
* only to be used if the string in question has no structure that determines his encoding
@ -3174,6 +3206,125 @@
return $date2return;
}
/**
* checkFileBasics
* check if formdata meets basic restrictions (in tmp dir, or vfs, mimetype, etc.)
*
* @param array $_formData passed by reference Array with information of name, type, file and size, mimetype may be adapted
* @param string $IDtoAddToFileName id to enrich the returned tmpfilename
* @param string $reqMimeType /(default message/rfc822, if set to false, mimetype check will not be performed
* @return mixed $fullPathtoFile or exception
*/
static function checkFileBasics(&$_formData, $IDtoAddToFileName='', $reqMimeType='message/rfc822')
{
//error_log(__METHOD__.__FILE__.array2string($_formData).' Id:'.$IDtoAddToFileName.' ReqMimeType:'.$reqMimeType);
$importfailed = $tmpFileName = false;
if ($_formData['size'] != 0 && (is_uploaded_file($_formData['file']) ||
realpath(dirname($_formData['file'])) == realpath($GLOBALS['egw_info']['server']['temp_dir']) ||
parse_url($_formData['file'],PHP_URL_SCHEME) == 'vfs'))
{
// ensure existance of eGW temp dir
// note: this is different from apache temp dir,
// and different from any other temp file location set in php.ini
if (!file_exists($GLOBALS['egw_info']['server']['temp_dir']))
{
@mkdir($GLOBALS['egw_info']['server']['temp_dir'],0700);
}
// if we were NOT able to create this temp directory, then make an ERROR report
if (!file_exists($GLOBALS['egw_info']['server']['temp_dir']))
{
$alert_msg .= 'Error:'.'<br>'
.'Server is unable to access phpgw tmp directory'.'<br>'
.$GLOBALS['egw_info']['server']['temp_dir'].'<br>'
.'Please check your configuration'.'<br>'
.'<br>';
}
// sometimes PHP is very clue-less about MIME types, and gives NO file_type
// rfc default for unknown MIME type is:
if ($reqMimeType == 'message/rfc822')
{
$mime_type_default = 'message/rfc';
}
else
{
$mime_type_default = $reqMimeType;
}
if (trim($_formData['type']) == '')
{
$_formData['type'] = 'application/octet-stream';
}
// if reqMimeType is set to false do not test for that
if ($reqMimeType)
{
// so if PHP did not pass any file_type info, then substitute the rfc default value
if (substr(strtolower(trim($_formData['type'])),0,strlen($mime_type_default)) != $mime_type_default)
{
// maybe its application/octet-stream -> this may mean that we could not determine the type
// so we check for the suffix too
$buff = explode('.',$_formData['name']);
$suffix = '';
if (is_array($buff)) $suffix = array_pop($buff); // take the last extension to check with ext2mime
if (!(strtolower(trim($_formData['type'])) == "application/octet-stream" && mime_magic::ext2mime($suffix)== $reqMimeType))
{
//error_log("Message rejected, no message/rfc. Is:".$_formData['type']);
$importfailed = true;
$alert_msg .= lang("File rejected, no %2. Is:%1",$_formData['type'],$reqMimeType);
}
if ((strtolower(trim($_formData['type'])) != $reqMimeType && mime_magic::ext2mime($suffix)== $reqMimeType))
{
$_formData['type'] = mime_magic::ext2mime($suffix);
}
}
}
$tmpFileName = $GLOBALS['egw_info']['server']['temp_dir'].
SEP.
$GLOBALS['egw_info']['user']['account_id'].
trim($IDtoAddToFileName).basename($_formData['file']);
if (parse_url($_formData['file'],PHP_URL_SCHEME) == 'vfs')
{
$tmpFileName = $_formData['file']; // no need to store it somewhere
}
elseif (is_uploaded_file($_formData['file']))
{
move_uploaded_file($_formData['file'],$tmpFileName); // requirement for safe_mode!
}
else
{
rename($_formData['file'],$tmpFileName);
}
} else {
//error_log("Import of message ".$_formData['file']." failes to meet basic restrictions");
$importfailed = true;
$alert_msg .= lang("Processing of file %1 failed. Failed to meet basic restrictions.",$_formData['name']);
}
if ($importfailed == true)
{
throw new egw_exception_wrong_userinput($alert_msg);
}
else
{
if (parse_url($tmpFileName,PHP_URL_SCHEME) == 'vfs')
{
egw_vfs::load_wrapper('vfs');
}
return $tmpFileName;
}
}
/**
* getRandomString - function to be used to fetch a random string and md5 encode that one
* @param none
* @return string - a random number which is md5 encoded
*/
static function getRandomString() {
mt_srand((float) microtime() * 1000000);
return md5(mt_rand (100000, 999999));
}
/**
* functions to allow access to mails through other apps to fetch content
* used in infolog, tracker

View File

@ -173,7 +173,7 @@
$sessionData = $this->bocompose->getSessionData();
if (is_array($_REQUEST['preset']))
{
#_debug_array($_REQUEST);
//_debug_array($_REQUEST);
if ($_REQUEST['preset']['mailto']) {
// handle mailto strings such as
// mailto:larry,dan?cc=mike&bcc=sue&subject=test&body=type+your&body=message+here
@ -241,6 +241,7 @@
{
$sessionData['to'] = base64_decode($_REQUEST['send_to']);
}
//is the MimeType set/requested
if (!empty($_REQUEST['mimeType']))
{
@ -369,7 +370,8 @@
foreach((array)$sessionData[$destination] as $key => $value) {
$selectDestination = html::select('destination[]', $destination, $this->destinations, false, "style='width: 100%;' onchange='fm_compose_changeInputType(this)'");
$this->t->set_var('select_destination', $selectDestination);
$this->t->set_var('address', @htmlentities($value, ENT_QUOTES, $this->displayCharset));
$address = bofelamimail::htmlentities($value, $this->displayCharset);
$this->t->set_var('address', $address);
$this->t->parse('destinationRows','destination_row',True);
$destinationRows++;
}
@ -388,7 +390,10 @@
$this->t->set_var('address', '');
$this->t->parse('destinationRows','destination_row',True);
$this->t->set_var("subject",@htmlentities($sessionData['subject'],ENT_QUOTES,$this->displayCharset));
// handle subject
$subject = bofelamimail::htmlentities($sessionData['subject'],$this->displayCharset);
$this->t->set_var("subject",$subject);
if ($GLOBALS['egw_info']['user']['apps']['addressbook']) {
$this->t->set_var('addressbookButton','<button class="menuButton" type="button" onclick="addybook();" title="'.lang('addressbook').'">
<img src="'.$GLOBALS['egw']->common->image('phpgwapi/templates/phpgw_website','users').'">

View File

@ -273,7 +273,7 @@
#_debug_array($rawheaders);exit;
$attachments = $this->bofelamimail->getMessageAttachments($this->uid, $partID);
#_debug_array($attachments); exit;
$envelope = $this->bofelamimail->getMessageEnvelope($this->uid, $partIDi,true);
$envelope = $this->bofelamimail->getMessageEnvelope($this->uid, $partID,true);
#_debug_array($envelope); exit;
// if not using iFrames, we need to retrieve the messageBody here
// by now this is a fixed value and controls the use/loading of the template and how the vars are set.
@ -454,10 +454,10 @@
$this->t->set_var("date_received",
@htmlspecialchars(bofelamimail::_strtotime($headers['DATE'],$GLOBALS['egw_info']['user']['preferences']['common']['dateformat']).' - '.bofelamimail::_strtotime($headers['DATE'],'H:i:s'),
ENT_QUOTES,$this->displayCharset));
$this->t->set_var("subject_data",
@htmlspecialchars($this->bofelamimail->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false),
ENT_QUOTES,$this->displayCharset));
//echo 'Envelope:'.preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']).'#0<br>';
$subject = bofelamimail::htmlspecialchars($this->bofelamimail->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false),
$this->displayCharset);
$this->t->set_var("subject_data",$subject);
$this->t->parse("header","message_header",True);
@ -720,7 +720,7 @@
// (regis) seems to be necessary to reopen...
$this->bofelamimail->reopen($this->mailbox);
$headers = $this->bofelamimail->getMessageHeader($this->uid, $partID);
$envelope = $this->bofelamimail->getMessageEnvelope($this->uid, $partID);
$envelope = $this->bofelamimail->getMessageEnvelope($this->uid, $partID,true);
if (PEAR::isError($headers)) {
print lang("ERROR: Message could not be displayed.")."<br>";
print "In Mailbox: $this->mailbox, with ID: $this->uid, and PartID: $partID<br>";
@ -741,9 +741,9 @@
$this->t->egroupware_hack = False;
$this->translate();
$this->t->set_var("subject_data",
@htmlspecialchars($this->bofelamimail->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT'])),
ENT_QUOTES,$this->displayCharset));
$subject = bofelamimail::htmlspecialchars($this->bofelamimail->decode_subject(preg_replace($nonDisplayAbleCharacters,'',$envelope['SUBJECT']),false),
$this->displayCharset);
$this->t->set_var("subject_data",$subject);
// attachments
if(is_array($attachments) && count($attachments) > 0) {
@ -953,7 +953,7 @@
}
static function emailAddressToHTML($_emailAddress, $_organisation='', $allwaysShowMailAddress=false, $showAddToAdrdessbookLink=true, $decode=true) {
#_debug_array($_emailAddress);
_debug_array($_emailAddress);
// create some nice formated HTML for senderaddress
#if($_emailAddress['EMAIL'] == 'undisclosed-recipients: ;')
# return $_emailAddress['EMAIL'];
@ -975,18 +975,24 @@
continue;
}
if($addressData['PERSONAL_NAME'] != 'NIL') {
$newSenderAddress = $addressData['RFC822_EMAIL'] != 'NIL' ? $addressData['RFC822_EMAIL'] : $addressData['EMAIL'];
if ($decode) $newSenderAddress = bofelamimail::decode_header($newSenderAddress);
$decodedPersonalName = ($decode ? bofelamimail::decode_header($addressData['PERSONAL_NAME']):$addressData['PERSONAL_NAME']);
if ($decode) $addressData['EMAIL'] = bofelamimail::decode_header($addressData['EMAIL']);
$newSenderAddressORG = $newSenderAddress = $addressData['RFC822_EMAIL'] != 'NIL' ? $addressData['RFC822_EMAIL'] : $addressData['EMAIL'];
$decodedPersonalNameORG = $decodedPersonalName = $addressData['PERSONAL_NAME'];
if ($decode)
{
$newSenderAddress = bofelamimail::decode_header($newSenderAddressORG);
$decodedPersonalName = bofelamimail::decode_header($decodedPersonalName);
$addressData['EMAIL'] = bofelamimail::decode_header($addressData['EMAIL']);
}
$realName = $decodedPersonalName;
// add mailaddress
if ($allwaysShowMailAddress) {
$realName .= ' <'.$addressData['EMAIL'].'>';
$decodedPersonalNameORG .= ' <'.$addressData['EMAIL'].'>';
}
// add organization
if(!empty($_organisation)) {
$realName .= ' ('. $_organisation . ')';
$decodedPersonalNameORG .= ' ('. $_organisation . ')';
}
$linkData = array (
@ -994,10 +1000,14 @@
'send_to' => base64_encode($newSenderAddress)
);
$link = $GLOBALS['egw']->link('/index.php',$linkData);
$newSenderAddress = bofelamimail::htmlentities($newSenderAddress);
$realName = bofelamimail::htmlentities($realName);
$senderAddress .= sprintf('<a href="%s" title="%s">%s</a>',
$link,
@htmlentities($newSenderAddress,ENT_QUOTES,bofelamimail::$displayCharset),
@htmlentities($realName, ENT_QUOTES, bofelamimail::$displayCharset));
$newSenderAddress,
$realName);
$linkData = array (
'menuaction' => 'addressbook.addressbook_ui.edit',
@ -1005,6 +1015,8 @@
'presets[org_name]' => $_organisation,
'referer' => $_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING']
);
$decodedPersonalName = $realName;
if (!empty($decodedPersonalName)) {
if($spacePos = strrpos($decodedPersonalName, ' ')) {
$linkData['presets[n_family]'] = substr($decodedPersonalName, $spacePos+1);
@ -1014,6 +1026,7 @@
}
$linkData['presets[n_fn]'] = $decodedPersonalName;
}
if ($showAddToAdrdessbookLink && $GLOBALS['egw_info']['user']['apps']['addressbook']) {
$urlAddToAddressbook = $GLOBALS['egw']->link('/index.php',$linkData);
$onClick = "window.open(this,this.target,'dependent=yes,width=850,height=440,location=no,menubar=no,toolbar=no,scrollbars=yes,status=yes'); return false;";
@ -1029,20 +1042,22 @@
lang('add to addressbook'));
}
} else {
if ($decode) $addressData['EMAIL'] = bofelamimail::decode_header($addressData['EMAIL']);
$addrEMailORG = $addrEMail = $addressData['EMAIL'];
if ($decode) $addrEMail = bofelamimail::decode_header($addrEMail);
$linkData = array (
'menuaction' => 'felamimail.uicompose.compose',
'send_to' => base64_encode($addressData['EMAIL'])
);
$link = $GLOBALS['egw']->link('/index.php',$linkData);
$senderEMail = bofelamimail::htmlentities($addrEMail);
$senderAddress .= sprintf('<a href="%s">%s</a>',
$link,@htmlentities($addressData['EMAIL'], ENT_QUOTES, bofelamimail::$displayCharset));
$link,$senderEMail);
//TODO: This uses old addressbook code, which should be removed in Version 1.4
//Please use addressbook.addressbook_ui.edit with proper paramenters
$linkData = array
(
'menuaction' => 'addressbook.addressbook_ui.edit',
'presets[email]' => $addressData['EMAIL'],
'presets[email]' => $senderEMail, //$addressData['EMAIL'],
'presets[org_name]' => $_organisation,
'referer' => $_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING']
);
@ -1443,9 +1458,8 @@
}
$this->t->set_var("date_data",
@htmlspecialchars(bofelamimail::_strtotime($headers['DATE'],$GLOBALS['egw_info']['user']['preferences']['common']['dateformat']).' - '.bofelamimail::_strtotime($headers['DATE'],'H:i:s'), ENT_QUOTES,$this->displayCharset));
// link to go back to the message view. the link differs if the print was called from a normal viewing window, or from compose
$subject = @htmlspecialchars($this->bofelamimail->decode_subject(preg_replace($nonDisplayAbleCharacters, '', $envelope['SUBJECT']),false), ENT_QUOTES, $this->displayCharset);
$subject = bofelamimail::htmlspecialchars($this->bofelamimail->decode_subject(preg_replace($nonDisplayAbleCharacters, '', $envelope['SUBJECT']),false), $this->displayCharset);
$this->t->set_var("subject_data", $subject);
$this->t->set_var("full_subject_data", $subject);
$linkData = array (