Selaa lähdekoodia

Merge pull request #11 from Miksus/dev/non_tls_support

ENH: Support non-STARTTLS connection.
Mikael Koli 4 vuotta sitten
vanhempi
sitoutus
4ff4130eff
4 muutettua tiedostoa jossa 131 lisäystä ja 11 poistoa
  1. 101 0
      docs/tutorials/client.rst
  2. 1 0
      docs/tutorials/index.rst
  3. 28 9
      redmail/email/sender.py
  4. 1 2
      redmail/test/email/test_send.py

+ 101 - 0
docs/tutorials/client.rst

@@ -0,0 +1,101 @@
+
+.. _config-smtp:
+
+Configuring SMTP Client
+=======================
+
+Often the default client setup is enough but sometimes it may become necessary to get more control
+of the connection with your SMTP server. In this discussion we discuss ways to customize the connection.
+
+By default Red Mail uses `STARTTLS <https://en.wikipedia.org/wiki/Opportunistic_TLS>`_ or opportunistic
+TLS in connecting to the SMTP server. You may also change this if needed by changing the 
+``cls_smtp`` to other SMTP client classes from `smtplib <https://docs.python.org/3/library/smtplib.html>`_
+in standard library.
+
+.. note::
+
+    Extra keyword arguments in :class:`.EmailSender` initiation are passed to the SMTP client.
+    Please see the documentation of the SMTP client you are seeking.
+
+STARTTLS
+-------
+
+By default, Red Mail uses `STARTTLS <https://en.wikipedia.org/wiki/Opportunistic_TLS>`_ 
+which is configured as this:
+
+.. code-block:: python
+
+    from redmail import EmailSender
+    from smtplib import SMTP
+
+    email = EmailSender(
+        host="smtp.myhost.com",
+        port=0,
+        cls_smtp=SMTP,
+        use_starttls=True
+    )
+
+
+SMTP TLS
+--------
+
+You may also continue using TLS:
+
+.. code-block:: python
+
+    from redmail import EmailSender
+
+    email = EmailSender(
+        host="smtp.myhost.com",
+        port=0,
+        use_starttls=False
+    )
+
+
+SMTP SSL
+--------
+
+To use SSL:
+
+.. code-block:: python
+
+    from redmail import EmailSender
+    from smtplib import SMTP_SSL
+
+    email = EmailSender(
+        host="smtp.myhost.com",
+        port=0,
+        cls_smtp=SMTP_SSL,
+    )
+
+You may also pass the SSL context:
+
+.. code-block:: python
+
+    from redmail import EmailSender
+    from smtplib import SMTP_SSL
+    from ssl import SSLContext
+
+    email = EmailSender(
+        host="smtp.myhost.com",
+        port=0,
+        cls_smtp=SMTP_SSL,
+        context=SSLContext(...)
+    )
+
+LMTP
+----
+
+To use LMTP:
+
+.. code-block:: python
+
+    from redmail import EmailSender
+    from smtplib import LMTP
+
+    email = EmailSender(
+        host="smtp.myhost.com",
+        port=0,
+        cls_smtp=LMTP
+    )
+

+ 1 - 0
docs/tutorials/index.rst

@@ -29,4 +29,5 @@ And see - :ref:`cookbook <cookbook>` for example use cases.
    templating
    cookbook
    config
+   client
    example

+ 28 - 9
redmail/email/sender.py

@@ -36,6 +36,15 @@ class EmailSender:
         User name to authenticate on the server.
     password : str, optional
         User password to authenticate on the server.
+    cls_smtp : smtplib.SMTP
+        SMTP class to use for connection. See options 
+        from `Python smtplib docs <https://docs.python.org/3/library/smtplib.html>`_.
+    use_starttls : bool
+        Whether to use `STARTTLS <https://en.wikipedia.org/wiki/Opportunistic_TLS>`_ 
+        when connecting to the SMTP server.
+    **kwargs : dict
+        Additional keyword arguments are passed to initiation in ``cls_smtp``.
+        These are stored as attribute ``kws_smtp``
 
     Attributes
     ----------
@@ -115,9 +124,7 @@ class EmailSender:
 
     attachment_encoding = 'UTF-8'
 
-    _cls_smtp_server = smtplib.SMTP
-
-    def __init__(self, host:str, port:int, user_name:str=None, password:str=None):
+    def __init__(self, host:str, port:int, user_name:str=None, password:str=None, cls_smtp:smtplib.SMTP=smtplib.SMTP, use_starttls:bool=True, **kwargs):
         self.host = host
         self.port = port
 
@@ -135,6 +142,10 @@ class EmailSender:
         self.html = None
         self.html_template = None
         self.text_template = None
+
+        self.use_starttls = use_starttls
+        self.cls_smtp = cls_smtp
+        self.kws_smtp = kwargs
         
     def send(self,
              subject:Optional[str]=None,
@@ -354,17 +365,25 @@ class EmailSender:
 
     def send_message(self, msg:EmailMessage):
         "Send the created message"
+
+        server = self.connect()
+        server.send_message(msg)
+        
+        server.quit()
+    
+    def connect(self) -> smtplib.SMTP:
+        "Connect to the SMTP Server"
         user = self.user_name
         password = self.password
         
-        server = self._cls_smtp_server(self.host, self.port)
-        server.starttls()
+        server = self.cls_smtp(self.host, self.port, **self.kws_smtp)
+        if self.use_starttls:
+            server.starttls()
+
         if user is not None or password is not None:
             server.login(user, password)
-        server.send_message(msg)
-        
-        server.quit()
-    
+        return server
+
     def get_params(self, sender:str) -> Dict[str, Any]:
         "Get Jinja parametes passed to both text and html bodies"
         # TODO: Add receivers to params

+ 1 - 2
redmail/test/email/test_send.py

@@ -25,8 +25,7 @@ class MockServer:
         return
 
 def test_send():
-    email = EmailSender(host="localhost", port=0)
-    email._cls_smtp_server = MockServer
+    email = EmailSender(host="localhost", port=0, cls_smtp=MockServer)
     # This should fail but we test everything else goes through
     msg = email.send(
         subject="An example",