Beispiel #1
0
def save_mdn(message, mdn_content):
    """ Process the received MDN and check status of sent message. Takes the raw mdn as input, verifies the signature
    if present and the extracts the status of the original message."""

    try:
        # Parse the raw mdn to an email.Message
        mdn_message = email.message_from_string(mdn_content)
        mdn_headers = ''
        for key in mdn_message.keys():
            mdn_headers += '%s: %s\n' % (key, mdn_message[key])
        message_id = mdn_message.get('message-id')

        # Raise error if message is not an MDN
        if mdn_message.get_content_type() not in [
                'multipart/signed', 'multipart/report'
        ]:
            raise as2utils.As2Exception(
                _(u'MDN report not found in the response'))

        # Raise error if signed MDN requested and unsigned MDN returned
        if message.partner.mdn_sign and mdn_message.get_content_type(
        ) != 'multipart/signed':
            models.Log.objects.create(
                message=message,
                status='W',
                text=_(u'Expected signed MDN but unsigned MDN returned'))

        mdn_signed = False
        if mdn_message.get_content_type() == 'multipart/signed':
            # Verify the signature in the MDN message
            models.Log.objects.create(
                message=message,
                status='S',
                text=_(
                    u'Verifying the signed MDN with partner key {0:s}'.format(
                        message.partner.signature_key)))
            mdn_signed = True

            # Get the partners public and ca certificates
            cert = str(message.partner.signature_key.certificate.path)
            ca_cert = cert
            if message.partner.signature_key.ca_cert:
                ca_cert = str(message.partner.signature_key.ca_cert.path)
            verify_cert = message.partner.signature_key.verify_cert

            # Extract the signed message and signature
            main_boundary = '--' + mdn_message.get_boundary()
            for part in mdn_message.get_payload():
                if part.get_content_type().lower() in [
                        "application/pkcs7-signature",
                        "application/x-pkcs7-signature"
                ]:
                    mdn_content, __ = as2utils.check_binary_sig(
                        part, main_boundary, mdn_content)
                else:
                    mdn_message = part

            # Verify the signature using raw MDN content
            try:
                as2utils.verify_payload(mdn_content, None, cert, ca_cert,
                                        verify_cert)
            except Exception, e:
                raise as2utils.As2Exception(
                    _(u'MDN Signature Verification Error, exception message is %s'
                      % e))

        # Save the MDN to the store
        filename = message_id.strip('<>') + '.mdn'
        full_filename = as2utils.storefile(
            pyas2init.gsettings['mdn_receive_store'], filename,
            as2utils.extractpayload(mdn_message), True)
        message.mdn = models.MDN.objects.create(
            message_id=message_id.strip('<>'),
            file=full_filename,
            status='R',
            headers=mdn_headers,
            signed=mdn_signed)

        # Process the MDN report to extract the AS2 message status
        if mdn_message.get_content_type() == 'multipart/report':
            for part in mdn_message.walk():
                if part.get_content_type(
                ) == 'message/disposition-notification':
                    pyas2init.logger.debug(
                        'Found MDN report for message %s:\n%s' %
                        (message.message_id, part.as_string()))
                    models.Log.objects.create(
                        message=message,
                        status='S',
                        text=_(u'Checking the MDN for status of the message'))
                    mdn = part.get_payload().pop()
                    mdn_status = mdn.get('Disposition').split(';')
                    # Check the status of the AS2 message
                    if mdn_status[1].strip() == 'processed':
                        models.Log.objects.create(
                            message=message,
                            status='S',
                            text=_(u'Message has been successfully processed, '
                                   u'verifying the MIC if present.'))
                        # Compare the MIC of the received message
                        if mdn.get('Received-Content-MIC') and message.mic:
                            mdn_mic = mdn.get('Received-Content-MIC').split(
                                ',')
                            if message.mic != mdn_mic[0]:
                                message.status = 'W'
                                models.Log.objects.create(
                                    message=message,
                                    status='W',
                                    text=_(
                                        u'Message Integrity check failed, please validate '
                                        u'message content with your partner'))
                            else:
                                message.status = 'S'
                                models.Log.objects.create(
                                    message=message,
                                    status='S',
                                    text=
                                    _(u'File Transferred successfully to the partner'
                                      ))
                        else:
                            message.status = 'S'
                            models.Log.objects.create(
                                message=message,
                                status='S',
                                text=
                                _(u'File Transferred successfully to the partner'
                                  ))

                        # Run the post successful send command
                        run_post_send(message)
                    else:
                        raise as2utils.As2Exception(
                            _(u'Partner failed to process file. '
                              u'MDN status is %s' % mdn.get('Disposition')))
        else:
            raise as2utils.As2Exception(
                _(u'MDN report not found in the response'))
