Exemple #1
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
Exemple #2
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 
Exemple #3
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()
Exemple #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()	
Exemple #5
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
Exemple #6
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()
Exemple #7
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
Exemple #8
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()