/** * Generate a MDN from the message * * @param object $exception The exception if error handled * * @return object The MDN generated */ public function generateMDN($exception = null) { $mdn = $this->mdnFactory->build($this); $message_id = $this->getHeader('message-id'); $partner = $this->getPartnerTo()->id; $mic = $this->getMicChecksum(); $mdn->setAttribute('Original-Recipient', 'rfc822; "' . $partner . '"'); $mdn->setAttribute('Final-Recipient', 'rfc822; "' . $partner . '"'); $mdn->setAttribute('Original-Message-ID', $message_id); if ($mic) { $mdn->setAttribute('Received-Content-MIC', $mic); } if (is_null($exception)) { $mdn->setMessage('The AS2 message has been received.'); $mdn->setAttribute('Disposition-Type', 'processed'); } else { if (!$exception instanceof AS2Exception) { $exception = new AS2Exception($exception->getMessage()); } $mdn->setMessage($exception->getMessage()); $mdn->setAttribute('Disposition-Type', 'failure'); $mdn->setAttribute('Disposition-Modifier', $exception->getLevel() . ': ' . $exception->getMessageShort()); } return $mdn; }
public function getObject() { // setup of full message $content = $this->getHeaders(true) . "\n\n"; $content .= file_get_contents($this->getPath()); $input = Adapter::getTempFilename(); file_put_contents($input, $content); // setup of mailmime decoder $params = array('include_bodies' => false, 'decode_headers' => true, 'decode_bodies' => false, 'input' => false); $decoder = new Mail_mimeDecode(file_get_contents($input)); $structure = $decoder->decode($params); $mimetype = $structure->ctype_primary . '/' . $structure->ctype_secondary; // handle crypted content $crypted = false; if (strtolower($mimetype) == 'application/pkcs7-mime') { try { // rewrite message into base64 encoding $content = file_get_contents($input); $mime_part = Horde_MIME_Structure::parseTextMIMEMessage($content); $input = Adapter::getTempFilename(); file_put_contents($input, $mime_part->toString(true)); $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'AS2 message is encrypted.')); $input = $this->adapter->decrypt($input); $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'The data has been decrypted using the key "' . $this->getPartnerTo() . '".')); $crypted = true; // reload extracted content to get mimetype $decoder = new Mail_mimeDecode(file_get_contents($input)); $structure = $decoder->decode($params); $mimetype = $structure->ctype_primary . '/' . $structure->ctype_secondary; } catch (Exception $e) { throw new AS2Exception($e->getMessage(), 3); } } // handle signed content $signed = false; $mic = false; if (strtolower($mimetype) == 'multipart/signed') { try { $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'AS2 message is signed.')); // get MicChecksum from signature $mic = $this->adapter->getMicChecksum($input); $input = $this->adapter->verify($input); $signed = true; $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'The sender used the algorithm "' . $structure->ctype_parameters['micalg'] . '" to sign the message.')); // reload extracted content to get mimetype $decoder = new Mail_mimeDecode(file_get_contents($input)); $structure = $decoder->decode($params); $mimetype = $structure->ctype_primary . '/' . $structure->ctype_secondary; $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'Using certificate "' . $this->getPartnerFrom() . '" to verify signature.')); } catch (Exception $e) { throw new AS2Exception($e->getMessage(), 5); } } else { // check requested algo $mic = Adapter::calculateMicChecksum($input, 'sha1'); } // security check if (strtolower($mimetype) == 'multipart/report') { // check about sign /*if ($this->getPartnerFrom()->sec_signature_algorithm == Partner::SIGN_NONE && !$this->getPartnerFrom()->mdn_signed && $signed){ throw new AS2Exception('AS2 message is signed and shouldn\'t be.', 4); } else*/ if ($this->getPartnerFrom()->sec_signature_algorithm != Partner::SIGN_NONE && $this->getPartnerFrom()->mdn_signed && !$signed) { throw new AS2Exception('AS2 message is not signed and should be.', 4); } } else { // check about crypt /*if ($this->getPartnerFrom()->sec_encrypt_algorithm == Partner::CRYPT_NONE && $crypted){ throw new AS2Exception('AS2 message is crypted and shouldn\'t be.', 4); } else*/ if ($this->getPartnerFrom()->sec_encrypt_algorithm != Partner::CRYPT_NONE && !$crypted) { throw new AS2Exception('AS2 message is not crypted and should be.', 4); } // check about sign /*if ($this->getPartnerFrom()->sec_signature_algorithm == Partner::SIGN_NONE && $signed){ throw new AS2Exception('AS2 message is signed and shouldn\'t be.', 4); } else*/ if ($this->getPartnerFrom()->sec_signature_algorithm != Partner::SIGN_NONE && !$signed) { throw new AS2Exception('AS2 message is not signed and should be.', 4); } } try { // build object with extracted content $message = file_get_contents($input); $mime_part = Horde_MIME_Structure::parseTextMIMEMessage($message); switch (strtolower($mimetype)) { case 'multipart/report': $params = array('partner_from' => $this->getPartnerTo(), 'partner_to' => $this->getPartnerFrom(), 'is_file' => false, 'mic' => $mic); $object = $this->mdnFactory->build($mime_part, $params); return $object; default: $params = array('partner_from' => $this->getPartnerFrom(), 'partner_to' => $this->getPartnerTo(), 'is_file' => false, 'mic' => $mic); $object = $this->messageFactory->build($mime_part, $params); $object->setHeaders($this->getHeaders()); return $object; } } catch (Exception $e) { throw new AS2Exception($e->getMessage(), 6); } throw new AS2Exception('Unexpected error while handling message.', 6); }
/** * Handle a request (server side) * * @param request (If not set, get data from standard input) * * @return request The request handled */ public function handle(Request $request) { // handle any problem in case of SYNC MDN process ob_start(); try { $error = null; $headers = $request->getHeaders(); $object = $request->getObject(); } catch (Exception $e) { // get error while handling request $error = $e; //throw $e; } // $mdn = null; if ($object instanceof Message || !is_null($error) && !$object instanceof MDN) { $object_type = self::TYPE_MESSAGE; $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'Incoming transmission is a Message.')); try { if (is_null($error)) { $object->decode(); $files = $object->getFiles(); $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, count($files) . ' payload(s) found in incoming transmission.')); foreach ($files as $key => $file) { $content = file_get_contents($file['path']); $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'Payload #' . ($key + 1) . ' : ' . round(strlen($content) / 1024, 2) . ' KB / "' . $file['filename'] . '".')); $this->saveMessage($content, array(), 'payload'); } $mdn = $object->generateMDN($error); $mdn->encode($object); } else { throw $error; } } catch (Exception $e) { $params = array('partner_from' => $headers->getHeader('as2-from'), 'partner_to' => $headers->getHeader('as2-to')); $mdn = $this->mdnFactory->build($e, $params); $mdn->setAttribute('original-message-id', $headers->getHeader('message-id')); $mdn->encode(); } } elseif ($object instanceof MDN) { $object_type = self::TYPE_MDN; $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'Incoming transmission is a MDN.')); } else { $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_ERROR, 'Malformed data.')); } // build MDN if (!is_null($error) && $object_type == self::TYPE_MESSAGE) { $params = array('partner_from' => $headers->getHeader('as2-from'), 'partner_to' => $headers->getHeader('as2-to')); $mdn = $this->mdnFactory->build($e, $params); $mdn->setAttribute('original-message-id', $headers->getHeader('message-id')); $mdn->encode(); } // send MDN if (!is_null($mdn)) { if (!$headers->getHeader('receipt-delivery-option')) { // SYNC method // re-active output data ob_end_clean(); // send headers foreach ($mdn->getHeaders() as $key => $value) { $header = str_replace(array("\r", "\n", "\r\n"), '', $key . ': ' . $value); header($header); } // output MDN echo $mdn->getContent(); $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'An AS2 MDN has been sent.')); } else { // ASYNC method // cut connection and wait a few seconds $this->closeConnectionAndWait(5); // delegate the mdn sending to the client $result = $this->client->sendRequest($mdn); if ($result['info']['http_code'] == '200') { $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_INFO, 'An AS2 MDN has been sent.')); } else { $this->eventDispatcher->dispatch('log', new Log(Log::TYPE_ERROR, 'An error occurs while sending MDN message : ' . $result['info']['http_code'])); } } } return $request; }