# encoding: utf-8

"""This module contains only the primary SMTP Dispatch class."""

import logging
log = logging.getLogger("turbomail.dispatch")

from smtplib import SMTP


__all__ = ['Dispatch']


class Dispatch(object):
	"""SMTP message dispatch class.
	
	An instance of the Dispatch class is created for each SMTP
	connection.  Usually, this means one Dispatch instance per
	running thread.
	
	Example usage::
	
		import turbomail
		dispatch = turbomail.Dispatch("localhost")
		message = turbomail.Message(
				"from@localhost",
				"to@localhost",
				"Subject",
				plain="Hello world!"
			)
		dispatch(message)
	"""

	def __init__(self, server, username=None, password=None, tls=None, debug=False):
		"""Initialize the Dispatch class.
		
		Authentication is only performed if both I{username} and
		I{password} are not None.  An instance of Dispatch is callable.
		
		@param server: The server (with optional port number) to
		               connect this instance to.
		@type server: string
		
		@param username: The username to use during authentication.
		                 I{Optional.}
		@type username: string
		
		@param password: The password to use during authentaction.
                         I{Optional.}
		@type password: string
		
		@param debug: Enable SMTP verbose logging.  This outputs all
		              communications between the client and server.
		@type debug: bool
		"""
		
		super(Dispatch, self).__init__()
		
		self.server = server
		self.username = username
		self.password = password
		self.tls = tls
		self.debug = debug
		self.log = None
		
		log.debug("Creating SMTP object.")
		self.connection = SMTP()
		self.connection.set_debuglevel(debug)

	@property
	def connected(self):
		"""Return the current SMTP connection status."""
		
		return getattr(self.connection, 'sock', None) is not None

	def connect(self):
		"""Connect to the SMTP server if not already connected.
		
		This process also automatically enables TLS, if available, and
		authenticates against the username and password previousally
		provided.
		"""
		
		if self.connected: return
		
		log.debug("Connecting to SMTP server %s." % self.server)
		self.connection.connect(self.server)

		if self.tls or self.tls is None:
			self.connection.ehlo()
			
			if self.connection.has_extn('STARTTLS') or self.tls:
				self.connection.starttls()
				self.connection.ehlo()
				log.debug("TLS enabled on SMTP server.")
				self.tls = True
			
			else:
				log.debug("TLS not available on SMTP server.")
				self.tls = False

		if self.username and self.password:
			log.debug("Authenticating as %s." % self.username)
			self.connection.login(self.username, self.password)
	
	def disconnect(self):
		"""Disconnect from the SMTP server if connected."""
		
		if not self.connected: return
		
		log.debug("Closing SMTP connection.")
		self.connection.quit()
	
	def __call__(self, message):
		"""Deliver a message via the current SMTP connection.
		
		Calling an instance of the Dispatch class will automatically
		connect, if needed, and will also automatically disconnect if
		debug mode has been enabled.
		
		@param message: This paramater must be a callable which returns
		                a tuple of (from, to, message), where all three
		                are strings, and message is valid content for an
		                e-mail message.
		@type message: callable
		"""
		
		self.connect()
		
		if callable(message):
			pack = message()
		else:
			pack = message

		pack.update(dict(
				user="",
				server=self.server,
				size=len(pack['message']),
			))
		if self.username: pack['user'] = self.username + "@"
		
		packUpdate = {}
		if isinstance(pack['sender'], tuple):
			packUpdate['addrfrom'] = '"%s" %s' % pack['sender']
			packUpdate['smtpfrom'] = pack['sender'][1]
		else:
			packUpdate['addrfrom'] = '- %s' % pack['sender']
			packUpdate['smtpfrom'] = pack['sender']
		
		if 'smtpfrom' in pack:
			if isinstance(pack['smtpfrom'], tuple):
				packUpdate['smtpfrom'] = pack['smtpfrom'][1]
			else:
				packUpdate['smtpfrom'] = pack['smtpfrom']
		
		pack.update(packUpdate)
		
		if len(pack['to']) > 1:
			pack.update(dict(addrto="- (%d)" % len(pack['to'])))
		else:
			if isinstance(pack['to'][0], tuple):
				pack.update(dict(addrto='"%s" %s' % pack['to'][0]))
			else:
				pack.update(dict(addrto='- %s' % pack['to'][0]))

		log.info(
				"%(user)s%(server)s %(size)d %(addrfrom)s %(addrto)s - %(subject)s" % pack
			)
		self.connection.sendmail(pack['smtpfrom'], pack['recipients'], pack['message'])
		
		if self.debug: self.disconnect()


syntax highlighted by Code2HTML, v. 0.9.1