Beispiel #2
0
def build_mdn(message, status, **kwargs):
    """ Function builds AS2 MDN report for the received message.
    Takes message status as input and returns the mdn content."""

    try:
        # Initialize variables
        mdn_body, mdn_message = None, None

        # Set the confirmation text message here
        confirmation_text = str()
        if message.organization and message.organization.confirmation_message:
            confirmation_text = message.organization.confirmation_message
        # overwrite with partner specific message
        if message.partner and message.partner.confirmation_message:
            confirmation_text = message.partner.confirmation_message
        # default message
        if confirmation_text.strip() == '':
            confirmation_text = _(
                u'The AS2 message has been processed. '
                u'Thank you for exchanging AS2 messages with Pyas2.')

        # Update message status and send mail here based on the created MDN
        if status != 'success':
            as2utils.senderrorreport(
                message,
                _(u'Failure in processing message from partner,\n '
                  u'Basic status : %s \n Advanced Status: %s' %
                  (kwargs['adv_status'], kwargs['status_message'])))
            confirmation_text = _(
                u'The AS2 message could not be processed. '
                u'The disposition-notification report has additional details.')
            models.Log.objects.create(message=message,
                                      status='E',
                                      text=kwargs['status_message'])
            message.status = 'E'
            message.adv_status = kwargs['status_message']
        else:
            message.status = 'S'

        # In case no MDN is requested exit from process
        header_parser = HeaderParser()
        message_header = header_parser.parsestr(message.headers)
        if not message_header.get('disposition-notification-to'):
            models.Log.objects.create(
                message=message,
                status='S',
                text=_(u'MDN not requested by partner, closing request.'))
            return mdn_body, mdn_message

        # Build the MDN report
        models.Log.objects.create(
            message=message,
            status='S',
            text=_(u'Building the MDN response to the request'))
        mdn_report = MIMEMultipart('report',
                                   report_type="disposition-notification")

        # Build the text message with confirmation text and add to report
        mdn_text = email.Message.Message()
        mdn_text.set_payload("%s\n" % confirmation_text)
        mdn_text.set_type('text/plain')
        mdn_text.set_charset('us-ascii')
        del mdn_text['MIME-Version']
        mdn_report.attach(mdn_text)

        # Build the MDN message and add to report
        mdn_base = email.Message.Message()
        mdn_base.set_type('message/disposition-notification')
        mdn_base.set_charset('us-ascii')
        mdn = 'Reporting-UA: Bots Opensource EDI Translator\n'
        mdn += 'Original-Recipient: rfc822; %s\n' % message_header.get(
            'as2-to')
        mdn += 'Final-Recipient: rfc822; %s\n' % message_header.get('as2-to')
        mdn += 'Original-Message-ID: <%s>\n' % message.message_id
        if status != 'success':
            mdn += 'Disposition: automatic-action/MDN-sent-automatically; ' \
                   'processed/%s: %s\n' % (status, kwargs['adv_status'])
        else:
            mdn += 'Disposition: automatic-action/MDN-sent-automatically; processed\n'
        if message.mic:
            mdn += 'Received-content-MIC: %s\n' % message.mic
        mdn_base.set_payload(mdn)
        del mdn_base['MIME-Version']
        mdn_report.attach(mdn_base)
        del mdn_report['MIME-Version']

        # If signed MDN is requested by partner then sign the MDN and attach to report
        pyas2init.logger.debug('MDN for message %s created:\n%s' %
                               (message.message_id, mdn_report.as_string()))
        mdn_signed = False
        if message_header.get('disposition-notification-options') and message.organization \
                and message.organization.signature_key:
            models.Log.objects.create(
                message=message,
                status='S',
                text=_(u'Signing the MDN using private key {0:s}'.format(
                    message.organization.signature_key)))
            mdn_signed = True
            # options = message_header.get('disposition-notification-options').split(";")
            # algorithm = options[1].split(",")[1].strip()
            signed_report = MIMEMultipart(
                'signed', protocol="application/pkcs7-signature")
            signed_report.attach(mdn_report)
            mic_alg, signature = as2utils.sign_payload(
                as2utils.canonicalize(
                    as2utils.mimetostring(mdn_report, 0) + '\n'),
                str(message.organization.signature_key.certificate.path),
                str(message.organization.signature_key.certificate_passphrase))
            pyas2init.logger.debug('Signature for MDN created:\n%s' %
                                   signature.as_string())
            signed_report.set_param('micalg', mic_alg)
            signed_report.attach(signature)
            mdn_message = signed_report
        else:
            mdn_message = mdn_report

        # Extract the MDN boy from the mdn message.
        # Add new line between the MDN message and the signature,
        # Found that without this MDN signature verification fails on Mendelson AS2
        main_boundary = '--' + mdn_report.get_boundary() + '--'
        mdn_body = as2utils.canonicalize(
            as2utils.extractpayload(mdn_message).replace(
                main_boundary, main_boundary + '\n'))

        # Add the relevant headers to the MDN message
        mdn_message.add_header('ediint-features', 'CEM')
        mdn_message.add_header('as2-from', message_header.get('as2-to'))
        mdn_message.add_header('as2-to', message_header.get('as2-from'))
        mdn_message.add_header('AS2-Version', '1.2')
        mdn_message.add_header('date', email.Utils.formatdate(localtime=True))
        mdn_message.add_header('Message-ID', email.utils.make_msgid())
        mdn_message.add_header('user-agent', 'PYAS2, A pythonic AS2 server')

        # Save the MDN to the store
        filename = mdn_message.get('message-id').strip('<>') + '.mdn'
        full_filename = as2utils.storefile(
            pyas2init.gsettings['mdn_send_store'], filename, mdn_body, True)

        # Extract the MDN headers as string
        mdn_headers = ''
        for key in mdn_message.keys():
            mdn_headers += '%s: %s\n' % (key, mdn_message[key])

        # Is Async mdn is requested mark MDN as pending and return None
        if message_header.get('receipt-delivery-option'):
            message.mdn = models.MDN.objects.create(
                message_id=filename,
                file=full_filename,
                status='P',
                signed=mdn_signed,
                headers=mdn_headers,
                return_url=message_header['receipt-delivery-option'])
            message.mdn_mode = 'ASYNC'
            mdn_body, mdn_message = None, None
            models.Log.objects.create(
                message=message,
                status='S',
                text=_(
                    u'Asynchronous MDN requested, setting status to pending'))

        # Else mark MDN as sent and return the MDN message
        else:
            message.mdn = models.MDN.objects.create(message_id=filename,
                                                    file=full_filename,
                                                    status='S',
                                                    signed=mdn_signed,
                                                    headers=mdn_headers)
            message.mdn_mode = 'SYNC'
            models.Log.objects.create(
                message=message,
                status='S',
                text=_(u'MDN created successfully and sent to partner'))
        return mdn_body, mdn_message
    finally:
        message.save()
Beispiel #3
0
def build_message(message):
    """ Build the AS2 mime message to be sent to partner. Encrypts, signs and compresses the message based on
    the partner profile. Returns the message final message content."""

    # Initialize the variables
    mic_content, mic_alg = None, None
    payload = email.Message.Message()

    # Build the As2 message headers as per specifications
    models.Log.objects.create(
        message=message,
        status='S',
        text=_(u'Build the AS2 message and header to send to the partner'))
    email_datetime = email.Utils.formatdate(localtime=True)
    as2_header = {
        'AS2-Version': '1.2',
        'ediint-features': 'CEM',
        'MIME-Version': '1.0',
        'Message-ID': '<%s>' % message.message_id,
        'AS2-From': as2utils.escape_as2name(message.organization.as2_name),
        'AS2-To': as2utils.escape_as2name(message.partner.as2_name),
        'Subject': message.partner.subject,
        'Date': email_datetime,
        'recipient-address': message.partner.target_url,
        'user-agent': 'PYAS2, A pythonic AS2 server'
    }

    # Create the payload message and add the data to be transferred as its contents
    with open(message.payload.file, 'rb') as fh:
        as2_content = fh.read()
    payload.set_payload(as2_content)
    payload.set_type(message.partner.content_type)
    payload.add_header('Content-Disposition',
                       'attachment',
                       filename=message.payload.name)
    del payload['MIME-Version']

    # Compress the message if requested in the profile
    if message.partner.compress:
        models.Log.objects.create(message=message,
                                  status='S',
                                  text=_(u'Compressing the payload.'))
        message.compressed = True
        compressed_message = email.Message.Message()
        compressed_message.set_type('application/pkcs7-mime')
        compressed_message.set_param('name', 'smime.p7z')
        compressed_message.set_param('smime-type', 'compressed-data')
        compressed_message.add_header('Content-Transfer-Encoding', 'base64')
        compressed_message.add_header('Content-Disposition',
                                      'attachment',
                                      filename='smime.p7z')
        compressed_message.set_payload(
            as2utils.compress_payload(
                as2utils.canonicalize(as2utils.mimetostring(payload, 0))))
        as2_content, payload = compressed_message.get_payload(
        ), compressed_message
        pyas2init.logger.debug('Compressed message %s payload as:\n%s' %
                               (message.message_id, payload.as_string()))

    # Sign the message if requested in the profile
    if message.partner.signature:
        models.Log.objects.create(
            message=message,
            status='S',
            text=_(u'Signing the message using organization key {0:s}'.format(
                message.organization.signature_key)))
        message.signed = True
        signed_message = MIMEMultipart('signed',
                                       protocol="application/pkcs7-signature")
        del signed_message['MIME-Version']
        mic_content = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        signed_message.attach(payload)
        mic_alg, signature = as2utils.sign_payload(
            mic_content,
            str(message.organization.signature_key.certificate.path),
            str(message.organization.signature_key.certificate_passphrase))
        signed_message.set_param('micalg', mic_alg)
        signed_message.attach(signature)
        signed_message.as_string()
        as2_content = as2utils.canonicalize(
            as2utils.extractpayload(signed_message))
        payload = signed_message
        pyas2init.logger.debug('Signed message %s payload as:\n%s' %
                               (message.message_id, payload.as_string()))

    # Encrypt the message if requested in the profile
    if message.partner.encryption:
        models.Log.objects.create(
            message=message,
            status='S',
            text=_(u'Encrypting the message using partner key {0:s}'.format(
                message.partner.encryption_key)))
        message.encrypted = True
        payload = as2utils.encrypt_payload(
            as2utils.canonicalize(as2utils.mimetostring(payload, 0)),
            message.partner.encryption_key.certificate.path,
            message.partner.encryption)
        payload.set_type('application/pkcs7-mime')
        as2_content = payload.get_payload().decode('base64')
        del payload['Content-Transfer-Encoding']
        pyas2init.logger.debug('Encrypted message %s payload as:\n%s' %
                               (message.message_id, payload.as_string()))

    # If MDN is to be requested from the partner, set the appropriate headers
    if message.partner.mdn:
        as2_header['disposition-notification-to'] = '*****@*****.**'
        if message.partner.mdn_sign:
            as2_header['disposition-notification-options'] = u'signed-receipt-protocol=required, ' \
                                                             u'pkcs7-signature; ' \
                                                             u'signed-receipt-micalg=optional, ' \
                                                             u'%s' % message.partner.mdn_sign
        message.mdn_mode = 'SYNC'
        if message.partner.mdn_mode == 'ASYNC':
            as2_header['receipt-delivery-option'] = pyas2init.gsettings[
                'mdn_url']
            message.mdn_mode = 'ASYNC'

    # If MIC content is set, i.e. message has been signed then calulcate the MIC
    if mic_content:
        pyas2init.logger.debug("Calculating MIC with alg %s for content:\n%s" %
                               (mic_alg, mic_content))
        calculate_mic = getattr(hashlib, mic_alg.replace('-', ''),
                                hashlib.sha1)
        message.mic = calculate_mic(mic_content).digest().encode(
            'base64').strip()

    # Extract the As2 headers as a string and save it to the message object
    as2_header.update(payload.items())
    message.headers = ''
    for key in as2_header:
        message.headers += '%s: %s\n' % (key, as2_header[key])
    message.save()

    models.Log.objects.create(
        message=message,
        status='S',
        text=
        _(u'AS2 message has been built successfully, sending it to the partner'
          ))
    return as2_content
