# encoding: utf-8

"""
Introduction
============

	TurboMail is a TurboGears extension - meaning that it starts up and
	shuts down alongside TurboGears applications you write in the same
	way that visit tracking and identity do.  TurboMail uses built-in
	Python modules for SMTP communication and MIME e-mail creation, but
	greatly simplifies these tasks by performing the grunt-work for you.
	
	Being multi-threaded, TurboMail allows you to enqueue messages to be
	sent and then immediately continue with processing, resulting in a
	much more fluid user experience.  Threads are handled intelligently
	(increasing the number of threads as demand increases) and they are
	automatically recycled.  There is only ever one SMTP connection per
	thread.
	
	Benchmarking
	------------
	
		Throughput using the default options is sufficient for most use:
		100 messages in 45 seconds; just over 2 messages a second. Using
		a greater number of threads, 10 vs. 4, 100 messags take 30
		seconds; just over 3 messages a second.  YMMV.  Note that if a
		thread is idle, it will immediately deliver requests added to
		the queue, thus increasing the idle time will increase sparse
		performance.
	
	TurboMail is heavily inspired by PHPMailer, a very, very handy class
	for PHP 4 & 5 by Brent R. Matzelle.
	

Installation
============

	Simply easy_install the package::

		easy_install TurboMail

	TurboMail installs no external scripts.

Upgrade
=======

	Upgrading also uses easy_install::
	
		easy_install -U TurboMail

Configuration
=============

	TurboMail understands a large number of configuration options, all
	piggy-backed from your application's configuration.  Organized into
	two groups, the advanced set can be safely ignored in most
	applications.  Each option is listed with its default value.
	
	Simple Options
	--------------
	
		 - I{mail.on} (Default: B{False}) Enable TurboMail.  B{Required.}
		 - I{mail.server} (Default: B{None}) SMTP server address.
		   B{Required.}
		 - I{mail.username} (Default: B{None})
		 - I{mail.password} (Default: B{None})
	
		Both a username and password are required to enable
		authentication.
	
	Advanced Options
	----------------
	
		 - I{mail.debug} (Default: B{False}) Output all SMTP server
		   communications.
		 - I{mail.interval} (Default: B{10}) Polling delay between new
		   thread creation, in seconds.
		 - I{mail.threads} (Default: B{4}) Maximum number of concurrent
		   threads.
		 - I{mail.jobs} (Default: B{10}) Maximum number of job units per
		   thread.
		 - I{mail.timeout} (Default: B{60}) Maximum time a worker thread
		   will wait for additional jobs, in seconds.
		 - I{mail.tls} (Defalut: None) Enable or disable TLS, None will
		   attempt to auto-detect TLS.  This will not always work.
		 - I{mail.encoding} (Default: B{'us-ascii'}) Set the character
		   set and encoding on the MIMEText parts of the message.
		   Common character sets include:
		    - us-ascii - I{Performs no encoding, but is 7bit only.}
		    - iso-8859-1 - I{Uses quoted-printable encoding.}
		    - utf-8 - I{Uses base64 encoding.}
		   Due to the way Python's email package handles character sets,
		   the following additional virtual character sets are provided
		   by TurboMail, and will override the global defaults:
		    - utf-8-qp - I{Sets utf-8 to use quoted-printable encoding.}
		   Headers are not encoded.  DIY.
		 - I{mail.polling} (Defaut: B{False}) If enabled, configures the
		   thread pool to poll every I{mail.interval} seconds for new
		   jobs.  This may give performance benefits to the running
		   application.  The default behaviour is to create new threads
		   as soon as work is enqueued, resulting in faster delivery.
		
		In debug mode using a single thread with a maximum of one job
		can be advantageous.  Having a single thread with a maximum of a
		single job limits TurboMail to a single SMTP connection at a
		time and automatically disconnects for I{each message}.

Basic Usage
===========

	To use TurboMail in your TurboGears application, after adding the
	appropriate configuration options to your application, perform the
	following steps:
	
	 1. Import TurboMail::
	
	    	import turbomail
	
	 2. Create a L{Message} or L{KIDMessage} object::
	
	    	message = turbomail.Message(from, to, subject)
	
	 3. Set some content for your message::
	
	    	message.plain = "Hello world!"
	
	 4. Enqueue your message::
	
	    	turbomail.enqueue(message)
	
	Your message will now have been enqueued.  It will take at most
	I{mail.interval} seconds for a thread to be created to deliver it.
	The best case scenario is if there is an idle thread waiting, in
	which case delivery will be immediate.

Advanced Usage - Logging
========================

	Additionally, you can configure your application to log TurboMail
	events differently, in a more loggable and machine parsable way.
	You do so by adding the following lines to the formatters section
	of your log.cfg::
	
		[[[timed_message]]]
		format='*(asctime)s *(message)s'
	
	Add the following to the handlers section of log.cfg::
	
		[[[mail_out]]]
		class='StreamHandler'
		level='INFO'
		args='(sys.stdout,)'
		formatter='timed_message'
	
	And finally, add the following to your dev.cfg::
	
		[[[mail]]]
		level='INFO'
		qualname='turbomail.dispatch'
		handlers=['mail_out']
		propagate=0
	
	If you wish to log mail dispatch to a file, for example in your
	production configuration, use this instead of the above::
	
		[[handlers]]
		[[[mail_out]]]
		args="('mail.log',)"
		class='StreamHandler'
		level='INFO'
		formatter='timed_message'
		
		[[loggers]]
		[[[mail]]]
		level='INFO'
		qualname='turbomail.dispatch'
		handlers=['mail_out']
		propagate=0
	
	The format of turbomail.dispatch INFO log entries is::
	
		[user@]server size ("from_name") from_addr ("to_name") to_addr - subject
	
	Designed for easy parsing and tracking of statistics, the log
	format uses the following conventions:
	
	 - Entries between square brackets are optional and may be omitted.
	 - Entries between round brackets may be replaced with a dash if
	   unavailable.
	 - The size field is in bytes and represents the total size of the
	   MIME-encoded message including headers, after character set
	   conversion.
	 - When sending to multiple recipients, the to_addr field becomes
	   the number of recipients wrapped in round brackets.  E.g. "(3)"
	 - The subject field extends to the EOL - quotes, dashes, and other
	   symbols should be treated as part of the subject.

Changelog
=========

	Version 1.0
	-----------
		 - Initial release.
	
	Version 1.0.1
	-------------
		 - Minor updates to remove unneeded arguments.
		 - Complete source-level epydoc documentation.
	
	Version 1.0.4.1
	---------------
		 - Better auto-detection of TLS capability.
		 - A new configuration directive, mail.tls; True, False, or
		   None to auto-detect.
		 - Fixes a bug in KIDMessage which rendered it
		   non-functional.
		 - Changed the behavior of a worker dying from old age to spawn
		   a new process immediately.
		 - Minor fixes and updates to the documentation.
		 - Benchmark results in the documentation.
	
	Version 1.0.4.2
	---------------
		 - Added encoding configuration directive.
		 - Encoding can be passed to a Message constructor to override
		   the encoding on a message-by-message basis.
	
	Version 1.1
	-----------
		 - Cleaned up the log output.
		 - Added documentation for logging.
		 - KID i18n session bug fixed in TurboGears 1.0 and trunk SVN.
		 - Marked as stable for the Python Cheese Shop.
	
	Version 2.0
	-----------
		 - Default thread creation mechanism is on-demand, not polling.
		   You can change back to the (old) polling mechanism by
		   setting the following configuration option::
		   	mail.polling = True
		 - TemplateMessage has been renamed KIDMessage.
		 - Can now use 'utf-8-qp' to configure the 'utf-8' charset for
		   quoted-printable encoding.
		 - MIME-encoded message generation was re-written.  Now, simple
		   plain-text-only messages have almost zero overhead.
		   Complexity of the generated document increases with feature
		   use.
		 - It is now safe to import enqueue from TurboMail - it no
		   longer polymorphs after TurboGears start-up and shutdown.
		 - Enhanced logging output - see above.
		 - Better tracking of when to rebuild the MIME message by using
		   a dirty flag.
		 - Many, many additional headers.  Look at the documentation for
		   the message class for more information.
		 - Multiple recipients.
		
		There is, however, an outstanding bug in KIDMessage.  When
		generating the plain-text alternative KID seems to default
		to ascii encoding, which bombs out if you use any extended
		characters in the template, or variables passed to the template.
		
	Version 2.0.1
	-------------
		 - Applied patch submitted by Jason Chu to allow overriding of
		   the Sender and Return-Path headers.
		 - Applied patch submitted by Jason Chu to correct the MIME
		   type of dual text & html messages with attachments.

	Version 2.0.2
	-------------
		 - Added a generic ControllerMessage which uses the output of
		   any function or method that returns HTML.
		 - Changed the behaviour of the attach and embed methods to
		   pull content from an existing file-like object or open an
		   on-disk file.
		 - Corrected a conditional testing for the presense of
		   smptfrom as a message property.  Thanks James!
		
	Version 2.0.3
	-------------
		 - The plain and rich properties of the Message class can now
		   be callables, executed at delivery-time.
                 - Removed ControllerMessage in favor of using the above.
                 - Deprecated use of KIDMessage in favor of the above.

@var _queue: After TurboGears startup within an application which has
            enabled TurboMail, I{queue} is an instance of a MailPool
            object.

"""

from turbomail.release import \
		version as __version__, \
		author as __author__, \
		email as __email__, \
		license as __license__, \
		copyright as __copyright__

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

from turbomail.startup import start_extension, shutdown_extension
from turbomail.exceptions import *
from turbomail.message import Message, KIDMessage


__all__ = ['MailException', 'MailNotEnabledException', 'MailConfigurationException', 'Message', 'KIDMessage', 'enqueue', 'dispatch', 'exceptions', 'message', 'pool', 'startup']


_queue = None

def enqueue(work):
	"""Enqueue a message in the message thread-pool queue."""
	
	if _queue is None:
		raise MailNotEnabledException
	
	else:
		_queue.enqueue(work)


syntax highlighted by Code2HTML, v. 0.9.1