Creates an array of streams based on the attached part's mime boundary
and child streams.

@param ParentHeaderPart $part passed in because $this->part is declared
as MessagePart
@return StreamInterface[]
500169
protected function getBoundaryAndChildStreams(ParentHeaderPart $part)
    {
        $boundary = $part->getHeaderParameter('Content-Type', 'boundary');
        if ($boundary === null) {
            return array_map(
                function ($child) {
                    return $child->getStream();
                },
                $part->getChildParts()
            );
        }
        $streams = [];
        foreach ($part->getChildParts() as $i => $child) {
            if ($i !== 0 || $part->hasContent()) {
                $streams[] = Psr7\stream_for("\r\n");
            }
            $streams[] = Psr7\stream_for("--$boundary\r\n");
            $streams[] = $child->getStream();
        }
        $streams[] = Psr7\stream_for("\r\n--$boundary--\r\n");
        
        return $streams;
    }
1144519
public function addPart(self $part = null): self
    {
        return $this->parts[] = $part === null ? new self : $part;
    }
1006144
public function listParents($fileId, $optParams = array()) {
      $params = array('fileId' => $fileId);
      $params = array_merge($params, $optParams);
      $data = $this->__call('list', array($params));
      if ($this->useObjects()) {
        return new ParentList($data);
      } else {
        return $data;
      }
    }
506780
public function uploadTransientDocument(MultipartStream $multipartStream, array $headers = [])
    {
        $request = $this->provider->getAuthenticatedRequest(
            'POST',
            "$this->baseUri/$this->version/transientDocuments",
            $this->accessToken, [
                'headers' => $headers,
                'body' => $multipartStream
            ]
        );

        $res = $this->provider->getResponse($request);

        return $this->parseResponse($res);
    }
392650
private function decodeBody(Part $part)
    {
        $content = '';

        $contentTransferEncoding = '7-bit';
        $contentCharset = 'auto';
        $headers                 = $part->getHeaders();
        if (!empty($headers)) {
            if ($headers->has('Content-Transfer-Encoding')) {
                $contentTransferEncodingHeader = $headers->get('Content-Transfer-Encoding');
                if (is_a($contentTransferEncodingHeader, 'ArrayIterator')) {
                    /*
                     * Multiple transfer encoding headers don't really make sense and are
                     * indicative of a malformed message. Just choose the first one and hope
                     * it works.
                     */
                    $contentTransferEncodingHeader = $headers->get('Content-Transfer-Encoding')[0];
                }

                $contentTransferEncoding = $contentTransferEncodingHeader->getFieldValue();
            }

            if ($this->hasHeader($part, 'Content-Type')) {
                $newContentCharset = $this
                    ->getContentType($part)
                    ->getParameter('charset');


                if (!empty($newContentCharset)
                    && in_array($this->prepareEncodingName($newContentCharset), $this->knownCharsets)
                ) {
                    $contentCharset = $newContentCharset;
                }
            }
        }

        switch ($contentTransferEncoding) {
            case 'base64':
                $content = base64_decode($part->getContent());
                break;

            case 'quoted-printable':
                $content = quoted_printable_decode($part->getContent());
                break;

            default:
                try {
                    $content = $part->getContent();
                } catch (Exception $e) {
                    /*
                     * do nothing, email has not content, there is not function
                     * to check if the email is empty and $content is already
                     * set to an empty string
                     */
                }

                break;
        }

        /*
         * mb_convert_encoding might produce warnings/error if the $contentCharset is wrong.
         * mb_check_encoding for some reason doesn't fail those cases, so there's no way
         * to check if the encoding is correct.
         *
         * Using a custom error handler allows marking the part as problematic when
         * mb_convert_encoding produces a warning, while preventing a php-internal warning.
         *
         * This way, log files won't get cluttered and there's an easy way to deal with the
         * problematic parts.
         */
        $hasError = false;

        set_error_handler(function($errorLevel, $errorMessage) use (&$hasError) {
            $hasError = true;

            return true;
        }, E_ALL);

        $convertedContent = mb_convert_encoding($content, 'UTF-8', $contentCharset);

        restore_error_handler();

        if ($hasError) {
            throw new UnexpectedValueException('Content: ' . $content, ParsedMail::INVALID_CHARSET_ERROR_CODE);
        }

        return trim($convertedContent);
    }