Beispiel #4
0
def build_mdn(message, status, **kwargs):
    ''' Function builds AS2 MDN report '''
    try:
        hparser = HeaderParser()
        message_header = hparser.parsestr(message.headers)

        text = str()

        # check for default orginization message
        if message.organization.confirmation_message:
            text = message.organization.confirmation_message

        # overwrite with partner specific message
        if message.partner.confirmation_message:
            text = message.partner.confirmation_message

        # default message
        if text.strip() == '':
            text = _(u'The AS2 message has been processed. Thank you for exchanging AS2 messages with Pyas2.')

        if status != 'success':
            #### Send mail here
            as2utils.senderrorreport(message, _(u'Failure in processing message from partner,\n Basic status : %s \n Advanced Status: %s'%(kwargs['adv_status'],kwargs['status_message'])))
            text = _(u'The AS2 message could not be processed. The disposition-notification report has additional details.')
            models.Log.objects.create(message=message, status='E', text = kwargs['status_message'])
            message.status = 'E'
        else:
            message.status = 'S'
        if not message_header.get('disposition-notification-to'):
            models.Log.objects.create(message=message, status='S', text=_(u'MDN not requested by partner, closing request.'))
            return None, None
        models.Log.objects.create(message=message, status='S', text=_(u'Building the MDN response to the request'))
        main = MIMEMultipart('report', report_type="disposition-notification")
        textmessage = email.Message.Message()
        textmessage.set_payload("%s\n"%text)
        textmessage.set_type('text/plain')
        textmessage.set_charset('us-ascii')
        del textmessage['MIME-Version']
        main.attach(textmessage)
        mdnbase = email.Message.Message()
        mdnbase.set_type('message/disposition-notification')
        mdnbase.set_charset('us-ascii')
        mdn = 'Reporting-UA: Bots Opensource EDI Translator\n'
        mdn = mdn + 'Original-Recipient: rfc822; %s\n'%message_header.get('as2-to')
        mdn = mdn + 'Final-Recipient: rfc822; %s\n'%message_header.get('as2-to')
        mdn = mdn + 'Original-Message-ID: <%s>\n'%message.message_id
        if status != 'success':
            mdn = mdn + 'Disposition: automatic-action/MDN-sent-automatically; processed/%s: %s\n'%(status, kwargs['adv_status'])
        else:
            mdn = mdn + 'Disposition: automatic-action/MDN-sent-automatically; processed\n'
        if message.mic:
            mdn = mdn + 'Received-content-MIC: %s\n'%message.mic
        mdnbase.set_payload(mdn)
        del mdnbase['MIME-Version']
        main.attach(mdnbase)
        del main['MIME-Version']
        pyas2init.logger.debug('MDN for message %s created:\n%s'%(message.message_id,main.as_string()))
        mdnsigned = False
        if message_header.get('disposition-notification-options') and message.organization and message.organization.signature_key: 
            models.Log.objects.create(message=message, status='S', text=_(u'Signing the MDN using private key %s'%message.organization.signature_key))
            mdnsigned = True
            options = message_header.get('disposition-notification-options').split(";")
            algorithm = options[1].split(",")[1].strip()
            signed = MIMEMultipart('signed', protocol="application/pkcs7-signature")
            signed.attach(main)
            micalg, signature = as2utils.sign_payload(
                    as2utils.canonicalize(as2utils.mimetostring(main, 0)+'\n'),
                    str(message.organization.signature_key.certificate.path), 
                    str(message.organization.signature_key.certificate_passphrase)
            )
            pyas2init.logger.debug('Signature for MDN created:\n%s'%signature.as_string())
            signed.set_param('micalg',micalg)
            signed.attach(signature)
            mdnmessage = signed
        else:
            mdnmessage = main
        ### Add new line between the MDN message and the signature
        mdnbody = as2utils.extractpayload(mdnmessage)
        mainboundary = '--' + main.get_boundary() + '--'
        mdnbody = as2utils.canonicalize(mdnbody.replace(mainboundary, mainboundary + '\n'))
        mdnmessage.add_header('ediint-features', 'CEM')
        mdnmessage.add_header('as2-from', message_header.get('as2-to'))
        mdnmessage.add_header('as2-to', message_header.get('as2-from')) 
        mdnmessage.add_header('AS2-Version', '1.2')
        mdnmessage.add_header('date', email.Utils.formatdate(localtime=True))
        mdnmessage.add_header('Message-ID', email.utils.make_msgid())
        mdnmessage.add_header('user-agent', 'PYAS2, A pythonic AS2 server')
        filename = mdnmessage.get('message-id').strip('<>') + '.mdn'
        fullfilename = as2utils.storefile(pyas2init.gsettings['mdn_send_store'],filename,mdnbody,True)
        mdn_headers = ''
        for key in mdnmessage.keys():
            mdn_headers = mdn_headers + '%s: %s\n'%(key, mdnmessage[key])
        if message_header.get('receipt-delivery-option'):
            message.mdn = models.MDN.objects.create(message_id=filename, file=fullfilename, status='P', signed=mdnsigned, headers=mdn_headers, return_url= message_header['receipt-delivery-option'])
            message.mdn_mode = 'ASYNC'
            mdnbody, mdnmessage = None, None
            models.Log.objects.create(message=message, status='S', text=_(u'Asynchronous MDN requested, setting status to pending'))
        else:
            message.mdn = models.MDN.objects.create(message_id=filename,file=fullfilename, status='S', signed=mdnsigned, headers=mdn_headers)
            message.mdn_mode = 'SYNC'
            models.Log.objects.create(message=message, status='S', text=_(u'MDN created successfully and sent to partner'))
        return mdnbody, mdnmessage
    finally:
        message.save()	
