| [ Index ] |
PHP Cross Reference of Akelos Framework |
[Summary view] [Print] [Text view]
1 <?php 2 3 class AkMailParser 4 { 5 var $decode_body = true; 6 var $content_type = 'text/plain'; 7 var $recode_messages = true; 8 var $recode_to_charset = AK_ACTION_MAILER_DEFAULT_CHARSET; 9 var $raw_message = ''; 10 var $options = array(); 11 12 var $html_charset_on_recoding_failure = false; 13 14 var $headers = array(); 15 var $body; 16 var $parts; 17 18 19 function AkMailParser($options = array()) 20 { 21 $this->options = $options; 22 $default_options = array( 23 'content_type' => $this->content_type, 24 'decode_body' => $this->decode_body, 25 ); 26 $options = array_merge($default_options, $options); 27 foreach ($options as $k=>$v){ 28 if($k[0] != '_'){ 29 $this->$k = $v; 30 } 31 } 32 } 33 34 function parse($raw_message = '', $options = array(), $object_type = 'AkMailMessage') 35 { 36 $parser_class = empty($options['parser_class']) ? 'AkMailParser' : $options['parser_class']; 37 $Parser =& new $parser_class($options); 38 $Mail = new $object_type(); 39 $raw_message = empty($raw_message) ? $Parser->raw_message : $raw_message; 40 if(!empty($raw_message)){ 41 list($raw_header, $raw_body) = $Parser->_getRawHeaderAndBody($raw_message); 42 43 $Mail->headers = $Parser->headers = $Parser->getParsedRawHeaders($raw_header); 44 $Parser->{$Parser->getContentTypeProcessorMethodName()}($raw_body); 45 } 46 $Parser->_expandHeadersOnMailObject($Mail); 47 $Mail->body = $Parser->body; 48 $Mail->parts = $Parser->parts; 49 50 return $Mail; 51 } 52 53 function getContentTypeProcessorMethodName() 54 { 55 $content_type = $this->findHeaderValueOrDefaultTo('content-type', $this->content_type); 56 $method_name = 'getParsed'.ucfirst(strtolower(substr($content_type,0,strpos($content_type,"/")))).'Body'; 57 return method_exists($this, $method_name) ? $method_name : 'getParsedTextBody'; 58 } 59 60 function getContentDisposition() 61 { 62 return $this->_findHeader('content-disposition'); 63 } 64 65 66 function getParsedTextBody($body) 67 { 68 $this->body = $this->_getDecodedBody($body); 69 } 70 71 function getParsedMultipartBody($body) 72 { 73 static $recursion_protection; 74 75 $boundary = trim($this->_findHeaderAttributeValue('content-type','boundary')); 76 $this->content_type = $this->options['content_type'] = (trim(strtolower($this->_findHeaderValue('content-type'))) == 'multipart/digest' ? 'message/rfc822' : 'text/plain'); 77 78 if(empty($boundary)){ 79 trigger_error(Ak::t('Could not fetch multipart boundary'), E_USER_WARNING); 80 return false; 81 } 82 83 $this->parts = array(); 84 $raw_parts = array_diff(array_map('trim',(array)preg_split('/([\-]{0,2}'.preg_quote($boundary).'[\-]{0,2})+/', $body)),array('')); 85 foreach ($raw_parts as $raw_part){ 86 $Parser = new AkMailParser($this->options); 87 $recursion_protection[$body] = @$recursion_protection[$body]+1; 88 if($recursion_protection[$body] > 50){ 89 trigger_error(Ak::t('Maximum multipart decoding recursion reached.'), E_USER_WARNING); 90 return false; 91 }else{ 92 $Part = $Parser->parse($raw_part, array(), 'AkMailPart'); 93 } 94 $this->parts[] = $Part; 95 } 96 } 97 98 function getParsedMessageBody($body) 99 { 100 $Parser = new AkMailParser($this->options); 101 $this->body = $Parser->parse($body); 102 } 103 104 function _getDecodedBody($body) 105 { 106 $encoding = trim(strtolower($this->_findHeaderValue('content-transfer-encoding'))); 107 $charset = trim(strtolower($this->_findHeaderAttributeValue('content-type','charset'))); 108 109 if($encoding == 'base64'){ 110 $body = base64_decode($body); 111 }elseif($encoding == 'quoted-printable'){ 112 $body = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", preg_replace("/=\r?\n/", '', $body)); 113 } 114 return empty($charset) ? $body : ($charset && $this->recode_messages ? Ak::recode($body, $this->recode_to_charset, $charset, $this->html_charset_on_recoding_failure) : $body); 115 } 116 117 function _findHeaderValue($name) 118 { 119 $header = $this->_findHeader($name); 120 return !empty($header['value']) ? $header['value'] : false; 121 } 122 123 function _findHeaderAttributeValue($name, $attribute) 124 { 125 $header = $this->_findHeader($name); 126 return !empty($header['attributes'][$attribute]) ? $header['attributes'][$attribute] : false; 127 } 128 129 function findHeaderValueOrDefaultTo($name, $default) 130 { 131 $value = $this->_findHeaderValue($name); 132 return !empty($value) ? $value : $default; 133 } 134 135 function _findHeader($name) 136 { 137 $results = array(); 138 foreach ($this->headers as $header) { 139 if(isset($header['name']) && strtolower($header['name']) == $name){ 140 $results[] = $header; 141 } 142 } 143 return empty($results) ? false : (count($results) > 1 ? $results : array_shift($results)); 144 } 145 146 function getParsedRawHeaders($raw_headers) 147 { 148 $raw_header_lines = array_diff(array_map('trim',explode("\n",$raw_headers."\n")), array('')); 149 $headers = array(); 150 if(!empty($raw_headers)){ 151 foreach ($raw_header_lines as $header_line){ 152 $headers[] = $this->_parseHeaderLine($header_line); 153 } 154 } 155 156 return $headers; 157 } 158 159 function _parseHeaderLine($header_line) 160 { 161 $header = array(); 162 if(preg_match("/^([A-Za-z\-]+)\: *(.*)$/",$header_line,$match)){ 163 $header['name'] = $match[1]; 164 $header['value'] = $match[2]; 165 $this->_decodeHeader_($header); 166 $this->_headerCanHaveAttributes($header) ? $this->_extractAttributesForHeader_($header) : null; 167 return $header; 168 } 169 170 } 171 172 function _headerCanHaveAttributes($header) 173 { 174 return !in_array(strtolower($header['name']), array('subject','to','from','cc','bcc')); 175 } 176 177 function _extractAttributesForHeader_(&$header) 178 { 179 $attributes = array(); 180 if(preg_match_all("/([A-Z\-_ ]+)". 181 "(\*[0-9 ]*)?". // RFC 2231 182 "=([^;]*);?/i", $header['value'], $matches)){ 183 $header['value'] = str_replace($matches[0],'', $header['value']); 184 foreach ($matches[0] as $k=>$match){ 185 $attribute_name = trim($matches[1][$k]); 186 $value = trim($matches[3][$k],'; "\''); 187 if(!empty($matches[2][$k])){ // RFC 2231 188 $value = (empty($attributes[$attribute_name]) ? '' : $attributes[$attribute_name]) 189 .$this->_decodeHeaderAttribute($value, $matches[2][$k]); 190 } 191 $attributes[$attribute_name] = $value; 192 } 193 } 194 195 $header['value'] = trim($header['value'],'; '); 196 197 if(strstr($header['value'],';') && strtolower($header['name']) != 'date' && 198 preg_match("/([; ])*(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),? *)?(\d\d?)". 199 " +(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +(\d\d\d\d) ". 200 "+(\d{2}:\d{2}(?::\d\d)) +([\( ]*(UT|GMT|EST|EDT|CST|CDT|MST|MDT|". 201 "PST|PDT|[A-Z]|(?:\+|\-)\d{4})[\) ]*)+/",$header['value'],$match)){ 202 $header['value'] = str_replace($match[0],'', $header['value']); 203 $attributes['Date'] = trim(str_replace(' ',' ',$match[0]),"; "); 204 } 205 206 $header['attributes'] = empty($attributes) ? false : $attributes; 207 } 208 209 function _decodeHeader_(&$header) 210 { 211 if(!empty($header['value'])){ 212 $encoded_header = preg_replace('/\?\=([^=^\n^\r]+)?\=\?/', "?=$1\n=?",$header['value']); 213 $header_value = $header['value']; 214 if(preg_match_all('/(\=\?([^\?]+)\?([BQ]{1})\?([^\?]+)\?\=?)+/i', $encoded_header, $match)){ 215 foreach (array_keys($match[0]) as $k){ 216 $charset = strtoupper($match[2][$k]); 217 $decode_function = strtolower($match[3][$k]) == 'q' ? 'quoted_printable_decode' : 'base64_decode'; 218 $decoded_part = trim( 219 Ak::recode($decode_function(str_replace('_',' ',$match[4][$k])), $this->recode_to_charset, $charset, $this->html_charset_on_recoding_failure) 220 221 ); 222 223 $header_value = str_replace(trim($match[0][$k]), $decoded_part, $header_value); 224 } 225 } 226 $header_value = trim(preg_replace("/(%0A|%0D|\n+|\r+)/i",'',$header_value)); 227 if($header_value != $header['value']){ 228 $header['encoded'] = $header['value']; 229 $header['value'] = $header_value; 230 isset($charset) && $header['charset'] = $charset; 231 } 232 } 233 } 234 235 /** 236 * RFC 2231 Implementation 237 */ 238 function _decodeHeaderAttribute($header_attribute, $charset = '') 239 { 240 if(preg_match("/^([A-Z0-9\-]+)(\'[A-Z\-]{2,5}\')?/i",$header_attribute,$match)){ 241 $charset = $match[1]; 242 $header_attribute = urldecode(str_replace(array('_','='),array('%20','%'), substr($header_attribute,strlen($match[0])))); 243 } 244 return Ak::recode($header_attribute, 'UTF-8', $charset); 245 } 246 247 function _getRawHeaderAndBody($raw_part) 248 { 249 return 250 array_map('trim', 251 preg_split("/\n\n/", 252 preg_replace("/(\n[\t ]+)/",'', // Join multiline headers 253 str_replace(array("\r\n","\n\r","\r"),"\n", $raw_part."\n") // Lets keep it simple and use only \n for decoding 254 )."\n\n",2)); 255 } 256 257 function _expandHeadersOnMailObject(&$Mail) 258 { 259 if(!empty($Mail->headers)){ 260 foreach ($Mail->headers as $details){ 261 if (empty($details['name'])) { 262 continue; 263 } 264 $caption = AkInflector::underscore($details['name']); 265 if(!in_array($caption, array('headers','body','parts'))){ 266 if(!empty($details['value'])){ 267 if(empty($Mail->$caption)){ 268 $Mail->$caption = $details['value']; 269 }elseif (!empty($Mail->$caption) && is_array($Mail->$caption)){ 270 $Mail->{$caption}[] = $details['value']; 271 }else{ 272 $Mail->$caption = array($Mail->$caption, $details['value']); 273 } 274 $Mail->header[$caption] =& $Mail->$caption; 275 } 276 if(!empty($details['attributes'])){ 277 $Mail->{$caption.'_attributes'} = $details['attributes']; 278 } 279 } 280 } 281 } 282 } 283 284 285 function importStructure(&$MailOrPart, $structure = array()) 286 { 287 if(isset($structure['header'])){ 288 $structure['headers'] = $structure['header']; 289 unset($structure['header']); 290 } 291 foreach ($structure as $attribute=>$value){ 292 if($attribute[0] != '_'){ 293 $attribute_setter = 'set'.AkInflector::camelize($attribute); 294 if(method_exists($MailOrPart, $attribute_setter)){ 295 $MailOrPart->$attribute_setter($value); 296 }else{ 297 $MailOrPart->{AkInflector::underscore($attribute)} = $value; 298 } 299 } 300 } 301 return ; 302 } 303 304 305 function extractImagesIntoInlineParts(&$Mail, $options = array()) 306 { 307 $html =& $Mail->body; 308 require_once (AK_LIB_DIR.DS.'AkActionView'.DS.'helpers'.DS.'text_helper.php'); 309 $images = TextHelper::get_image_urls_from_html($html); 310 $html_images = array(); 311 if(!empty($images)){ 312 require_once (AK_LIB_DIR.DS.'AkImage.php'); 313 require_once (AK_LIB_DIR.DS.'AkActionView'.DS.'helpers'.DS.'asset_tag_helper.php'); 314 315 $images = array_diff(array_unique($images), array('')); 316 317 foreach ($images as $image){ 318 $original_image_name = $image; 319 $image = $this->_getImagePath($image); 320 if(!empty($image)){ 321 $extenssion = substr($image, strrpos('.'.$image,'.')); 322 $image_name = Ak::uuid().'.'.$extenssion; 323 $html_images[$original_image_name] = 'cid:'.$image_name; 324 325 $Mail->setAttachment('image/'.$extenssion, array( 326 'body' => Ak::file_get_contents($image), 327 'filename' => $image_name, 328 'content_disposition' => 'inline', 329 'content_id' => '<'.$image_name.'>', 330 )); 331 } 332 } 333 $modified_html = str_replace(array_keys($html_images),array_values($html_images), $html); 334 if($modified_html != $html){ 335 $html = $modified_html; 336 $Mail->_moveBodyToInlinePart(); 337 } 338 } 339 } 340 341 function _getImagePath($path) 342 { 343 if(preg_match('/^http(s)?:\/\//', $path)){ 344 $path_info = pathinfo($path); 345 $base_file_name = Ak::sanitize_include($path_info['basename'], 'paranaoid'); 346 if(empty($path_info['extension'])){ // no extension, we don't do magic stuff 347 $path = ''; 348 }else{ 349 $local_path = AK_TMP_DIR.DS.'mailer'.DS.'remote_images'.DS.md5($base_file_name['dirname']).DS.$base_file_name.'.'.$path_info['extension']; 350 if(!file_exists($local_path) || (time() > @filemtime($local_path)+7200)){ 351 if(!Ak::file_put_contents($local_path, Ak::url_get_contents($path))){ 352 return ''; 353 } 354 } 355 return $local_path; 356 } 357 } 358 359 $path = AK_PUBLIC_DIR.Ak::sanitize_include($path); 360 361 if(!file_exists($path)){ 362 $path = ''; 363 } 364 return $path; 365 } 366 367 } 368 369 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Oct 27 12:43:49 2008 | Cross-referenced by PHPXref 0.6 |