Exemplo n.º 1
0
    def _find_mail_server(self, email_from, mail_servers=None):
        """Find the appropriate mail server for the given email address.

        Returns: Record<ir.mail_server>, email_from
        - Mail server to use to send the email (None if we use the odoo-bin arguments)
        - Email FROM to use to send the email (in some case, it might be impossible
          to use the given email address directly if no mail server is configured for)
        """
        email_from_normalized = email_normalize(email_from)
        email_from_domain = email_domain_extract(email_from_normalized)
        notifications_email = email_normalize(self._get_default_from_address())
        notifications_domain = email_domain_extract(notifications_email)

        if mail_servers is None:
            mail_servers = self.sudo().search([], order='sequence')

        # 1. Try to find a mail server for the right mail from
        mail_server = mail_servers.filtered(lambda m: email_normalize(m.from_filter) == email_from_normalized)
        if mail_server:
            return mail_server[0], email_from

        mail_server = mail_servers.filtered(lambda m: email_domain_normalize(m.from_filter) == email_from_domain)
        if mail_server:
            return mail_server[0], email_from

        # 2. Try to find a mail server for <*****@*****.**>
        if notifications_email:
            mail_server = mail_servers.filtered(lambda m: email_normalize(m.from_filter) == notifications_email)
            if mail_server:
                return mail_server[0], notifications_email

            mail_server = mail_servers.filtered(lambda m: email_domain_normalize(m.from_filter) == notifications_domain)
            if mail_server:
                return mail_server[0], notifications_email

        # 3. Take the first mail server without "from_filter" because
        # nothing else has been found... Will spoof the FROM because
        # we have no other choices
        mail_server = mail_servers.filtered(lambda m: not m.from_filter)
        if mail_server:
            return mail_server[0], email_from

        # 4. Return the first mail server even if it was configured for another domain
        if mail_servers:
            return mail_servers[0], email_from

        # 5: SMTP config in odoo-bin arguments
        from_filter = self.env['ir.config_parameter'].sudo().get_param(
            'mail.default.from_filter', tools.config.get('from_filter'))

        if self._match_from_filter(email_from, from_filter):
            return None, email_from

        if notifications_email and self._match_from_filter(notifications_email, from_filter):
            return None, notifications_email

        return None, email_from
Exemplo n.º 2
0
    def _get_email_from(self, email_from):
        """Logic which determines which email to use when sending the email.

        - If the system parameter `mail.force.smtp.from` is set we encapsulate all
          outgoing email from
        - If the previous system parameter is not set and if both `mail.dynamic.smtp.from`
          and `mail.catchall.domain` are set, we encapsulate the FROM only if the domain
          of the email is not the same as the domain of the catchall parameter
        - Otherwise we do not encapsulate the email and given email_from is used as is

        :param email_from: The initial FROM headers
        :return: The FROM to used in the headers and optionally the Return-Path
        """
        force_smtp_from = self.env['ir.config_parameter'].sudo().get_param('mail.force.smtp.from')
        dynamic_smtp_from = self.env['ir.config_parameter'].sudo().get_param('mail.dynamic.smtp.from')
        catchall_domain = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.domain')

        if force_smtp_from:
            rfc2822_force_smtp_from = extract_rfc2822_addresses(force_smtp_from)
            rfc2822_force_smtp_from = rfc2822_force_smtp_from[0] if rfc2822_force_smtp_from else None
            return encapsulate_email(email_from, force_smtp_from), rfc2822_force_smtp_from

        elif dynamic_smtp_from and catchall_domain and email_domain_extract(email_from) != catchall_domain:
            rfc2822_dynamic_smtp_from = extract_rfc2822_addresses(dynamic_smtp_from)
            rfc2822_dynamic_smtp_from = rfc2822_dynamic_smtp_from[0] if rfc2822_dynamic_smtp_from else None
            return encapsulate_email(email_from, dynamic_smtp_from), rfc2822_dynamic_smtp_from

        return email_from, None
