vendor/ssilence/php-imap-client/ImapClient/IncomingMessage.php line 178

Open in your IDE?
  1. <?php
  2. /**
  3.  * Copyright (C) 2016-2017  SSilence
  4.  * For the full license, please see LICENSE.
  5.  */
  6. namespace SSilence\ImapClient;
  7. /**
  8.  * Class for all incoming messages.
  9.  *
  10.  * This class get message and generates a message structure of the form:
  11.  * ```php
  12.  * $incomingMessage = new IncomingMessage();
  13.  * $incomingMessage->header;
  14.  * $incomingMessage->message;
  15.  * $incomingMessage->attachments;
  16.  * $incomingMessage->section;
  17.  * $incomingMessage->structure;
  18.  * $incomingMessage->debug;
  19.  * ```
  20.  * And marks the message read.
  21.  * TODO: Format class correctly.
  22.  *
  23.  * @copyright  Copyright (c) Tobias Zeising (http://www.aditu.de)
  24.  * @author    Sergey144010
  25.  */
  26. class IncomingMessage
  27. {
  28.     /**
  29.      * Used to handle sections of the e-mail easier.
  30.      */
  31.     const SECTION_ATTACHMENTS 1;
  32.     /**
  33.      * Used to handle sections of the e-mail easier.
  34.      */
  35.     const SECTION_BODY 2;
  36.     /**
  37.      * Do not use decode incoming message.
  38.      */
  39.     const NOT_DECODE 'not_decode';
  40.     /**
  41.      * Use decode incoming message.
  42.      */
  43.     const DECODE 'decode';
  44.     /**
  45.      * Header of the message.
  46.      *
  47.      * @var object
  48.      */
  49.     public $header;
  50.     /**
  51.      * The message.
  52.      *
  53.      * @var object
  54.      */
  55.     public $message;
  56.     /**
  57.      * Attachments.
  58.      *
  59.      * @var array
  60.      */
  61.     public $attachments;
  62.     /**
  63.      * Section of the message.
  64.      *
  65.      * @var string|array
  66.      */
  67.     public $section;
  68.     /**
  69.      * Structure of the message.
  70.      *
  71.      * @var object
  72.      */
  73.     public $structure;
  74.     /**
  75.      * Debug on or off.
  76.      *
  77.      * @var object
  78.      */
  79.     public $debug;
  80.     /**
  81.      * The imap string.
  82.      *
  83.      * @var resource
  84.      */
  85.     private $imapStream;
  86.     /**
  87.      * ID of the message.
  88.      *
  89.      * @var int
  90.      */
  91.     private $id;
  92.     /**
  93.      * UID of the message.
  94.      *
  95.      * @var int
  96.      */
  97.     private $uid;
  98.     /**
  99.      * Count the attachments.
  100.      *
  101.      * @var int
  102.      */
  103.     private $countAttachment;
  104.     /**
  105.      * Disable/enable decode current incoming message.
  106.      *
  107.      * @var string
  108.      */
  109.     private $_decode;
  110.     /**
  111.      * Called when the class has a new instance made of it.
  112.      *
  113.      * @param resource $imapStream
  114.      * @param int      $id
  115.      * @param string   $decode
  116.      *
  117.      * @return IncomingMessage
  118.      */
  119.     public function __construct($imapStream$id$decode self::DECODE)
  120.     {
  121.         $this->imapStream $imapStream;
  122.         if (is_array($id)) {
  123.             $identifier $id;
  124.             if (isset($identifier['id'])) {
  125.                 $this->id $identifier['id'];
  126.                 $this->uid null;
  127.             }
  128.             if (isset($identifier['uid'])) {
  129.                 $this->uid $identifier['uid'];
  130.                 $this->id null;
  131.             }
  132.             unset($identifier);
  133.         }
  134.         if (is_int($id)) {
  135.             $this->id $id;
  136.         }
  137.         if (isset($decode)) {
  138.             $this->_decode $decode;
  139.         }
  140.         $this->init();
  141.     }
  142.     /**
  143.      * Main process.
  144.      *
  145.      * @return void
  146.      */
  147.     protected function init()
  148.     {
  149.         $structure $this->imapFetchstructure();
  150.         $this->structure $structure;
  151.         if (isset($structure->parts)) {
  152.             $countSection count($structure->parts);
  153.             $this->countAttachment $countSection 1;
  154.         }
  155.         $this->getCountSection();
  156.         $this->getHeader();
  157.         $this->getAttachments();
  158.         $this->getBody();
  159.         if ($this->_decode === self::DECODE) {
  160.             $this->decode();
  161.         }
  162.     }
  163.     /**
  164.      * Get headers in the current message.
  165.      *
  166.      * Set
  167.      * ```php
  168.      * $this->header
  169.      * $this->header->details
  170.      * ```
  171.      *
  172.      * @return void
  173.      */
  174.     protected function getHeader()
  175.     {
  176.         $header $this->imapFetchOverview();
  177.         $this->header $header[0];
  178.         $this->header->details $this->imapHeaderInfo();
  179.     }
  180.     /**
  181.      * Returns current object.
  182.      *
  183.      * Set $this->debug
  184.      *
  185.      * @return void
  186.      */
  187.     public function debug()
  188.     {
  189.         $this->debug $this;
  190.     }
  191.     /**
  192.      * Get count section.
  193.      *
  194.      * We take $this->section and make a simple array from an array of arrays.
  195.      * If getRecursiveSections($this->structure) set $this->section to NULL,
  196.      * then we think that there is only one section in the letter.
  197.      * We install $this->section[0] = [0],
  198.      * and then we will take this into account in subsequent processing.
  199.      * Namely here getSection() and $this->getSectionStructure()
  200.      * or getSectionStructureFromIncomingStructure().
  201.      * Because if the message id is correct and the structure is returned,
  202.      * then there is exactly one section in the message.
  203.      *
  204.      * @return array sections
  205.      */
  206.     protected function getCountSection()
  207.     {
  208.         $this->getRecursiveSections($this->structure);
  209.         $sections = array();
  210.         if (!isset($this->section)) {
  211.             $this->section[0] = array(0);
  212.         }
  213.         foreach ($this->section as $array) {
  214.             foreach ($array as $section) {
  215.                 $sections[] = $section;
  216.             }
  217.         }
  218.         $sections array_unique($sections);
  219.         sort($sections);
  220.         $this->section $sections;
  221.         return $this->section;
  222.     }
  223.     /**
  224.      * Bypasses the recursive parts current message.
  225.      *
  226.      * Counts sections based on $obj->parts.
  227.      * And sets $this->section as an array of arrays or null.
  228.      * Null if $obj->parts is not.
  229.      *
  230.      * @param object $obj
  231.      * @param string $before
  232.      *
  233.      * @return void
  234.      */
  235.     protected function getRecursiveSections($obj$before null)
  236.     {
  237.         if (!isset($obj->parts)) {
  238.             return;
  239.         }
  240.         $countParts count($obj->parts);
  241.         $out = array();
  242.         $beforeSave $before;
  243.         foreach ($obj->parts as $key => $subObj) {
  244.             if (!isset($beforeSave)) {
  245.                 $before = ($key 1);
  246.             } else {
  247.                 $before $beforeSave.'.'.($key 1);
  248.             }
  249.             $this->getRecursiveSections($subObj$before);
  250.             $out[] = (string) $before;
  251.         }
  252.         $this->section[] = $out;
  253.     }
  254.     /**
  255.      * Gets all sections, or if parameter is specified sections by type.
  256.      *
  257.      * @param string $type
  258.      *
  259.      * @throws ImapClientException
  260.      *
  261.      * @return array
  262.      */
  263.     protected function getSections($type null)
  264.     {
  265.         if (!$type) {
  266.             return $this->section;
  267.         }
  268.         $types null;
  269.         switch ($type) {
  270.             case self::SECTION_ATTACHMENTS:
  271.                 $types TypeAttachments::get();
  272.                 break;
  273.             case self::SECTION_BODY:
  274.                 $types TypeBody::get();
  275.                 break;
  276.             default:
  277.                 throw new ImapClientException('Section type not recognised/supported');
  278.                 break;
  279.         }
  280.         $sections = array();
  281.         foreach ($this->section as $section) {
  282.             $obj $this->getSectionStructure($section);
  283.             if (!isset($obj->subtype)) {
  284.                 continue;
  285.             }
  286.             if (in_array($obj->subtype$typesfalse)) {
  287.                 $sections[] = $section;
  288.             }
  289.         }
  290.         return $sections;
  291.     }
  292.     /**
  293.      * Get attachments in the current message.
  294.      *
  295.      * Set
  296.      * $this->attachments->name
  297.      * $this->attachments->body
  298.      * $this->attachments->info
  299.      *
  300.      * @return array
  301.      */
  302.     protected function getAttachments()
  303.     {
  304.         $attachments = array();
  305.         foreach ($this->getSections(self::SECTION_ATTACHMENTS) as $section) {
  306.             $obj $this->getSection($section);
  307.             $attachment = new IncomingMessageAttachment($obj);
  308.             $objNew = new \stdClass();
  309.             $objNew->name $attachment->name;
  310.             $objNew->body $attachment->body;
  311.             $objNew->info $obj;
  312.             $attachments[] = $objNew;
  313.         }
  314.         $this->attachments $attachments;
  315.     }
  316.     /**
  317.      * Get body current message.
  318.      *
  319.      * Set
  320.      * $this->message->$subtype
  321.      * $this->message->$subtype->charset
  322.      * $this->message->text
  323.      * $this->message->info[]
  324.      * $this->message->types[]
  325.      *
  326.      * @return object
  327.      */
  328.     protected function getBody()
  329.     {
  330.         $objNew = new \stdClass();
  331.         $i 1;
  332.         $subType = new SubtypeBody();
  333.         foreach ($this->getSections(self::SECTION_BODY) as $section) {
  334.             $obj $this->getSection($section, array('class' => $subType));
  335.             if(!is_object($obj) || !$obj->__get('structure')) {
  336.                 continue;
  337.             }
  338.             $subtype strtolower($obj->__get('structure')->subtype);
  339.             if(!empty($subtype)) {
  340.                 if (!isset($objNew->$subtype)) {
  341.                     $objNew->$subtype $obj;
  342.                 } else {
  343.                     $subtype $subtype.'_'.$i;
  344.                     $objNew->$subtype $obj;
  345.                     $i++;
  346.                 }
  347.                 $objNew->info[] = $obj;
  348.                 $objNew->types[] = $subtype;
  349.                 /*
  350.                 * Set charset
  351.                 */
  352.                 foreach ($objNew->$subtype->__get('structure')->parameters as $parameter) {
  353.                     $attribute strtolower($parameter->attribute);
  354.                     if ($attribute === 'charset') {
  355.                         $value strtolower($parameter->value);
  356.                         /*
  357.                         * Here must be array, but
  358.                         */
  359.                         //$objNew->$subtype->charset[] = $value;
  360.                         $objNew->$subtype->charset $value;
  361.                     }
  362.                 }
  363.             }
  364.         }
  365.         if (isset($objNew->plain)) {
  366.             switch ($objNew->plain->structure->encoding) {
  367.                 case 3:
  368.                     $objNew->text imap_base64(mb_convert_encoding$objNew->plain"utf-8"$objNew->plain->charset ));
  369.                     break;
  370.                 default:
  371.                     $objNew->text quoted_printable_decode(mb_convert_encoding$objNew->plain"utf-8"$objNew->plain->charset ));
  372.                     break;
  373.             }
  374.             $objNew->types[] = 'text';
  375.         } else {
  376.             $objNew->text null;
  377.         }
  378.         $this->message $objNew;
  379.     }
  380.     /**
  381.      * Get a section message.
  382.      *
  383.      * Return object with 2 properties:
  384.      * $obj->structure
  385.      * $obj->body
  386.      *
  387.      * @param string     $section
  388.      * @param array|null $options have one option $options['class']. It create object, which must be instance \SSilence\ImapClient\Section.
  389.      *
  390.      * @throws ImapClientException
  391.      *
  392.      * @return \SSilence\ImapClient\Section object
  393.      */
  394.     public function getSection($section$options null)
  395.     {
  396.         if (isset($options['class'])) {
  397.             $sectionObj = new $options['class']();
  398.             if ($sectionObj instanceof Section) {
  399.             } else {
  400.                 throw new ImapClientException('Incoming class not instance \SSilence\ImapClient\Section');
  401.             }
  402.         } else {
  403.             $sectionObj = new Section();
  404.         }
  405.         if ($section === 0) {
  406.             /*
  407.             If the message id is correct and the structure is returned,
  408.             then there is exactly one section in the message.
  409.             */
  410.             $sectionObj->structure $this->imapBodystruct(1);
  411.             $sectionObj->body $this->imapFetchbody(1);
  412.         } else {
  413.             $sectionObj->structure $this->imapBodystruct($section);
  414.             $sectionObj->body $this->imapFetchbody($section);
  415.         }
  416.         return $sectionObj;
  417.     }
  418.     /**
  419.      * Alias for getSectionStructureFromIncomingStructure();.
  420.      *
  421.      * @param string $section
  422.      *
  423.      * @return object|null
  424.      */
  425.     public function getSectionStructure($section)
  426.     {
  427.         return $this->getSectionStructureFromIncomingStructure($section);
  428.     }
  429.     /**
  430.      * Get section structure from incoming structure.
  431.      *
  432.      * @param string $section
  433.      *
  434.      * @return object|null
  435.      */
  436.     protected function getSectionStructureFromIncomingStructure($section)
  437.     {
  438.         $pos strpos($section'.');
  439.         if ($pos === false) {
  440.             $section = (int) $section;
  441.             if ($section === 0) {
  442.                 return $this->structure;
  443.             }
  444.             return $this->structure->parts[($section 1)];
  445.         }
  446.         $sections explode('.'$section);
  447.         $count count($sections);
  448.         $outObject null;
  449.         foreach ($sections as $section) {
  450.             $section = (int) $section;
  451.             if (!isset($outObject)) {
  452.                 $outObject $this->getObjectStructureFromParts($this->structure, ($section 1));
  453.             } else {
  454.                 $outObject $this->getObjectStructureFromParts($outObject, ($section 1));
  455.             }
  456.         }
  457.         return $outObject;
  458.     }
  459.     /**
  460.      * Get object structure from parts.
  461.      *
  462.      * @param object $inObject
  463.      * @param int    $part
  464.      *
  465.      * @return object
  466.      */
  467.     protected function getObjectStructureFromParts($inObject$part)
  468.     {
  469.         return $inObject->parts[$part];
  470.     }
  471.     /**
  472.      * Get a specific section.
  473.      *
  474.      * @param string $section
  475.      *
  476.      * @return string
  477.      */
  478.     protected function imapFetchbody($section)
  479.     {
  480.         /*
  481.      * Update note: We must add FT_PEEK to perserve the unread status of the email.
  482.      * Documentation of this can see seen here: http://php.net/manual/en/function.imap-fetchbody.php under options
  483.      */
  484.         return imap_fetchbody($this->imapStream$this->id$sectionFT_PEEK);
  485.     }
  486.     /**
  487.      * Structure all messages.
  488.      *
  489.      * @return object
  490.      */
  491.     protected function imapFetchstructure()
  492.     {
  493.         return imap_fetchstructure($this->imapStream$this->id);
  494.     }
  495.     /**
  496.      * Structure specific section.
  497.      *
  498.      * @param string $section
  499.      *
  500.      * @return object
  501.      */
  502.     protected function imapBodystruct($section)
  503.     {
  504.         return imap_bodystruct($this->imapStream$this->id$section);
  505.     }
  506.     /**
  507.      * Fetch a quick "Overview" on a message.
  508.      *
  509.      * @see http://php.net/manual/ru/function.imap-fetch-overview.php
  510.      *
  511.      * @throws ImapClientException
  512.      *
  513.      * @return object
  514.      */
  515.     protected function imapFetchOverview()
  516.     {
  517.         if (isset($this->id) && isset($this->uid)) {
  518.             throw new ImapClientException('What to use id or uid?');
  519.         }
  520.         $sequence null;
  521.         $options null;
  522.         if (isset($this->id) && !isset($this->uid)) {
  523.             $sequence $this->id;
  524.             $options null;
  525.         }
  526.         if (!isset($this->id) && isset($this->uid)) {
  527.             $sequence $this->uid;
  528.             $options FT_UID;
  529.         }
  530.         return imap_fetch_overview($this->imapStream$sequence$options);
  531.     }
  532.     /**
  533.      * Imap Header Info.
  534.      *
  535.      * Wrapper for http://php.net/manual/ru/function.imap-headerinfo.php
  536.      *
  537.      * @see http://php.net/manual/ru/function.imap-headerinfo.php
  538.      * @see http://php.net/manual/en/function.imap-headerinfo.php#98809
  539.      *
  540.      * @return object
  541.      */
  542.     protected function imapHeaderInfo()
  543.     {
  544.      return imap_rfc822_parse_headers(imap_fetchheader($this->imapStream$this->id));
  545.     }
  546.     /**
  547.      * Convert to utf8 if necessary.
  548.      *
  549.      * @param string $str utf8 encoded string
  550.      *
  551.      * @return string
  552.      */
  553.     public function convertToUtf8($str$charset 'ISO-8859-1')
  554.     {
  555.         if ($charset == 'default') {
  556.             $charset 'utf-8';
  557.         }
  558.         $encoding mb_detect_encoding($strmb_detect_order(), false);
  559.         if($encoding == 'UTF-8'){
  560.             $str mb_convert_encoding($str'UTF-8''UTF-8');
  561.         }
  562.         $str iconv(mb_detect_encoding($strmb_detect_order(), false), "UTF-8//IGNORE"$str);
  563. //        $str = iconv($charset, 'UTF-8//IGNORE', $str);
  564.         return $str;
  565.     }
  566.     /**
  567.      * Wrapper for imap_mime_header_decode()
  568.      * http://php.net/manual/ru/function.imap-mime-header-decode.php.
  569.      *
  570.      * @see http://php.net/manual/ru/function.imap-mime-header-decode.php
  571.      *
  572.      * @param string $string
  573.      *
  574.      * @return array
  575.      */
  576.     protected function imapMimeHeaderDecode($string)
  577.     {
  578.         return imap_mime_header_decode($string);
  579.     }
  580.     /**
  581.      * Decodes and glues the title bar
  582.      * http://php.net/manual/ru/function.imap-mime-header-decode.php.
  583.      *
  584.      * @see http://php.net/manual/ru/function.imap-mime-header-decode.php
  585.      *
  586.      * @param string $string
  587.      *
  588.      * @return string
  589.      */
  590.     protected function mimeHeaderDecode($string)
  591.     {
  592.         $cache null;
  593.         $array $this->imapMimeHeaderDecode($string);
  594.         foreach ($array as $object) {
  595.             $cache .= $this->convertToUtf8($object->text$object->charset);
  596.         }
  597.         return $cache;
  598.     }
  599.     /**
  600.      * Decode incoming message.
  601.      *
  602.      * @return void
  603.      */
  604.     protected function decode()
  605.     {
  606.         $this->decodeHeader();
  607.         $this->decodeBody();
  608.         $this->decodeAttachments();
  609.     }
  610.     /**
  611.      * Decode header.
  612.      *
  613.      * @return void
  614.      */
  615.     protected function decodeHeader()
  616.     {
  617.         if (isset($this->header->subject)) {
  618.             $this->header->subject $this->mimeHeaderDecode($this->header->subject);
  619.         }
  620.         if (isset($this->header->details->subject)) {
  621.             $this->header->details->subject $this->mimeHeaderDecode($this->header->details->subject);
  622.         }
  623.         if (isset($this->header->details->Subject)) {
  624.             $this->header->details->Subject $this->mimeHeaderDecode($this->header->details->Subject);
  625.         }
  626.         if (isset($this->header->from)) {
  627.             $this->header->from $this->mimeHeaderDecode($this->header->from);
  628.         }
  629.         if (isset($this->header->to)) {
  630.             $this->header->to $this->mimeHeaderDecode($this->header->to);
  631.         }
  632.     }
  633.     /**
  634.      * Decode attachments.
  635.      *
  636.      * @return void
  637.      */
  638.     protected function decodeAttachments()
  639.     {
  640.         foreach ($this->attachments as $key => $attachment) {
  641.             if(
  642.                 is_object($attachment) &&
  643.                 property_exists($attachment'info') &&
  644.                 is_object($attachment->info) &&
  645.                 property_exists($attachment->info'structure') &&
  646.                 is_object($attachment->info->structure) &&
  647.                 property_exists($attachment->info->structure'encoding')
  648.             ) {
  649.                 /*
  650.                  * Decode body
  651.                  */
  652.                 switch ($attachment->info->structure->encoding) {
  653.                     case 3:
  654.                         $this->attachments[$key]->body imap_base64($attachment->body);
  655.                         break;
  656.                     case 4:
  657.                         $this->attachments[$key]->body quoted_printable_decode($attachment->body);
  658.                         break;
  659.                 }
  660.                 /*
  661.                  * Decode name
  662.                  */
  663.                 $this->attachments[$key]->name $this->mimeHeaderDecode($attachment->name);
  664.             }
  665.         }
  666.     }
  667.     /**
  668.      * Decode body.
  669.      *
  670.      * @return void
  671.      */
  672.     protected function decodeBody()
  673.     {
  674.         if(is_object($this->message)){
  675.             if(property_exists($this->message'types')) {
  676.                 foreach ($this->message->types as $typeMessage) {
  677.                     if(is_object($this->message->$typeMessage)){
  678.                         switch ($this->message->$typeMessage->structure->encoding) {
  679.                             case 3:
  680.                                 $this->message->$typeMessage->body imap_base64($this->message->$typeMessage->body);
  681.                                 break;
  682.                             case 4:
  683.                                 $this->message->$typeMessage->body imap_qprint($this->message->$typeMessage->body);
  684.                                 break;
  685.                         }
  686.                     }
  687.                     if(is_object($this->message->$typeMessage)){
  688.                         if ($this->message->$typeMessage->charset) {
  689.                             $this->message->$typeMessage->body $this->convertToUtf8($this->message->$typeMessage->body$this->message->$typeMessage->charset);
  690.                         }
  691.                     }
  692.                 }
  693.             }
  694.         }
  695.     }
  696.     /**
  697.      * Info about this object.
  698.      *
  699.      * @return array
  700.      */
  701.     public function __debugInfo()
  702.     {
  703.         return array(
  704.             'header' => $this->header,
  705.             'message' => $this->message,
  706.             'attachments' => $this->attachments,
  707.             'section' => $this->section,
  708.             'structure' => $this->structure,
  709.             'debug' => $this->debug,
  710.         );
  711.     }
  712.     
  713.     /**
  714.      * Get the id property of the message
  715.      *
  716.      * @return int
  717.      */
  718.     public function getID()
  719.     {
  720.         return $this->id;   
  721.     }
  722. }