SoapServer/Client now handle binary files correctly & large tests/fixtures update

Soap Server and Client were breaking binary files during transfer due to invalid Mime Message Parser. Now is it working fine with no errors, but the message parser is about to be rewritten into a better form.
This commit is contained in:
Petr Bechyně
2017-04-04 18:36:18 +02:00
parent 311f9e6d08
commit 564005da93
42 changed files with 1135 additions and 250 deletions

View File

@ -37,12 +37,12 @@ class MimeFilter implements SoapRequestFilter, SoapResponseFilter
$soapPart = new MimePart($request->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT);
$soapVersion = $request->getVersion();
if ($soapVersion == SOAP_1_1 && $attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
if ($soapVersion === SOAP_1_1 && $attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
$multipart->setHeader('Content-Type', 'type', 'application/xop+xml');
$multipart->setHeader('Content-Type', 'start-info', 'text/xml');
$soapPart->setHeader('Content-Type', 'application/xop+xml');
$soapPart->setHeader('Content-Type', 'type', 'text/xml');
} elseif ($soapVersion == SOAP_1_2) {
} elseif ($soapVersion === SOAP_1_2) {
$multipart->setHeader('Content-Type', 'type', 'application/soap+xml');
$soapPart->setHeader('Content-Type', 'application/soap+xml');
}

View File

@ -19,6 +19,8 @@ use BeSimple\SoapClient\Curl\CurlOptionsBuilder;
use BeSimple\SoapClient\Curl\CurlResponse;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use BeSimple\SoapCommon\Fault\SoapFaultEnum;
use BeSimple\SoapCommon\Fault\SoapFaultPrefixEnum;
use BeSimple\SoapCommon\Fault\SoapFaultSourceGetter;
use BeSimple\SoapCommon\Mime\PartFactory;
use BeSimple\SoapCommon\SoapKernel;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
@ -38,8 +40,11 @@ use SoapFault;
*/
class SoapClient extends \SoapClient
{
/** @var SoapClientOptions */
protected $soapClientOptions;
/** @var SoapOptions */
protected $soapOptions;
/** @var Curl */
private $curl;
/** @var SoapAttachment[] */
private $soapAttachmentsOnRequestStorage;
@ -110,12 +115,38 @@ class SoapClient extends \SoapClient
public function soapCall($functionName, array $arguments, array $soapAttachments = [], array $options = null, $inputHeaders = null, array &$outputHeaders = null)
{
$this->setSoapAttachmentsOnRequestToStorage($soapAttachments);
$soapResponseAsObject = parent::__soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders);
try {
$soapResponse = $this->getSoapResponseFromStorage();
$soapResponse->setResponseObject($soapResponseAsObject);
$soapResponseAsObject = parent::__soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders);
$soapResponse = $this->getSoapResponseFromStorage();
$soapResponse->setResponseObject($soapResponseAsObject);
return $soapResponse;
return $soapResponse;
} catch (SoapFault $soapFault) {
if (SoapFaultSourceGetter::isNativeSoapFault($soapFault)) {
$soapResponse = $this->getSoapResponseFromStorage();
if ($soapResponse instanceof SoapResponse) {
$soapFault = $this->throwSoapFaultByTracing(
SoapFaultPrefixEnum::PREFIX_PHP . '-' . $soapFault->getCode(),
$soapFault->getMessage(),
new SoapResponseTracingData(
'Content-Type: ' . $soapResponse->getRequest()->getContentType(),
$soapResponse->getRequest()->getContent(),
'Content-Type: ' . $soapResponse->getContentType(),
$soapResponse->getResponseContent()
)
);
} else {
$soapFault = new SoapFault(
SoapFaultPrefixEnum::PREFIX_PHP . '-unresolved',
'Got SoapFault message with no response: '.$soapFault->getMessage()
);
}
}
throw $soapFault;
}
}
/**
@ -268,7 +299,6 @@ class SoapClient extends \SoapClient
if ($curlResponse->curlStatusSuccess()) {
$soapResponse = $this->returnSoapResponseByTracing(
$this->soapClientOptions->getTrace(),
$soapRequest,
$curlResponse,
$soapResponseTracingData
@ -288,16 +318,14 @@ class SoapClient extends \SoapClient
} else if ($curlResponse->curlStatusFailed()) {
return $this->throwSoapFaultByTracing(
$this->soapClientOptions->getTrace(),
SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
$curlResponse->getCurlErrorMessage(),
$soapResponseTracingData
);
} else {
return $this->throwSoapFaultByTracing(
$this->soapClientOptions->getTrace(),
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Cannot process curl response with unresolved status: ' . $curlResponse->getCurlStatus(),
$soapResponseTracingData
);
@ -341,19 +369,16 @@ class SoapClient extends \SoapClient
}
private function returnSoapResponseByTracing(
$isTracingEnabled,
SoapRequest $soapRequest,
CurlResponse $curlResponse,
SoapResponseTracingData $soapResponseTracingData,
array $soapAttachments = []
) {
if ($isTracingEnabled === true) {
if ($this->soapClientOptions->getTrace() === true) {
return SoapResponseFactory::createWithTracingData(
$soapRequest,
$curlResponse->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$curlResponse->getHttpResponseContentType(),
$soapResponseTracingData,
$soapAttachments
@ -372,9 +397,15 @@ class SoapClient extends \SoapClient
}
}
private function throwSoapFaultByTracing($isTracingEnabled, $soapFaultCode, $soapFaultMessage, SoapResponseTracingData $soapResponseTracingData)
/**
* @param string $soapFaultCode
* @param string $soapFaultMessage
* @param SoapResponseTracingData $soapResponseTracingData
* @throws SoapFault
*/
private function throwSoapFaultByTracing($soapFaultCode, $soapFaultMessage, SoapResponseTracingData $soapResponseTracingData)
{
if ($isTracingEnabled === true) {
if ($this->soapClientOptions->getTrace() === true) {
throw new SoapFaultWithTracingData(
$soapFaultCode,

View File

@ -2,6 +2,7 @@
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
class SoapResponse extends CommonSoapResponse
@ -10,6 +11,8 @@ class SoapResponse extends CommonSoapResponse
protected $responseObject;
/** @var SoapResponseTracingData */
protected $tracingData;
/** @var SoapRequest */
protected $request;
public function getResponseContent()
{
@ -40,4 +43,14 @@ class SoapResponse extends CommonSoapResponse
{
$this->tracingData = $tracingData;
}
public function setRequest(SoapRequest $request)
{
$this->request = $request;
}
public function getRequest()
{
return $this->request;
}
}

View File

@ -14,6 +14,7 @@ namespace BeSimple\SoapClient;
use BeSimple\SoapBundle\Soap\SoapAttachment;
use BeSimple\SoapCommon\Mime\PartFactory;
use BeSimple\SoapCommon\SoapRequest;
/**
* SoapResponseFactory for SoapClient. Provides factory function for SoapResponse object.
@ -50,7 +51,7 @@ class SoapResponseFactory
$response->setContentType($contentType);
if (count($attachments) > 0) {
$response->setAttachments(
self::createAttachmentParts($attachments)
PartFactory::createAttachmentParts($attachments)
);
}
@ -60,29 +61,26 @@ class SoapResponseFactory
/**
* Factory method for SoapClient\SoapResponse with SoapResponseTracingData.
*
* @param SoapRequest $soapRequest related request object
* @param string $content Content
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
* @param string $contentType Content type header
* @param SoapResponseTracingData $tracingData Data value object suitable for tracing SOAP traffic
* @param SoapAttachment[] $attachments SOAP attachments
* @return SoapResponse
*/
public static function createWithTracingData(
SoapRequest $soapRequest,
$content,
$location,
$action,
$version,
$contentType,
SoapResponseTracingData $tracingData,
array $attachments = []
) {
$response = new SoapResponse();
$response->setRequest($soapRequest);
$response->setContent($content);
$response->setLocation($location);
$response->setAction($action);
$response->setVersion($version);
$response->setLocation($soapRequest->getLocation());
$response->setAction($soapRequest->getAction());
$response->setVersion($soapRequest->getVersion());
$response->setContentType($contentType);
if ($tracingData !== null) {
$response->setTracingData($tracingData);

View File

@ -0,0 +1,9 @@
<?php
namespace BeSimple\SoapCommon\Fault;
class SoapFaultPrefixEnum
{
const PREFIX_DEFAULT = 'be';
const PREFIX_PHP = 'php';
}

View File

@ -0,0 +1,25 @@
<?php
namespace BeSimple\SoapCommon\Fault;
use SoapFault;
class SoapFaultSourceGetter
{
public static function isNativeSoapFault(SoapFault $soapFault)
{
return self::isBeSimpleSoapFault($soapFault) === false;
}
public static function isBeSimpleSoapFault(SoapFault $soapFault)
{
$defaultPrefix = SoapFaultPrefixEnum::PREFIX_DEFAULT;
if (strpos($soapFault->getCode(), $defaultPrefix) === 0) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace BeSimple\SoapCommon\Mime\Boundary;
class MimeBoundaryAnalyser
{
/**
* @param string[] $mimeMessageLines
* @return bool
*/
public static function hasMessageBoundary(array $mimeMessageLines)
{
foreach ($mimeMessageLines as $mimeMessageLine) {
if (self::isMessageLineBoundary($mimeMessageLine)) {
return true;
}
}
return false;
}
/**
* @param string $mimeMessageLine
* @return bool
*/
public static function isMessageLineBoundary($mimeMessageLine)
{
return strlen($mimeMessageLine) > 0 && $mimeMessageLine[0] === "-";
}
/**
* @param string $mimeMessageLine
* @param string $mimeTypeBoundary
* @return bool
*/
public static function isMessageLineMiddleBoundary($mimeMessageLine, $mimeTypeBoundary)
{
return strcmp(trim($mimeMessageLine), '--'.$mimeTypeBoundary) === 0;
}
/**
* @param string $mimeMessageLine
* @param string $mimeTypeBoundary
* @return bool
*/
public static function isMessageLineLastBoundary($mimeMessageLine, $mimeTypeBoundary)
{
return strcmp(trim($mimeMessageLine), '--'.$mimeTypeBoundary.'--') === 0;
}
}

View File

@ -12,9 +12,10 @@
namespace BeSimple\SoapCommon\Mime;
use BeSimple\SoapCommon\Mime\Boundary\MimeBoundaryAnalyser;
use BeSimple\SoapCommon\Mime\Parser\ContentTypeParser;
use BeSimple\SoapCommon\Mime\Parser\ParsedPart;
use BeSimple\SoapCommon\Mime\Parser\ParsedPartList;
use BeSimple\SoapCommon\Mime\Parser\ParsedPartsGetter;
use Exception;
/**
@ -25,9 +26,6 @@ use Exception;
*/
class Parser
{
const HAS_HTTP_REQUEST_HEADERS = true;
const HAS_NO_HTTP_REQUEST_HEADERS = false;
/**
* Parse the given Mime-Message and return a \BeSimple\SoapCommon\Mime\MultiPart object.
*
@ -39,16 +37,26 @@ class Parser
public static function parseMimeMessage($mimeMessage, array $headers = [])
{
$multiPart = new MultiPart();
$mimeMessageLines = preg_split("/(\n)/", $mimeMessage);
$mimeMessageLines = explode("\n", $mimeMessage);
$mimeMessageLineCount = count($mimeMessageLines);
if ($mimeMessageLineCount <= 1) {
throw new Exception(
sprintf(
'Cannot process message of %d characters: got unexpectable low number of lines: %s',
mb_strlen($mimeMessage),
(string)$mimeMessageLineCount
)
);
}
// add given headers, e.g. coming from HTTP headers
if (count($headers) > 0) {
self::setMultiPartHeaders($multiPart, $headers);
$hasHttpRequestHeaders = self::HAS_HTTP_REQUEST_HEADERS;
$hasHttpRequestHeaders = ParsedPartsGetter::HAS_HTTP_REQUEST_HEADERS;
} else {
$hasHttpRequestHeaders = self::HAS_NO_HTTP_REQUEST_HEADERS;
$hasHttpRequestHeaders = ParsedPartsGetter::HAS_NO_HTTP_REQUEST_HEADERS;
}
if (self::hasBoundary($mimeMessageLines)) {
$parsedPartList = self::getPartsFromMimeMessageLines(
if (MimeBoundaryAnalyser::hasMessageBoundary($mimeMessageLines) === true) {
$parsedPartList = ParsedPartsGetter::getPartsFromMimeMessageLines(
$multiPart,
$mimeMessageLines,
$hasHttpRequestHeaders
@ -79,115 +87,6 @@ class Parser
return $multiPart;
}
/**
* @param MultiPart $multiPart
* @param string[] $mimeMessageLines
* @param bool $hasHttpHeaders = self::HAS_HTTP_REQUEST_HEADERS|self::HAS_NO_HTTP_REQUEST_HEADERS
* @return ParsedPartList
*/
private static function getPartsFromMimeMessageLines(
MultiPart $multiPart,
array $mimeMessageLines,
$hasHttpHeaders
) {
$parsedParts = [];
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
$currentPart = $multiPart;
$messagePartStringContent = '';
$inHeader = $hasHttpHeaders;
$hitFirstBoundary = false;
foreach ($mimeMessageLines as $mimeMessageLine) {
// ignore http status code and POST *
if (substr($mimeMessageLine, 0, 5) == 'HTTP/' || substr($mimeMessageLine, 0, 4) == 'POST') {
continue;
}
if (isset($currentHeader)) {
if (isset($mimeMessageLine[0]) && ($mimeMessageLine[0] === ' ' || $mimeMessageLine[0] === "\t")) {
$currentHeader .= $mimeMessageLine;
continue;
}
if (strpos($currentHeader, ':') !== false) {
list($headerName, $headerValue) = explode(':', $currentHeader, 2);
$headerValueWithNoCrAtTheEnd = trim($headerValue);
$headerValue = iconv_mime_decode($headerValueWithNoCrAtTheEnd, 0, Part::CHARSET_UTF8);
$parsedMimeHeaders = ContentTypeParser::parseContentTypeHeader($headerName, $headerValue);
foreach ($parsedMimeHeaders as $parsedMimeHeader) {
$currentPart->setHeader(
$parsedMimeHeader->getName(),
$parsedMimeHeader->getValue(),
$parsedMimeHeader->getSubValue()
);
}
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
}
unset($currentHeader);
}
if ($inHeader === true) {
if (trim($mimeMessageLine) == '') {
$inHeader = false;
continue;
}
$currentHeader = $mimeMessageLine;
continue;
} else {
if (self::isBoundary($mimeMessageLine)) {
if (self::isMiddleBoundary($mimeMessageLine, $contentTypeBoundary)) {
if ($currentPart instanceof Part) {
$currentPartContent = self::decodeContent(
$currentPart,
substr($messagePartStringContent, 0, -1)
);
$currentPart->setContent($currentPartContent);
// check if there is a start parameter given, if not set first part
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
} else {
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
}
}
$currentPart = new Part();
$hitFirstBoundary = true;
$inHeader = true;
$messagePartStringContent = '';
} else if (self::isLastBoundary($mimeMessageLine, $contentTypeBoundary)) {
$currentPartContent = self::decodeContent(
$currentPart,
substr($messagePartStringContent, 0, -1)
);
$currentPart->setContent($currentPartContent);
// check if there is a start parameter given, if not set first part
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
} else {
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
}
$messagePartStringContent = '';
} else {
// else block migrated from https://github.com/progmancod/BeSimpleSoap/commit/bf9437e3bcf35c98c6c2f26aca655ec3d3514694
// be careful to replace \r\n with \n
$messagePartStringContent .= $mimeMessageLine . "\n";
}
} else {
if ($hitFirstBoundary === false) {
if (trim($mimeMessageLine) !== '') {
$inHeader = true;
$currentHeader = $mimeMessageLine;
continue;
}
}
$messagePartStringContent .= $mimeMessageLine . "\n";
}
}
}
return new ParsedPartList($parsedParts);
}
/**
* @param ParsedPartList $parsedPartList
* @param MultiPart $multiPart
@ -224,56 +123,4 @@ class Parser
}
}
}
/**
* Decodes the content of a Mime part
*
* @param Part $part Part to add content
* @param string $partStringContent Content to decode
* @return string $partStringContent decodedContent
*/
private static function decodeContent(Part $part, $partStringContent)
{
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding'));
$charset = strtolower($part->getHeader('Content-Type', 'charset'));
if ($encoding === Part::ENCODING_BASE64) {
$partStringContent = base64_decode($partStringContent);
} else if ($encoding === Part::ENCODING_QUOTED_PRINTABLE) {
$partStringContent = quoted_printable_decode($partStringContent);
}
if ($charset !== Part::CHARSET_UTF8) {
return iconv($charset, Part::CHARSET_UTF8, $partStringContent);
}
return $partStringContent;
}
private static function hasBoundary(array $lines)
{
foreach ($lines as $line) {
if (self::isBoundary($line)) {
return true;
}
}
return false;
}
private static function isBoundary($mimeMessageLine)
{
return strlen($mimeMessageLine) > 0 && $mimeMessageLine[0] === "-";
}
private static function isMiddleBoundary($mimeMessageLine, $contentTypeBoundary)
{
return strcmp(trim($mimeMessageLine), '--'.$contentTypeBoundary) === 0;
}
private static function isLastBoundary($mimeMessageLine, $contentTypeBoundary)
{
return strcmp(trim($mimeMessageLine), '--'.$contentTypeBoundary.'--') === 0;
}
}
}

View File

@ -0,0 +1,179 @@
<?php
namespace BeSimple\SoapCommon\Mime\Parser;
use BeSimple\SoapCommon\Mime\Boundary\MimeBoundaryAnalyser;
use BeSimple\SoapCommon\Mime\MultiPart;
use BeSimple\SoapCommon\Mime\Part;
use Exception;
class ParsedPartsGetter
{
const HAS_HTTP_REQUEST_HEADERS = true;
const HAS_NO_HTTP_REQUEST_HEADERS = false;
/**
* @param MultiPart $multiPart
* @param string[] $mimeMessageLines
* @param bool $hasHttpHeaders = self::HAS_HTTP_REQUEST_HEADERS|self::HAS_NO_HTTP_REQUEST_HEADERS
* @return ParsedPartList
*/
public static function getPartsFromMimeMessageLines(
MultiPart $multiPart,
array $mimeMessageLines,
$hasHttpHeaders
) {
$parsedParts = [];
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
if ($contentTypeBoundary === null) {
throw new Exception(
'Unable to get Content-Type boundary from given MultiPart: ' . var_export($multiPart->getHeaders(), true)
);
}
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
if ($contentTypeContentIdStart === null) {
throw new Exception(
'Unable to get Content-Type start from given MultiPart: ' . var_export($multiPart->getHeaders(), true)
);
}
$currentPart = $multiPart;
$messagePartStringContent = '';
$inHeader = $hasHttpHeaders;
$hitFirstBoundary = false;
foreach ($mimeMessageLines as $mimeMessageLine) {
if (substr($mimeMessageLine, 0, 5) === 'HTTP/' || substr($mimeMessageLine, 0, 4) === 'POST') {
continue;
}
if (isset($currentHeader)) {
if (isset($mimeMessageLine[0]) && ($mimeMessageLine[0] === ' ' || $mimeMessageLine[0] === "\t")) {
$currentHeader .= $mimeMessageLine;
continue;
}
if (strpos($currentHeader, ':') !== false) {
list($headerName, $headerValue) = explode(':', $currentHeader, 2);
$headerValueWithNoCrAtTheEnd = trim($headerValue);
try {
$headerValue = iconv_mime_decode($headerValueWithNoCrAtTheEnd, 0, Part::CHARSET_UTF8);
} catch (Exception $e) {
if ($hitFirstBoundary === false) {
throw new Exception(
'Unable to parse message: cannot parse headers before hitting the first boundary'
);
}
throw new Exception(
sprintf(
'Unable to get header value: possible parsing message contents of %s characters in header parser: %s',
mb_strlen($headerValueWithNoCrAtTheEnd),
$e->getMessage()
)
);
}
$parsedMimeHeaders = ContentTypeParser::parseContentTypeHeader($headerName, $headerValue);
foreach ($parsedMimeHeaders as $parsedMimeHeader) {
$currentPart->setHeader(
$parsedMimeHeader->getName(),
$parsedMimeHeader->getValue(),
$parsedMimeHeader->getSubValue()
);
}
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
}
unset($currentHeader);
}
if ($inHeader === true) {
if (trim($mimeMessageLine) === '') {
$inHeader = false;
continue;
}
$currentHeader = $mimeMessageLine;
continue;
} else {
if (MimeBoundaryAnalyser::isMessageLineBoundary($mimeMessageLine)) {
if (MimeBoundaryAnalyser::isMessageLineMiddleBoundary($mimeMessageLine, $contentTypeBoundary)) {
if ($currentPart instanceof Part) {
$currentPartContent = self::decodeContent(
$currentPart,
substr($messagePartStringContent, 0, -1)
);
if ($currentPartContent[strlen($currentPartContent) - 1] === "\r") {
// temporary hack: if there is a CRLF before any middle boundary, then the remaining CR must be removed
$currentPartContent = substr($currentPartContent, 0, -1);
}
$currentPart->setContent($currentPartContent);
// check if there is a start parameter given, if not set first part
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
} else {
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
}
}
$currentPart = new Part();
$hitFirstBoundary = true;
$inHeader = true;
$messagePartStringContent = '';
} else if (MimeBoundaryAnalyser::isMessageLineLastBoundary($mimeMessageLine, $contentTypeBoundary)) {
$currentPartContent = self::decodeContent(
$currentPart,
substr($messagePartStringContent, 0, -1)
);
if ($currentPartContent[strlen($currentPartContent) - 1] === "\r") {
// temporary hack: if there is a CRLF before last boundary, then the remaining CR must be removed
$currentPartContent = substr($currentPartContent, 0, -1);
}
$currentPart->setContent($currentPartContent);
// check if there is a start parameter given, if not set first part
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
} else {
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
}
$messagePartStringContent = '';
} else {
// else block migrated from https://github.com/progmancod/BeSimpleSoap/commit/bf9437e3bcf35c98c6c2f26aca655ec3d3514694
// be careful to replace \r\n with \n
$messagePartStringContent .= $mimeMessageLine . "\n";
}
} else {
if ($hitFirstBoundary === false) {
if (trim($mimeMessageLine) !== '') {
$inHeader = true;
$currentHeader = $mimeMessageLine;
continue;
}
}
$messagePartStringContent .= $mimeMessageLine . "\n";
}
}
}
return new ParsedPartList($parsedParts);
}
/**
* Decodes the content of a Mime part
*
* @param Part $part Part to add content
* @param string $partStringContent Content to decode
* @return string $partStringContent decodedContent
*/
private static function decodeContent(Part $part, $partStringContent)
{
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding'));
$charset = strtolower($part->getHeader('Content-Type', 'charset'));
if ($encoding === Part::ENCODING_BASE64) {
$partStringContent = base64_decode($partStringContent);
} else if ($encoding === Part::ENCODING_QUOTED_PRINTABLE) {
$partStringContent = quoted_printable_decode($partStringContent);
}
if ($charset !== Part::CHARSET_UTF8) {
return iconv($charset, Part::CHARSET_UTF8, $partStringContent);
}
return $partStringContent;
}
}

View File

@ -75,6 +75,11 @@ abstract class PartHeader
return null;
}
public function getHeaders()
{
return $this->headers;
}
/**
* Generate headers.
*

View File

@ -13,6 +13,8 @@
namespace BeSimple\SoapCommon;
use DOMDocument;
/**
* Base class for SoapRequest and SoapResponse.
*
@ -73,13 +75,6 @@ abstract class SoapMessage
*/
protected $content;
/**
*
* Enter description here ...
* @var \DOMDocument
*/
protected $contentDomDocument = null;
/**
* Message content type.
*
@ -170,11 +165,6 @@ abstract class SoapMessage
*/
public function getContent()
{
if ($this->contentDomDocument !== null) {
$this->content = $this->contentDomDocument->saveXML();
$this->contentDomDocument = null;
}
return $this->content;
}
@ -186,9 +176,6 @@ abstract class SoapMessage
public function setContent($content)
{
$this->content = $content;
if (null !== $this->contentDomDocument) {
$this->contentDomDocument->loadXML($this->content);
}
}
/**
@ -198,12 +185,10 @@ abstract class SoapMessage
*/
public function getContentDocument()
{
if (null === $this->contentDomDocument) {
$this->contentDomDocument = new \DOMDocument();
$this->contentDomDocument->loadXML($this->content);
}
$contentDomDocument = new DOMDocument();
$contentDomDocument->loadXML($this->content);
return $this->contentDomDocument;
return $contentDomDocument;
}
/**

View File

@ -204,8 +204,12 @@ class SoapServer extends \SoapServer
$attachments = [];
if ($soapRequest->hasAttachments()) {
foreach ($soapRequest->getAttachments() as $attachment) {
$fileName = $attachment->getHeader('Content-Disposition', 'filename');
if ($fileName === null) {
$fileName = basename($attachment->getHeader('Content-Location'));
}
$attachments[] = new SoapAttachment(
$attachment->getHeader('Content-Disposition', 'filename'),
$fileName,
$attachment->getHeader('Content-Type'),
$attachment->getContent()
);

View File

@ -6,7 +6,7 @@ use BeSimple\SoapServer\SoapOptions\SoapServerOptions;
class SoapServerOptionsBuilder
{
static public function createWithDefaults($handlerClassOrObject)
public static function createWithDefaults($handlerClassOrObject)
{
return new SoapServerOptions(
$handlerClassOrObject,