From 6b53486b9e054b9bc853f314ed946c9726a8423c Mon Sep 17 00:00:00 2001 From: Peter Rotich Date: Mon, 4 Mar 2013 23:09:14 -0500 Subject: [PATCH] Balance html tags & neutralize unsafe tags on emailed html'ed emails. --- include/class.mailfetch.php | 59 ++++++++++++++++++------------------- include/class.mailparse.php | 52 ++++++++++++++++---------------- 2 files changed, 55 insertions(+), 56 deletions(-) diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index c7612ccb..1bcf6d69 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -29,20 +29,20 @@ class MailFetcher { var $charset = 'UTF-8'; var $encodings =array('UTF-8','WINDOWS-1251', 'ISO-8859-5', 'ISO-8859-1','KOI8-R'); - + function MailFetcher($email, $charset='UTF-8') { - + if($email && is_numeric($email)) //email_id $email=Email::lookup($email); if(is_object($email)) $this->ht = $email->getMailAccountInfo(); elseif(is_array($email) && $email['host']) //hashtable of mail account info - $this->ht = $email; + $this->ht = $email; else $this->ht = null; - + $this->charset = $charset; if($this->ht) { @@ -59,12 +59,12 @@ function MailFetcher($email, $charset='UTF-8') { $this->srvstr=sprintf('{%s:%d/%s', $this->getHost(), $this->getPort(), $this->getProtocol()); if(!strcasecmp($this->getEncryption(), 'SSL')) $this->srvstr.='/ssl'; - + $this->srvstr.='/novalidate-cert}'; } - //Set timeouts + //Set timeouts if(function_exists('imap_timeout')) imap_timeout(1,20); } @@ -92,7 +92,7 @@ function getEncryption() { function getUsername() { return $this->ht['username']; } - + function getPassword() { return $this->ht['password']; } @@ -112,7 +112,7 @@ function getArchiveFolder() { } /* Core */ - + function connect() { return ($this->mbox && $this->ping())?$this->mbox:$this->open(); } @@ -123,7 +123,7 @@ function ping() { /* Default folder is inbox - TODO: provide user an option to fetch from diff folder/label */ function open($box='INBOX') { - + if($this->mbox) $this->close(); @@ -157,7 +157,7 @@ function getMailboxes() { function createMailbox($folder) { if(!$folder) return false; - + return imap_createmailbox($this->mbox, imap_utf7_encode($this->srvstr.trim($folder))); } @@ -187,23 +187,23 @@ function decode($text, $encoding) { $text=imap_qprint($text); break; } - + return $text; } //Convert text to desired encoding..defaults to utf8 - function mime_encode($text, $charset=null, $encoding='utf-8') { //Thank in part to afterburner + function mime_encode($text, $charset=null, $encoding='utf-8') { //Thank in part to afterburner return Format::encode($text, $charset, $encoding); } - + //Generic decoder - resulting text is utf8 encoded -> mirrors imap_utf8 function mime_decode($text, $encoding='utf-8') { - + $str = ''; $parts = imap_mime_header_decode($text); foreach ($parts as $part) $str.= $this->mime_encode($part->text, $part->charset, $encoding); - + return $str?$str:imap_utf8($text); } @@ -215,12 +215,12 @@ function getMimeType($struct) { $mimeType = array('TEXT', 'MULTIPART', 'MESSAGE', 'APPLICATION', 'AUDIO', 'IMAGE', 'VIDEO', 'OTHER'); if(!$struct || !$struct->subtype) return 'TEXT/PLAIN'; - + return $mimeType[(int) $struct->type].'/'.$struct->subtype; } function getHeaderInfo($mid) { - + if(!($headerinfo=imap_headerinfo($this->mbox, $mid)) || !$headerinfo->from) return null; @@ -237,7 +237,7 @@ function getHeaderInfo($mid) { //search for specific mime type parts....encoding is the desired encoding. function getPart($mid, $mimeType, $encoding=false, $struct=null, $partNumber=false) { - + if(!$struct && $mid) $struct=@imap_fetchstructure($this->mbox, $mid); @@ -264,7 +264,7 @@ function getPart($mid, $mimeType, $encoding=false, $struct=null, $partNumber=fal $text=''; if($struct && $struct->parts) { while(list($i, $substruct) = each($struct->parts)) { - if($partNumber) + if($partNumber) $prefix = $partNumber . '.'; if(($result=$this->getPart($mid, $mimeType, $encoding, $substruct, $prefix.($i+1)))) $text.=$result; @@ -332,29 +332,28 @@ function getHeader($mid) { return imap_fetchheader($this->mbox, $mid,FT_PREFETCHTEXT); } - + function getPriority($mid) { return Mail_Parse::parsePriority($this->getHeader($mid)); } function getBody($mid) { - + $body =''; if(!($body = $this->getPart($mid,'TEXT/PLAIN', $this->charset))) { if(($body = $this->getPart($mid,'TEXT/HTML', $this->charset))) { //Convert tags of interest before we striptags $body=str_replace("
", "\n", $body); $body=str_replace(array("
", "
", "
", "
"), "\n", $body); - $body=Format::html($body); //Balance html tags before stripping. - $body=Format::striptags($body); //Strip tags?? + $body=Format::safe_html($body); //Balance html tags & neutralize unsafe tags. } } return $body; } - //email to ticket - function createTicket($mid) { + //email to ticket + function createTicket($mid) { global $ost; if(!($mailinfo = $this->getHeaderInfo($mid))) @@ -387,7 +386,7 @@ function createTicket($mid) { if($ost->getConfig()->useEmailPriority()) $vars['priorityId']=$this->getPriority($mid); - + $ticket=null; $newticket=true; //Check the subject line for possible ID. @@ -397,7 +396,7 @@ function createTicket($mid) { if(!($ticket=Ticket::lookupByExtId($tid, $vars['email']))) $ticket=null; } - + $errors=array(); if($ticket) { if(!($message=$ticket->postMessage($vars, 'Email'))) @@ -494,11 +493,11 @@ function log($error) { /* MailFetcher::run() - Static function called to initiate email polling + Static function called to initiate email polling */ function run() { global $ost; - + if(!$ost->getConfig()->isEmailPollingEnabled()) return; @@ -510,7 +509,7 @@ function run() { return; } - //Hardcoded error control... + //Hardcoded error control... $MAXERRORS = 5; //Max errors before we start delayed fetch attempts $TIMEOUT = 10; //Timeout in minutes after max errors is reached. diff --git a/include/class.mailparse.php b/include/class.mailparse.php index 342fc529..23f5d64d 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -19,16 +19,16 @@ class.mailparse.php require_once(PEAR_DIR.'Mail/RFC822.php'); class Mail_Parse { - + var $mime_message; var $include_bodies; var $decode_headers; var $decode_bodies; - + var $struct; - + function Mail_parse($mimeMessage,$includeBodies=true,$decodeHeaders=TRUE,$decodeBodies=TRUE){ - + $this->mime_message=$mimeMessage; $this->include_bodies=$includeBodies; $this->decode_headers=$decodeHeaders; @@ -42,9 +42,9 @@ function decode() { 'include_bodies'=> $this->include_bodies, 'decode_headers'=> $this->decode_headers, 'decode_bodies' => $this->decode_bodies); - $this->splitBodyHeader(); + $this->splitBodyHeader(); $this->struct=Mail_mimeDecode::decode($params); - + return (PEAR::isError($this->struct) || !(count($this->struct->headers)>1))?FALSE:TRUE; } @@ -94,7 +94,7 @@ function splitBodyHeader() { } return $array; } - + function getStruct(){ return $this->struct; @@ -109,8 +109,8 @@ function getHeader() { function getError(){ return PEAR::isError($this->struct)?$this->struct->getMessage():''; } - - + + function getFromAddressList(){ return Mail_Parse::parseAddressList($this->struct->headers['from']); } @@ -119,7 +119,7 @@ function getToAddressList(){ //Delivered-to incase it was a BBC mail. return Mail_Parse::parseAddressList($this->struct->headers['to']?$this->struct->headers['to']:$this->struct->headers['delivered-to']); } - + function getCcAddressList(){ return $this->struct->headers['cc']?Mail_Parse::parseAddressList($this->struct->headers['cc']):null; } @@ -127,27 +127,27 @@ function getCcAddressList(){ function getMessageId(){ return $this->struct->headers['message-id']; } - + function getSubject(){ return $this->struct->headers['subject']; } - + function getBody(){ - + $body=''; if(!($body=$this->getPart($this->struct,'text/plain'))) { if(($body=$this->getPart($this->struct,'text/html'))) { //Cleanup the html. - $body=str_replace("
", "\n", $body); + $body=str_replace("
", "\n", $body); $body=str_replace(array("
", "
", "
", "
"), "\n", $body); - $body=Format::striptags(Format::html($body)); + $body=Format::safe_html($body); //Balance html tags & neutralize unsafe tags. } } return $body; } - + function getPart($struct,$ctypepart) { - + if($struct && !$struct->parts) { $ctype = @strtolower($struct->ctype_primary.'/'.$struct->ctype_secondary); if($ctype && strcasecmp($ctype,$ctypepart)==0) @@ -164,7 +164,7 @@ function getPart($struct,$ctypepart) { return $data; } - + function mime_encode($text, $charset=null, $encoding='utf-8') { return Format::encode($text, $charset, $encoding); } @@ -175,15 +175,15 @@ function getAttachments($part=null){ $part=$this->getStruct(); if($part && $part->disposition - && (!strcasecmp($part->disposition,'attachment') - || !strcasecmp($part->disposition,'inline') + && (!strcasecmp($part->disposition,'attachment') + || !strcasecmp($part->disposition,'inline') || !strcasecmp($part->ctype_primary,'image'))){ - + if(!($filename=$part->d_parameters['filename']) && $part->d_parameters['filename*']) $filename=$part->d_parameters['filename*']; //Do we need to decode? - + $file=array( - 'name' => $filename, + 'name' => $filename, 'type' => strtolower($part->ctype_primary.'/'.$part->ctype_secondary), 'data' => $this->mime_encode($part->body, $part->ctype_parameters['charset']) ); @@ -245,7 +245,7 @@ class EmailDataParser { function EmailDataParser($stream=null) { $this->stream = $stream; } - + function parse($stream) { $contents =''; @@ -260,7 +260,7 @@ function parse($stream) { $parser= new Mail_Parse($contents); if(!$parser->decode()) //Decode...returns false on decoding errors return $this->err('Email parse failed ['.$parser->getError().']'); - + $data =array(); //FROM address: who sent the email. if(($fromlist = $parser->getFromAddressList()) && !PEAR::isError($fromlist)) { @@ -293,7 +293,7 @@ function parse($stream) { break; } } - + $data['subject'] = Format::utf8encode($parser->getSubject()); $data['message'] = Format::utf8encode(Format::stripEmptyLines($parser->getBody())); $data['header'] = $parser->getHeader();