Finalize multipart data.

@param (array) $multiparts Multipart data with its headers.

@return (string) Raw HTTP multipart data formatted according to the RFC.

@see https://www.ietf.org/rfc/rfc2388.txt
510338
protected function multipartFinalize(Array $multiparts = []) {
    $contents = '';
    foreach ($multiparts as $multipart) {
      $contents .= $multipart;
    }
    $contents .= '--' . $this->boundary  . '--';
    $contents .= static::EOL;
    return $contents;
  }
541944
function part_structure($p, $body = null)
    {
        if ($p['mimetype'] == 'text/plain' || $p['mimetype'] == 'application/pgp') {
            $this->parse_plain($p, $body);
        }
        else if ($p['mimetype'] == 'multipart/signed') {
            $this->parse_signed($p, $body);
        }
        else if ($p['mimetype'] == 'multipart/encrypted') {
            $this->parse_encrypted($p);
        }
        else if ($p['mimetype'] == 'application/pkcs7-mime') {
            $this->parse_encrypted($p);
        }

        return $p;
    }
1344071
public function multipart()
    {

        if (empty($this->_multiparts)) {

            $this->_multiparts = array();

            if (!$this->isContentType('multipart/mixed')) {
                throw new Exception('Response is not multipart');
            }

            // Step 1. Get boundary

            preg_match('/boundary=([^";]+)/i', $this->getContentType(), $matches);

            if (empty($matches[1])) {
                throw new Exception('Boundary not found');
            }

            $boundary = $matches[1];

            // Step 2. Split by boundary and remove first and last parts if needed

            $parts = explode('--' . $boundary . '', $this->text()); //TODO Handle as stream

            if (empty(trim($parts[0]))) {
                array_shift($parts);
            }

            if (trim($parts[count($parts) - 1]) == '--') {
                array_pop($parts);
            }

            if (count($parts) == 0) {
                throw new Exception('No parts found');
            }

            // Step 3. Create status info object

            $statusInfoPart = array_shift($parts);
            $statusInfoObj = new self(null,
                self::createResponse(trim($statusInfoPart), $this->response()->getStatusCode())
            );
            $statusInfo = $statusInfoObj->json()->response;

            // Step 4. Parse all parts into Response objects

            foreach ($parts as $i => $part) {

                $partInfo = $statusInfo[$i];

                $this->_multiparts[] = new self(null, self::createResponse(trim($part), $partInfo->status));

            }

        }

        return $this->_multiparts;

    }
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);
    }
444159
private function fromCompactSerializationRecipientToSerialization(array $parts)
    {
        $recipient = [];
        if (!empty($parts[1])) {
            $recipient['encrypted_key'] = $parts[1];
        }

        $recipients = [
            'recipients' => [$recipient],
        ];
        foreach ([0 => 'protected', 2 => 'iv', 3 => 'ciphertext', 4 => 'tag'] as $part => $key) {
            if (!empty($parts[$part])) {
                $recipients[$key] = $parts[$part];
            }
        }

        $this->log(LogLevel::INFO, 'JWE in Compact JSON Serialization mode loaded.');

        return $recipients;
    }
308808
protected function getPartBodyFromText(&$part)
    {
        $start = $part['starting-pos-body'];
        $end = $part['ending-pos-body'];

        return substr($this->data, $start, $end - $start);
    }
889059
public function url(...$parts)
    {
        $uri = implode('/', $parts);
        return (string)$this->request->uri->make($uri);
    }
625221
public function finishMultipart()
    {
        if ( isset( $this->parts[0] ) )
        {
            $this->report->setReadablePart( $this->parts[0] );
        }
        if ( isset( $this->parts[1] ) )
        {
            $this->report->setMachinePart( $this->parts[1] );
        }
        if ( isset( $this->parts[2] ) )
        {
            $this->report->setOriginalPart( $this->parts[2] );
        }
        $size = 0;
        foreach ( $this->report->getParts() as $part )
        {
            $size += $part->size;
        }
        $this->report->size = $size;
        return $this->report;
    }
1326795
public static function http_parse_headers($raw_headers)
    {
        $headers = [];
        $key     = ''; // [+]

        foreach (explode("\n", $raw_headers) as $i => $h) {
            $h = explode(':', $h, 2);

            if (isset($h[1])) {
                if (!isset($headers[$h[0]])) {
                    $headers[$h[0]] = trim($h[1]);
                } elseif (is_array($headers[$h[0]])) {
                    // $tmp = array_merge($headers[$h[0]], array(trim($h[1]))); // [-]
                    // $headers[$h[0]] = $tmp; // [-]
                    $headers[$h[0]] = array_merge($headers[$h[0]], [trim($h[1])]); // [+]
                } else {
                    // $tmp = array_merge(array($headers[$h[0]]), array(trim($h[1]))); // [-]
                    // $headers[$h[0]] = $tmp; // [-]
                    $headers[$h[0]] = array_merge([$headers[$h[0]]], [trim($h[1])]); // [+]
                }

                $key = $h[0]; // [+]
            } else {
                // [+]
                // [+]
                if (substr($h[0], 0, 1) == "\t") {
                    // [+]
                    $headers[$key] .= "\r\n\t" . trim($h[0]);
                } // [+]
                elseif (!$key) {
                    // [+]
                    $headers[0] = trim($h[0]);
                }
                trim($h[0]); // [+]
            } // [+]
        }

        return $headers;
    }