Exemplo n.º 3
0
    def res_partner_enrich_and_create_company(self, partner_id):
        """
        Route used when the user clicks on the create and enrich partner button
        it will try to find a company using IAP, if a company is found
        the enriched company will then be created in the database
        """
        response = {}

        partner = request.env['res.partner'].browse(partner_id)

        if partner.parent_id:
            return {
                'error': _("The partner already has a company related to him")
            }

        normalized_email = partner.email_normalized
        if not normalized_email:
            response = {'error': _('Contact has no valid email')}
            return response

        company_domain = tools.email_domain_extract(normalized_email)

        company, enrichment_info = self._create_company_from_iap(
            company_domain)

        response['enrichment_info'] = enrichment_info
        response['company'] = self._prepare_company_values(company)
        if company:
            partner.write({'parent_id': company})

        return response
Exemplo n.º 4
0
 def _find_existing_company(self, normalized_email):
     sender_domain = tools.email_domain_extract(normalized_email)
     search = sender_domain if sender_domain not in iap_tools._MAIL_DOMAIN_BLACKLIST else normalized_email
     return request.env['res.partner'].search(
         [('is_company', '=', True),
          ('email_normalized', '=ilike', '%' + search)],
         limit=1)
Exemplo n.º 5
0
    def res_partner_get(self,
                        email=None,
                        name=None,
                        partner_id=None,
                        **kwargs):
        """
        returns a partner given it's id or an email and a name.
        In case the partner does not exist, we return partner having an id -1, we also look if an existing company
        matching the contact exists in the database, if none is found a new company is enriched and created automatically

        old route name "/mail_client_extension/partner/get is deprecated as of saas-14.3, it is not needed for newer
        versions of the mail plugin but necessary for supporting older versions, only the route name is deprecated not
        the entire method.
        """

        if not (partner_id or (name and email)):
            return {
                'error':
                _('You need to specify at least the partner_id or the name and the email'
                  )
            }

        if partner_id:
            partner = request.env['res.partner'].browse(partner_id)
            return self._prepare_contact_values(partner)

        normalized_email = tools.email_normalize(email)
        if not normalized_email:
            return {'error': _('Bad Email.')}

        # Search for the partner based on the email.
        # If multiple are found, take the first one.
        partner = request.env['res.partner'].search([
            '|', ('email', 'in', [normalized_email, email]),
            ('email_normalized', '=', normalized_email)
        ],
                                                    limit=1)

        response = self._prepare_contact_values(partner)

        # if no partner is found in the database, we should also return an empty one having id = -1, otherwise older versions of
        # plugin won't work
        if not response['partner']:
            sender_domain = tools.email_domain_extract(email)
            response['partner'] = {
                'id': -1,
                'email': email,
                'name': name,
                'enrichment_info': None,
            }
            company = self._find_existing_company(normalized_email)
            if not company:  # create and enrich company
                company, enrichment_info = self._create_company_from_iap(
                    sender_domain)
                response['partner']['enrichment_info'] = enrichment_info
            response['partner']['company'] = self._prepare_company_values(
                company)

        return response
Exemplo n.º 6
0
    def _get_iap_search_term(self, email):
        """Return the domain or the email depending if the domain is blacklisted or not.

        So if the domain is blacklisted, we search based on the entire email address
        (e.g. [email protected]). But if the domain is not blacklisted, we search based on
        the domain (e.g. [email protected] -> sncb.be)
        """
        domain = tools.email_domain_extract(email)
        return ("@" + domain) if domain not in iap_tools._MAIL_DOMAIN_BLACKLIST else email