Beispiel #5
0
def build_message(message):
    ''' Build the AS2 mime message to be sent to partner'''
    models.Log.objects.create(message=message, status='S', text=_(u'Build the AS2 message and header to send to the partner'))
    reference = '<%s>'%message.message_id
    micContent = None
    email_datetime = email.Utils.formatdate(localtime=True)
    as2Header = {
        'AS2-Version'         : '1.2',
        'ediint-features'     : 'CEM',
        'MIME-Version'        : '1.0',  
        'Message-ID'          : reference,
        'AS2-From'            : as2utils.escape_as2name(message.organization.as2_name),
        'AS2-To'              : as2utils.escape_as2name(message.partner.as2_name),
        'Subject'             : message.partner.subject,
        'Date'                : email_datetime,
        'recipient-address'   : message.partner.target_url,
        'user-agent'          : 'PYAS2, A pythonic AS2 server'
    }
    payload = email.Message.Message()
    with open(message.payload.file, 'rb') as fh:
        payload.set_payload(fh.read())
        fh.close()
    payload.set_type(message.partner.content_type)
    payload.add_header('Content-Disposition', 'attachment', filename=message.payload.name)
    del payload['MIME-Version']
    #micContent,cmicContent,content = payload.get_payload(),None,payload.get_payload()
    content = payload.get_payload()
    if message.partner.compress:
        models.Log.objects.create(message=message, status='S', text=_(u'Compressing the payload.'))
        message.compressed = True
        #micContent = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        cmessage = email.Message.Message()
        cmessage.set_type('application/pkcs7-mime')
        cmessage.set_param('name', 'smime.p7z')
        cmessage.set_param('smime-type', 'compressed-data') 
        cmessage.add_header('Content-Transfer-Encoding', 'base64')
        cmessage.add_header('Content-Disposition', 'attachment', filename='smime.p7z')
        cmessage.set_payload(as2utils.compress_payload(as2utils.canonicalize(as2utils.mimetostring(payload, 0))))
        content,payload = cmessage.get_payload(),cmessage
        pyas2init.logger.debug('Compressed message %s payload as:\n%s'%(message.message_id,payload.as_string()))
    if message.partner.signature: 
        models.Log.objects.create(message=message, status='S', text=_(u'Signing the message using organzation key %s'%message.organization.signature_key))
        message.signed = True
        multipart = MIMEMultipart('signed',protocol="application/pkcs7-signature")
        del multipart['MIME-Version']
        #micContent = as2utils.canonicalize2(payload)
        micContent = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        multipart.attach(payload)
        micalg, signature = as2utils.sign_payload(micContent, str(message.organization.signature_key.certificate.path), str(message.organization.signature_key.certificate_passphrase))
        multipart.set_param('micalg',micalg)
        multipart.attach(signature)
        multipart.as_string()
        content = as2utils.canonicalize(as2utils.extractpayload(multipart))
        payload = multipart
        pyas2init.logger.debug('Signed message %s payload as:\n%s'%(message.message_id,payload.as_string()))
    if message.partner.encryption: 
        #if not message.compressed and not message.signed:
            #micContent = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        models.Log.objects.create(message=message, status='S', text=_(u'Encrypting the message using partner key %s'%message.partner.encryption_key))
        message.encrypted = True
        payload = as2utils.encrypt_payload(as2utils.canonicalize(as2utils.mimetostring(payload, 0)), message.partner.encryption_key.certificate.path , message.partner.encryption)
        payload.set_type('application/pkcs7-mime')
        content = payload.get_payload()
        pyas2init.logger.debug('Encrypted message %s payload as:\n%s'%(message.message_id,payload.as_string()))
    if message.partner.mdn:
        as2Header['disposition-notification-to'] = '*****@*****.**' 
        if message.partner.mdn_sign:			
            as2Header['disposition-notification-options'] = 'signed-receipt-protocol=required, pkcs7-signature; signed-receipt-micalg=optional, %s'%message.partner.mdn_sign
        message.mdn_mode = 'SYNC'
        if message.partner.mdn_mode == 'ASYNC':
            as2Header['receipt-delivery-option'] = pyas2init.gsettings['mdn_url']
            message.mdn_mode = 'ASYNC'
    if micContent:
        pyas2init.logger.debug("Calcualting MIC with alg %s for content:\n%s"%(micalg,micContent))
        calcMIC = getattr(hashlib, micalg.replace('-',''), hashlib.sha1)
        message.mic = calcMIC(micContent).digest().encode('base64').strip()
    as2Header.update(payload.items())
    message.headers = ''
    for key in as2Header:
        message.headers = message.headers + '%s: %s\n'%(key, as2Header[key])
    message.save()
    models.Log.objects.create(message=message, status='S', text=_(u'AS2 message has been built successfully, sending it to the partner'))
    return content 