1244379
protected function parseDocumentTitle(array $parts)
    {
        $parts = $this->parseDocumentTitleParts($parts);
        $delim = $this->parseDocumentTitleSeparator();
        $title = implode($delim, $parts);

        return $title;
    }
63563
def multipart_listparts(self, multipart):
        
        return self.make_response(
            data=Part.query_by_multipart(
                multipart).order_by(Part.part_number).limit(1000).all(),
            context={
                'class': Part,
                'multipart': multipart,
                'many': True,
            }
        )
559569
protected function prepareRestRequest(array &$parts)
    {
        if (!empty($parts) && in_array($parts[0], $this->restPrefixes)) {
            /*
             * if the request is something like /_/posts/1/comments/2
             * it will convert it to something like /posts/comments/1/2
             */

            // first remove the REST prefix
            array_shift($parts);

            $restRoute = $restParams = [];
            $i = 1;

            foreach ($parts as $part) {
                if ($i % 2) {
                    $restRoute[] = $part;
                } else {
                    $restParams[] = $part;
                }

                $i++;
            }

            $parts = array_merge($restRoute, $restParams);
        }
    }
439988
public function finalize()
    {
        // Output our chunks.
        // Every output is split into 4 chunk strings (header, messages,
        // data, footer).
        foreach ($this->chunkStrings as $chunkString) {
            // Save everything to the file after we are done.
            $this->pool->chunks->saveDechunkedToFile($chunkString);
        }
    }
362584
public function parseData()
    {
        $headers = $this->getHeaders();

        if (!in_array($this->httpRequest->getMethod(), ['PUT', 'POST'])) {
            return $this->emit('end');
        }

        $this->httpRequest->on('data', function($data) use ($headers, &$dataResult) {
            $dataResult .= $data;

            if (isset($headers["content-length"])) {
                if (strlen($dataResult) == $headers["content-length"][0]) {
                    $this->httpRequest->close();
                }
            } else {
                $this->httpRequest->close();
            }
        });

        $this->httpRequest->on('end', function() use (&$dataResult) {
            $this->onEnd($dataResult);
        });
    }
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;
    }
74047
function parseMultipart(req, boundary, cb) {
	// multipart, the first part has to be the same format as single part post data

	var parser = MultipartParser.create(boundary);
	var files = {};
	var cmdData = '';

	parser.on('part', function (part) {
		var m, disp, partName, fileName, isCmdData;

		disp = part.headers['content-disposition'];
		if (!disp) {
			// unidentifiable parts cannot be used

			logger.warning.data('partHeaders', part.headers).log('Received an unidentifyable part, skipping.');
			return;
		}

		m = disp.match(/name="(.+?)"/);
		if (!m) {
			// unnamed parts cannot be used

			logger.warning.data('partHeaders', part.headers).log('Received an unnamed part, skipping.');
			return;
		}

		partName = m[1];

		logger.verbose('Receiving multipart-part:', partName);

		isCmdData = (partName === 'cmddata');

		m = disp.match(/filename="(.+?)"/);
		if (m) {
			// a filename is optional

			fileName = m[1];
		}

		// the first part is ready and is expected to be the same format as single part post data

		var data = [];

		part.on('data', function (chunk) {
			if (isCmdData) {
				// treat as utf8

				cmdData += chunk.toString('utf8');
			} else {
				data.push(chunk);
			}
		});

		if (!isCmdData) {
			part.on('end', function () {
				// create the files object for the following files, the command center can take care of the rest

				files[partName] = {
					data: data,
					partName: partName,
					fileName: fileName,
					type: part.headers['content-type']
				};
			});
		}
	});

	// It has been observed that the "end" event on the parser would fire more than once. This might
	// be related to Node.js 0.10 streams. To avoid the situation, we use once('end') instead of
	// on('end').

	parser.once('end', function () {
		logger.verbose('Finished reading multipart', req.method, 'request.');

		cb(null, cmdData, files);
	});

	// pipe all incoming data straight into the multipart parser

	req.pipe(parser);
}
958122
private function fromCompactSerializationSignatureToSerialization(array $parts)
    {
        $temp = [];

        if (!empty($parts[1])) {
            $temp['payload'] = $parts[1];
        }
        $temp['signatures'] = [[
            'protected' => $parts[0],
            'signature' => $parts[2],
        ]];

        return $temp;
    }