Exemplo n.º 7
0
    def _create_company_from_iap(self, email):
        domain = tools.email_domain_extract(email)
        iap_data = self._iap_enrich(domain)
        if 'enrichment_info' in iap_data:
            return None, iap_data['enrichment_info']

        phone_numbers = iap_data.get('phone_numbers')
        emails = iap_data.get('email')
        new_company_info = {
            'is_company': True,
            'name': iap_data.get("name") or domain,
            'street': iap_data.get("street_name"),
            'city': iap_data.get("city"),
            'zip': iap_data.get("postal_code"),
            'phone': phone_numbers[0] if phone_numbers else None,
            'website': iap_data.get("domain"),
            'email': emails[0] if emails else None
        }

        logo_url = iap_data.get('logo')
        if logo_url:
            try:
                response = requests.get(logo_url, timeout=2)
                if response.ok:
                    new_company_info['image_1920'] = base64.b64encode(response.content)
            except Exception as e:
                _logger.warning('Download of image for new company %s failed, error %s', new_company_info.name, e)

        if iap_data.get('country_code'):
            country = request.env['res.country'].search([('code', '=', iap_data['country_code'].upper())])
            if country:
                new_company_info['country_id'] = country.id
                if iap_data.get('state_code'):
                    state = request.env['res.country.state'].search([
                        ('code', '=', iap_data['state_code']),
                        ('country_id', '=', country.id)
                    ])
                    if state:
                        new_company_info['state_id'] = state.id

        new_company_info.update({
            'iap_search_domain': self._get_iap_search_term(email),
            'iap_enrich_info': json.dumps(iap_data),
        })

        new_company = request.env['res.partner'].create(new_company_info)

        new_company.message_post_with_view(
            'iap_mail.enrich_company',
            values=iap_data,
            subtype_id=request.env.ref('mail.mt_note').id,
        )

        return new_company, {'type': 'company_created'}
Exemplo n.º 8
0
    def _match_from_filter(self, email_from, from_filter):
        """Return True is the given email address match the "from_filter" field.

        The from filter can be Falsy (always match),
        a domain name or an full email address.
        """
        if not from_filter:
            return True

        normalized_mail_from = email_normalize(email_from)
        if '@' in from_filter:
            return email_normalize(from_filter) == normalized_mail_from

        return email_domain_extract(normalized_mail_from) == email_domain_normalize(from_filter)
Exemplo n.º 9
0
    def _get_company_domain(self):
        """ Extract the company domain to be used by IAP services.
        The domain is extracted from the website or the email information.
        e.g:
            - www.info.proximus.be -> proximus.be
            - [email protected] -> proximus.be """
        self.ensure_one()

        company_domain = tools.email_domain_extract(
            self.email) if self.email else False
        if company_domain and company_domain not in iap_tools._MAIL_DOMAIN_BLACKLIST:
            return company_domain

        company_domain = tools.url_domain_extract(
            self.website) if self.website else False
        if not company_domain or company_domain in [
                'localhost', 'example.com'
        ]:
            return False

        return company_domain
Exemplo n.º 10
0
    def res_partner_enrich_and_update_company(self, partner_id):
        """
        Enriches an existing company using IAP
        """
        partner = request.env['res.partner'].browse(partner_id).exists()

        if not partner:
            return {'error': _("This partner does not exist")}

        if not partner.is_company:
            return {'error': 'Contact must be a company'}

        normalized_email = partner.email_normalized
        if not normalized_email:
            return {'error': 'Contact has no valid email'}

        domain = tools.email_domain_extract(normalized_email)
        iap_data = self._iap_enrich(domain)

        if 'enrichment_info' in iap_data:  # means that an issue happened with the enrichment request
            return {
                'enrichment_info': iap_data['enrichment_info'],
                'company': self._get_company_data(partner),
            }

        phone_numbers = iap_data.get('phone_numbers')

        partner_values = {}

        if not partner.phone and phone_numbers:
            partner_values.update({'phone': phone_numbers[0]})

        if not partner.iap_enrich_info:
            partner_values.update({'iap_enrich_info': json.dumps(iap_data)})

        if not partner.image_128:
            logo_url = iap_data.get('logo')
            if logo_url:
                try:
                    response = requests.get(logo_url, timeout=2)
                    if response.ok:
                        partner_values.update({'image_1920': base64.b64encode(response.content)})
                except Exception:
                    pass

        model_fields_to_iap_mapping = {
            'street': 'street_name',
            'city': 'city',
            'zip': 'postal_code',
            'website': 'domain',
        }

        # only update keys for which we dont have values yet
        partner_values.update({
            model_field: iap_data.get(iap_key)
            for model_field, iap_key in model_fields_to_iap_mapping.items() if not partner[model_field]
        })

        partner.write(partner_values)

        partner.message_post_with_view(
            'iap_mail.enrich_company',
            values=iap_data,
            subtype_id=request.env.ref('mail.mt_note').id,
        )

        return {
            'enrichment_info': {'type': 'company_updated'},
            'company': self._get_company_data(partner),
        }