def handle(self, *args, **options): if len(args) != 3: raise CommandError( _(u'Insufficient number of arguments specified, please check help for correct usage' )) try: org = models.Organization.objects.get(as2_name=args[0]) except models.Organization.DoesNotExist: raise CommandError(_(u'Organization "%s" does not exist' % args[0])) try: partner = models.Partner.objects.get(as2_name=args[1]) except models.Partner.DoesNotExist: raise CommandError(_(u'Partner "%s" does not exist' % args[1])) if not os.path.isfile(args[2]): raise CommandError( _(u'Payload at location "%s" does not exist' % args[2])) if options['delete'] and not os.access(args[2], os.W_OK): raise CommandError('Insufficient file permission for payload %s' % args[2]) outdir = as2utils.join(pyas2init.gsettings['payload_send_store'], time.strftime('%Y%m%d')) as2utils.dirshouldbethere(outdir) outfile = as2utils.join(outdir, os.path.basename(args[2])) shutil.copy2(args[2], outfile) if options['delete']: os.remove(args[2]) payload = models.Payload.objects.create( name=os.path.basename(args[2]), file=outfile, content_type=partner.content_type) message = models.Message.objects.create( message_id=email.utils.make_msgid().strip('<>'), partner=partner, organization=org, direction='OUT', status='IP', payload=payload) try: payload = as2lib.build_message(message) as2lib.send_message(message, payload) except Exception, e: txt = as2utils.txtexc() reporttxt = _(u'Failed to send message, error:\n%(txt)s') % { 'txt': txt } pyas2init.logger.error(reporttxt) message.status = 'E' models.Log.objects.create( message=message, status='E', text=_(u'Failed to send message, error is %s' % e)) message.save() ### Send mail here as2utils.senderrorreport( message, _(u'Failed to send message, error is %s' % e)) sys.exit(2)
def update_dirs(): partners = Partner.objects.all() orgs = Organization.objects.all() for partner in partners: for org in orgs: as2utils.dirshouldbethere(as2utils.join(pyas2init.gsettings['root_dir'], 'messages', org.as2_name, 'inbox', partner.as2_name)) for org in orgs: for partner in partners: as2utils.dirshouldbethere(as2utils.join(pyas2init.gsettings['root_dir'], 'messages', partner.as2_name, 'outbox', org.as2_name))
def check_odirs(sender, instance, created, **kwargs): partners = Partner.objects.all() for partner in partners: as2utils.dirshouldbethere( as2utils.join(pyas2init.gsettings['root_dir'], 'messages', instance.as2_name, 'inbox', partner.as2_identifier)) as2utils.dirshouldbethere( as2utils.join(pyas2init.gsettings['root_dir'], 'messages', partner.as2_identifier, 'outbox', instance.as2_name))
def check_pdirs(sender, instance, created, **kwargs): orgs = Organization.objects.all() for org in orgs: as2utils.dirshouldbethere( as2utils.join(pyas2init.gsettings['root_dir'], 'messages', org.as2_name, 'inbox', instance.as2_name)) as2utils.dirshouldbethere( as2utils.join(pyas2init.gsettings['root_dir'], 'messages', instance.as2_name, 'outbox', org.as2_name))
def handle(self, *args, **options): if len(args) != 3: raise CommandError( _(u'Insufficient number of arguments specified, please check help for correct usage' )) try: org = models.Organization.objects.get(as2_name=args[0]) except models.Organization.DoesNotExist: raise CommandError( _(u'Organization "%s" does not exist' % args[0])) try: partner = models.Partner.objects.get(as2_name=args[1]) except models.Partner.DoesNotExist: raise CommandError(_(u'Partner "%s" does not exist' % args[1])) if not os.path.isfile(args[2]): raise CommandError( _(u'Payload at location "%s" does not exist' % args[2])) if options['delete'] and not os.access(args[2], os.W_OK): raise CommandError( 'Insufficient file permission for payload %s' % args[2]) outdir = as2utils.join(pyas2init.gsettings['payload_send_store'], time.strftime('%Y%m%d')) as2utils.dirshouldbethere(outdir) outfile = as2utils.join(outdir, os.path.basename(args[2])) shutil.copy2(args[2], outfile) if options['delete']: os.remove(args[2]) payload = models.Payload.objects.create( name=os.path.basename(args[2]), file=outfile, content_type=partner.content_type) message = models.Message.objects.create( message_id=email.utils.make_msgid().strip('<>'), partner=partner, organization=org, direction='OUT', status='IP', payload=payload) try: payload = as2lib.build_message(message) as2lib.send_message(message, payload) except Exception, e: txt = as2utils.txtexc() reporttxt = _(u'Failed to send message, error:\n%(txt)s') % { 'txt': txt } pyas2init.logger.error(reporttxt) message.status = 'E' models.Log.objects.create( message=message, status='E', text=_(u'Failed to send message, error is %s' % e)) message.save() ### Send mail here as2utils.senderrorreport( message, _(u'Failed to send message, error is %s' % e)) sys.exit(2)
def update_dirs(): partners = Partner.objects.all() orgs = Organization.objects.all() for partner in partners: for org in orgs: as2utils.dirshouldbethere( as2utils.join(pyas2init.gsettings['root_dir'], 'messages', org.as2_name, 'inbox', partner.as2_identifier)) for org in orgs: for partner in partners: as2utils.dirshouldbethere( as2utils.join(pyas2init.gsettings['root_dir'], 'messages', partner.as2_identifier, 'outbox', org.as2_name))
def initialize(): global gsettings pyas2_settings = {} if hasattr(settings, 'PYAS2'): pyas2_settings = settings.PYAS2 if not gsettings: gsettings['environment'] = pyas2_settings.get('ENVIRONMENT','production') gsettings['port'] = pyas2_settings.get('PORT', 8080) gsettings['ssl_certificate'] = pyas2_settings.get('SSLCERTIFICATE',None) gsettings['ssl_private_key'] = pyas2_settings.get('SSLPRIVATEKEY',None) gsettings['environment_text'] = pyas2_settings.get('ENVIRONMENTTEXT','Production') gsettings['environment_text_color'] = pyas2_settings.get('ENVIRONMENTTEXTCOLOR','Black') gsettings['root_dir'] = settings.BASE_DIR gsettings['python_path'] = pyas2_settings.get('PYTHONPATH', sys.executable) gsettings['managepy_path'] = as2utils.join(settings.BASE_DIR, 'manage.py') gsettings['daemon_port'] = pyas2_settings.get('DAEMONPORT', 16388) if pyas2_settings.get('DATADIR') and os.path.isdir(pyas2_settings.get('DATADIR')): gsettings['root_dir'] = pyas2_settings.get('DATADIR') gsettings['payload_receive_store'] = as2utils.join(gsettings['root_dir'], 'messages', '__store', 'payload', 'received') gsettings['payload_send_store'] = as2utils.join(gsettings['root_dir'], 'messages', '__store', 'payload', 'sent') gsettings['mdn_receive_store'] = as2utils.join(gsettings['root_dir'], 'messages', '__store', 'mdn', 'received') gsettings['mdn_send_store'] = as2utils.join(gsettings['root_dir'], 'messages', '__store', 'mdn', 'sent') gsettings['log_dir'] = as2utils.join(gsettings['root_dir'], 'logging') for sett in ['payload_receive_store', 'payload_send_store', 'mdn_receive_store', 'mdn_send_store', 'log_dir']: as2utils.dirshouldbethere(gsettings[sett]) gsettings['log_level'] = pyas2_settings.get('LOGLEVEL','INFO') gsettings['log_console'] = pyas2_settings.get('LOGCONSOLE',True) gsettings['log_console_level'] = pyas2_settings.get('LOGCONSOLELEVEL','STARTINFO') gsettings['max_retries'] = pyas2_settings.get('MAXRETRIES',30) gsettings['mdn_url'] = pyas2_settings.get('MDNURL','http://localhost:8080/pyas2/as2receive') gsettings['async_mdn_wait'] = pyas2_settings.get('ASYNCMDNWAIT',30) gsettings['max_arch_days'] = pyas2_settings.get('MAXARCHDAYS',30) gsettings['minDate'] = 0 - gsettings['max_arch_days']
def initialize(): """ Function initializes the global variables for pyAS2 """ global gsettings pyas2_settings = {} if hasattr(settings, 'PYAS2'): pyas2_settings = settings.PYAS2 if not gsettings: gsettings['environment'] = pyas2_settings.get('ENVIRONMENT', 'production') gsettings['port'] = pyas2_settings.get('PORT', 8080) gsettings['ssl_certificate'] = pyas2_settings.get( 'SSLCERTIFICATE', None) gsettings['ssl_private_key'] = pyas2_settings.get( 'SSLPRIVATEKEY', None) gsettings['environment_text'] = pyas2_settings.get( 'ENVIRONMENTTEXT', 'Production') gsettings['environment_text_color'] = pyas2_settings.get( 'ENVIRONMENTTEXTCOLOR', 'Black') gsettings['root_dir'] = settings.BASE_DIR gsettings['python_path'] = pyas2_settings.get('PYTHONPATH', sys.executable) gsettings['managepy_path'] = as2utils.join(settings.BASE_DIR, 'manage.py') gsettings['daemon_port'] = pyas2_settings.get('DAEMONPORT', 16388) if pyas2_settings.get('DATADIR') and os.path.isdir( pyas2_settings.get('DATADIR')): gsettings['root_dir'] = pyas2_settings.get('DATADIR') gsettings['payload_receive_store'] = as2utils.join( gsettings['root_dir'], 'messages', '__store', 'payload', 'received') gsettings['payload_send_store'] = as2utils.join( gsettings['root_dir'], 'messages', '__store', 'payload', 'sent') gsettings['mdn_receive_store'] = as2utils.join(gsettings['root_dir'], 'messages', '__store', 'mdn', 'received') gsettings['mdn_send_store'] = as2utils.join(gsettings['root_dir'], 'messages', '__store', 'mdn', 'sent') gsettings['log_dir'] = as2utils.join(gsettings['root_dir'], 'logging') for sett in [ 'payload_receive_store', 'payload_send_store', 'mdn_receive_store', 'mdn_send_store', 'log_dir' ]: as2utils.dirshouldbethere(gsettings[sett]) gsettings['log_level'] = pyas2_settings.get('LOGLEVEL', 'INFO') gsettings['log_console'] = pyas2_settings.get('LOGCONSOLE', True) gsettings['log_console_level'] = pyas2_settings.get( 'LOGCONSOLELEVEL', 'STARTINFO') gsettings['max_retries'] = pyas2_settings.get('MAXRETRIES', 30) gsettings['mdn_url'] = pyas2_settings.get( 'MDNURL', 'http://localhost:8080/pyas2/as2receive') gsettings['async_mdn_wait'] = pyas2_settings.get('ASYNCMDNWAIT', 30) gsettings['max_arch_days'] = pyas2_settings.get('MAXARCHDAYS', 30) gsettings['minDate'] = 0 - gsettings['max_arch_days']
def as2receive(request, *args, **kwargs): """ Function receives AS2 requests from partners. Checks whether its an AS2 message or an MDN and acts accordingly. """ if request.method == 'POST': # Create separate raw_payload with only message-id and content type # as M2Crypto's signatur verification method does not like many headers raw_payload = '%s: %s\n' % ('message-id', request.META['HTTP_MESSAGE_ID']) raw_payload += '%s: %s\n\n' % ('content-type', request.META['CONTENT_TYPE']) raw_payload += request.body # Extract all the relevant headers from the http request as2headers = '' for key in request.META: if key.startswith('HTTP') or key.startswith('CONTENT'): h_key = key.replace("HTTP_", "").replace("_", "-").lower() as2headers += '%s: %s\n' % (h_key, request.META[key]) request_content = as2headers.encode('utf-8') + '\n'.encode( 'utf-8') + request.body pyas2init.logger.debug( 'Recevied an HTTP POST from %s with payload :\n%s' % (request.META['REMOTE_ADDR'], request_content)) try: pyas2init.logger.debug( 'Check payload to see if its an AS2 Message or ASYNC MDN.') # Load the request header and body as a MIME Email Message payload = email.message_from_string(request_content) # Get the message sender and receiver AS2 identifiers message_org_as2name = as2utils.unescape_as2name( payload.get('as2-to')) message_partner_as2name = as2utils.unescape_as2name( payload.get('as2-from')) message = None # Check if this is an MDN message mdn_message = None if payload.get_content_type() == 'multipart/report': mdn_message = payload elif payload.get_content_type() == 'multipart/signed': for part in payload.walk(): if part.get_content_type() == 'multipart/report': mdn_message = part # If this is an MDN, get the message ID and check if it exists if mdn_message: msg_id = None for part in mdn_message.walk(): if part.get_content_type( ) == 'message/disposition-notification': msg_id = part.get_payload().pop().get( 'Original-Message-ID') pyas2init.logger.info( 'Asynchronous MDN received for AS2 message %s to organization %s ' 'from partner %s' % (msg_id, message_org_as2name, message_partner_as2name)) try: # Get the related organization, partner and message from the db. org = get_object_or_404(models.Organization, as2_name=message_org_as2name) partner = as2lib.get_partner_from_payload(payload) if not partner: raise Http404('No Partner matches the given query.') message = get_object_or_404(models.Message, message_id=msg_id.strip('<>'), organization=org, partner=partner) models.Log.objects.create( message=message, status='S', text=_( u'Processing asynchronous mdn received from partner' )) as2lib.save_mdn(message, raw_payload) except Http404: # Send 404 response pyas2init.logger.error( 'Unknown Asynchronous MDN AS2 message %s. ' 'Either the partner, org or message was not found in the system' % msg_id) return HttpResponseServerError( _(u'Unknown AS2 MDN received. Will not be processed')) except Exception, e: message.status = 'E' message.adv_status = _( u'Failed to send message, error is:\n %s' % traceback.format_exc(None).decode('utf-8', 'ignore')) models.Log.objects.create(message=message, status='E', text=message.adv_status) # Send mail here as2utils.senderrorreport(message, message.adv_status) finally: # Save message and send response to HTTP request if message: message.save() return HttpResponse(_(u'AS2 ASYNC MDN has been received')) else: try: # Process the received AS2 message from partner # Initialize the processing status variables status, adv_status, status_message = '', '', '' pyas2init.logger.info( 'Received an AS2 message with id %s for organization %s from partner %s' % (payload.get('message-id'), message_org_as2name, message_partner_as2name)) # Raise duplicate message error in case message already exists in the system # TODO: Create composite key (message_id, organization, partner) if models.Message.objects.filter(message_id=payload.get( 'message-id').strip('<>')).exists(): message = models.Message.objects.create( message_id='%s_%s' % (payload.get('message-id').strip('<>'), payload.get('date')), direction='IN', status='IP', headers=as2headers) raise as2utils.As2DuplicateDocument( _(u'An identical message has already ' u'been sent to our server')) # Create a new message in the system message = models.Message.objects.create( message_id=payload.get('message-id').strip('<>'), direction='IN', status='IP', headers=as2headers) # Process the received payload to extract the actual message from partner payload = as2lib.save_message(message, payload, raw_payload) # Get the inbox folder for this partner and organization output_dir = as2utils.join(pyas2init.gsettings['root_dir'], 'messages', message.organization.as2_name, 'inbox', message.partner.as2_identifier) # Get the filename from the header and if not there set to message id if message.partner.keep_filename and payload.get_filename( ): filename = payload.get_filename()[:100] else: filename = '%s.msg' % uuid.uuid4() # Save the message content to the store and inbox content = payload.get_payload(decode=True) full_filename = as2utils.storefile(output_dir, filename, content, False) store_filename = as2utils.storefile( pyas2init.gsettings['payload_receive_store'], message.message_id, content, True) models.Log.objects.create( message=message, status='S', text=_(u'Message has been saved successfully to %s' % full_filename)) message.payload = models.Payload.objects.create( name=filename, file=store_filename, content_type=payload.get_content_type()) # Set processing status and run the post receive command. status = 'success' as2lib.run_post_receive(message, full_filename) message.save() # Catch each of the possible exceptions while processing an as2 message except as2utils.As2DuplicateDocument, e: status = 'warning' adv_status = 'duplicate-document' status_message = _( u'An error occurred during the AS2 message processing: %s' % e) except as2utils.As2PartnerNotFound, e: status = 'error' adv_status = 'unknown-trading-partner' status_message = _( u'An error occurred during the AS2 message processing: %s' % e)
def check_pdirs(sender, instance, created, **kwargs): orgs = Organization.objects.all() for org in orgs: as2utils.dirshouldbethere(as2utils.join(pyas2init.gsettings['root_dir'], 'messages', org.as2_name, 'inbox', instance.as2_name)) as2utils.dirshouldbethere(as2utils.join(pyas2init.gsettings['root_dir'], 'messages', instance.as2_name, 'outbox', org.as2_name))
def check_odirs(sender, instance, created, **kwargs): partners = Partner.objects.all() for partner in partners: as2utils.dirshouldbethere(as2utils.join(pyas2init.gsettings['root_dir'], 'messages', instance.as2_name, 'inbox', partner.as2_name)) as2utils.dirshouldbethere(as2utils.join(pyas2init.gsettings['root_dir'], 'messages', partner.as2_name, 'outbox', instance.as2_name))
def handle(self, *args, **options): pyas2init.logger.info(_(u'Starting PYAS2 send daemon.')) try: engine_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) engine_socket.bind( ('127.0.0.1', pyas2init.gsettings['daemon_port'])) except socket.error: engine_socket.close() raise CommandError( _(u'An instance of the send daemon is already running')) else: atexit.register(engine_socket.close) cond = threading.Condition() tasks = set() dir_watch_data = [] orgs = models.Organization.objects.all() partners = models.Partner.objects.all() python_executable_path = pyas2init.gsettings['python_path'] managepy_path = pyas2init.gsettings['managepy_path'] for partner in partners: for org in orgs: dir_watch_data.append({}) dir_watch_data[-1]['path'] = as2utils.join( pyas2init.gsettings['root_dir'], 'messages', partner.as2_name, 'outbox', org.as2_name) dir_watch_data[-1]['organization'] = org.as2_name dir_watch_data[-1]['partner'] = partner.as2_name if not dir_watch_data: pyas2init.logger.error(_(u'No partners have been configured!')) sys.exit(0) pyas2init.logger.info(_(u'Process existing files in the directory.')) for dir_watch in dir_watch_data: files = [ f for f in os.listdir(dir_watch['path']) if os.path.isfile(as2utils.join(dir_watch['path'], f)) ] for file in files: lijst = [ python_executable_path, managepy_path, 'sendas2message', '--delete', dir_watch['organization'], dir_watch['partner'], as2utils.join(dir_watch['path'], file) ] pyas2init.logger.info( u'Send as2 message with params "%(task)s".', {'task': lijst}) subprocess.Popen(lijst) if os.name == 'nt': # for windows: start a thread per directory watcher for dir_watch in dir_watch_data: dir_watch_thread = threading.Thread( target=windows_event_handler, args=(pyas2init.logger, dir_watch, cond, tasks)) dir_watch_thread.daemon = True # do not wait for thread when exiting dir_watch_thread.start() else: # for linux: one watch-thread, but multiple watches. dir_watch_thread = threading.Thread(target=linux_event_handler, args=(pyas2init.logger, dir_watch_data, cond, tasks)) dir_watch_thread.daemon = True # do not wait for thread when exiting dir_watch_thread.start() # this main thread get the results from the watch-thread(s). pyas2init.logger.info(_(u'PYAS2 send daemon started started.')) active_receiving = False timeout = 2.0 cond.acquire() while True: # this functions as a buffer: all events go into set tasks. # the tasks are fired to jobqueue after TIMOUT sec. # this is to avoid firing to many tasks to jobqueue; events typically come in bursts. # is value of timeout is larger, reaction times are slower...but less tasks are fired to jobqueue. # in itself this is not a problem, as jobqueue will alos discard duplicate jobs. # 2 sec seems to e a good value: reasonable quick, not to nervous. cond.wait( timeout=timeout) # get back when results, or after timeout sec if tasks: if not active_receiving: # first request (after tasks have been fired, or startup of dirmonitor) active_receiving = True last_time = time.time() else: # active receiving events current_time = time.time() if current_time - last_time >= timeout: try: for task in tasks: lijst = [ python_executable_path, managepy_path, 'sendas2message', '--delete', task[0], task[1], task[2] ] pyas2init.logger.info( u'Send as2 message with params "%(task)s".', {'task': lijst}) subprocess.Popen(lijst) except Exception as msg: pyas2init.logger.info( u'Error in running task: "%(msg)s".', {'msg': msg}) tasks.clear() active_receiving = False else: pyas2init.logger.debug(u'time difference to small.') last_time = current_time cond.release() sys.exit(0)
def handle(self, *args, **options): # Check if organization and partner exists try: org = models.Organization.objects.get( as2_name=options['organization_as2name']) except models.Organization.DoesNotExist: raise CommandError( _(u'Organization "%s" does not exist' % options['organization_as2name'])) try: partner = models.Partner.objects.get( as2_name=options['partner_as2name']) except models.Partner.DoesNotExist: raise CommandError( _(u'Partner "%s" does not exist' % options['partner_as2name'])) # Check if file exists and we have the right permissions if not os.path.isfile(options['path_to_payload']): raise CommandError( _(u'Payload at location "%s" does not exist' % options['path_to_payload'])) if options['delete'] and not os.access(options['path_to_payload'], os.W_OK): raise CommandError('Insufficient file permission for payload %s' % options['path_to_payload']) # Copy the file to the store output_dir = as2utils.join(pyas2init.gsettings['payload_send_store'], time.strftime('%Y%m%d')) as2utils.dirshouldbethere(output_dir) outfile = as2utils.join(output_dir, os.path.basename(options['path_to_payload'])) shutil.copy2(options['path_to_payload'], outfile) # Delete original file if option is set if options['delete']: os.remove(options['path_to_payload']) # Create the payload and message objects payload = models.Payload.objects.create( name=os.path.basename(options['path_to_payload']), file=outfile, content_type=partner.content_type) message = models.Message.objects.create( message_id=email.utils.make_msgid().strip('<>'), partner=partner, organization=org, direction='OUT', status='IP', payload=payload) # Build and send the AS2 message try: payload = as2lib.build_message(message) as2lib.send_message(message, payload) except Exception: message.status = 'E' txt = traceback.format_exc(None).decode('utf-8', 'ignore') message.adv_status = \ _(u'Failed to send message, error:\n%(txt)s') % {'txt': txt} pyas2init.logger.error(message.adv_status) models.Log.objects.create(message=message, status='E', text=message.adv_status) message.save() # Send mail here as2utils.senderrorreport(message, message.adv_status) sys.exit(2) sys.exit(0)
def as2receive(request, *args, **kwargs): """ Function receives AS2 requests from partners. Checks whether its an AS2 message or an MDN and acts accordingly. """ if request.method == 'POST': # Process the posted AS2 message request_body = request.read() # Create separate raw_payload with only message-id and content type as M2Crypto's signature # verification method does not like too many header raw_payload = '%s: %s\n' % ('message-id', request.META['HTTP_MESSAGE_ID']) raw_payload += '%s: %s\n\n' % ('content-type', request.META['CONTENT_TYPE']) raw_payload += request_body # Extract all the relevant headers from the http request as2headers = '' for key in request.META: if key.startswith('HTTP') or key.startswith('CONTENT'): as2headers += '%s: %s\n' % (key.replace("HTTP_", "").replace("_", "-").lower(), request.META[key]) pyas2init.logger.debug('Recevied an HTTP POST from %s with payload :\n%s' % (request.META['REMOTE_ADDR'], as2headers + '\n' + request_body)) try: pyas2init.logger.debug('Check payload to see if its an AS2 Message or ASYNC MDN.') # Load the request header and body as a MIME Email Message payload = email.message_from_string(as2headers + '\n' + request_body) # Get the message sender and receiver AS2 IDs message_org = as2utils.unescape_as2name(payload.get('as2-to')) message_partner = as2utils.unescape_as2name(payload.get('as2-from')) message = None # Check if this is an MDN message mdn_message = None if payload.get_content_type() == 'multipart/report': mdn_message = payload elif payload.get_content_type() == 'multipart/signed': for part in payload.walk(): if part.get_content_type() == 'multipart/report': mdn_message = part # If this is an MDN, get the message ID and check if it exists if mdn_message: msg_id = None for part in mdn_message.walk(): if part.get_content_type() == 'message/disposition-notification': msg_id = part.get_payload().pop().get('Original-Message-ID') pyas2init.logger.info('Asynchronous MDN received for AS2 message %s to organization %s ' 'from partner %s' % (msg_id, message_org, message_partner)) try: # Get the related organization, partner and message from the db. org = get_object_or_404(models.Organization, as2_name=message_org) partner = get_object_or_404(models.Partner, as2_name=message_partner) message = get_object_or_404(models.Message, message_id=msg_id.strip('<>'), organization=org, partner=partner) models.Log.objects.create(message=message, status='S', text=_(u'Processing asynchronous mdn received from partner')) as2lib.save_mdn(message, raw_payload) except Http404: # Send 404 response pyas2init.logger.error('Unknown Asynchronous MDN AS2 message %s. ' 'Either the partner, org or message was not found in the system' % msg_id) return HttpResponseServerError(_(u'Unknown AS2 MDN received. Will not be processed')) except Exception, e: message.status = 'E' models.Log.objects.create(message=message, status='E', text=_(u'Failed to send message, error is %s' % e)) # Send mail here as2utils.senderrorreport(message, _(u'Failed to send message, error is %s' % e)) finally: # Save message and send response to HTTP request if message: message.save() return HttpResponse(_(u'AS2 ASYNC MDN has been received')) else: try: # Process the received AS2 message from partner # Initialize the processing status variables status, adv_status, status_message = '', '', '' pyas2init.logger.info('Received an AS2 message with id %s for organization %s from partner %s' % (payload.get('message-id'), message_org, message_partner)) # Raise duplicate message error in case message already exists in the system # TODO: Create composite key (message_id, organization, partner) if models.Message.objects.filter(message_id=payload.get('message-id').strip('<>')).exists(): message = models.Message.objects.create( message_id='%s_%s' % (payload.get('message-id').strip('<>'), payload.get('date')), direction='IN', status='IP', headers=as2headers ) raise as2utils.As2DuplicateDocument(_(u'An identical message has already ' u'been sent to our server')) # Create a new message in the system message = models.Message.objects.create( message_id=payload.get('message-id').strip('<>'), direction='IN', status='IP', headers=as2headers) # Process the received payload to extract the actual message from partner payload = as2lib.save_message(message, payload, raw_payload) # Get the inbox folder for this partner and organization output_dir = as2utils.join(pyas2init.gsettings['root_dir'], 'messages', message.organization.as2_name, 'inbox', message.partner.as2_name) # Get the filename from the header and if not there set to message id if message.partner.keep_filename and payload.get_filename(): filename = payload.get_filename() else: filename = '%s.msg' % message.message_id # Save the message content to the store and inbox content = payload.get_payload(decode=True) full_filename = as2utils.storefile(output_dir, filename, content, False) store_filename = as2utils.storefile(pyas2init.gsettings['payload_receive_store'], message.message_id, content, True) models.Log.objects.create(message=message, status='S', text=_(u'Message has been saved successfully to %s' % full_filename)) message.payload = models.Payload.objects.create(name=filename, file=store_filename, content_type=payload.get_content_type()) # Set processing status and run the post receive command. status = 'success' as2lib.run_post_receive(message, full_filename) message.save() # Catch each of the possible exceptions while processing an as2 message except as2utils.As2DuplicateDocument, e: status = 'warning' adv_status = 'duplicate-document' status_message = _(u'An error occurred during the AS2 message processing: %s' % e) except as2utils.As2PartnerNotFound, e: status = 'error' adv_status = 'unknown-trading-partner' status_message = _(u'An error occurred during the AS2 message processing: %s' % e)
def handle(self, *args, **options): pyas2init.logger.info(_(u'Starting PYAS2 send daemon.')) try: engine_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) engine_socket.bind(('127.0.0.1', pyas2init.gsettings['daemon_port'])) except socket.error: engine_socket.close() raise CommandError(_(u'An instance of the send daemon is already running')) else: atexit.register(engine_socket.close) cond = threading.Condition() tasks = set() dir_watch_data = [] orgs = models.Organization.objects.all() partners = models.Partner.objects.all() python_executable_path = pyas2init.gsettings['python_path'] managepy_path = pyas2init.gsettings['managepy_path'] ##as2utils.join(os.path.dirname(os.path.dirname(__file__)), 'manage.py') for partner in partners: for org in orgs: dir_watch_data.append({}) dir_watch_data[-1]['path'] = as2utils.join(pyas2init.gsettings['root_dir'], 'messages', partner.as2_name, 'outbox', org.as2_name) dir_watch_data[-1]['organization'] = org.as2_name dir_watch_data[-1]['partner'] = partner.as2_name if not dir_watch_data: pyas2init.logger.error(_(u'No partners have been configured!')) sys.exit(0) pyas2init.logger.info(_(u'Process exisitng files in the directory.')) for dir_watch in dir_watch_data: files = [f for f in os.listdir(dir_watch['path']) if os.path.isfile(as2utils.join(dir_watch['path'],f))] for file in files: lijst = [python_executable_path,managepy_path,'sendas2message','--delete',dir_watch['organization'],dir_watch['partner'],as2utils.join(dir_watch['path'],file)] pyas2init.logger.info(u'Send as2 message with params "%(task)s".',{'task':lijst}) subprocess.Popen(lijst) if os.name == 'nt': #for windows: start a thread per directory watcher for dir_watch in dir_watch_data: dir_watch_thread = threading.Thread(target=windows_event_handler, args=(pyas2init.logger,dir_watch,cond,tasks)) dir_watch_thread.daemon = True #do not wait for thread when exiting dir_watch_thread.start() else: #for linux: one watch-thread, but multiple watches. dir_watch_thread = threading.Thread(target=linux_event_handler, args=(pyas2init.logger,dir_watch_data,cond,tasks)) dir_watch_thread.daemon = True #do not wait for thread when exiting dir_watch_thread.start() # this main thread get the results from the watch-thread(s). pyas2init.logger.info(_(u'PYAS2 send daemon started started.')) active_receiving = False timeout = 2.0 cond.acquire() while True: #this functions as a buffer: all events go into set tasks. #the tasks are fired to jobqueue after TIMOUT sec. #this is to avoid firing to many tasks to jobqueue; events typically come in bursts. #is value of timeout is larger, reaction times are slower...but less tasks are fired to jobqueue. #in itself this is not a problem, as jobqueue will alos discard duplicate jobs. #2 sec seems to e a good value: reasonable quick, not to nervous. cond.wait(timeout=timeout) #get back when results, or after timeout sec if tasks: if not active_receiving: #first request (after tasks have been fired, or startup of dirmonitor) active_receiving = True last_time = time.time() else: #active receiving events current_time = time.time() if current_time - last_time >= timeout: try: for task in tasks: lijst = [python_executable_path,managepy_path,'sendas2message','--delete',task[0],task[1],task[2]] pyas2init.logger.info(u'Send as2 message with params "%(task)s".',{'task':lijst}) subprocess.Popen(lijst) except Exception as msg: pyas2init.logger.info(u'Error in running task: "%(msg)s".',{'msg':msg}) tasks.clear() active_receiving = False else: pyas2init.logger.debug(u'time difference to small.') last_time = current_time cond.release() sys.exit(0)
def as2receive(request,*args,**kwargs): ''' Function receives requests from partners. Checks whether its an AS2 message or an MDN and acts accordingly. ''' if request.method == 'POST': as2payload = '' for key in request.META: if key.startswith('HTTP') or key.startswith('CONTENT'): as2payload = as2payload + '%s: %s\n'%(key.replace("HTTP_","").replace("_","-").lower(), request.META[key]) as2headers = as2payload as2payload = as2payload + '\n' + request.read() pyas2init.logger.debug('Recevied an HTTP POST from %s with payload :\n%s'%(request.META['REMOTE_ADDR'],as2payload)) try: pyas2init.logger.debug('Check payload to see if its an AS2 Message or ASYNC MDN.') payload = email.message_from_string(as2payload) mdn_message = None if payload.get_content_type() == 'multipart/report': mdn_message = payload elif payload.get_content_type() == 'multipart/signed': for part in payload.walk(): if part.get_content_type() == 'multipart/report': mdn_message = part if mdn_message: for part in mdn_message.walk(): if (part.get_content_type() == 'message/disposition-notification'): msg_id = part.get_payload().pop().get('Original-Message-ID') pyas2init.logger.debug('Received MDN for AS2 message %s'%msg_id) message = models.Message.objects.get(message_id=msg_id.strip('<>')) models.Log.objects.create(message=message, status='S', text=_(u'Processing asynchronous mdn received from partner')) try: as2lib.save_mdn(message, as2payload) except Exception,e: message.status = 'E' models.Log.objects.create(message=message, status='E', text=_(u'Failed to send message, error is %s' %e)) #### Send mail here as2utils.senderrorreport(message,_(u'Failed to send message, error is %s' %e)) finally: message.save() return HttpResponse(_(u'AS2 ASYNC MDN has been received')) else: try: pyas2init.logger.debug('Received an AS2 message with id %s'%payload.get('message-id')) if models.Message.objects.filter(message_id=payload.get('message-id').strip('<>')).exists(): message = models.Message.objects.create(message_id='%s_%s'%(payload.get('message-id').strip('<>'),payload.get('date')),direction='IN', status='IP', headers=as2headers) raise as2utils.as2duplicatedocument(_(u'An identical message has already been sent to our server')) message = models.Message.objects.create(message_id=payload.get('message-id').strip('<>'),direction='IN', status='IP', headers=as2headers) payload = as2lib.save_message(message, as2payload) outputdir = as2utils.join(pyas2init.gsettings['root_dir'], 'messages', message.organization.as2_name, 'inbox', message.partner.as2_name) storedir = pyas2init.gsettings['payload_receive_store'] if message.partner.keep_filename and payload.get_filename(): filename = payload.get_filename() else: filename = '%s.msg' %message.message_id content = payload.get_payload(decode=True) fullfilename = as2utils.storefile(outputdir,filename,content,False) storefilename = as2utils.storefile(pyas2init.gsettings['payload_receive_store'],message.message_id,content,True) models.Log.objects.create(message=message, status='S', text=_(u'Message has been saved successfully to %s'%fullfilename)) message.payload = models.Payload.objects.create(name=filename, file=storefilename, content_type=payload.get_content_type()) status, adv_status, status_message = 'success', '', '' as2lib.run_postreceive(message,fullfilename) message.save() except as2utils.as2duplicatedocument,e: status, adv_status, status_message = 'warning', 'duplicate-document', _(u'An error occured during the AS2 message processing: %s'%e) except as2utils.as2partnernotfound,e: status, adv_status, status_message = 'error', 'unknown-trading-partner', _(u'An error occured during the AS2 message processing: %s'%e)