Beispiel #6
0
def save_mdn(message, mdn_content):
    """ Process the received MDN and check status of sent message. Takes the raw mdn as input, verifies the signature
    if present and the extracts the status of the original message."""

    try:
        # Parse the raw mdn to an email.Message
        mdn_message = email.message_from_string(mdn_content)
        mdn_headers = ''
        for key in mdn_message.keys():
            mdn_headers += '%s: %s\n' % (key, mdn_message[key])
        message_id = mdn_message.get('message-id')

        # Raise error if message is not an MDN
        if mdn_message.get_content_type() not in ['multipart/signed', 'multipart/report']:
            raise as2utils.As2Exception(_(u'MDN report not found in the response'))

        # Raise error if signed MDN requested and unsigned MDN returned
        if message.partner.mdn_sign and mdn_message.get_content_type() != 'multipart/signed':
            models.Log.objects.create(message=message,
                                      status='W',
                                      text=_(u'Expected signed MDN but unsigned MDN returned'))

        mdn_signed = False
        if mdn_message.get_content_type() == 'multipart/signed':
            # Verify the signature in the MDN message
            models.Log.objects.create(message=message,
                                      status='S',
                                      text=_(u'Verifying the signed MDN with partner key {0:s}'.format(
                                          message.partner.signature_key)))
            mdn_signed = True

            # Get the partners public and ca certificates
            cert = str(message.partner.signature_key.certificate.path)
            ca_cert = cert
            if message.partner.signature_key.ca_cert:
                ca_cert = str(message.partner.signature_key.ca_cert.path)
            verify_cert = message.partner.signature_key.verify_cert

            # Extract the signed message and signature
            main_boundary = '--' + mdn_message.get_boundary()
            for part in mdn_message.get_payload():
                if part.get_content_type().lower() == "application/pkcs7-signature":
                    mdn_content, __ = as2utils.check_binary_sig(
                        part, main_boundary, mdn_content)
                else:
                    mdn_message = part

            # Verify the signature using raw MDN content
            try:
                as2utils.verify_payload(mdn_content, None, cert, ca_cert, verify_cert)
            except Exception, e:
                raise as2utils.As2Exception(_(u'MDN Signature Verification Error, exception message is %s' % e))

        # Save the MDN to the store
        filename = message_id.strip('<>') + '.mdn'
        full_filename = as2utils.storefile(pyas2init.gsettings['mdn_receive_store'],
                                           filename,
                                           as2utils.extractpayload(mdn_message),
                                           True)
        message.mdn = models.MDN.objects.create(message_id=message_id.strip('<>'),
                                                file=full_filename,
                                                status='R',
                                                headers=mdn_headers,
                                                signed=mdn_signed)

        # Process the MDN report to extract the AS2 message status
        if mdn_message.get_content_type() == 'multipart/report':
            for part in mdn_message.walk():
                if part.get_content_type() == 'message/disposition-notification':
                    pyas2init.logger.debug('Found MDN report for message %s:\n%s' % (message.message_id,
                                                                                     part.as_string()))
                    models.Log.objects.create(message=message,
                                              status='S',
                                              text=_(u'Checking the MDN for status of the message'))
                    mdn = part.get_payload().pop()
                    mdn_status = mdn.get('Disposition').split(';')
                    # Check the status of the AS2 message
                    if mdn_status[1].strip() == 'processed':
                        models.Log.objects.create(message=message,
                                                  status='S',
                                                  text=_(u'Message has been successfully processed, '
                                                         u'verifying the MIC if present.'))
                        # Compare the MIC of the received message
                        if mdn.get('Received-Content-MIC') and message.mic:
                            mdn_mic = mdn.get('Received-Content-MIC').split(',')
                            if message.mic != mdn_mic[0]:
                                message.status = 'W'
                                models.Log.objects.create(message=message,
                                                          status='W',
                                                          text=_(u'Message Integrity check failed, please validate '
                                                                 u'message content with your partner'))
                            else:
                                message.status = 'S'
                                models.Log.objects.create(message=message,
                                                          status='S',
                                                          text=_(u'File Transferred successfully to the partner'))
                        else:
                            message.status = 'S'
                            models.Log.objects.create(message=message,
                                                      status='S',
                                                      text=_(u'File Transferred successfully to the partner'))

                        # Run the post successful send command
                        run_post_send(message)
                    else:
                        raise as2utils.As2Exception(_(u'Partner failed to process file. '
                                                      u'MDN status is %s' % mdn.get('Disposition')))
        else:
            raise as2utils.As2Exception(_(u'MDN report not found in the response'))
Beispiel #7
0
         raw_sig = sig.get_payload().encode('base64').strip()
     ### Verify the signature using raw contents
     try:
         as2utils.verify_payload(mdnContent,None,cert,ca_cert,verify_cert)
     except Exception, e:
         ### Verify the signature using extracted signature and message
         try:
             as2utils.verify_payload(as2utils.extractpayload_fromstring1(mdnContent,main_boundary),raw_sig,cert,ca_cert,verify_cert)
         except Exception, e:
             ### Verify the signature using extracted signature and message without extra trailing new line in message
             try:
                 as2utils.verify_payload(as2utils.extractpayload_fromstring2(mdnContent,main_boundary),raw_sig,cert,ca_cert,verify_cert)
             except Exception, e:
                 raise as2utils.as2exception(_(u'MDN Signature Verification Error, exception message is %s' %e))
 filename = messageId.strip('<>') + '.mdn'
 fullfilename = as2utils.storefile(pyas2init.gsettings['mdn_receive_store'],filename,as2utils.extractpayload(mdnMessage),True)
 message.mdn = models.MDN.objects.create(message_id=messageId.strip('<>'),file=fullfilename, status='R', headers=mdnHeaders, signed=mdnsigned)
 if mdnMessage.get_content_type() == 'multipart/report':
     for part in mdnMessage.walk():
         if (part.get_content_type() == 'message/disposition-notification'):
             pyas2init.logger.debug('Found MDN report for message %s:\n%s'%(message.message_id,part.as_string()))
             models.Log.objects.create(message=message, status='S', text=_(u'Checking the MDN for status of the message'))
             mdn =  part.get_payload().pop()
             mdnOMessageId = mdn.get('Original-Message-ID')
             mdnStatus = mdn.get('Disposition').split(';')
             if (mdnStatus[1].strip() == 'processed'):
                 models.Log.objects.create(message=message, status='S', text=_(u'Message has been successfully processed, verifying the MIC if present.'))
                 if mdn.get('Received-Content-MIC') and message.mic:
                     mdnMIC = mdn.get('Received-Content-MIC').split(',');
                     if (message.mic != mdnMIC[0]):
                         message.status = 'W'
