tiamo / phpas2

PHPAS2 is a php-based implementation of the EDIINT AS2 standard.
42 stars 41 forks source link

isReport() in processMdn returning false on text/html #30

Closed mackieee closed 3 years ago

mackieee commented 3 years ago

Afternoon!

To begin, I apologise if this isn't a bug! I've incorporated the awesome phpas2 library into a CodeIginiter setup. I've encountered an issue where, I think it may be a bug - although I'm not entirely sure it's a fault of the framework or my build up of a request/response.

I'm able to send payloads to a receiver server successfully, read & process them. It's when the synchronous MDN response is generated - the sender fails to accept that the response is an MDN.

// Raise error if message is not an MDN
if (!$payload->isReport()) {
    throw new \RuntimeException('MDN report not found in the response [' . $payload . ']');
}

This, returns false as per the log:

2021-06-23 13:15:46 - debug - Build the AS2 message to send to the partner - []
2021-06-23 13:15:46 - debug - Signing the message using partner key - []
2021-06-23 13:15:46 - debug - Calculate MIC - {"mic":"pmqfSeaJlqgMtiLo\/XlOtDUvlktpAe7nqMR7A\/F9qjE=, sha256"}
2021-06-23 13:15:46 - debug - Encrypting the message using partner public key - []
2021-06-23 13:15:46 - debug - AS2 message has been built successfully - []
2021-06-23 13:15:46 - info - Sender: Built Message, now sending. - []
2021-06-23 13:15:46 - debug - AS2 message successfully sent to partner - []
2021-06-23 13:15:46 - debug - Synchronous MDN received from partner - []
2021-06-23 13:15:46 - critical - MDN report not found in the response [
Date: Wed, 23 Jun 2021 13:15:22 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: ci_session=dq7rmnactn6dg4gs48q40pkggsuqhhb1; expires=Wed, 23-Jun-2021 17:55:22 GMT; Max-Age=16800; path=/; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 3312
Content-Type: text/html; charset=UTF-8

------569A442EF7514191F2563C50F0DB4B7B
Content-Type: multipart/report; report-type=disposition-notification; boundary="----=_361855cff16330619ada7062af3b983a10a9eaae"

------=_361855cff16330619ada7062af3b983a10a9eaae
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Your message was successfully received and processed.

------=_361855cff16330619ada7062af3b983a10a9eaae
Content-Type: message/disposition-notification
Content-Transfer-Encoding: 7bit

Reporting-UA: PHPAS2
Original-Recipient: rfc822; PRIMARYSERVER
Final-Recipient: rfc822; PRIMARYSERVER
Original-Message-ID: <2021-06-23-60d33402b52390.09254653@mypc.computername>
Disposition: automatic-action/MDN-sent-automatically; processed
Received-Content-MIC: pmqfSeaJlqgMtiLo/XlOtDUvlktpAe7nqMR7A/F9qjE=, sha256

------=_361855cff16330619ada7062af3b983a10a9eaae--

------569A442EF7514191F2563C50F0DB4B7B
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
Content-Type: application/pkcs7-signature; name=smime.p7s; smime-type=signed-data

MIIGWQYJKoZIhvcNAQcCoIIGSjCCBkYCAQExDzANBglghkgBZQMEAgEFADALBgkq
hkiG9w0BBwGgggN+MIIDejCCAmICCQCX1XKw7jN4BjANBgkqhkiG9w0BAQsFADB...

The above MDN response is returning false on MimePart.php at line 157:

/**
 * @return bool
 */
public function isReport()
{
      $isReport = $this->getParsedHeader('content-type', 0, 0) === self::MULTIPART_REPORT;

      if ($isReport) {
          return true;
      }

      if ($this->isSigned()) {
            foreach ($this->getParts() as $part) {
                  if ($part->isReport()) {
                        return true;
                  }
            }
      }

      return false;
}

Where $this->getParsedHeader('content-type', 0, 0) is returning text/html. Which I can see is the outer envelope of the message payload.

I can see later in the processMdn() method, it loops through the parts and checks for the message/disposition-notification - which it does find when I make isReport() to default true.

However, I'm unsure if other AS2 Servers & Clients would also test the first outer Content-type thus would also fail.

mackieee commented 3 years ago

Well this appears to be CodeIginiter forcing it's own set of headers upon final output, it defaults to text/html unless specified otherwise.

/** @var AS2\Server **/
$this->as2Server = new AS2\Server( $this->as2Manager, $this->partnerRepository, $this->messageRepository );

/** @var \GuzzleHttp\Psr7\Response $response */
$response = $this->as2Server->execute();

/** Send the MDN. **/
/* header( "Content-Type: multipart/report" ); - This works too */
$this->output->set_header( "Content-Type: multipart/report", true );
echo $response->getBody()->getContents();

Although I would of thought execute()'s ending statement return new Response($responseStatus, $responseHeaders, $responseBody); would have Guzzle set & override the headers, but it seems the Framework gets the final say.

Final message contents as a result:

Date: Wed, 23 Jun 2021 22:26:24 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: ci_session=fjdld1sjuaa12ui3ov1691ugilupe7e4; expires=Thu, 24-Jun-2021 03:06:24 GMT; Max-Age=16800; path=/; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 3312
Content-Type: multipart/report

------569A442EF7514191F2563C50F0DB4B7B
Content-Type: multipart/report; report-type=disposition-notification; boundary="----=_361855cff16330619ada7062af3b983a10a9eaae"

------=_361855cff16330619ada7062af3b983a10a9eaae
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Your message was successfully received and processed.

------=_361855cff16330619ada7062af3b983a10a9eaae
Content-Type: message/disposition-notification
Content-Transfer-Encoding: 7bit

Reporting-UA: PHPAS2
Original-Recipient: rfc822; PRIMARYSERVER
Final-Recipient: rfc822; PRIMARYSERVER
Original-Message-ID: <2021-06-23-60d33402b52390.09254653@mypc.computername>
Disposition: automatic-action/MDN-sent-automatically; processed
Received-Content-MIC: pmqfSeaJlqgMtiLo/XlOtDUvlktpAe7nqMR7A/F9qjE=, sha256