diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 07d0aabe7cd..d37c43bc1ff 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -26,6 +26,7 @@ import logging import re import time from email.header import decode_header +from email.message import Message import tools from osv import osv @@ -87,7 +88,6 @@ class mail_message_common(osv.osv_memory): "select plaintext or rich text contents accordingly", readonly=1), 'body_text': fields.text('Text contents', help="Plain-text version of the message"), 'body_html': fields.text('Rich-text contents', help="Rich-text/HTML version of the message"), - 'original': fields.text('Original', help="Original version of the message, as it was sent on the network", readonly=1), } _defaults = { @@ -175,6 +175,7 @@ class mail_message(osv.osv): ('cancel', 'Cancelled'), ], 'State', readonly=True), 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it"), + 'original': fields.binary('Original', help="Original version of the message, as it was sent on the network", readonly=1), } _defaults = { @@ -307,13 +308,16 @@ class mail_message(osv.osv): _logger.exception("Failed processing mail queue") return res - def parse_message(self, message): + def parse_message(self, message, save_original=False): """Parses a string or email.message.Message representing an RFC-2822 email, and returns a generic dict holding the message details. :param message: the message to parse :type message: email.message.Message | string | unicode + :param bool save_original: whether the returned dict + should include an ``original`` entry with the base64 + encoded source of the message. :rtype: dict :return: A dict with the following structure, where each field may not be present if missing in original @@ -349,8 +353,11 @@ class mail_message(osv.osv): message_id = msg_txt.get('message-id', False) msg = {} - # save original, we need to be able to read the original email sometimes - msg['original'] = message + if save_original: + # save original, we need to be able to read the original email sometimes + msg['original'] = message.as_string() if isinstance(message, Message) \ + else message + msg['original'] = base64.b64encode(msg['original']) # binary fields are b64 if not message_id: # Very unusual situation, be we should be fault-tolerant here @@ -487,8 +494,7 @@ class mail_message(osv.osv): mail_server_id=message.mail_server_id.id, context=context) if res: - message.write({'state':'sent', 'message_id': res, - 'original': msg.as_string(message.email_from)}) + message.write({'state':'sent', 'message_id': res}) else: message.write({'state':'exception'}) diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index c3ceb0740fa..83e7474fe1a 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -36,12 +36,12 @@ - - - + + + @@ -66,7 +66,7 @@ - + diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index fa613cb219e..6ec9b0247aa 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -153,10 +153,14 @@ class mail_thread(osv.osv): containing all the details passed as parameters. All attachments will be attached to the thread record as well as to the actual message. + If only the ``threads`` and ``subject`` parameters are provided, + a *event log* message is created, without the usual envelope + attributes (sender, recipients, etc.). :param threads: list of thread ids, or list of browse_records representing threads to which a new message should be attached - :param subject: Thread action keyword e.g.: If thread is closed "Close" keyword is used + :param subject: subject of the message, or description of the event if this + is an *event log* entry. :param email_to: Email-To / Recipient address :param email_from: Email From / Sender address if any :param email_cc: Comma-Separated list of Carbon Copy Emails To addresse if any @@ -291,7 +295,8 @@ class mail_thread(osv.osv): context = context) - def message_process(self, cr, uid, model, message, custom_values=None, context=None): + def message_process(self, cr, uid, model, message, custom_values=None, + save_original=False, context=None): """Process an incoming RFC2822 email message related to the given thread model, relying on ``mail.message.parse()`` for the parsing operation, and then calling ``message_new`` @@ -307,6 +312,8 @@ class mail_thread(osv.osv): to pass to ``message_new`` if a new record needs to be created. Ignored if the thread record already exists. + :param bool save_original: whether to keep a copy of the original + email source attached to the message after it is imported. """ # extract message bytes - we are forced to pass the message as binary because # we don't know its encoding until we parse its headers and hence can't @@ -328,7 +335,7 @@ class mail_thread(osv.osv): if isinstance(message, unicode): message = message.encode('utf-8') msg_txt = email.message_from_string(message) - msg = mail_message.parse_message(msg_txt) + msg = mail_message.parse_message(msg_txt, save_original=save_original) # Create New Record into particular model def create_record(msg): @@ -357,6 +364,9 @@ class mail_thread(osv.osv): if model_pool.exists(cr, uid, res_id): if hasattr(model_pool, 'message_update'): model_pool.message_update(cr, uid, [res_id], msg, {}, context=context) + else: + # referenced thread was not found, we'll have to create a new one + res_id = False if not res_id: res_id = create_record(msg) #To forward the email to other followers @@ -386,8 +396,8 @@ class mail_thread(osv.osv): followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id] else: followers = self.message_thread_followers(cr, uid, [res.id])[res.id] - message_followers_emails = mail_message.to_email(','.join(filter(None, followers))) - message_recipients = mail_message.to_email(','.join(filter(None, + message_followers_emails = to_email(','.join(filter(None, followers))) + message_recipients = to_email(','.join(filter(None, [decode(msg['from']), decode(msg['to']), decode(msg['cc'])]))) @@ -398,7 +408,7 @@ class mail_thread(osv.osv): del msg['reply-to'] msg['reply-to'] = res.section_id.reply_to - smtp_from = mail_message.to_email(msg['from']) + smtp_from = to_email(msg['from']) msg['from'] = smtp_from msg['to'] = forward_to msg['message-id'] = tools.generate_tracking_message_id(res.id) diff --git a/addons/mail/scripts/openerp_mailgate/openerp_mailgate.py b/addons/mail/scripts/openerp_mailgate/openerp_mailgate.py index b75c92d8d34..7e6ba87945f 100755 --- a/addons/mail/scripts/openerp_mailgate/openerp_mailgate.py +++ b/addons/mail/scripts/openerp_mailgate/openerp_mailgate.py @@ -46,13 +46,14 @@ class DefaultConfig(object): OPENERP_PORT = 8069 OPENERP_DEFAULT_DATABASE = 'openerp' MAIL_ERROR = 'error@example.com' - MAIL_SERVER = 'localhost' - MAIL_ADMINS = ('admin@example.com',) + MAIL_SERVER = 'smtp.example.com' + MAIL_SERVER_PORT = 25 + MAIL_ADMINS = ('info@example.com',) config = DefaultConfig() -def send_mail(_from_, to_, subject, text, files=None, server=config.MAIL_SERVER): +def send_mail(_from_, to_, subject, text, files=None, server=config.MAIL_SERVER, port=config.MAIL_SERVER_PORT): assert isinstance(to_, (list, tuple)) if files is None: @@ -74,7 +75,7 @@ def send_mail(_from_, to_, subject, text, files=None, server=config.MAIL_SERVER) % file_name) msg.attach(part) - smtp = smtplib.SMTP(server) + smtp = smtplib.SMTP(server, port=port) smtp.sendmail(_from_, to_, msg.as_string() ) smtp.close() @@ -104,16 +105,15 @@ class EmailParser(object): self.email_default = email_default - def parse(self, message, custom_values=None): - if custom_values is None: - custom_values = {} + def parse(self, message, custom_values=None, save_original=None): # pass message as bytes because we don't know its encoding until we parse its headers # and hence can't convert it to utf-8 for transport res_id = self.rpc('mail.thread', 'message_process', self.model, xmlrpclib.Binary(message), - custom_values) + custom_values or {}, + save_original or False) def configure_parser(): parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog v1.1') @@ -145,6 +145,10 @@ def configure_parser(): parser.add_option("--custom-values", dest="custom_values", help="Add Custom Values to the object", default=None) + parser.add_option("-s", dest="save_original", + action="store_true", + help="Attach a copy of original email to the message entry", + default=False) return parser @@ -169,12 +173,14 @@ def main(): custom_values = {} try: + print "custom vals: %s" % options.custom_values custom_values = dict(eval(options.custom_values or {} )) except: - pass + import traceback + traceback.print_exc() try: - email_parser.parse(msg_txt, custom_values) + email_parser.parse(msg_txt, custom_values, options.save_original or False) except Exception: msg = '\n'.join([ 'parameters', @@ -191,6 +197,7 @@ def main(): config.MAIL_ADMINS, subject, msg, files=[('message.txt', msg_txt)] ) + sys.stderr.write("Failed to deliver email to OpenERP Server, sending error notification to %s\n" % config.MAIL_ADMINS) if __name__ == '__main__': main()