Beispiel #8
0
def build_message(message):
    """ Build the AS2 mime message to be sent to partner. Encrypts, signs and compresses the message based on
    the partner profile. Returns the message final message content."""

    # Initialize the variables
    mic_content, mic_alg = None, None
    payload = email.Message.Message()

    # Build the As2 message headers as per specifications
    models.Log.objects.create(message=message,
                              status='S',
                              text=_(u'Build the AS2 message and header to send to the partner'))
    email_datetime = email.Utils.formatdate(localtime=True)
    as2_header = {
        'AS2-Version': '1.2',
        'ediint-features': 'CEM',
        'MIME-Version': '1.0',
        'Message-ID': '<%s>' % message.message_id,
        'AS2-From': as2utils.escape_as2name(message.organization.as2_name),
        'AS2-To': as2utils.escape_as2name(message.partner.as2_name),
        'Subject': message.partner.subject,
        'Date': email_datetime,
        'recipient-address': message.partner.target_url,
        'user-agent': 'PYAS2, A pythonic AS2 server'
    }

    # Create the payload message and add the data to be transferred as its contents
    with open(message.payload.file, 'rb') as fh:
        as2_content = fh.read()
    payload.set_payload(as2_content)
    payload.set_type(message.partner.content_type)
    payload.add_header('Content-Disposition', 'attachment', filename=message.payload.name)
    del payload['MIME-Version']

    # Compress the message if requested in the profile
    if message.partner.compress:
        models.Log.objects.create(message=message, status='S', text=_(u'Compressing the payload.'))
        message.compressed = True
        compressed_message = email.Message.Message()
        compressed_message.set_type('application/pkcs7-mime')
        compressed_message.set_param('name', 'smime.p7z')
        compressed_message.set_param('smime-type', 'compressed-data')
        compressed_message.add_header('Content-Transfer-Encoding', 'base64')
        compressed_message.add_header('Content-Disposition', 'attachment', filename='smime.p7z')
        compressed_message.set_payload(
            as2utils.compress_payload(as2utils.canonicalize(as2utils.mimetostring(payload, 0))))
        as2_content, payload = compressed_message.get_payload(), compressed_message
        pyas2init.logger.debug('Compressed message %s payload as:\n%s' % (message.message_id, payload.as_string()))

    # Sign the message if requested in the profile
    if message.partner.signature:
        models.Log.objects.create(message=message,
                                  status='S',
                                  text=_(u'Signing the message using organization key {0:s}'.format(
                                      message.organization.signature_key)))
        message.signed = True
        signed_message = MIMEMultipart('signed', protocol="application/pkcs7-signature")
        del signed_message['MIME-Version']
        mic_content = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        signed_message.attach(payload)
        mic_alg, signature = as2utils.sign_payload(mic_content,
                                                   str(message.organization.signature_key.certificate.path),
                                                   str(message.organization.signature_key.certificate_passphrase))
        signed_message.set_param('micalg', mic_alg)
        signed_message.attach(signature)
        signed_message.as_string()
        as2_content = as2utils.canonicalize(as2utils.extractpayload(signed_message))
        payload = signed_message
        pyas2init.logger.debug('Signed message %s payload as:\n%s' % (message.message_id, payload.as_string()))

    # Encrypt the message if requested in the profile
    if message.partner.encryption:
        models.Log.objects.create(message=message,
                                  status='S',
                                  text=_(u'Encrypting the message using partner key {0:s}'.format(
                                      message.partner.encryption_key)))
        message.encrypted = True
        payload = as2utils.encrypt_payload(as2utils.canonicalize(as2utils.mimetostring(payload, 0)),
                                           message.partner.encryption_key.certificate.path,
                                           message.partner.encryption)
        payload.set_type('application/pkcs7-mime')
        as2_content = payload.get_payload()
        pyas2init.logger.debug('Encrypted message %s payload as:\n%s' % (message.message_id, payload.as_string()))

    # If MDN is to be requested from the partner, set the appropriate headers
    if message.partner.mdn:
        as2_header['disposition-notification-to'] = '*****@*****.**'
        if message.partner.mdn_sign:
            as2_header['disposition-notification-options'] = u'signed-receipt-protocol=required, ' \
                                                             u'pkcs7-signature; ' \
                                                             u'signed-receipt-micalg=optional, ' \
                                                             u'%s' % message.partner.mdn_sign
        message.mdn_mode = 'SYNC'
        if message.partner.mdn_mode == 'ASYNC':
            as2_header['receipt-delivery-option'] = pyas2init.gsettings['mdn_url']
            message.mdn_mode = 'ASYNC'

    # If MIC content is set, i.e. message has been signed then calulcate the MIC
    if mic_content:
        pyas2init.logger.debug("Calculating MIC with alg %s for content:\n%s" % (mic_alg, mic_content))
        calculate_mic = getattr(hashlib, mic_alg.replace('-', ''), hashlib.sha1)
        message.mic = calculate_mic(mic_content).digest().encode('base64').strip()

    # Extract the As2 headers as a string and save it to the message object
    as2_header.update(payload.items())
    message.headers = ''
    for key in as2_header:
        message.headers += '%s: %s\n' % (key, as2_header[key])
    message.save()

    models.Log.objects.create(message=message,
                              status='S',
                              text=_(u'AS2 message has been built successfully, sending it to the partner'))
    return as2_content
Beispiel #9
0
def build_mdn(message, status, **kwargs):
    """ Function builds AS2 MDN report for the received message.
    Takes message status as input and returns the mdn content."""

    try:
        # Initialize variables
        mdn_body, mdn_message = None, None

        # Set the confirmation text message here
        confirmation_text = str()
        if message.organization and message.organization.confirmation_message:
            confirmation_text = message.organization.confirmation_message
        # overwrite with partner specific message
        if message.partner and message.partner.confirmation_message:
            confirmation_text = message.partner.confirmation_message
        # default message
        if confirmation_text.strip() == '':
            confirmation_text = _(u'The AS2 message has been processed. '
                                  u'Thank you for exchanging AS2 messages with Pyas2.')

        # Update message status and send mail here based on the created MDN
        if status != 'success':
            as2utils.senderrorreport(message, _(u'Failure in processing message from partner,\n '
                                                u'Basic status : %s \n Advanced Status: %s' %
                                                (kwargs['adv_status'], kwargs['status_message'])))
            confirmation_text = _(u'The AS2 message could not be processed. '
                                  u'The disposition-notification report has additional details.')
            models.Log.objects.create(message=message, status='E', text=kwargs['status_message'])
            message.status = 'E'
        else:
            message.status = 'S'

        # In case no MDN is requested exit from process
        header_parser = HeaderParser()
        message_header = header_parser.parsestr(message.headers)
        if not message_header.get('disposition-notification-to'):
            models.Log.objects.create(message=message, status='S',
                                      text=_(u'MDN not requested by partner, closing request.'))
            return mdn_body, mdn_message

        # Build the MDN report
        models.Log.objects.create(message=message, status='S', text=_(u'Building the MDN response to the request'))
        mdn_report = MIMEMultipart('report', report_type="disposition-notification")

        # Build the text message with confirmation text and add to report
        mdn_text = email.Message.Message()
        mdn_text.set_payload("%s\n" % confirmation_text)
        mdn_text.set_type('text/plain')
        mdn_text.set_charset('us-ascii')
        del mdn_text['MIME-Version']
        mdn_report.attach(mdn_text)

        # Build the MDN message and add to report
        mdn_base = email.Message.Message()
        mdn_base.set_type('message/disposition-notification')
        mdn_base.set_charset('us-ascii')
        mdn = 'Reporting-UA: Bots Opensource EDI Translator\n'
        mdn += 'Original-Recipient: rfc822; %s\n' % message_header.get('as2-to')
        mdn += 'Final-Recipient: rfc822; %s\n' % message_header.get('as2-to')
        mdn += 'Original-Message-ID: <%s>\n' % message.message_id
        if status != 'success':
            mdn += 'Disposition: automatic-action/MDN-sent-automatically; ' \
                   'processed/%s: %s\n' % (status, kwargs['adv_status'])
        else:
            mdn += 'Disposition: automatic-action/MDN-sent-automatically; processed\n'
        if message.mic:
            mdn += 'Received-content-MIC: %s\n' % message.mic
        mdn_base.set_payload(mdn)
        del mdn_base['MIME-Version']
        mdn_report.attach(mdn_base)
        del mdn_report['MIME-Version']

        # If signed MDN is requested by partner then sign the MDN and attach to report
        pyas2init.logger.debug('MDN for message %s created:\n%s' % (message.message_id, mdn_report.as_string()))
        mdn_signed = False
        if message_header.get('disposition-notification-options') and message.organization \
                and message.organization.signature_key:
            models.Log.objects.create(message=message,
                                      status='S',
                                      text=_(u'Signing the MDN using private key {0:s}'.format(
                                          message.organization.signature_key)))
            mdn_signed = True
            # options = message_header.get('disposition-notification-options').split(";")
            # algorithm = options[1].split(",")[1].strip()
            signed_report = MIMEMultipart('signed', protocol="application/pkcs7-signature")
            signed_report.attach(mdn_report)
            mic_alg, signature = as2utils.sign_payload(
                    as2utils.canonicalize(as2utils.mimetostring(mdn_report, 0)+'\n'),
                    str(message.organization.signature_key.certificate.path),
                    str(message.organization.signature_key.certificate_passphrase)
            )
            pyas2init.logger.debug('Signature for MDN created:\n%s' % signature.as_string())
            signed_report.set_param('micalg', mic_alg)
            signed_report.attach(signature)
            mdn_message = signed_report
        else:
            mdn_message = mdn_report

        # Extract the MDN boy from the mdn message.
        # Add new line between the MDN message and the signature,
        # Found that without this MDN signature verification fails on Mendelson AS2
        main_boundary = '--' + mdn_report.get_boundary() + '--'
        mdn_body = as2utils.canonicalize(
            as2utils.extractpayload(mdn_message).replace(main_boundary, main_boundary+'\n'))

        # Add the relevant headers to the MDN message
        mdn_message.add_header('ediint-features', 'CEM')
        mdn_message.add_header('as2-from', message_header.get('as2-to'))
        mdn_message.add_header('as2-to', message_header.get('as2-from'))
        mdn_message.add_header('AS2-Version', '1.2')
        mdn_message.add_header('date', email.Utils.formatdate(localtime=True))
        mdn_message.add_header('Message-ID', email.utils.make_msgid())
        mdn_message.add_header('user-agent', 'PYAS2, A pythonic AS2 server')

        # Save the MDN to the store
        filename = mdn_message.get('message-id').strip('<>') + '.mdn'
        full_filename = as2utils.storefile(pyas2init.gsettings['mdn_send_store'], filename, mdn_body, True)

        # Extract the MDN headers as string
        mdn_headers = ''
        for key in mdn_message.keys():
            mdn_headers += '%s: %s\n' % (key, mdn_message[key])

        # Is Async mdn is requested mark MDN as pending and return None
        if message_header.get('receipt-delivery-option'):
            message.mdn = models.MDN.objects.create(message_id=filename,
                                                    file=full_filename,
                                                    status='P',
                                                    signed=mdn_signed,
                                                    headers=mdn_headers,
                                                    return_url=message_header['receipt-delivery-option'])
            message.mdn_mode = 'ASYNC'
            mdn_body, mdn_message = None, None
            models.Log.objects.create(message=message,
                                      status='S',
                                      text=_(u'Asynchronous MDN requested, setting status to pending'))

        # Else mark MDN as sent and return the MDN message
        else:
            message.mdn = models.MDN.objects.create(message_id=filename,
                                                    file=full_filename,
                                                    status='S',
                                                    signed=mdn_signed,
                                                    headers=mdn_headers)
            message.mdn_mode = 'SYNC'
            models.Log.objects.create(message=message,
                                      status='S',
                                      text=_(u'MDN created successfully and sent to partner'))
        return mdn_body, mdn_message
    finally:
        message.save()
