[ADD] {google,fetchmail}_gmail: OAuth for gmail servers

Purpose
=======
Less secured apps are no longer supported by google, therefore, we need
to transition to the OAuth2 authentication system.

Specifications
==============
1. User will need to fill their Gmail API credentials in the main
   settings page
2. Then, in the incoming / outgoing mail server form view, they will
   need to tick the Gmail support checkbox
3. A link will be available to be redirected to Gmail and accept the
   permission
4. The user can now copy / paste the authorization code in Odoo, set
   his email as "login" and then send / receive emails with Gmail

Task-2170676

closes odoo/odoo#83424

X-original-commit: ff223afc5300b2421b8ce935bb468f503c938c5d
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
qmo-odoo
2022-01-27 09:12:50 +00:00
committed by std-odoo
parent eb0c5f2566
commit 04e86613c8
19 changed files with 582 additions and 2 deletions
+10
View File
@@ -267,6 +267,11 @@ file_filter = addons/fetchmail/i18n/<lang>.po
source_file = addons/fetchmail/i18n/fetchmail.pot
source_lang = en
[odoo-master.fetchmail_gmail]
file_filter = addons/fetchmail_gmail/i18n/<lang>.po
source_file = addons/fetchmail_gmail/i18n/fetchmail_gmail.pot
source_lang = en
[odoo-master.fleet]
file_filter = addons/fleet/i18n/<lang>.po
source_file = addons/fleet/i18n/fleet.pot
@@ -307,6 +312,11 @@ file_filter = addons/google_recaptcha/i18n/<lang>.po
source_file = addons/google_recaptcha/i18n/google_recaptcha.pot
source_lang = en
[odoo-master.google_gmail]
file_filter = addons/google_gmail/i18n/<lang>.po
source_file = addons/google_gmail/i18n/google_gmail.pot
source_lang = en
[odoo-master.google_spreadsheet]
file_filter = addons/google_spreadsheet/i18n/<lang>.po
source_file = addons/google_spreadsheet/i18n/google_spreadsheet.pot
+11 -1
View File
@@ -105,7 +105,7 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
connection = IMAP4_SSL(self.server, int(self.port))
else:
connection = IMAP4(self.server, int(self.port))
connection.login(self.user, self.password)
self._imap_login(connection)
elif self.server_type == 'pop':
if self.is_ssl:
connection = POP3_SSL(self.server, int(self.port))
@@ -119,6 +119,16 @@ odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWO
connection.sock.settimeout(MAIL_TIMEOUT)
return connection
def _imap_login(self, connection):
"""Authenticate the IMAP connection.
Can be overridden in other module for different authentication methods.
:param connection: The IMAP connection to authenticate
"""
self.ensure_one()
connection.login(self.user, self.password)
def button_confirm_login(self):
for server in self:
try:
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+16
View File
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
"name": "Fetchmail Gmail",
"version": "1.0",
"category": "Hidden",
"description": "Google authentication for incoming mail server",
"depends": [
"google_gmail",
"fetchmail",
],
"data": ["views/fetchmail_server_views.xml"],
"auto_install": True,
"license": "LGPL-3",
}
@@ -0,0 +1,44 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fetchmail_gmail
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-25 14:36+0000\n"
"PO-Revision-Date: 2022-01-25 14:36+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: fetchmail_gmail
#: model_terms:ir.ui.view,arch_db:fetchmail_gmail.fetchmail_server_view_form
msgid "Authorization Code"
msgstr ""
#. module: fetchmail_gmail
#: model_terms:ir.ui.view,arch_db:fetchmail_gmail.fetchmail_server_view_form
msgid "Gmail"
msgstr ""
#. module: fetchmail_gmail
#: code:addons/fetchmail_gmail/models/fetchmail_server.py:0
#, python-format
msgid "Gmail authentication only supports IMAP server type."
msgstr ""
#. module: fetchmail_gmail
#: model:ir.model,name:fetchmail_gmail.model_fetchmail_server
msgid "Incoming Mail Server"
msgstr ""
#. module: fetchmail_gmail
#: model_terms:ir.ui.view,arch_db:fetchmail_gmail.fetchmail_server_view_form
msgid ""
"Setup your Gmail API credentials in the general settings to link a Gmail "
"account."
msgstr ""
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import fetchmail_server
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models, _
from odoo.exceptions import UserError
class FetchmailServer(models.Model):
_name = 'fetchmail.server'
_inherit = ['fetchmail.server', 'google.gmail.mixin']
@api.constrains('use_google_gmail_service', 'server_type')
def _check_use_google_gmail_service(self):
if any(server.use_google_gmail_service and server.server_type != 'imap' for server in self):
raise UserError(_('Gmail authentication only supports IMAP server type.'))
@api.onchange('use_google_gmail_service')
def _onchange_use_google_gmail_service(self):
"""Set the default configuration for a IMAP Gmail server."""
if self.use_google_gmail_service:
self.server = 'imap.gmail.com'
self.server_type = 'imap'
self.is_ssl = True
self.port = 993
else:
self.server_type = 'pop'
self.is_ssl = False
self.google_gmail_authorization_code = False
self.google_gmail_refresh_token = False
self.google_gmail_access_token = False
self.google_gmail_access_token_expiration = False
def _imap_login(self, connection):
"""Authenticate the IMAP connection.
If the mail server is Gmail, we use the OAuth2 authentication protocol.
"""
self.ensure_one()
if self.use_google_gmail_service:
auth_string = self._generate_oauth2_string(self.user, self.google_gmail_refresh_token)
connection.authenticate('XOAUTH2', lambda x: auth_string)
connection.select('INBOX')
else:
super(FetchmailServer, self)._imap_login(connection)
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="fetchmail_server_view_form" model="ir.ui.view">
<field name="name">fetchmail.server.view.form.inherit.gmail</field>
<field name="model">fetchmail.server</field>
<field name="inherit_id" ref="fetchmail.view_email_server_form"/>
<field name="arch" type="xml">
<field name="server" position="before">
<field name="use_google_gmail_service" string="Gmail" attrs="{'readonly': [('state', '=', 'done')]}"/>
</field>
<field name="user" position="after">
<field string="Authorization Code" name="google_gmail_authorization_code" password="True"
attrs="{'required': [('use_google_gmail_service', '=', True)], 'invisible': [('use_google_gmail_service', '=', False)], 'readonly': [('state', '=', 'done')]}"
style="word-break: break-word;"/>
<field name="google_gmail_uri"
class="fa fa-arrow-right oe_edit_only"
widget="url"
text=" Get an Authorization Code"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '=', False)]}"
nolabel="1"/>
<div class="alert alert-warning" role="alert"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '!=', False)]}">
Setup your Gmail API credentials in the general settings to link a Gmail account.
</div>
</field>
<field name="password" position="attributes">
<attribute name="attrs">{'required' : [('server_type', '!=', 'local'), ('use_google_gmail_service', '=', False), ('password', '!=', False)], 'invisible' : [('use_google_gmail_service', '=', True)]}</attribute>
</field>
</field>
</record>
</odoo>
@@ -9,6 +9,7 @@ import requests
from werkzeug import urls
from odoo import api, fields, models, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
@@ -119,6 +120,42 @@ class GoogleService(models.AbstractModel):
error_msg = _("Something went wrong during your token generation. Maybe your Authorization Code is invalid")
raise self.env['res.config.settings'].get_config_warning(error_msg)
@api.model
def _get_access_token(self, refresh_token, service, scope):
"""Fetch the access token thanks to the refresh token."""
get_param = self.env['ir.config_parameter'].sudo().get_param
client_id = get_param('google_%s_client_id' % service, default=False)
client_secret = get_param('google_%s_client_secret' % service, default=False)
if not client_id or not client_secret:
raise UserError(_('Google %s is not yet configured.', service.title()))
if not refresh_token:
raise UserError(_('The refresh token for authentication is not set.'))
try:
result = requests.post(
GOOGLE_TOKEN_ENDPOINT,
data={
'client_id': client_id,
'client_secret': client_secret,
'refresh_token': refresh_token,
'grant_type': 'refresh_token',
'scope': scope,
},
headers={'Content-type': 'application/x-www-form-urlencoded'},
timeout=TIMEOUT,
)
result.raise_for_status()
except requests.HTTPError:
raise UserError(
_('Something went wrong during the token generation. Please request again an authorization code.')
)
json_result = result.json()
return json_result.get('access_token'), json_result.get('expires_in')
@api.model
def _do_request(self, uri, params=None, headers=None, method='POST', preuri="https://www.googleapis.com", timeout=TIMEOUT):
""" Execute the request to Google API. Return a tuple ('HTTP_CODE', 'HTTP_RESPONSE')
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+19
View File
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
"name": "Google Gmail",
"version": "1.0",
"category": "Hidden",
"description": "Gmail support for incoming / outgoing mail servers",
"depends": [
"mail",
"google_account",
],
"data": [
"views/ir_mail_server_views.xml",
"views/res_config_settings_views.xml",
],
"auto_install": True,
"license": "LGPL-3",
}
+131
View File
@@ -0,0 +1,131 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * google_gmail
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-25 14:36+0000\n"
"PO-Revision-Date: 2022-01-25 14:36+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: google_gmail
#: model_terms:ir.ui.view,arch_db:google_gmail.res_config_settings_view_form
msgid "<span class=\"o_form_label\">Gmail Credentials</span>"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__google_gmail_access_token
#: model:ir.model.fields,field_description:google_gmail.field_ir_mail_server__google_gmail_access_token
msgid "Access Token"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__google_gmail_access_token_expiration
#: model:ir.model.fields,field_description:google_gmail.field_ir_mail_server__google_gmail_access_token_expiration
msgid "Access Token Expiration Timestamp"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__google_gmail_authorization_code
#: model:ir.model.fields,field_description:google_gmail.field_ir_mail_server__google_gmail_authorization_code
#: model_terms:ir.ui.view,arch_db:google_gmail.ir_mail_server_view_form
msgid "Authorization Code"
msgstr ""
#. module: google_gmail
#: model_terms:ir.ui.view,arch_db:google_gmail.res_config_settings_view_form
msgid "Client ID"
msgstr ""
#. module: google_gmail
#: model_terms:ir.ui.view,arch_db:google_gmail.res_config_settings_view_form
msgid "Client Secret"
msgstr ""
#. module: google_gmail
#: model:ir.model,name:google_gmail.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__display_name
msgid "Display Name"
msgstr ""
#. module: google_gmail
#: model_terms:ir.ui.view,arch_db:google_gmail.ir_mail_server_view_form
msgid "Gmail"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__use_google_gmail_service
#: model:ir.model.fields,field_description:google_gmail.field_ir_mail_server__use_google_gmail_service
msgid "Gmail Authentication"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_res_config_settings__google_gmail_client_identifier
msgid "Gmail Client Id"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_res_config_settings__google_gmail_client_secret
msgid "Gmail Client Secret"
msgstr ""
#. module: google_gmail
#: model:ir.model,name:google_gmail.model_google_gmail_mixin
msgid "Google Gmail Mixin"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__id
msgid "ID"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin____last_update
msgid "Last Modified on"
msgstr ""
#. module: google_gmail
#: model:ir.model,name:google_gmail.model_ir_mail_server
msgid "Mail Server"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__google_gmail_refresh_token
#: model:ir.model.fields,field_description:google_gmail.field_ir_mail_server__google_gmail_refresh_token
msgid "Refresh Token"
msgstr ""
#. module: google_gmail
#: model_terms:ir.ui.view,arch_db:google_gmail.res_config_settings_view_form
msgid "Send and receive email with your Gmail account."
msgstr ""
#. module: google_gmail
#: model_terms:ir.ui.view,arch_db:google_gmail.ir_mail_server_view_form
msgid ""
"Setup your Gmail API credentials in the general settings to link a Gmail "
"account."
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,help:google_gmail.field_google_gmail_mixin__google_gmail_uri
#: model:ir.model.fields,help:google_gmail.field_ir_mail_server__google_gmail_uri
msgid "The URL to generate the authorization code from Google"
msgstr ""
#. module: google_gmail
#: model:ir.model.fields,field_description:google_gmail.field_google_gmail_mixin__google_gmail_uri
#: model:ir.model.fields,field_description:google_gmail.field_ir_mail_server__google_gmail_uri
msgid "URI"
msgstr ""
+6
View File
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import google_gmail_mixin
from . import ir_mail_server
from . import res_config_settings
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import time
from odoo import api, fields, models
_logger = logging.getLogger(__name__)
class GoogleGmailMixin(models.AbstractModel):
_name = 'google.gmail.mixin'
_description = 'Google Gmail Mixin'
_SERVICE_SCOPE = 'https://mail.google.com/'
use_google_gmail_service = fields.Boolean('Gmail Authentication')
google_gmail_authorization_code = fields.Char(string='Authorization Code', groups='base.group_system')
google_gmail_refresh_token = fields.Char(string='Refresh Token', groups='base.group_system')
google_gmail_access_token = fields.Char(string='Access Token', groups='base.group_system')
google_gmail_access_token_expiration = fields.Integer(string='Access Token Expiration Timestamp', groups='base.group_system')
google_gmail_uri = fields.Char(compute='_compute_gmail_uri', string='URI', help='The URL to generate the authorization code from Google', groups='base.group_system')
@api.depends('google_gmail_authorization_code')
def _compute_gmail_uri(self):
Config = self.env['ir.config_parameter'].sudo()
google_gmail_client_id = Config.get_param('google_gmail_client_id')
google_gmail_client_secret = Config.get_param('google_gmail_client_secret')
if not google_gmail_client_id or not google_gmail_client_secret:
self.google_gmail_uri = False
else:
google_gmail_uri = self.env['google.service']._get_google_token_uri('gmail', scope=self._SERVICE_SCOPE)
self.google_gmail_uri = google_gmail_uri
@api.model_create_multi
def create(self, vals_list):
for values in vals_list:
if values.get('google_gmail_authorization_code'):
# Generate the refresh token
values['google_gmail_refresh_token'] = self.env['google.service'].generate_refresh_token(
'gmail', values['google_gmail_authorization_code'])
values['google_gmail_access_token'] = False
values['google_gmail_access_token_expiration'] = False
return super(GoogleGmailMixin, self).create(vals_list)
def write(self, values):
authorization_code = values.get('google_gmail_authorization_code')
if (
authorization_code
and not all(authorization_code == code for code in self.mapped('google_gmail_authorization_code'))
):
# Update the refresh token
values['google_gmail_refresh_token'] = self.env['google.service'].generate_refresh_token(
'gmail', authorization_code)
values['google_gmail_access_token'] = False
values['google_gmail_access_token_expiration'] = False
return super(GoogleGmailMixin, self).write(values)
def _generate_oauth2_string(self, user, refresh_token):
"""Generate a OAuth2 string which can be used for authentication.
:param user: Email address of the Gmail account to authenticate
:param refresh_token: Refresh token for the given Gmail account
:return: The SASL argument for the OAuth2 mechanism.
"""
self.ensure_one()
now_timestamp = int(time.time())
if not self.google_gmail_access_token \
or not self.google_gmail_access_token_expiration \
or self.google_gmail_access_token_expiration < now_timestamp:
self.google_gmail_access_token, expires_in = self.env['google.service']._get_access_token(
refresh_token, 'gmail', self._SERVICE_SCOPE)
self.google_gmail_access_token_expiration = now_timestamp + expires_in
_logger.info('Google Gmail: fetch new access token. Expire in %i minutes', expires_in // 60)
else:
_logger.info(
'Google Gmail: reuse existing access token. Expire in %i minutes',
(self.google_gmail_access_token_expiration - now_timestamp) // 60)
return 'user=%s\1auth=Bearer %s\1\1' % (user, self.google_gmail_access_token)
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from odoo import models, api
class IrMailServer(models.Model):
"""Represents an SMTP server, able to send outgoing emails, with SSL and TLS capabilities."""
_name = 'ir.mail_server'
_inherit = ['ir.mail_server', 'google.gmail.mixin']
@api.onchange('smtp_encryption')
def _onchange_encryption(self):
"""Do not change the SMTP configuration if it's a Gmail server
(e.g. the port which is already set)"""
if not self.use_google_gmail_service:
super()._onchange_encryption()
@api.onchange('use_google_gmail_service')
def _onchange_use_google_gmail_service(self):
if self.use_google_gmail_service:
self.smtp_host = 'smtp.gmail.com'
self.smtp_encryption = 'starttls'
self.smtp_port = 587
else:
self.smtp_encryption = 'none'
self.google_gmail_authorization_code = False
self.google_gmail_refresh_token = False
self.google_gmail_access_token = False
self.google_gmail_access_token_expiration = False
def _smtp_login(self, connection, smtp_user, smtp_password):
if len(self) == 1 and self.use_google_gmail_service:
auth_string = self._generate_oauth2_string(smtp_user, self.google_gmail_refresh_token)
oauth_param = base64.b64encode(auth_string.encode()).decode()
connection.ehlo()
connection.docmd('AUTH', 'XOAUTH2 %s' % oauth_param)
else:
super(IrMailServer, self)._smtp_login(connection, smtp_user, smtp_password)
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
google_gmail_client_identifier = fields.Char('Gmail Client Id', config_parameter='google_gmail_client_id')
google_gmail_client_secret = fields.Char('Gmail Client Secret', config_parameter='google_gmail_client_secret')
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="ir_mail_server_view_form" model="ir.ui.view">
<field name="name">ir.mail_server.view.form.inherit.gmail</field>
<field name="model">ir.mail_server</field>
<field name="inherit_id" ref="base.ir_mail_server_form"/>
<field name="arch" type="xml">
<field name="smtp_host" position="before">
<field name="use_google_gmail_service" string="Gmail"/>
</field>
<field name="smtp_user" position="after">
<field string="Authorization Code" name="google_gmail_authorization_code" password="True"
attrs="{'required': [('use_google_gmail_service', '=', True)], 'invisible': [('use_google_gmail_service', '=', False)]}"
style="word-break: break-word;"/>
<field name="google_gmail_uri"
class="fa fa-arrow-right oe_edit_only"
widget="url"
text=" Get an Authorization Code"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '=', False)]}"
nolabel="1"/>
<div class="alert alert-warning" role="alert"
attrs="{'invisible': ['|', ('use_google_gmail_service', '=', False), ('google_gmail_uri', '!=', False)]}">
Setup your Gmail API credentials in the general settings to link a Gmail account.
</div>
</field>
<field name="smtp_pass" position="attributes">
<attribute name="attrs">{'invisible' : [('use_google_gmail_service', '=', True)]}</attribute>
</field>
</field>
</record>
</odoo>
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.google_gmail</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
<field name="arch" type="xml">
<div id="emails" position="inside">
<div class="col-12 col-lg-6 o_setting_box" id="google_gmail_setting"
attrs="{'invisible': [('external_email_server_default', '=', False)]}">
<div class="o_setting_right_pane">
<span class="o_form_label">Gmail Credentials</span>
<div class="text-muted">
Send and receive email with your Gmail account.
</div>
<div class="content-group">
<div class="row mt16" id="gmail_client_identifier">
<label string="Client ID" for="google_gmail_client_identifier" class="col-lg-3 o_light_label"/>
<field name="google_gmail_client_identifier" class="ml-2"/>
</div>
<div class="row mt16" id="gmail_client_secret">
<label string="Client Secret" for="google_gmail_client_secret" class="col-lg-3 o_light_label"/>
<field name="google_gmail_client_secret" class="ml-2"/>
</div>
</div>
</div>
</div>
</div>
</field>
</record>
</data>
</odoo>
+16 -1
View File
@@ -226,7 +226,10 @@ class IrMailServer(models.Model):
elif not host:
mail_server, smtp_from = self.sudo()._find_mail_server(smtp_from)
if not mail_server:
mail_server = self.env['ir.mail_server']
ssl_context = None
if mail_server:
smtp_server = mail_server.smtp_host
smtp_port = mail_server.smtp_port
@@ -314,7 +317,7 @@ class IrMailServer(models.Model):
local, at, domain = smtp_user.rpartition('@')
if at:
smtp_user = local + at + idna.encode(domain).decode('ascii')
connection.login(smtp_user, smtp_password or '')
mail_server._smtp_login(connection, smtp_user, smtp_password or '')
# Some methods of SMTP don't check whether EHLO/HELO was sent.
# Anyway, as it may have been sent by login(), all subsequent usages should consider this command as sent.
@@ -327,6 +330,18 @@ class IrMailServer(models.Model):
return connection
def _smtp_login(self, connection, smtp_user, smtp_password):
"""Authenticate the SMTP connection.
Can be overridden in other module for different authentication methods.Can be
called on the model itself or on a singleton.
:param connection: The SMTP connection to authenticate
:param smtp_user: The user to used for the authentication
:param smtp_password: The password to used for the authentication
"""
connection.login(smtp_user, smtp_password)
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False,
attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None,
body_alternative=None, subtype_alternative='plain'):