[ Index ]

PHP Cross Reference of Akelos Framework

title

Body

[close]

/AkActionMailer/ -> AkMailParser.php (source)

   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  ?>


Generated: Mon Oct 27 12:43:49 2008 Cross-referenced by PHPXref 0.6