diff --git a/.gitignore b/.gitignore index 22d0d82..f8d178e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ vendor +/phpunit.xml +.buildpath +.project +.settings \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Helper.php b/src/BeSimple/SoapCommon/Helper.php new file mode 100644 index 0000000..58f9779 --- /dev/null +++ b/src/BeSimple/SoapCommon/Helper.php @@ -0,0 +1,194 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +/** + * Soap helper class with static functions that are used in the client and + * server implementations. It also provides namespace and configuration + * constants. + * + * @author Andreas Schamberger + */ +class Helper +{ + /** + * Attachment type: xsd:base64Binary (native in ext/soap). + */ + const ATTACHMENTS_TYPE_BASE64 = 1; + + /** + * Attachment type: MTOM (SOAP Message Transmission Optimization Mechanism). + */ + const ATTACHMENTS_TYPE_MTOM = 2; + + /** + * Attachment type: SWA (SOAP Messages with Attachments). + */ + const ATTACHMENTS_TYPE_SWA = 4; + + /** + * Web Services Security: SOAP Message Security 1.0 (WS-Security 2004) + */ + const NAME_WSS_SMS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0'; + + /** + * Web Services Security: SOAP Message Security 1.1 (WS-Security 2004) + */ + const NAME_WSS_SMS_1_1 = 'http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1'; + + /** + * Web Services Security UsernameToken Profile 1.0 + */ + const NAME_WSS_UTP = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0'; + + /** + * Web Services Security X.509 Certificate Token Profile + */ + const NAME_WSS_X509 = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0'; + + /** + * Soap 1.1 namespace. + */ + const NS_SOAP_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'; + + /** + * Soap 1.1 namespace. + */ + const NS_SOAP_1_2 = 'http://www.w3.org/2003/05/soap-envelope/'; + + /** + * Web Services Addressing 1.0 namespace. + */ + const NS_WSA = 'http://www.w3.org/2005/08/addressing'; + + /** + * Web Services Security Extension namespace. + */ + const NS_WSS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'; + + /** + * Web Services Security Utility namespace. + */ + const NS_WSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'; + + /** + * Describing Media Content of Binary Data in XML namespace. + */ + const NS_XMLMIME = 'http://www.w3.org/2004/11/xmlmime'; + + /** + * XML Schema namespace. + */ + const NS_XML_SCHEMA = 'http://www.w3.org/2001/XMLSchema'; + + /** + * XML Schema instance namespace. + */ + const NS_XML_SCHEMA_INSTANCE = 'http://www.w3.org/2001/XMLSchema-instance'; + + /** + * XML-binary Optimized Packaging namespace. + */ + const NS_XOP = 'http://www.w3.org/2004/08/xop/include'; + + /** + * Web Services Addressing 1.0 prefix. + */ + const PFX_WSA = 'wsa'; + + /** + * Web Services Security Extension namespace. + */ + const PFX_WSS = 'wsse'; + + /** + * Web Services Security Utility namespace prefix. + */ + const PFX_WSU = 'wsu'; + + /** + * Describing Media Content of Binary Data in XML namespace prefix. + */ + const PFX_XMLMIME = 'xmlmime'; + + /** + * XML Schema namespace prefix. + */ + const PFX_XML_SCHEMA = 'xsd'; + + /** + * XML Schema instance namespace prefix. + */ + const PFX_XML_SCHEMA_INSTANCE = 'xsi'; + + /** + * XML-binary Optimized Packaging namespace prefix. + */ + const PFX_XOP = 'xop'; + + /** + * Generate a pseudo-random version 4 UUID. + * + * @see http://de.php.net/manual/en/function.uniqid.php#94959 + * @return string + */ + public static function generateUUID() + { + return sprintf( + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + // 32 bits for "time_low" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), + // 16 bits for "time_mid" + mt_rand(0, 0xffff), + // 16 bits for "time_hi_and_version", + // four most significant bits holds version number 4 + mt_rand(0, 0x0fff) | 0x4000, + // 16 bits, 8 bits for "clk_seq_hi_res", + // 8 bits for "clk_seq_low", + // two most significant bits holds zero and one for variant DCE1.1 + mt_rand(0, 0x3fff) | 0x8000, + // 48 bits for "node" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) + ); + } + + /** + * Get SOAP namespace for the given $version. + * + * @param int $version SOAP_1_1|SOAP_1_2 + * @return string + */ + public static function getSoapNamespace($version) + { + if ($version === SOAP_1_2) { + return self::NS_SOAP_1_2; + } else { + return self::NS_SOAP_1_1; + } + } + + /** + * Get SOAP version from namespace URI. + * + * @param string $namespace NS_SOAP_1_1|NS_SOAP_1_2 + * @return int SOAP_1_1|SOAP_1_2 + */ + public static function getSoapVersionFromNamespace($namespace) + { + if ($namespace === self::NS_SOAP_1_2) { + return SOAP_1_2; + } else { + return SOAP_1_1; + } + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/MultiPart.php b/src/BeSimple/SoapCommon/Mime/MultiPart.php new file mode 100644 index 0000000..1a3b7e1 --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/MultiPart.php @@ -0,0 +1,170 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Mime; + +use BeSimple\SoapCommon\Helper; + +/** + * Mime multi part container. + * + * Headers: + * - MIME-Version + * - Content-Type + * - Content-ID + * - Content-Location + * - Content-Description + * + * @author Andreas Schamberger + */ +class MultiPart extends PartHeader +{ + /** + * Content-ID of main part. + * + * @var string + */ + protected $mainPartContentId; + + /** + * Mime parts. + * + * @var array(\BeSimple\SoapCommon\Mime\Part) + */ + protected $parts = array(); + + /** + * Construct new mime object. + * + * @param string $boundary Boundary string + * @return void + */ + public function __construct($boundary = null) + { + $this->setHeader('MIME-Version', '1.0'); + $this->setHeader('Content-Type', 'multipart/related'); + $this->setHeader('Content-Type', 'type', 'text/xml'); + $this->setHeader('Content-Type', 'charset', 'utf-8'); + if (is_null($boundary)) { + $boundary = $this->generateBoundary(); + } + $this->setHeader('Content-Type', 'boundary', $boundary); + } + + /** + * Get mime message of this object (without headers). + * + * @param boolean $withHeaders Returned mime message contains headers + * @return string + */ + public function getMimeMessage($withHeaders = false) + { + $message = ($withHeaders === true) ? $this->generateHeaders() : ""; + // add parts + foreach ($this->parts as $part) { + $message .= "\r\n" . '--' . $this->getHeader('Content-Type', 'boundary') . "\r\n"; + $message .= $part->getMessagePart(); + } + $message .= "\r\n" . '--' . $this->getHeader('Content-Type', 'boundary') . '--'; + return $message; + } + + /** + * Get string array with MIME headers for usage in HTTP header (with CURL). + * Only 'Content-Type' and 'Content-Description' headers are returned. + * + * @return arrray(string) + */ + public function getHeadersForHttp() + { + $allowed = array( + 'Content-Type', + 'Content-Description', + ); + $headers = array(); + foreach ($this->headers as $fieldName => $value) { + if (in_array($fieldName, $allowed)) { + $fieldValue = $this->generateHeaderFieldValue($value); + // for http only ISO-8859-1 + $headers[] = $fieldName . ': '. iconv('utf-8', 'ISO-8859-1//TRANSLIT', $fieldValue); + } + } + return $headers; + } + + /** + * Add new part to MIME message. + * + * @param \BeSimple\SoapCommon\Mime\Part $part Part that is added + * @param boolean $isMain Is the given part the main part of mime message + * @return void + */ + public function addPart(Part $part, $isMain = false) + { + $contentId = trim($part->getHeader('Content-ID'), '<>'); + if ($isMain === true) { + $this->mainPartContentId = $contentId; + $this->setHeader('Content-Type', 'start', $part->getHeader('Content-ID')); + } + $this->parts[$contentId] = $part; + } + + /** + * Get part with given content id. If there is no content id given it + * returns the main part that is defined through the content-id start + * parameter. + * + * @param string $contentId Content id of desired part + * @return \BeSimple\SoapCommon\Mime\Part|null + */ + public function getPart($contentId = null) + { + if (is_null($contentId)) { + $contentId = $this->mainPartContentId; + } + if (isset($this->parts[$contentId])) { + return $this->parts[$contentId]; + } + return null; + } + + /** + * Get all parts. + * + * @param boolean $includeMainPart Should main part be in result set + * @return array(\BeSimple\SoapCommon\Mime\Part) + */ + public function getParts($includeMainPart = false) + { + if ($includeMainPart === true) { + $parts = $this->parts; + } else { + $parts = array(); + foreach ($this->parts as $cid => $part) { + if ($cid != $this->mainPartContentId) { + $parts[$cid] = $part; + } + } + } + return $parts; + } + + /** + * Returns a unique boundary string. + * + * @return string + */ + protected function generateBoundary() + { + return 'urn:uuid:' . Helper::generateUUID(); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/Parser.php b/src/BeSimple/SoapCommon/Mime/Parser.php new file mode 100644 index 0000000..ab41b97 --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/Parser.php @@ -0,0 +1,184 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Mime; + +/** + * Simple Multipart-Mime parser. + * + * @author Andreas Schamberger + */ +class Parser +{ + /** + * Parse the given Mime-Message and return a \BeSimple\SoapCommon\Mime\MultiPart object. + * + * @param string $mimeMessage Mime message string + * @param array(string=>string) $headers Array of header elements (e.g. coming from http request) + * @return \BeSimple\SoapCommon\Mime\MultiPart + */ + public static function parseMimeMessage($mimeMessage, array $headers = array()) + { + $boundary = null; + $start = null; + $multipart = new MultiPart(); + // add given headers, e.g. coming from HTTP headers + if (count($headers) > 0) { + foreach ($headers as $name => $value) { + if ($name == 'Content-Type') { + self::parseContentTypeHeader($multipart, $name, $value); + $boundary = $multipart->getHeader('Content-Type', 'boundary'); + $start = $multipart->getHeader('Content-Type', 'start'); + } else { + $multipart->setHeader($name, $value); + } + } + } + $hitFirstBoundary = false; + $inHeader = true; + $content = ''; + $currentPart = $multipart; + $lines = preg_split("/\r\n|\n/", $mimeMessage); + foreach ($lines as $line) { + // ignore http status code and POST * + if (substr($line, 0, 5) == 'HTTP/' || substr($line, 0, 4) == 'POST') { + continue; + } + if (isset($currentHeader)) { + if (isset($line[0]) && ($line[0] === ' ' || $line[0] === "\t")) { + $currentHeader .= $line; + continue; + } + if (strpos($currentHeader, ':') !== false) { + list($headerName, $headerValue) = explode(':', $currentHeader, 2); + $headerValue = iconv_mime_decode($headerValue, 0, 'utf-8'); + if (strpos($headerValue, ';') !== false) { + self::parseContentTypeHeader($currentPart, $headerName, $headerValue); + $boundary = $multipart->getHeader('Content-Type', 'boundary'); + $start = $multipart->getHeader('Content-Type', 'start'); + } else { + $currentPart->setHeader($headerName, trim($headerValue)); + } + } + unset($currentHeader); + } + if ($inHeader) { + if ($line == '') { + $inHeader = false; + continue; + } + $currentHeader = $line; + continue; + } else { + // check if we hit any of the boundaries + if (strlen($line) > 0 && $line[0] == "-") { + if (strcmp(trim($line), '--' . $boundary) === 0) { + if ($currentPart instanceof Part) { + $content = iconv_substr($content, 0, -2, 'utf-8'); + self::decodeContent($currentPart, $content); + // check if there is a start parameter given, if not set first part + $isMain = (is_null($start) || $start == $currentPart->getHeader('Content-ID')) ? true : false; + if ($isMain === true) { + $start = $currentPart->getHeader('Content-ID'); + } + $multipart->addPart($currentPart, $isMain); + } + $currentPart = new Part(); + $hitFirstBoundary = true; + $inHeader = true; + $content = ''; + } elseif (strcmp(trim($line), '--' . $boundary . '--') === 0) { + $content = iconv_substr($content, 0, -2, 'utf-8'); + self::decodeContent($currentPart, $content); + // check if there is a start parameter given, if not set first part + $isMain = (is_null($start) || $start == $currentPart->getHeader('Content-ID')) ? true : false; + if ($isMain === true) { + $start = $currentPart->getHeader('Content-ID'); + } + $multipart->addPart($currentPart, $isMain); + $content = ''; + } + } else { + if ($hitFirstBoundary === false) { + if ($line != '') { + $inHeader = true; + $currentHeader = $line; + continue; + } + } + $content .= $line . "\r\n"; + } + } + + } + return $multipart; + } + + /** + * Parse a "Content-Type" header with multiple sub values. + * e.g. Content-Type: multipart/related; boundary=boundary; type=text/xml; + * start="<123@abc>" + * + * Based on: https://labs.omniti.com/alexandria/trunk/OmniTI/Mail/Parser.php + * + * @param \BeSimple\SoapCommon\Mime\PartHeader $part Header part + * @param string $headerName Header name + * @param string $headerValue Header value + * @return null + */ + private static function parseContentTypeHeader(PartHeader $part, $headerName, $headerValue) + { + list($value, $remainder) = explode(';', $headerValue, 2); + $value = trim($value); + $part->setHeader($headerName, $value); + $remainder = trim($remainder); + while (strlen($remainder) > 0) { + if (!preg_match('/^([a-zA-Z0-9_-]+)=(.{1})/', $remainder, $matches)) { + break; + } + $name = $matches[1]; + $delimiter = $matches[2]; + $remainder = substr($remainder, strlen($name)+1); + if (!preg_match('/([^;]+)(;)?(\s|$)?/', $remainder, $matches)) { + break; + } + $value = rtrim($matches[1], ';'); + if ($delimiter == "'" || $delimiter == '"') { + $value = trim($value, $delimiter); + } + $part->setHeader($headerName, $name, $value); + $remainder = substr($remainder, strlen($matches[0])); + } + } + + /** + * Decodes the content of a Mime part. + * + * @param \BeSimple\SoapCommon\Mime\Part $part Part to add content + * @param string $content Content to decode + * @return null + */ + private static function decodeContent(Part $part, $content) + { + $encoding = strtolower($part->getHeader('Content-Transfer-Encoding')); + $charset = strtolower($part->getHeader('Content-Type', 'charset')); + if ($encoding == Part::ENCODING_BASE64) { + $content = base64_decode($content); + } elseif ($encoding == Part::ENCODING_QUOTED_PRINTABLE) { + $content = quoted_printable_decode($content); + } + if ($charset != 'utf-8') { + $content = iconv($charset, 'utf-8', $content); + } + $part->setContent($content); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/Part.php b/src/BeSimple/SoapCommon/Mime/Part.php new file mode 100644 index 0000000..88a2ca7 --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/Part.php @@ -0,0 +1,166 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Mime; + +use BeSimple\SoapCommon\Helper; + +/** + * Mime part. Everything must be UTF-8. Default charset for text is UTF-8. + * + * Headers: + * - Content-Type + * - Content-Transfer-Encoding + * - Content-ID + * - Content-Location + * - Content-Description + * + * @author Andreas Schamberger + */ +class Part extends PartHeader +{ + /** + * Encoding type base 64 + */ + const ENCODING_BASE64 = 'base64'; + + /** + * Encoding type binary + */ + const ENCODING_BINARY = 'binary'; + + /** + * Encoding type eight bit + */ + const ENCODING_EIGHT_BIT = '8bit'; + + /** + * Encoding type seven bit + */ + const ENCODING_SEVEN_BIT = '7bit'; + + /** + * Encoding type quoted printable + */ + const ENCODING_QUOTED_PRINTABLE = 'quoted-printable'; + + /** + * Content. + * + * @var mixed + */ + protected $content; + + /** + * Construct new mime object. + * + * @param mixed $content Content + * @param string $contentType Content type + * @param string $charset Charset + * @param string $encoding Encoding + * @param string $contentId Content id + * @return void + */ + public function __construct($content = null, $contentType = 'application/octet-stream', $charset = null, $encoding = self::ENCODING_BINARY, $contentId = null) + { + $this->content = $content; + $this->setHeader('Content-Type', $contentType); + if (!is_null($charset)) { + $this->setHeader('Content-Type', 'charset', $charset); + } else { // if (substr($contentType, 0, 4) == 'text') { + $this->setHeader('Content-Type', 'charset', 'utf-8'); + } + $this->setHeader('Content-Transfer-Encoding', $encoding); + if (is_null($contentId)) { + $contentId = $this->generateContentId(); + } + $this->setHeader('Content-ID', '<' . $contentId . '>'); + } + + /** + * __toString. + * + * @return mixed + */ + public function __toString() + { + return $this->content; + } + + /** + * Get mime content. + * + * @return mixed + */ + public function getContent() + { + return $this->content; + } + + /** + * Set mime content. + * + * @param mixed $content Content to set + * @return void + */ + public function setContent($content) + { + $this->content = $content; + } + + /** + * Get complete mime message of this object. + * + * @return string + */ + public function getMessagePart() + { + return $this->generateHeaders() . "\r\n" . $this->generateBody(); + } + + /** + * Generate body. + * + * @return string + */ + protected function generateBody() + { + $encoding = strtolower($this->getHeader('Content-Transfer-Encoding')); + $charset = strtolower($this->getHeader('Content-Type', 'charset')); + if ($charset != 'utf-8') { + $content = iconv('utf-8', $charset . '//TRANSLIT', $this->content); + } else { + $content = $this->content; + } + switch ($encoding) { + case self::ENCODING_BASE64: + return substr(chunk_split(base64_encode($content), 76, "\r\n"), -2); + case self::ENCODING_QUOTED_PRINTABLE: + return quoted_printable_encode($content); + case self::ENCODING_BINARY: + case self::ENCODING_SEVEN_BIT: + case self::ENCODING_EIGHT_BIT: + default: + return preg_replace("/\r\n|\r|\n/", "\r\n", $content); + } + } + + /** + * Returns a unique ID to be used for the Content-ID header. + * + * @return string + */ + protected function generateContentId() + { + return 'urn:uuid:' . Helper::generateUUID(); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapCommon/Mime/PartHeader.php b/src/BeSimple/SoapCommon/Mime/PartHeader.php new file mode 100644 index 0000000..8e78bea --- /dev/null +++ b/src/BeSimple/SoapCommon/Mime/PartHeader.php @@ -0,0 +1,142 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon\Mime; + +/** + * Mime part base class. + * + * @author Andreas Schamberger + */ +abstract class PartHeader +{ + /** + * Mime headers. + * + * @var array(string=>mixed|array(mixed)) + */ + protected $headers = array(); + + /** + * Add a new header to the mime part. + * + * @param string $name Header name + * @param string $value Header value + * @param string $subValue Is sub value? + * @return void + */ + public function setHeader($name, $value, $subValue = null) + { + if (isset($this->headers[$name]) && !is_null($subValue)) { + if (!is_array($this->headers[$name])) { + $this->headers[$name] = array( + '@' => $this->headers[$name], + $value => $subValue, + ); + } else { + $this->headers[$name][$value] = $subValue; + } + } elseif (isset($this->headers[$name]) && is_array($this->headers[$name]) && isset($this->headers[$name]['@'])) { + $this->headers[$name]['@'] = $value; + } else { + $this->headers[$name] = $value; + } + } + + /** + * Get given mime header. + * + * @param string $name Header name + * @param string $subValue Sub value name + * @return mixed|array(mixed) + */ + public function getHeader($name, $subValue = null) + { + if (isset($this->headers[$name])) { + if (!is_null($subValue)) { + if (is_array($this->headers[$name]) && isset($this->headers[$name][$subValue])) { + return $this->headers[$name][$subValue]; + } else { + return null; + } + } elseif (is_array($this->headers[$name]) && isset($this->headers[$name]['@'])) { + return $this->headers[$name]['@']; + } else { + return $this->headers[$name]; + } + } + return null; + } + + /** + * Generate headers. + * + * @return string + */ + protected function generateHeaders() + { + $charset = strtolower($this->getHeader('Content-Type', 'charset')); + $preferences = array( + 'scheme' => 'Q', + 'input-charset' => 'utf-8', + 'output-charset' => $charset, + ); + $headers = ''; + foreach ($this->headers as $fieldName => $value) { + $fieldValue = $this->generateHeaderFieldValue($value); + // do not use proper encoding as Apache Axis does not understand this + // $headers .= iconv_mime_encode($field_name, $field_value, $preferences) . "\r\n"; + $headers .= $fieldName . ': ' . $fieldValue . "\r\n"; + } + return $headers; + } + + /** + * Generates a header field value from the given value paramater. + * + * @param array(string=>string)|string $value Header value + * @return string + */ + protected function generateHeaderFieldValue($value) + { + $fieldValue = ''; + if (is_array($value)) { + if (isset($value['@'])) { + $fieldValue .= $value['@']; + } + foreach ($value as $subName => $subValue) { + if ($subName != '@') { + $fieldValue .= '; ' . $subName . '=' . $this->quoteValueString($subValue); + } + } + } else { + $fieldValue .= $value; + } + return $fieldValue; + } + + /** + * Quote string with '"' if it contains one of the special characters: + * "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "=" + * + * @param string $string String to quote + * @return string + */ + private function quoteValueString($string) + { + if (preg_match('~[()<>@,;:\\"/\[\]?=]~', $string)) { + return '"' . $string . '"'; + } else { + return $string; + } + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/MimePartHeader.php b/tests/BeSimple/Tests/SoapCommon/Fixtures/MimePartHeader.php new file mode 100644 index 0000000..2ecd558 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/MimePartHeader.php @@ -0,0 +1,9 @@ + +--xxx-MIME-Boundary-xxx-0xa36cb38-0a36cb38-xxx-END-xxx +Content-ID: <0x9d6ad00-0xa19ef48-0x9de7500-0xa4fae78-0xa382698> +Content-Type: application/binary + + + + +--xxx-MIME-Boundary-xxx-0xa36cb38-0a36cb38-xxx-END-xxx-- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-axis.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-axis.txt new file mode 100644 index 0000000..58cda6f --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/SwA-response-axis.txt @@ -0,0 +1,13 @@ +HTTP/1.1 200 OK +Date: Sat, 11 Sep 2010 12:52:57 GMT +Server: Simple-Server/1.1 +Transfer-Encoding: chunked +Content-Type: multipart/related; boundary=MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582; type="application/soap+xml"; start="<0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org>"; action="urn:getVersionResponse" + +--MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582 +Content-Type: application/soap+xml; charset=utf-8 +Content-Transfer-Encoding: 8bit +Content-ID: <0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org> + +urn:getVersionResponseuuid:665aab53-4cef-4435-8934-4c10ed24fd42Hi - the Axis2 version is 1.5.1 +--MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582-- \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request.txt new file mode 100644 index 0000000..efe9192 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request.txt @@ -0,0 +1,22 @@ +POST http://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8 HTTP/1.1 +Content-Type: multipart/related; type="application/xop+xml";start="";boundary="uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7";start-info="application/soap+xml" +Host: 131.107.72.15 +Content-Length: 1941 +Expect: 100-continue +HTTP/1.1 100 Continue + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: 8bit +Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" + +http://xmlsoap.org/echoBinaryAsStringurn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8ahttp://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8 + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: binary +Content-Type: application/octet-stream + +H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7-- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request_noheader.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request_noheader.txt new file mode 100644 index 0000000..f6d9942 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-request_noheader.txt @@ -0,0 +1,16 @@ + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: 8bit +Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" + +http://xmlsoap.org/echoBinaryAsStringurn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8ahttp://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://131.107.72.15/Mtom/svc/service.svc/Soap12MtomUTF8 + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7 +Content-ID: +Content-Transfer-Encoding: binary +Content-Type: application/octet-stream + +H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! H e l l o W o r l d ! + +--uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7-- diff --git a/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-response.txt b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-response.txt new file mode 100644 index 0000000..7a76cb4 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Fixtures/WS-I-MTOM-response.txt @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +Proxy-Connection: Keep-Alive +Connection: Keep-Alive +Content-Length: 1166 +Via: 1.1 RED-PRXY-03 +Date: Fri, 09 Sep 2005 06:57:22 GMT +Content-Type: multipart/related; type="application/xop+xml";start="";boundary="uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46";start-info="application/soap+xml" + + + +--uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46 +Content-ID: +Content-Transfer-Encoding: 8bit +Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" + +*urn:uuid:1bf061d6-d532-4b0c-930b-8b8202c38b8ahttp://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousHello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World! +--uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46-- diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/MultiPartTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/MultiPartTest.php new file mode 100644 index 0000000..1d1ab16 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/MultiPartTest.php @@ -0,0 +1,144 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\MultiPart; +use BeSimple\SoapCommon\Mime\Part; +use BeSimple\SoapCommon\Mime\PartHeader; + +class MultiPartTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $mp = new MultiPart(); + + $this->assertEquals('1.0', $mp->getHeader('MIME-Version')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('text/xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertRegExp('~urn:uuid:.*~', $mp->getHeader('Content-Type', 'boundary')); + } + + public function testGetMimeMessage() + { + $mp = new MultiPart(); + + /* + string(51) " + --urn:uuid:a81ca327-591e-4656-91a1-8f177ada95b0--" + */ + $this->assertEquals(51, strlen($mp->getMimeMessage())); + + $p = new Part('test'); + $mp->addPart($p, true); + + /* + string(259) " + --urn:uuid:a81ca327-591e-4656-91a1-8f177ada95b0 + Content-Type: application/octet-stream; charset=utf-8 + Content-Transfer-Encoding: binary + Content-ID: + + test + --urn:uuid:a81ca327-591e-4656-91a1-8f177ada95b0--" + */ + $this->assertEquals(259, strlen($mp->getMimeMessage())); + } + + public function testGetMimeMessageWithHeaders() + { + $mp = new MultiPart(); + + /* + string(189) "MIME-Version: 1.0 + Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="urn:uuid:231833e2-a23b-410a-862e-250524fc38f6" + + --urn:uuid:231833e2-a23b-410a-862e-250524fc38f6--" + */ + $this->assertEquals(193, strlen($mp->getMimeMessage(true))); + + $p = new Part('test'); + $mp->addPart($p, true); + + /* + string(452) "MIME-Version: 1.0 + Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="urn:uuid:231833e2-a23b-410a-862e-250524fc38f6"; start="" + + --urn:uuid:231833e2-a23b-410a-862e-250524fc38f6 + Content-Type: application/octet-stream; charset=utf-8 + Content-Transfer-Encoding: binary + Content-ID: + + test + --urn:uuid:231833e2-a23b-410a-862e-250524fc38f6--" + */ + $this->assertEquals(458, strlen($mp->getMimeMessage(true))); + } + + public function testGetHeadersForHttp() + { + $mp = new MultiPart(); + + $result = array( + 'Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="' . $mp->getHeader('Content-Type', 'boundary') . '"', + ); + $this->assertEquals($result, $mp->getHeadersForHttp()); + + $result = array( + 'Content-Type: multipart/related; type="text/xml"; charset=utf-8; boundary="' . $mp->getHeader('Content-Type', 'boundary') . '"', + 'Content-Description: test', + ); + $mp->setHeader('Content-Description', 'test'); + $this->assertEquals($result, $mp->getHeadersForHttp()); + } + + public function testAddGetPart() + { + $mp = new MultiPart(); + + $p = new Part('test'); + $p->setHeader('Content-ID', 'mycontentid'); + $mp->addPart($p); + $this->assertEquals($p, $mp->getPart('mycontentid')); + } + + public function testAddGetPartWithMain() + { + $mp = new MultiPart(); + + $p = new Part('test'); + $mp->addPart($p, true); + $this->assertEquals($p, $mp->getPart()); + } + + public function testGetParts() + { + $mp = new MultiPart(); + + $p1 = new Part('test'); + $mp->addPart($p1, true); + $p2 = new Part('test'); + $mp->addPart($p2); + + $withoutMain = array( + trim($p2->getHeader('Content-ID'),'<>') => $p2, + ); + $this->assertEquals($withoutMain, $mp->getParts()); + + $withMain = array( + trim($p1->getHeader('Content-ID'),'<>') => $p1, + trim($p2->getHeader('Content-ID'),'<>') => $p2, + ); + $this->assertEquals($withMain, $mp->getParts(true)); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php new file mode 100644 index 0000000..8efa1f9 --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/ParserTest.php @@ -0,0 +1,153 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\MultiPart; +use BeSimple\SoapCommon\Mime\Parser; +use BeSimple\SoapCommon\Mime\Part; +use BeSimple\SoapCommon\Mime\PartHeader; + +class ParserTest extends \PHPUnit_Framework_TestCase +{ + public function testParserRequestWsi() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/WS-I-MTOM-request.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertsForWsiMtomRequest($mp); + } + + public function testParserResponseAmazon() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/SwA-response-amazon.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertEquals('Fri, 12 Feb 2010 15:46:00 GMT', $mp->getHeader('Date')); + $this->assertEquals('Server', $mp->getHeader('Server')); + $this->assertEquals('1.0', $mp->getHeader('MIME-Version')); + $this->assertEquals('close', $mp->getHeader('Cneonction')); + $this->assertEquals('chunked', $mp->getHeader('Transfer-Encoding')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('text/xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('xxx-MIME-Boundary-xxx-0xa36cb38-0a36cb38-xxx-END-xxx', $mp->getHeader('Content-Type', 'boundary')); + + $p1 = $mp->getPart(); + $this->assertEquals('text/xml', $p1->getHeader('Content-Type')); + $this->assertEquals('UTF-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals(389, strlen($p1->getContent())); + + $p2 = $mp->getPart('0x9d6ad00-0xa19ef48-0x9de7500-0xa4fae78-0xa382698'); + $this->assertEquals('binary', $p2->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/binary', $p2->getHeader('Content-Type')); + $this->assertEquals(81, strlen($p2->getContent())); + } + + public function testParserResponseAxis() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/SwA-response-axis.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertEquals('Sat, 11 Sep 2010 12:52:57 GMT', $mp->getHeader('Date')); + $this->assertEquals('Simple-Server/1.1', $mp->getHeader('Server')); + $this->assertEquals('1.0', $mp->getHeader('MIME-Version')); + $this->assertEquals('chunked', $mp->getHeader('Transfer-Encoding')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('application/soap+xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('<0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org>', $mp->getHeader('Content-Type', 'start')); + $this->assertEquals('urn:getVersionResponse', $mp->getHeader('Content-Type', 'action')); + $this->assertEquals('MIMEBoundaryurn_uuid_2DB7ABF3DC5BED7FA51284209577582', $mp->getHeader('Content-Type', 'boundary')); + + $p1 = $mp->getPart('0.urn:uuid:2DB7ABF3DC5BED7FA51284209577583@apache.org'); + $this->assertEquals('8bit', $p1->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/soap+xml', $p1->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals(499, strlen($p1->getContent())); + } + + public function testParserResponseWsi() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/WS-I-MTOM-response.txt'; + $mimeMessage = file_get_contents($filename); + + $mp = Parser::parseMimeMessage($mimeMessage); + $this->assertEquals('Keep-Alive', $mp->getHeader('Proxy-Connection')); + $this->assertEquals('Keep-Alive', $mp->getHeader('Connection')); + $this->assertEquals('1166', $mp->getHeader('Content-Length')); + $this->assertEquals('1.1 RED-PRXY-03', $mp->getHeader('Via')); + $this->assertEquals('Fri, 09 Sep 2005 06:57:22 GMT', $mp->getHeader('Date')); + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('application/xop+xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('', $mp->getHeader('Content-Type', 'start')); + $this->assertEquals('application/soap+xml', $mp->getHeader('Content-Type', 'start-info')); + $this->assertEquals('uuid:b71dc628-ec8f-4422-8a4a-992f041cb94c+id=46', $mp->getHeader('Content-Type', 'boundary')); + + $p1 = $mp->getPart('http://tempuri.org/0'); + $this->assertEquals('8bit', $p1->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/xop+xml', $p1->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals('application/soap+xml', $p1->getHeader('Content-Type', 'type')); + $this->assertEquals(910, strlen($p1->getContent())); + } + + public function testParserWithHeaderArray() + { + $filename = dirname(__DIR__).DIRECTORY_SEPARATOR.'Fixtures/WS-I-MTOM-request_noheader.txt'; + $mimeMessage = file_get_contents($filename); + + $headers = array( + 'Content-Type' => 'multipart/related; type="application/xop+xml";start="";boundary="uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7";start-info="application/soap+xml"', + 'Content-Length' => 1941, + 'Host' => '131.107.72.15', + 'Expect' => '100-continue', + ); + + $mp = Parser::parseMimeMessage($mimeMessage, $headers); + $this->assertsForWsiMtomRequest($mp); + } + + /* + * used in: + * - testParserWithHeaderArray + * - testParserRequestWsi + */ + private function assertsForWsiMtomRequest(MultiPart $mp) + { + $this->assertEquals('multipart/related', $mp->getHeader('Content-Type')); + $this->assertEquals('application/xop+xml', $mp->getHeader('Content-Type', 'type')); + $this->assertEquals('utf-8', $mp->getHeader('Content-Type', 'charset')); + $this->assertEquals('', $mp->getHeader('Content-Type', 'start')); + $this->assertEquals('application/soap+xml', $mp->getHeader('Content-Type', 'start-info')); + $this->assertEquals('uuid:0ca0e16e-feb1-426c-97d8-c4508ada5e82+id=7', $mp->getHeader('Content-Type', 'boundary')); + $this->assertEquals('1941', $mp->getHeader('Content-Length')); + $this->assertEquals('131.107.72.15', $mp->getHeader('Host')); + $this->assertEquals('100-continue', $mp->getHeader('Expect')); + + $p1 = $mp->getPart('http://tempuri.org/0'); + $this->assertEquals('8bit', $p1->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/xop+xml', $p1->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p1->getHeader('Content-Type', 'charset')); + $this->assertEquals('application/soap+xml', $p1->getHeader('Content-Type', 'type')); + $this->assertEquals(737, strlen($p1->getContent())); + + $p2 = $mp->getPart('http://tempuri.org/1/632618206527087310'); + $this->assertEquals('binary', $p2->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('application/octet-stream', $p2->getHeader('Content-Type')); + $this->assertEquals(769, strlen($p2->getContent())); + } +} diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/PartHeaderTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/PartHeaderTest.php new file mode 100644 index 0000000..ef9f08d --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/PartHeaderTest.php @@ -0,0 +1,57 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\PartHeader; +use BeSimple\Tests\SoapCommon\Fixtures\MimePartHeader; + +class PartHeaderTest extends \PHPUnit_Framework_TestCase +{ + public function testSetGetHeader() + { + $ph = new MimePartHeader(); + $ph->setHeader('Content-Type', 'text/xml'); + $this->assertEquals('text/xml', $ph->getHeader('Content-Type')); + } + + public function testSetGetHeaderSubvalue() + { + $ph = new MimePartHeader(); + $ph->setHeader('Content-Type', 'utf-8', 'charset'); + $this->assertEquals(null, $ph->getHeader('Content-Type', 'charset')); + + $ph->setHeader('Content-Type', 'text/xml'); + $ph->setHeader('Content-Type', 'charset', 'utf-8'); + $this->assertEquals('utf-8', $ph->getHeader('Content-Type', 'charset')); + } + + public function testGenerateHeaders() + { + $ph = new MimePartHeader(); + + $class = new \ReflectionClass($ph); + $method = $class->getMethod('generateHeaders'); + $method->setAccessible(true); + + $this->assertEquals('', $method->invoke($ph)); + + $ph->setHeader('Content-Type', 'text/xml'); + $this->assertEquals("Content-Type: text/xml\r\n", $method->invoke($ph)); + + $ph->setHeader('Content-Type', 'charset', 'utf-8'); + $this->assertEquals("Content-Type: text/xml; charset=utf-8\r\n", $method->invoke($ph)); + + $ph->setHeader('Content-Type', 'type', 'text/xml'); + $this->assertEquals("Content-Type: text/xml; charset=utf-8; type=\"text/xml\"\r\n", $method->invoke($ph)); + } +} \ No newline at end of file diff --git a/tests/BeSimple/Tests/SoapCommon/Mime/PartTest.php b/tests/BeSimple/Tests/SoapCommon/Mime/PartTest.php new file mode 100644 index 0000000..031ba5b --- /dev/null +++ b/tests/BeSimple/Tests/SoapCommon/Mime/PartTest.php @@ -0,0 +1,62 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\Tests\SoapCommon\Soap; + +use BeSimple\SoapCommon\Mime\Part; +use BeSimple\SoapCommon\Mime\PartHeader; + +class PartTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $p = new Part('', 'text/xml', 'utf-8', Part::ENCODING_BINARY, 'urn:myuniqueresource'); + + $this->assertEquals('', $p->getContent()); + $this->assertEquals('text/xml', $p->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p->getHeader('Content-Type', 'charset')); + $this->assertEquals(Part::ENCODING_BINARY, $p->getHeader('Content-Transfer-Encoding')); + $this->assertEquals('', $p->getHeader('Content-ID')); + } + + public function testDefaultConstructor() + { + $p = new Part(); + + $this->assertNull($p->getContent()); + $this->assertEquals('application/octet-stream', $p->getHeader('Content-Type')); + $this->assertEquals('utf-8', $p->getHeader('Content-Type', 'charset')); + $this->assertEquals(Part::ENCODING_BINARY, $p->getHeader('Content-Transfer-Encoding')); + $this->assertRegExp('~~', $p->getHeader('Content-ID')); + } + + public function testSetContent() + { + $p = new Part(); + + $p->setContent(''); + $this->assertEquals('', $p->getContent()); + } + + public function testGetMessagePart() + { + $p = new Part('', 'text/xml', 'utf-8', Part::ENCODING_BINARY, 'urn:myuniqueresource'); + + $messagePart = "Content-Type: text/xml; charset=utf-8\r\n" . + "Content-Transfer-Encoding: binary\r\n" . + "Content-ID: \r\n" . + "\r\n". + ""; + + $this->assertEquals($messagePart, $p->getMessagePart()); + } +} \ No newline at end of file