Skip to content

Commit 125361d

Browse files
committed
v1.3.4
1 parent aa6f182 commit 125361d

File tree

5 files changed

+66
-37
lines changed

5 files changed

+66
-37
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "tiamo/phpas2",
33
"description": "PHPAS2 is a php-based implementation of the EDIINT AS2 standard",
4-
"version": "1.3.3",
4+
"version": "1.3.4",
55
"authors": [
66
{
77
"name": "Vladyslav K",

src/CryptoHelper.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,20 @@ public static function calculateMIC($payload, $algo = 'sha256', $includeHeaders
4040
* Sign data which contains mime headers
4141
*
4242
* @param string|MimePart $data
43-
* @param string $cert
44-
* @param string $key
43+
* @param string|resource $cert
44+
* @param string|resource $privateKey
4545
* @param array $headers
4646
* @param array $micAlgo
4747
* @return MimePart
4848
* @throws \RuntimeException
4949
*/
50-
public static function sign($data, $cert, $key = null, $headers = [], $micAlgo = null)
50+
public static function sign($data, $cert, $privateKey = null, $headers = [], $micAlgo = null)
5151
{
5252
if ($data instanceof MimePart) {
5353
$data = self::getTempFilename($data->toString());
5454
}
5555
$temp = self::getTempFilename();
56-
if (! openssl_pkcs7_sign($data, $temp, $cert, $key, $headers, PKCS7_DETACHED)) {
56+
if (! openssl_pkcs7_sign($data, $temp, $cert, $privateKey, $headers, PKCS7_DETACHED)) {
5757
throw new \RuntimeException(
5858
sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string())
5959
);
@@ -67,6 +67,7 @@ public static function sign($data, $cert, $key = null, $headers = [], $micAlgo =
6767
if ($micAlgo) {
6868
$contentType = preg_replace('/micalg=(.+);/i', 'micalg="' . $micAlgo . '";', $contentType);
6969
}
70+
7071
/** @var MimePart $payload */
7172
$payload = $payload->withHeader('Content-Type', $contentType);
7273
foreach ($payload->getParts() as $key => $part) {
@@ -131,9 +132,11 @@ public static function encrypt($data, $cert, $cipher = OPENSSL_CIPHER_3DES)
131132
if ($data instanceof MimePart) {
132133
$data = self::getTempFilename((string) $data);
133134
}
135+
134136
if (is_string($cipher) && defined('OPENSSL_CIPHER_' . strtoupper($cipher))) {
135137
$cipher = constant('OPENSSL_CIPHER_' . strtoupper($cipher));
136138
}
139+
137140
$temp = self::getTempFilename();
138141
if (! openssl_pkcs7_encrypt($data, $temp, (array) $cert, [], PKCS7_BINARY, $cipher)) {
139142
throw new \RuntimeException(
@@ -156,6 +159,7 @@ public static function decrypt($data, $cert, $key = null)
156159
if ($data instanceof MimePart) {
157160
$data = self::getTempFilename((string) $data);
158161
}
162+
159163
$temp = self::getTempFilename();
160164
if (! openssl_pkcs7_decrypt($data, $temp, $cert, $key)) {
161165
throw new \RuntimeException(
@@ -218,8 +222,10 @@ public static function compress($data, $encoding = MimePart::ENCODING_BASE64)
218222
public static function decompress($data, $encoding = MimePart::ENCODING_BASE64)
219223
{
220224
if ($data instanceof MimePart) {
221-
// $encoding = $data->getHeaderLine('Content-Transfer-Encoding');
222225
$encoding = $data->getHeaderLine('Content-Encoding');
226+
if (empty($encoding)) {
227+
$encoding = $data->getHeaderLine('Content-Transfer-Encoding');
228+
}
223229
$data = $data->getBody();
224230
}
225231

src/Management.php

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
class Management implements LoggerAwareInterface
1111
{
1212
const AS2_VERSION = '1.2';
13+
const EDIINT_FEATURES = 'CEM'; // multiple-attachments,
1314
const USER_AGENT = 'PHPAS2';
1415

1516
/**
@@ -48,8 +49,12 @@ public function __construct($options = [])
4849
* @return MimePart
4950
* @throws \Exception
5051
*/
51-
public function buildMessageFromFile(MessageInterface $message, $filePath, $contentType = null, $encoding = 'binary')
52-
{
52+
public function buildMessageFromFile(
53+
MessageInterface $message,
54+
$filePath,
55+
$contentType = null,
56+
$encoding = 'binary'
57+
) {
5358
if (! $contentType) {
5459
$contentType = $message->getReceiver()->getContentType();
5560
}
@@ -98,8 +103,8 @@ public function buildMessage(MessageInterface $message, $payload)
98103
'AS2-To' => $receiver->getAs2Id(),
99104
'Subject' => $receiver->getSubject() ? $receiver->getSubject() : 'AS2 Message',
100105
'Date' => date('r'),
101-
'Recipient-Address' => $receiver->getTargetUrl(),
102-
'Ediint-Features' => 'CEM',
106+
// 'Recipient-Address' => $receiver->getTargetUrl(),
107+
'Ediint-Features' => self::EDIINT_FEATURES,
103108
];
104109

105110
if (! ($payload instanceof MimePart)) {
@@ -148,13 +153,15 @@ public function buildMessage(MessageInterface $message, $payload)
148153
}
149154

150155
// If MDN is to be requested from the partner, set the appropriate headers
151-
if ($receiver->getMdnMode()) {
156+
if ($mdnMode = $receiver->getMdnMode()) {
152157

153-
$as2headers['Disposition-Notification-To'] = $sender->getTargetUrl();
154-
$as2headers['Disposition-Notification-Options'] = $receiver->getMdnOptions();
158+
$as2headers['Disposition-Notification-To'] = $sender->getEmail();
159+
if ($mdnOptions = $receiver->getMdnOptions()) {
160+
$as2headers['Disposition-Notification-Options'] = $mdnOptions;
161+
}
155162

156163
// PARTNER IS ASYNC MDN
157-
if ($receiver->getMdnMode() == PartnerInterface::MDN_MODE_ASYNC) {
164+
if ($mdnMode == PartnerInterface::MDN_MODE_ASYNC) {
158165
$message->setMdnMode(PartnerInterface::MDN_MODE_ASYNC);
159166
$as2headers['Receipt-Delivery-Option'] = $sender->getTargetUrl();
160167
} else {
@@ -174,7 +181,7 @@ public function buildMessage(MessageInterface $message, $payload)
174181

175182
$message->setHeaders($as2Message->getHeaderLines());
176183

177-
$this->getLogger()->debug('AS2 message has been built successfully, sending it to the partner');
184+
$this->getLogger()->debug('AS2 message has been built successfully');
178185

179186
return $as2Message;
180187
}
@@ -207,7 +214,7 @@ public function setLogger(LoggerInterface $logger)
207214
* Takes the message as argument and posts the as2 message to the partner.
208215
*
209216
* @param MessageInterface $message
210-
* @param MimePart|string $payload
217+
* @param MimePart $payload
211218
* @return \Psr\Http\Message\ResponseInterface|false
212219
* @throws \GuzzleHttp\Exception\GuzzleException
213220
*/
@@ -223,7 +230,7 @@ public function sendMessage(MessageInterface $message, $payload)
223230
$options = [
224231
'headers' => $payload->getHeaders(),
225232
'body' => $payload->getBody(),
226-
// 'cert' => '' // TODO: partner https cert ?
233+
// 'cert' => '' // TODO: partner https cert ?
227234
];
228235

229236
if ($partner->getAuthMethod()) {
@@ -249,6 +256,7 @@ public function sendMessage(MessageInterface $message, $payload)
249256

250257
$body = $response->getBody()->getContents();
251258
$payload = new MimePart($response->getHeaders(), $body);
259+
$response->getBody()->rewind();
252260

253261
$this->processMdn($message, $payload);
254262
}
@@ -330,15 +338,13 @@ public function processMdn(MessageInterface $message, $payload)
330338
$this->getLogger()->debug('Found MDN report for message', [$messageId]);
331339
try {
332340
$bodyPayload = MimePart::fromString($part->getBody());
333-
334341
if ($bodyPayload->hasHeader('disposition')) {
335342
$mdnStatus = $bodyPayload->getParsedHeader('Disposition', 0, 1);
336343
if ($mdnStatus == 'processed') {
337344
$this->getLogger()->debug('Message has been successfully processed, verifying the MIC if present.');
338345
// Compare the MIC of the received message
339346
$receivedMic = $bodyPayload->getHeaderLine('Received-Content-MIC');
340347
if ($receivedMic && $message->getMic()) {
341-
342348
if (Utils::normalizeMic($message->getMic()) != Utils::normalizeMic($receivedMic)) {
343349
throw new \Exception(
344350
sprintf('The Message Integrity Code (MIC) does not match the sent AS2 message (required: %s, returned: %s)',
@@ -372,7 +378,7 @@ public function processMdn(MessageInterface $message, $payload)
372378
*
373379
* @param MessageInterface $message
374380
* @param string $confirmationText
375-
* @param string $errorMessage
381+
* @param string $errorMessage // TODO: detailedStatus
376382
* @return MimePart
377383
* @throws \InvalidArgumentException
378384
*/
@@ -397,36 +403,40 @@ public function buildMdn(MessageInterface $message, $confirmationText = null, $e
397403

398404
// Parse Message Headers
399405
$messageHeaders = MimePart::fromString($message->getHeaders());
400-
$notificationOptions = $messageHeaders->hasHeader('disposition-notification-options');
406+
$isSigned = $messageHeaders->hasHeader('disposition-notification-options');
401407

402408
// TODO: parse (signed-receipt-protocol, signed-receipt-micalg)
403409
// $notificationOptions = Utils::parseHeader($notificationOptions);
404410

405-
$headers = [
411+
// Set up the message headers
412+
$mdnHeaders = [
406413
'Message-ID' => '<' . Utils::generateMessageID($receiver) . '>',
407414
'Date' => date('r'),
408-
'Ediint-Features' => 'CEM', // multiple-attachments, CEM
409415
'AS2-From' => $receiver->getAs2Id(),
410416
'AS2-To' => $sender->getAs2Id(),
411417
'AS2-Version' => self::AS2_VERSION,
412418
'User-Agent' => self::USER_AGENT,
413-
'Connection' => 'close',
419+
'Ediint-Features' => self::EDIINT_FEATURES,
420+
// 'Connection' => 'close',
414421
];
415422

416-
if (! $notificationOptions) {
423+
// TODO: refactory
424+
if (! $isSigned) {
417425
$reportHeaders['Mime-Version'] = '1.0';
418-
$reportHeaders += $headers;
426+
$reportHeaders += $mdnHeaders;
419427
}
420428

429+
// TODO: partner.mdn_confirm_text
421430
if (empty($confirmationText)) {
422-
$confirmationText = 'The AS2 message has been received';
431+
$confirmationText = 'Your message was successfully received and processed.';
423432
}
424433

425-
// Build the text message with confirmation text and add to report
426434
$report = new MimePart($reportHeaders);
435+
436+
// Build the text message with confirmation text and add to report
427437
$report->addPart(new MimePart([
428438
'Content-Type' => 'text/plain',
429-
'Content-Transfer-Encoding' => '7bit',
439+
'Content-Transfer-Encoding' => '7bit', // TODO: check 8bit
430440
], $confirmationText));
431441

432442
// Build the MDN message and add to report
@@ -437,20 +447,25 @@ public function buildMdn(MessageInterface $message, $confirmationText = null, $e
437447
'Original-Message-ID' => '<' . $message->getMessageId() . '>',
438448
'Disposition' => 'automatic-action/MDN-sent-automatically; processed' . ($errorMessage ? '/error: ' . $errorMessage : ''),
439449
];
450+
440451
if ($mic = $message->getMic()) {
441452
$mdnData['Received-Content-MIC'] = $mic;
442453
}
454+
443455
$report->addPart(new MimePart([
444456
'Content-Type' => 'message/disposition-notification',
445457
'Content-Transfer-Encoding' => '7bit',
446458
], Utils::normalizeHeaders($mdnData)));
447459

448460
// If signed MDN is requested by partner then sign the MDN and attach to report
449-
if ($notificationOptions) {
461+
if ($isSigned) {
450462
$this->getLogger()->debug('Outbound MDN has been signed.');
451-
$x509 = openssl_x509_read($receiver->getCertificate());
452-
$key = openssl_get_privatekey($receiver->getPrivateKey(), $receiver->getPrivateKeyPassPhrase());
453-
$report = CryptoHelper::sign($report, $x509, $key, $headers);
463+
// $x509 = openssl_x509_read($receiver->getCertificate());
464+
// $key = openssl_get_privatekey($receiver->getPrivateKey(), $receiver->getPrivateKeyPassPhrase());
465+
$report = CryptoHelper::sign($report, $receiver->getCertificate(), [
466+
$receiver->getPrivateKey(),
467+
$receiver->getPrivateKeyPassPhrase(),
468+
], $mdnHeaders);
454469
}
455470

456471
$this->getLogger()->debug(sprintf('Outbound MDN created for AS2 message "%s".', $messageId));
@@ -464,6 +479,7 @@ public function buildMdn(MessageInterface $message, $confirmationText = null, $e
464479
$message->setMdnStatus(MessageInterface::MDN_STATUS_SENT);
465480
}
466481

482+
// TODO: before sign ?
467483
$message->setMdnPayload($report->toString());
468484

469485
return $report;

src/MimePart.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class MimePart implements \Psr\Http\Message\MessageInterface
3939

4040
/**
4141
* MimePart constructor.
42+
*
4243
* @param array $headers
4344
* @param null $body
4445
*/

src/Server.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ public function execute(ServerRequestInterface $request = null)
8989

9090
$encoding = $request->getHeaderLine('content-transfer-encoding');
9191
if (! $encoding) {
92-
$encoding = $sender->getContentTransferEncoding();
92+
$encoding = $request->getHeaderLine('content-encoding');
93+
if (! $encoding) {
94+
$encoding = $sender->getContentTransferEncoding();
95+
}
9396
}
9497
// Force encode binary data to base64, because openssl_pkcs7 doesn't work with binary data
9598
if ($encoding != 'base64') {
@@ -114,11 +117,14 @@ public function execute(ServerRequestInterface $request = null)
114117

115118
$micalg = $payload->getParsedHeader('Disposition-Notification-Options', 2, 0);
116119

117-
118120
// Check if payload is encrypted and if so decrypt it
119121
if ($payload->isEncrypted()) {
120122
$this->getLogger()->debug('Inbound AS2 message is encrypted.');
121-
$payload = CryptoHelper::decrypt($payload, $receiver->getCertificate(), $receiver->getPrivateKey());
123+
// TODO: check passKey
124+
$payload = CryptoHelper::decrypt($payload, $receiver->getCertificate(), [
125+
$receiver->getPrivateKey(),
126+
$receiver->getPrivateKeyPassPhrase(),
127+
]);
122128
$this->getLogger()->debug('The inbound AS2 message data has been decrypted.');
123129
$message->setEncrypted();
124130
}
@@ -186,7 +192,6 @@ public function execute(ServerRequestInterface $request = null)
186192
$this->storage->saveMessage($message);
187193
$responseBody = 'AS2 ASYNC MDN has been received';
188194
} else {
189-
190195
$message->setPayload((string) $payload);
191196
$message->setStatus(MessageInterface::STATUS_SUCCESS);
192197
// $this->manager->processMessage($message, $payload);
@@ -201,6 +206,7 @@ public function execute(ServerRequestInterface $request = null)
201206
$responseHeaders = $mdn->getHeaders();
202207
$responseBody = $mdn->getBody();
203208
} else {
209+
// TODO: cron, queue, new thread
204210
$this->getLogger()->debug(sprintf('Asynchronous MDN sent as answer to message "%s".',
205211
$messageId));
206212
$this->manager->sendMdn($message);

0 commit comments

Comments
 (0)