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
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
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()
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()
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
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()
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
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()