555577
protected function structure_part($part, $count = 0, $parent = '', $mime_headers = null)
    {
        $struct = new rcube_message_part;
        $struct->mime_id = empty($parent) ? (string)$count : "$parent.$count";

        // multipart
        if (is_array($part[0])) {
            $struct->ctype_primary = 'multipart';

        /* RFC3501: BODYSTRUCTURE fields of multipart part
            part1 array
            part2 array
            part3 array
            ....
            1. subtype
            2. parameters (optional)
            3. description (optional)
            4. language (optional)
            5. location (optional)
        */

            // find first non-array entry
            for ($i=1; $i<count($part); $i++) {
                if (!is_array($part[$i])) {
                    $struct->ctype_secondary = strtolower($part[$i]);

                    // read content type parameters
                    if (is_array($part[$i+1])) {
                        $struct->ctype_parameters = array();
                        for ($j=0; $j<count($part[$i+1]); $j+=2) {
                            $param = strtolower($part[$i+1][$j]);
                            $struct->ctype_parameters[$param] = $part[$i+1][$j+1];
                        }
                    }

                    break;
                }
            }

            $struct->mimetype = 'multipart/'.$struct->ctype_secondary;

            // build parts list for headers pre-fetching
            for ($i=0; $i<count($part); $i++) {
                if (!is_array($part[$i])) {
                    break;
                }
                // fetch message headers if message/rfc822
                // or named part (could contain Content-Location header)
                if (!is_array($part[$i][0])) {
                    $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
                    if (strtolower($part[$i][0]) == 'message' && strtolower($part[$i][1]) == 'rfc822') {
                        $mime_part_headers[] = $tmp_part_id;
                    }
                    else if (in_array('name', (array)$part[$i][2]) && empty($part[$i][3])) {
                        $mime_part_headers[] = $tmp_part_id;
                    }
                }
            }

            // pre-fetch headers of all parts (in one command for better performance)
            // @TODO: we could do this before _structure_part() call, to fetch
            // headers for parts on all levels
            if ($mime_part_headers) {
                $mime_part_headers = $this->conn->fetchMIMEHeaders($this->folder,
                    $this->msg_uid, $mime_part_headers);
            }

            $struct->parts = array();
            for ($i=0, $count=0; $i<count($part); $i++) {
                if (!is_array($part[$i])) {
                    break;
                }
                $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
                $struct->parts[] = $this->structure_part($part[$i], ++$count, $struct->mime_id,
                    $mime_part_headers[$tmp_part_id]);
            }

            return $struct;
        }

        /* RFC3501: BODYSTRUCTURE fields of non-multipart part
            0. type
            1. subtype
            2. parameters
            3. id
            4. description
            5. encoding
            6. size
          -- text
            7. lines
          -- message/rfc822
            7. envelope structure
            8. body structure
            9. lines
          --
            x. md5 (optional)
            x. disposition (optional)
            x. language (optional)
            x. location (optional)
        */

        // regular part
        $struct->ctype_primary = strtolower($part[0]);
        $struct->ctype_secondary = strtolower($part[1]);
        $struct->mimetype = $struct->ctype_primary.'/'.$struct->ctype_secondary;

        // read content type parameters
        if (is_array($part[2])) {
            $struct->ctype_parameters = array();
            for ($i=0; $i<count($part[2]); $i+=2) {
                $struct->ctype_parameters[strtolower($part[2][$i])] = $part[2][$i+1];
            }

            if (isset($struct->ctype_parameters['charset'])) {
                $struct->charset = $struct->ctype_parameters['charset'];
            }
        }

        // #1487700: workaround for lack of charset in malformed structure
        if (empty($struct->charset) && !empty($mime_headers) && $mime_headers->charset) {
            $struct->charset = $mime_headers->charset;
        }

        // read content encoding
        if (!empty($part[5])) {
            $struct->encoding = strtolower($part[5]);
            $struct->headers['content-transfer-encoding'] = $struct->encoding;
        }

        // get part size
        if (!empty($part[6])) {
            $struct->size = intval($part[6]);
        }

        // read part disposition
        $di = 8;
        if ($struct->ctype_primary == 'text') {
            $di += 1;
        }
        else if ($struct->mimetype == 'message/rfc822') {
            $di += 3;
        }

        if (is_array($part[$di]) && count($part[$di]) == 2) {
            $struct->disposition = strtolower($part[$di][0]);
            if ($struct->disposition && $struct->disposition !== 'inline' && $struct->disposition !== 'attachment') {
                // RFC2183, Section 2.8 - unrecognized type should be treated as "attachment"
                $struct->disposition = 'attachment';
            }
            if (is_array($part[$di][1])) {
                for ($n=0; $n<count($part[$di][1]); $n+=2) {
                    $struct->d_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1];
                }
            }
        }

        // get message/rfc822's child-parts
        if (is_array($part[8]) && $di != 8) {
            $struct->parts = array();
            for ($i=0, $count=0; $i<count($part[8]); $i++) {
                if (!is_array($part[8][$i])) {
                    break;
                }
                $struct->parts[] = $this->structure_part($part[8][$i], ++$count, $struct->mime_id);
            }
        }

        // get part ID
        if (!empty($part[3])) {
            $struct->content_id = $part[3];
            $struct->headers['content-id'] = $part[3];

            if (empty($struct->disposition)) {
                $struct->disposition = 'inline';
            }
        }

        // fetch message headers if message/rfc822 or named part (could contain Content-Location header)
        if ($struct->ctype_primary == 'message' || ($struct->ctype_parameters['name'] && !$struct->content_id)) {
            if (empty($mime_headers)) {
                $mime_headers = $this->conn->fetchPartHeader(
                    $this->folder, $this->msg_uid, true, $struct->mime_id);
            }

            if (is_string($mime_headers)) {
                $struct->headers = rcube_mime::parse_headers($mime_headers) + $struct->headers;
            }
            else if (is_object($mime_headers)) {
                $struct->headers = get_object_vars($mime_headers) + $struct->headers;
            }

            // get real content-type of message/rfc822
            if ($struct->mimetype == 'message/rfc822') {
                // single-part
                if (!is_array($part[8][0])) {
                    $struct->real_mimetype = strtolower($part[8][0] . '/' . $part[8][1]);
                }
                // multi-part
                else {
                    for ($n=0; $n<count($part[8]); $n++) {
                        if (!is_array($part[8][$n])) {
                            break;
                        }
                    }
                    $struct->real_mimetype = 'multipart/' . strtolower($part[8][$n]);
                }
            }

            if ($struct->ctype_primary == 'message' && empty($struct->parts)) {
                if (is_array($part[8]) && $di != 8) {
                    $struct->parts[] = $this->structure_part($part[8], ++$count, $struct->mime_id);
                }
            }
        }

        // normalize filename property
        $this->set_part_filename($struct, $mime_headers);

        return $struct;
    }
