Is there a simple way to check if a mail backend is valid in Django without sending a mail?

Viewed 360

In my tool the users can provide a mail backend using certain infos on a model and send their mails via the backend which gets created from those values. This all works, but I would love to have a quick check if the provided backend actually will work before using it. Using something like this check_mail_connection doesn't work as this actually returns False even though I entered valid connection parameters.

from django.core.mail import get_connection

class User(models.Model):
    ...
    def get_mail_connection(self, fail_silently=False)
        return get_connection(host=self.email_host,
                   port=self.email_port,
                   username=self.email_username,
                   password=self.email_password ... )

    def check_mail_connection(self) -> bool:
        from socket import error as socket_error
        from smtplib import SMTP, SMTPConnectError
        smtp = SMTP(host=self.email_host, port=self.email_port)
        try:
            smtp.connect()
            return True
        except SMTPConnectError:
            return False
        except socket_error:
            return False

I don't want to send a test mail to confirm, as this can easily get lost or fail on a different part of the system. This feature is for sending out emails from the users mail servers, as I suspect most of my users have a mail server anyways and I basically offer white labeling and similar stuff to them.

2 Answers

You have the following line smtp.connect() in your code that attempts to make a connection. If you look at the documentation for smtplib the signature for this method is:

SMTP.connect(host='localhost', port=0)

Meaning you are trying to connect to localhost with port 25 (standard SMTP port). Of course there is no server listening there and you get a ConnectionRefusedError which you catch and return False. In fact you don't even need to call connect because the documentation states:

If the optional host and port parameters are given, the SMTP connect() method is called with those parameters during initialization.

Hence you can simply write:

def check_mail_connection(self) -> bool:
    from smtplib import SMTP
    try:
        smtp = SMTP(host=self.email_host, port=self.email_port)
        return True
    except OSError:
        return False

You can also simply use the open method of the email backend's instance rather than creating the SMTP instance and calling connect yourself:

def check_mail_connection(self) -> bool:
    try:
        email_backend = self.get_mail_connection()
        silent_exception = email_backend.open() is None
        email_backend.close()
        return not silent_exception
    except OSError:
        return False

I have a few questions for you and would like for you to answer these questions before we can go further.

What type of OS are you running the server on? What mail client and tutorial did you follow? Postfix? Can a user on the server send local mail to another user on the server? What ports are open and what type of security features do you have installed? What did your logs say when the email failed? Are you self hosting/ are you acting as the server admin? (It's fine if this is your first time. Everyone had a first day.)

SSL and A FQDN isn't too important if your just sending mail out. The system will still work, you just won't be able to receive mail.

(I'm talking from the sense of making sure it at least will send an email. You should at least use SSL as it can be gotten for free.)

If you checked all of these things, there is a part of the mail client that you are using and it probably won't send mail out unless it has approval. There are a lot of variables.

All of these things matter or it wont work.

Sorry meant to make this as a comment. I'm not use to speaking on here.

Related