Beispiel #10
0
def build_message(message):
    """ Build the AS2 mime message to be sent to partner"""
    models.Log.objects.create(
        message=message, status="S", text=_(u"Build the AS2 message and header to send to the partner")
    )
    reference = "<%s>" % message.message_id
    micContent = None
    email_datetime = email.Utils.formatdate(localtime=True)
    as2Header = {
        "AS2-Version": "1.2",
        "ediint-features": "CEM",
        "MIME-Version": "1.0",
        "Message-ID": reference,
        "AS2-From": as2utils.escape_as2name(message.organization.as2_name),
        "AS2-To": as2utils.escape_as2name(message.partner.as2_name),
        "Subject": message.partner.subject,
        "Date": email_datetime,
        "recipient-address": message.partner.target_url,
        "user-agent": "PYAS2, A pythonic AS2 server",
    }
    payload = email.Message.Message()
    with open(message.payload.file, "rb") as fh:
        payload.set_payload(fh.read())
        fh.close()
    payload.set_type(message.partner.content_type)
    payload.add_header("Content-Disposition", "attachment", filename=message.payload.name)
    del payload["MIME-Version"]
    # micContent,cmicContent,content = payload.get_payload(),None,payload.get_payload()
    content = payload.get_payload()
    if message.partner.compress:
        models.Log.objects.create(message=message, status="S", text=_(u"Compressing the payload."))
        message.compressed = True
        # micContent = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        cmessage = email.Message.Message()
        cmessage.set_type("application/pkcs7-mime")
        cmessage.set_param("name", "smime.p7z")
        cmessage.set_param("smime-type", "compressed-data")
        cmessage.add_header("Content-Transfer-Encoding", "base64")
        cmessage.add_header("Content-Disposition", "attachment", filename="smime.p7z")
        cmessage.set_payload(as2utils.compress_payload(as2utils.canonicalize(as2utils.mimetostring(payload, 0))))
        content, payload = cmessage.get_payload(), cmessage
        pyas2init.logger.debug("Compressed message %s payload as:\n%s" % (message.message_id, payload.as_string()))
    if message.partner.signature:
        models.Log.objects.create(
            message=message,
            status="S",
            text=_(u"Signing the message using organzation key %s" % message.organization.signature_key),
        )
        message.signed = True
        multipart = MIMEMultipart("signed", protocol="application/pkcs7-signature")
        del multipart["MIME-Version"]
        # micContent = as2utils.canonicalize2(payload)
        micContent = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        multipart.attach(payload)
        micalg, signature = as2utils.sign_payload(
            micContent,
            str(message.organization.signature_key.certificate.path),
            str(message.organization.signature_key.certificate_passphrase),
        )
        multipart.set_param("micalg", micalg)
        multipart.attach(signature)
        multipart.as_string()
        content = as2utils.canonicalize(as2utils.extractpayload(multipart))
        payload = multipart
        pyas2init.logger.debug("Signed message %s payload as:\n%s" % (message.message_id, payload.as_string()))
    if message.partner.encryption:
        # if not message.compressed and not message.signed:
        # micContent = as2utils.canonicalize(as2utils.mimetostring(payload, 0))
        models.Log.objects.create(
            message=message,
            status="S",
            text=_(u"Encrypting the message using partner key %s" % message.partner.encryption_key),
        )
        message.encrypted = True
        payload = as2utils.encrypt_payload(
            as2utils.canonicalize(as2utils.mimetostring(payload, 0)),
            message.partner.encryption_key.certificate.path,
            message.partner.encryption,
        )
        payload.set_type("application/pkcs7-mime")
        content = payload.get_payload()
        pyas2init.logger.debug("Encrypted message %s payload as:\n%s" % (message.message_id, payload.as_string()))
    if message.partner.mdn:
        as2Header["disposition-notification-to"] = "*****@*****.**"
        if message.partner.mdn_sign:
            as2Header["disposition-notification-options"] = (
                "signed-receipt-protocol=required, pkcs7-signature; signed-receipt-micalg=optional, %s"
                % message.partner.mdn_sign
            )
        message.mdn_mode = "SYNC"
        if message.partner.mdn_mode == "ASYNC":
            as2Header["receipt-delivery-option"] = pyas2init.gsettings["mdn_url"]
            message.mdn_mode = "ASYNC"
    if micContent:
        pyas2init.logger.debug("Calcualting MIC with alg %s for content:\n%s" % (micalg, micContent))
        calcMIC = getattr(hashlib, micalg.replace("-", ""), hashlib.sha1)
        message.mic = calcMIC(micContent).digest().encode("base64").strip()
    as2Header.update(payload.items())
    message.headers = ""
    for key in as2Header:
        message.headers = message.headers + "%s: %s\n" % (key, as2Header[key])
    message.save()
    models.Log.objects.create(
        message=message, status="S", text=_(u"AS2 message has been built successfully, sending it to the partner")
    )
    return content