958394
private function writePartContentTo(StreamInterface $stream)
    {
        $contentStream = $this->part->getContentStream();
        if ($contentStream !== null) {
            $copyStream = $this->streamFactory->newNonClosingStream($stream);
            $es = $this->getTransferEncodingDecoratorForStream($copyStream);
            $cs = $this->getCharsetDecoratorForStream($es);
            Psr7\copy_to_stream($contentStream, $cs);
            $cs->close();
        }
    }
263759
protected function processParts(array $parts)
    {
        return array_values(array_filter($parts, function ($part) {
            if (empty($part) || $part instanceof CommentPart) {
                return false;
            }
            return true;
        }));
    }
1198707
protected function getPartHeaderFromFile(&$part)
    {
        $start = $part['starting-pos'];
        $end = $part['starting-pos-body'];
        fseek($this->stream, $start, SEEK_SET);
        $header = fread($this->stream, $end - $start);
        return $header;
    }
1001548
private function extractHeaderAddressPart($header, $part) {
        if (property_exists($header, $part)) {
            $this->$part = $this->parseAddresses($header->$part);
        }
    }
378657
public function parse(StreamInterface $stream)
    {
        $partBuilder = $this->read($stream);
        return $partBuilder->createMessagePart($stream);
    }
1077163
public function getChildParts(PartFilter $filter = null)
    {
        if ($filter !== null) {
            return array_values(array_filter($this->children, [ $filter, 'filter' ]));
        }
        return $this->children;
    }
1258368
private static function gatherAttachments(PartInterface $part): array
    {
        $attachments = [];
        foreach ($part->getParts() as $childPart) {
            if ($childPart instanceof Attachment) {
                $attachments[] = $childPart;
            }
            if ($childPart->hasChildren()) {
                $attachments = \array_merge($attachments, self::gatherAttachments($childPart));
            }
        }

        return $attachments;
    }
102455
protected function createStream(array $elements)
    {
        $stream = new AppendStream();

        foreach ($elements as $element) {
            $this->addElement($stream, $element);
        }

        // Add the trailing boundary with CRLF
        $stream->addStream(stream_for("--{$this->boundary}--\r\n"));

        return $stream;
    }
372994
private function failsHeaderFor(MessagePart $part, $type, $name, $header)
    {
        $headerValue = null;
        
        static $map = [
            'content-type' => 'getContentType',
            'content-disposition' => 'getContentDisposition',
            'content-transfer-encoding' => 'getContentTransferEncoding',
            'content-id' => 'getContentId'
        ];
        $lower = strtolower($name);
        if (isset($map[$lower])) {
            $headerValue = call_user_func([$part, $map[$lower]]);
        } elseif (!($part instanceof MimePart)) {
            return ($type === static::FILTER_INCLUDE);
        } else {
            $headerValue = $part->getHeaderValue($name);
        }
        
        return (($type === static::FILTER_EXCLUDE && strcasecmp($headerValue, $header) === 0)
            || ($type === static::FILTER_INCLUDE && strcasecmp($headerValue, $header) !== 0));
    }
460032
public function newInstance(PartBuilder $partBuilder, StreamInterface $messageStream = null)
    {
        $partStream = null;
        $contentStream = null;
        if ($messageStream !== null) {
            $partStream = $this->streamFactory->getLimitedPartStream($messageStream, $partBuilder);
            $contentStream = $this->streamFactory->getLimitedContentStream($messageStream, $partBuilder);
        }
        return new NonMimePart(
            $this->partStreamFilterManagerFactory->newInstance(),
            $this->streamFactory,
            $partStream,
            $contentStream
        );
    }
245663
public function getInlineParts($type = 'text')
    {
        $inline_parts = [];
        $mime_types = [
            'text'         => 'text/plain',
            'html'         => 'text/html',
        ];

        if (!in_array($type, array_keys($mime_types))) {
            throw new Exception('Invalid type specified for getInlineParts(). "type" can either be text or html.');
        }

        foreach ($this->parts as $partId => $part) {
            if ($this->getPart('content-type', $part) == $mime_types[$type]
                && $this->getPart('content-disposition', $part) != 'attachment'
                && !$this->partIdIsChildOfAnAttachment($partId)
            ) {
                $headers = $this->getPart('headers', $part);
                $encodingType = array_key_exists('content-transfer-encoding', $headers) ?
                    $headers['content-transfer-encoding'] : '';
                $undecoded_body = $this->decodeContentTransfer($this->getPartBody($part), $encodingType);
                $inline_parts[] = $this->charset->decodeCharset($undecoded_body, $this->getPartCharset($part));
            }
        }

        return $inline_parts;
    }