diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 38904aecb5e..0799c473e80 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -631,8 +631,10 @@ class actions_server(osv.osv): subject = self.merge_message(cr, uid, action.subject, action, context) body = self.merge_message(cr, uid, action.message, action, context) - smtp_server_obj = self.pool.get('ir.mail_server') - if smtp_server_obj.send_email(uid, user, [address], subject, body, debug=False, subtype='html', cr=cr) == True: + smtp_server_pool = self.pool.get('ir.mail_server') + msg = smtp_server_pool.pack_message(cr, uid, subject, body, subtype='html') + res_email = smtp_server_pool.send_email(cr, uid, user, [address], msg, debug=False) + if res_email: logger.info('Email successfully sent to: %s', address) else: logger.warning('Failed to send email to: %s', address) diff --git a/openerp/addons/base/ir/ir_mail_server.py b/openerp/addons/base/ir/ir_mail_server.py index 02700302325..053e825255d 100644 --- a/openerp/addons/base/ir/ir_mail_server.py +++ b/openerp/addons/base/ir/ir_mail_server.py @@ -55,14 +55,14 @@ from email.generator import Generator from openerp.loglevels import get_encodings, ustr, exception_to_unicode _logger = logging.getLogger('tools') - priorities = { '1': '1 (Highest)', '2': '2 (High)', '3': '3 (Normal)', '4': '4 (Low)', '5': '5 (Lowest)', - } +} + class ir_mail_server(osv.osv): """ @@ -74,26 +74,28 @@ class ir_mail_server(osv.osv): 'name': fields.char('Name', size=64, required=True, select=True, - help="The Name is used as the Sender name along with the provided From Email, \ -unless it is already specified in the From Email, e.g: John Doe ", ), 'smtp_host': fields.char('Server', size=120, required=True, - help="Enter name of outgoing server, eg: smtp.yourdomain.com"), + help="Hostname or IP of SMTP server"), 'smtp_port': fields.integer('SMTP Port', size=64, required=True, - help="Enter port number, eg: 25 or 587"), + help="SMTP Port of SMPT server"), 'smtp_user': fields.char('User Name', size=120, required=False, - help="Specify the username if your SMTP server requires authentication, " - "otherwise leave it empty."), + help="Username for SMTP authentication"), 'smtp_pass': fields.char('Password', size=120, - required=False), - 'smtp_tls':fields.boolean('TLS'), - 'smtp_ssl':fields.boolean('SSL/TLS'), - 'priority': fields.integer('Priority', help="If no specific server is \ - requested for a mail then the highest priority one is used"), + required=False, help="Password for SMTP authentication"), + 'smtp_tls': fields.boolean('TLS', help="If True, TLS encryption will be requested at \ + beginning of SMTP transactions. Do not use if SSL is \ + enabled, or if the server does not support it."), + 'smtp_ssl':fields.boolean('SSL/TLS', help="If True, SMTPS (Secure SMTP over SSL encryption) \ + will be used. When selected, change smtp_port to 465. \ + Do not use with TLS or if the server does not support it."), + 'priority': fields.integer('Priority', help="If no specific \ + server is requested for a mail, the highest priority one \ + is used. Default priority is 10"), } _defaults = { @@ -112,26 +114,27 @@ unless it is already specified in the From Email, e.g: John Doe ", """ Test SMTP connection works """ - try: - for smtp_server in self.browse(cr, uid, ids, context=context): - smtp = self.connect_smtp_server(cr, uid, smtp_server.smtp_host, - smtp_server.smtp_port, user_name=smtp_server.smtp_user, - user_password=smtp_server.smtp_pass, ssl=smtp_server.smtp_ssl, - tls=smtp_server.smtp_tls, debug=False) + for smtp_server in self.browse(cr, uid, ids, context=context): + smtp = False + try: + smtp = self.connect_smtp_server(smtp_server.smtp_host, + smtp_server.smtp_port, user_name=smtp_server.smtp_user, + user_password=smtp_server.smtp_pass, ssl=smtp_server.smtp_ssl, + tls=smtp_server.smtp_tls, debug=False) + except Exception, error: + raise osv.except_osv( + _("SMTP Connection: Test failed"), + _("Reason: %s") % error + finally: ) try: - smtp.quit() + if smtp:smtp.quit() except Exception: # ignored, just a consequence of the previous exception pass - except Exception, error: - raise osv.except_osv( - _("SMTP Connection: Test failed"), - _("Reason: %s") % error - ) - + raise osv.except_osv(_("SMTP Connection: Test Successfully!"), '') - def connect_smtp_server(self, cr, uid, server_host, server_port, user_name=None, + def connect_smtp_server(self, server_host, server_port, user_name=None, user_password=None, ssl=False, tls=False, debug=False): """ Connect SMTP Server and returned the (SMTP) object @@ -163,51 +166,99 @@ unless it is already specified in the From Email, e.g: John Doe ", raise error return smtp_server - def generate_tracking_message_id(self, openobject_id): - """Returns a string that can be used in the Message-ID RFC822 header field so we - can track the replies related to a given object thanks to the "In-Reply-To" or - "References" fields that Mail User Agents will set. + def pack_message(self, cr, uid, subject, body, email_cc=None, email_bcc=None, reply_to=False, + attach=None, message_id=None, references=None, openobject_id=False, debug=False, subtype='plain', x_headers=None, priority='3'): + """ - return "<%s-openobject-%s@%s>" % (time.time(), openobject_id, socket.gethostname()) - - def send_email(self, cr, uid, smtp_from, email_to, message=None, subject=None, body=None, email_cc=None, - email_bcc=None, reply_to=False, attach=None, message_id=None, - references=None, openobject_id=False, debug=False, subtype='plain', - x_headers=None, priority='3', smtp_server=None, smtp_port=None, - ssl=False, smtp_user=None, smtp_password=None, id=None): - - """Send an email. + Pack all message attributes into one object. + Return email.message object after packed all email attribure. """ if x_headers is None: x_headers = {} - if not (smtp_from or config['email_from']): + if not email_cc: email_cc = [] + if not email_bcc: email_bcc = [] + if not body: body = u'' + + email_body = ustr(body).encode('utf-8') + email_text = MIMEText(email_body or '',_subtype=subtype,_charset='utf-8') + msg = MIMEMultipart() + + if not message_id and openobject_id: + message_id = tools.generate_tracking_message_id(openobject_id) + else: + message_id = Utils.make_msgid() + if references: + msg['references'] = references + msg['Message-Id'] = message_id + msg['Subject'] = Header(ustr(subject), 'utf-8') + msg['From'] = email_from + del msg['Reply-To'] + if reply_to: + msg['Reply-To'] = reply_to + else: + msg['Reply-To'] = msg['From'] + msg['To'] = COMMASPACE.join(email_to) + if email_cc: + msg['Cc'] = COMMASPACE.join(email_cc) + if email_bcc: + msg['Bcc'] = COMMASPACE.join(email_bcc) + msg['Date'] = formatdate(localtime=True) + + msg['X-Priority'] = priorities.get(priority, '3 (Normal)') + + # Add dynamic X Header + for key, value in x_headers.iteritems(): + msg['%s' % key] = str(value) + + if html2text and subtype == 'html': + text = tools.html2text(email_body.decode('utf-8')).encode('utf-8') + alternative_part = MIMEMultipart(_subtype="alternative") + alternative_part.attach(MIMEText(text, _charset='utf-8', _subtype='plain')) + alternative_part.attach(email_text) + msg.attach(alternative_part) + else: + msg.attach(email_text) + + if attach: + for (fname,fcontent) in attach: + part = MIMEBase('application', "octet-stream") + part.set_payload( fcontent ) + Encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment; filename="%s"' % (fname,)) + msg.attach(part) + return msg + + def send_email(self, cr, uid, smtp_from, smtp_to_list, message, + mail_server_id=None, smtp_server=None, smtp_port=None, + smtp_user=None, smtp_password=None, ssl=False, tls=True, debug=False): + + """Send an email. + If the id of a mail server is provided, send using this mail server, ignoring other smtp_* arguments. + If mail_server_id == None and smtp_server == None, use the default mail server (highest priority). + If mail_server_id == None and smtp_server is not None, use the provided smtp_* arguments. + Return messageID of message if Successfully sent Email otherwise return False + """ + if not (smtp_from or config.get('email_from', False)): raise ValueError("Sending an email requires either providing a sender " "address or having configured one") if not smtp_from: smtp_from = config.get('email_from', False) - smtp_from = ustr(smtp_from).encode('utf-8') + smtp_from = tools.ustr(smtp_from).encode('utf-8') - if id: - server = self.browse(cr, uid, id) - smtp_server = server.smtp_host - smtp_user = server.smtp_user - smtp_password = server.smtp_pass - smtp_port = server.smtp_port - ssl = server.smtp_ssl - elif not (id and smtp_server): - server_ids = self.search(cr, uid, [], order='priority', limit=1) - server = self.browse(cr, uid, server_ids[0]) - smtp_server = server.smtp_host - smtp_user = server.smtp_user - smtp_password = server.smtp_pass - smtp_port = server.smtp_port - ssl = server.smtp_ssl - elif not id and smtp_server: - smtp_server = smtp_server - smtp_user = smtp_user - smtp_password = smtp_password - smtp_port = smtp_port - ssl = ssl + # Get SMTP Server Details from Mail Server + mail_server = False + if mail_server_id: + mail_server = self.browse(cr, uid, mail_server_id) + elif not (mail_server_id and smtp_server): + mail_server_ids = self.search(cr, uid, [], order='priority', limit=1) + mail_server = self.browse(cr, uid, server_ids[0]) + if mail_server: + smtp_server = mail_server.smtp_host + smtp_user = mail_server.smtp_user + smtp_password = mail_server.smtp_pass + smtp_port = mail_server.smtp_port + ssl = mail_server.smtp_ssl + tls = mail_server.smtp_tls class WriteToLogger(object): def __init__(self): @@ -216,89 +267,40 @@ unless it is already specified in the From Email, e.g: John Doe ", def write(self, s): self.logger.notifyChannel('email_send', loglevels.LOG_DEBUG, s) - #preparing the message - if not message: - if not email_cc: email_cc = [] - if not email_bcc: email_bcc = [] - if not body: body = u'' - email_body = ustr(body).encode('utf-8') - email_text = MIMEText(email_body or '', _subtype=subtype, _charset='utf-8') - msg = MIMEMultipart() - - if not message_id and openobject_id: - message_id = self.generate_tracking_message_id(openobject_id) - else: - message_id = Utils.make_msgid() - - msg['Message-Id'] = message_id - if references: - msg['references'] = references - msg['Subject'] = Header(ustr(subject), 'utf-8') - msg['From'] = smtp_from - - del msg['Reply-To'] - if reply_to: - msg['Reply-To'] = reply_to - else: - msg['Reply-To'] = msg['From'] - - msg['To'] = COMMASPACE.join(email_to) - - if email_cc: - msg['Cc'] = COMMASPACE.join(email_cc) - if email_bcc: - msg['Bcc'] = COMMASPACE.join(email_bcc) - - msg['X-Priority'] = priorities.get(priority, '3 (Normal)') - msg['Date'] = formatdate(localtime=True) - - # Add dynamic X Header - for key, value in x_headers.iteritems(): - msg['%s' % key] = str(value) - if html2text and sub_type == 'html': - text = html2text(email_body.decode('utf-8')).encode('utf-8') - alternative_part = MIMEMultipart(_subtype="alternative") - alternative_part.attach(MIMEText(text, _charset='utf-8', _subtype='plain')) - alternative_part.attach(email_text) - msg.attach(alternative_part) - else: - msg.attach(email_text) - - if attach: - for (fname, fcontent) in attach: - part = MIMEBase('application', "octet-stream") - part.set_payload( fcontent ) - Encoders.encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="%s"' % (fname,)) - msg.attach(part) - message = msg - + try: - smtp_server = smtp_server or config['smtp_server'] + message_id = message['Message-Id'] + smtp_server = smtp_server or config.get('smtp_server') + # Add email in Maildir if smtp_server contains maildir. if smtp_server.startswith('maildir:/'): from mailbox import Maildir maildir_path = smtp_server[8:] mdir = Maildir(maildir_path,factory=None, create = True) mdir.add(message.as_string(True)) - return True + return message_id if debug: oldstderr = smtplib.stderr smtplib.stderr = WriteToLogger() - if not ssl: - ssl = config.get('smtp_ssl', False) - - smtp = self.connect_smtp_server(cr, uid, smtp_server, smtp_port, - user_name=smtp_user, user_password=smtp_password, ssl=ssl, tls=True, debug=debug) + # Open Connection of SMTP Server + smtp = self.connect_smtp_server( + smtp_server, + smtp_port or config.get('smtp_port', 25), + user_name=smtp_user or config,get('smtp_user', False), + user_password=smtp_password or config.get('smtp_password', False), + ssl=ssl or config.get('smtp_ssl', False), + tls=tls, debug=debug) try: + # Send Email smtp.sendmail(smtp_from, email_to, message.as_string()) except Exception: _logger.error('could not deliver Email(s)', exc_info=True) return False finally: try: + # Close Connection of SMTP Server smtp.quit() except Exception: # ignored, just a consequence of the previous exception diff --git a/openerp/addons/base/res/partner/wizard/partner_wizard_spam.py b/openerp/addons/base/res/partner/wizard/partner_wizard_spam.py index a141bfaf275..bf71fbe3da2 100644 --- a/openerp/addons/base/res/partner/wizard/partner_wizard_spam.py +++ b/openerp/addons/base/res/partner/wizard/partner_wizard_spam.py @@ -62,11 +62,9 @@ class partner_wizard_spam(osv.osv_memory): to = '"%s" <%s>' % (name, adr.email) #TODO: add some tests to check for invalid email addresses #CHECKME: maybe we should use res.partner/email_send - smtp_server_obj = self.pool.get('ir.mail_server') - smtp_server_obj.send_email(uid, data.email_from, - [to], data.subject, - data.text, subtype=type_, - cr=cr) + smtp_server_pool = self.pool.get('ir.mail_server') + msg = smtp_server_pool.pack_message(cr, uid, data.subject, data.text, subtype=type_) + smtp_server_pool.send_email(cr, uid, data.email_from, [to], msg) nbr += 1 event_pool.create(cr, uid, {'name': 'Email(s) sent through mass mailing', diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index d3edc80f4ba..1fff17fe267 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -413,26 +413,66 @@ def html2plaintext(html, body_id=None, encoding='utf-8'): return html -def email_send(smtp_from, smtp_to_list, message, ssl=False, debug=False, smtp_server=None, smtp_port=None, - smtp_user=None, smtp_password=None, cr=None, uid=None): +def generate_tracking_message_id(openobject_id): + """Returns a string that can be used in the Message-ID RFC822 header field so we + can track the replies related to a given object thanks to the "In-Reply-To" or + "References" fields that Mail User Agents will set. + """ + return "<%s-openobject-%s@%s>" % (time.time(), openobject_id, socket.gethostname()) + + +def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, + attach=None, message_id=None, references=None, openobject_id=False, debug=False, subtype='plain', x_headers=None, priority='3', + smtp_server=None, smtp_port=None, ssl=False, smtp_user=None, smtp_password=None, cr=None, uid=None): + + """Send an email. + + Arguments: + + `email_from`: A string used to fill the `From` header, if falsy, + config['email_from'] is used instead. Also used for + the `Reply-To` header if `reply_to` is not provided + + `email_to`: a sequence of addresses to send the mail to. + """ + + # If not cr, get cr from current thread database if not cr: db_name = getattr(threading.currentThread(), 'dbname', None) if db_name: cr = pooler.get_db_only(db_name).cursor() else: raise Exception("No database cursor found!") + + # if not uid, take uid as a root + #TOFIX: uid should taken from current thread if not uid: uid = 1 + + if not (email_from or config['email_from']): + raise ValueError("Sending an email requires either providing a sender " + "address or having configured one") + + if not email_from: email_from = config.get('email_from', False) + + email_from = ustr(email_from).encode('utf-8') + + mail_server_pool = pooler.get_pool(cr.dbname).get('ir.mail_server') + # Pack Message + msg = mail_server_pool.pack_message(cr, uid, subject, body, email_cc, email_bcc, reply_to, + attach, message_id, references, openobject_id, debug, subtype, x_headers, priority) + + # Send Email + res = False try: - server_pool = pooler.get_pool(cr.dbname).get('ir.mail_server') - server_pool.send_email(cr, uid, smtp_from, smtp_to_list, message=message, - ssl=ssl,debug=debug, smtp_server=smtp_server, smtp_port=smtp_port, - smtp_user=smtp_user, smtp_password=smtp_password) + res = mail_server_pool.send_email(cr, uid, email_from, flatten([email_to, email_cc, email_bcc]), msg, ssl=ssl, debug=debug, + smtp_server=smtp_server, smtp_port=smtp_port, smtp_user=smtp_user, smtp_password=smtp_password) except Exception: return False finally: cr.close() - return True + return res + #---------------------------------------------------------- # SMS