Beispiel #11
0
def build_mdn(message, status, **kwargs):
    """ Function builds AS2 MDN report """
    try:
        hparser = HeaderParser()
        message_header = hparser.parsestr(message.headers)
        text = _(u"The AS2 message has been processed. Thank you for exchanging AS2 messages with Pyas2.")
        if status != "success":
            #### Send mail here
            as2utils.senderrorreport(
                message,
                _(
                    u"Failure in processing message from partner,\n Basic status : %s \n Advanced Status: %s"
                    % (kwargs["adv_status"], kwargs["status_message"])
                ),
            )
            text = _(
                u"The AS2 message could not be processed. The disposition-notification report has additional details."
            )
            models.Log.objects.create(message=message, status="E", text=kwargs["status_message"])
            message.status = "E"
        else:
            message.status = "S"
        if not message_header.get("disposition-notification-to"):
            models.Log.objects.create(
                message=message, status="S", text=_(u"MDN not requested by partner, closing request.")
            )
            return None, None
        models.Log.objects.create(message=message, status="S", text=_(u"Building the MDN response to the request"))
        main = MIMEMultipart("report", report_type="disposition-notification")
        textmessage = email.Message.Message()
        textmessage.set_payload("%s\n" % text)
        textmessage.set_type("text/plain")
        textmessage.set_charset("us-ascii")
        del textmessage["MIME-Version"]
        main.attach(textmessage)
        mdnbase = email.Message.Message()
        mdnbase.set_type("message/disposition-notification")
        mdnbase.set_charset("us-ascii")
        mdn = "Reporting-UA: Bots Opensource EDI Translator\n"
        mdn = mdn + "Original-Recipient: rfc822; %s\n" % message_header.get("as2-to")
        mdn = mdn + "Final-Recipient: rfc822; %s\n" % message_header.get("as2-to")
        mdn = mdn + "Original-Message-ID: <%s>\n" % message.message_id
        if status != "success":
            mdn = mdn + "Disposition: automatic-action/MDN-sent-automatically; processed/%s: %s\n" % (
                status,
                kwargs["adv_status"],
            )
        else:
            mdn = mdn + "Disposition: automatic-action/MDN-sent-automatically; processed\n"
        if message.mic:
            mdn = mdn + "Received-content-MIC: %s\n" % message.mic
        mdnbase.set_payload(mdn)
        del mdnbase["MIME-Version"]
        main.attach(mdnbase)
        del main["MIME-Version"]
        pyas2init.logger.debug("MDN for message %s created:\n%s" % (message.message_id, main.as_string()))
        mdnsigned = False
        if (
            message_header.get("disposition-notification-options")
            and message.organization
            and message.organization.signature_key
        ):
            models.Log.objects.create(
                message=message,
                status="S",
                text=_(u"Signing the MDN using private key %s" % message.organization.signature_key),
            )
            mdnsigned = True
            options = message_header.get("disposition-notification-options").split(";")
            algorithm = options[1].split(",")[1].strip()
            signed = MIMEMultipart("signed", protocol="application/pkcs7-signature")
            signed.attach(main)
            micalg, signature = as2utils.sign_payload(
                as2utils.canonicalize(as2utils.mimetostring(main, 0) + "\n"),
                str(message.organization.signature_key.certificate.path),
                str(message.organization.signature_key.certificate_passphrase),
            )
            pyas2init.logger.debug("Signature for MDN created:\n%s" % signature.as_string())
            signed.set_param("micalg", micalg)
            signed.attach(signature)
            mdnmessage = signed
        else:
            mdnmessage = main
        ### Add new line between the MDN message and the signature
        mdnbody = as2utils.extractpayload(mdnmessage)
        mainboundary = "--" + main.get_boundary() + "--"
        mdnbody = as2utils.canonicalize(mdnbody.replace(mainboundary, mainboundary + "\n"))
        mdnmessage.add_header("ediint-features", "CEM")
        mdnmessage.add_header("as2-from", message_header.get("as2-to"))
        mdnmessage.add_header("as2-to", message_header.get("as2-from"))
        mdnmessage.add_header("AS2-Version", "1.2")
        mdnmessage.add_header("date", email.Utils.formatdate(localtime=True))
        mdnmessage.add_header("Message-ID", email.utils.make_msgid())
        mdnmessage.add_header("user-agent", "PYAS2, A pythonic AS2 server")
        filename = mdnmessage.get("message-id").strip("<>") + ".mdn"
        fullfilename = as2utils.storefile(pyas2init.gsettings["mdn_send_store"], filename, mdnbody, True)
        mdn_headers = ""
        for key in mdnmessage.keys():
            mdn_headers = mdn_headers + "%s: %s\n" % (key, mdnmessage[key])
        if message_header.get("receipt-delivery-option"):
            message.mdn = models.MDN.objects.create(
                message_id=filename,
                file=fullfilename,
                status="P",
                signed=mdnsigned,
                headers=mdn_headers,
                return_url=message_header["receipt-delivery-option"],
            )
            message.mdn_mode = "ASYNC"
            mdnbody, mdnmessage = None, None
            models.Log.objects.create(
                message=message, status="S", text=_(u"Asynchronous MDN requested, setting status to pending")
            )
        else:
            message.mdn = models.MDN.objects.create(
                message_id=filename, file=fullfilename, status="S", signed=mdnsigned, headers=mdn_headers
            )
            message.mdn_mode = "SYNC"
            models.Log.objects.create(
                message=message, status="S", text=_(u"MDN created successfully and sent to partner")
            )
        return mdnbody, mdnmessage
    finally:
        message.save()
Beispiel #12
0
             )
         except Exception, e:
             ### Verify the signature using extracted signature and message without extra trailing new line in message
             try:
                 as2utils.verify_payload(
                     as2utils.extractpayload_fromstring2(mdnContent, main_boundary),
                     raw_sig,
                     cert,
                     ca_cert,
                     verify_cert,
                 )
             except Exception, e:
                 raise as2utils.as2exception(_(u"MDN Signature Verification Error, exception message is %s" % e))
 filename = messageId.strip("<>") + ".mdn"
 fullfilename = as2utils.storefile(
     pyas2init.gsettings["mdn_receive_store"], filename, as2utils.extractpayload(mdnMessage), True
 )
 message.mdn = models.MDN.objects.create(
     message_id=messageId.strip("<>"), file=fullfilename, status="R", headers=mdnHeaders, signed=mdnsigned
 )
 if mdnMessage.get_content_type() == "multipart/report":
     for part in mdnMessage.walk():
         if part.get_content_type() == "message/disposition-notification":
             pyas2init.logger.debug(
                 "Found MDN report for message %s:\n%s" % (message.message_id, part.as_string())
             )
             models.Log.objects.create(
                 message=message, status="S", text=_(u"Checking the MDN for status of the message")
             )
             mdn = part.get_payload().pop()
             mdnOMessageId = mdn.get("Original-Message-ID")