platypush/platypush/plugins/mail/_plugin/_base.py

131 lines
3.4 KiB
Python

import logging
import os
import re
from abc import ABC, abstractmethod
from typing import Dict, Optional
from urllib.parse import urlparse
from .._model import AccountConfig, ServerConfig, TransportEncryption
email_match_re = re.compile(r'[^@]+@[^@]+\.[^@]+')
class BaseMailPlugin(ABC): # pylint: disable=too-few-public-methods
"""
Base class for mail plugins.
"""
def __init__(
self,
server: str,
account: AccountConfig,
timeout: float,
keyfile: Optional[str] = None,
certfile: Optional[str] = None,
domain: Optional[str] = None,
**_,
):
self.logger = logging.getLogger(self.__class__.__name__)
self.account = account
self.server = self._get_server_config(
server=server,
timeout=timeout,
keyfile=keyfile,
certfile=certfile,
domain=domain,
)
@staticmethod
def _get_path(path: str) -> str:
return os.path.abspath(os.path.expanduser(path))
@classmethod
def _get_server_config(
cls,
server: str,
timeout: float,
keyfile: Optional[str],
certfile: Optional[str],
domain: Optional[str],
) -> ServerConfig:
url = urlparse(server)
assert url.hostname, f'No hostname specified: "{server}"'
ssl = TransportEncryption.by_url_scheme(url.scheme)
port = url.port or cls.default_ports().get(ssl)
assert port, f'No port specified and no default available: "{server}"'
if keyfile:
keyfile = cls._get_path(keyfile)
if certfile:
certfile = cls._get_path(certfile)
return ServerConfig(
server=url.hostname,
port=port,
encryption=ssl,
timeout=timeout,
keyfile=keyfile,
certfile=certfile,
domain=domain,
)
@property
def from_string(self):
"""
:return: The from string for the account.
"""
return self.account.display_name or self.account.username
@classmethod
@abstractmethod
def _matches_url_scheme(cls, scheme: str) -> bool:
raise NotImplementedError()
@classmethod
def can_handle(cls, url: str) -> bool:
"""
Check whether the plugin can handle the specified URL.
"""
return cls._matches_url_scheme(urlparse(url).scheme)
@classmethod
@abstractmethod
def default_ports(cls) -> Dict[TransportEncryption, int]:
"""
:return: A mapping of transport encryption to default port.
"""
raise NotImplementedError()
@classmethod
def build(
cls,
server: str,
account: AccountConfig,
timeout: float,
keyfile: Optional[str] = None,
certfile: Optional[str] = None,
) -> 'BaseMailPlugin':
from ._utils import mail_plugins
url_parsed = urlparse(server)
assert url_parsed.hostname, f'No hostname specified: "{server}"'
mail_cls = next(
(plugin for plugin in mail_plugins if plugin.can_handle(server)),
None,
)
assert mail_cls, f'No mail plugin found for URL: "{server}"'
return mail_cls(
server=server,
account=account,
timeout=timeout,
keyfile=keyfile,
certfile=certfile,
)
# vim:sw=4:ts=4:et: