#!/usr/bin/env python3
import ssl
import smtplib
import imaplib
from rich.console import Console
from cryptography import x509
import tls_utils
from tls_utils import TLSDetails
from abc import ABC, abstractmethod
class MailHandler(ABC):
def __init__(self, host: str, port: int, context: ssl.SSLContext):
self.host = host
self.port = port
self.context = context
def connect(self, verification: bool) -> int:
connection = self.protocol_init(self.host, self.port)
if verification:
connection.starttls(**self.protocol_starttls_args())
else:
connection.starttls()
cert = connection.sock.getpeercert()
self.protocol_close(connection)
return tls_utils.get_validity_days(cert)[1]
@abstractmethod
def protocol_init(self, host, port):
raise NotImplementedError()
@abstractmethod
def protocol_close(self, connection):
raise NotImplementedError()
@abstractmethod
def protocol_starttls_args(self):
raise NotImplementedError()
@staticmethod
def create_handler(protocol: str):
if protocol == "smtp":
return SMTPHandler
elif protocol == "imap":
return IMAPHandler
else:
raise ValueError("Invalid protocol")
class IMAPHandler(MailHandler):
def protocol_init(self, host, port):
return imaplib.IMAP4(host, port)
def protocol_close(self, connection):
connection.logout()
def protocol_starttls_args(self):
return {"ssl_context": self.context}
class SMTPHandler(MailHandler):
def protocol_init(self, host, port):
return smtplib.SMTP(host, port)
def protocol_close(self, connection):
connection.quit()
def protocol_starttls_args(self):
return {"context": self.context}
class MailVerificator:
def __init__(self, context: ssl.SSLContext):
self.context = context
def connect(self, domain: str, port: int, protocol: str) -> TLSDetails:
mail = MailHandler.create_handler(protocol)(domain, port, self.context)
try:
expiry = mail.connect(True)
return TLSDetails(domain_name=domain, expires_in_days=expiry)
except ssl.SSLCertVerificationError as e:
if (e.verify_code == tls_utils.EXPIRED_VERIFY_CODE):
expiry = mail.connect(False)
return TLSDetails(domain_name=domain, expires_in_days=expiry)
else:
error = "failed verification:", e.verify_message + "."
return TLSDetails(domain_name=domain, error_message=error)