- Timestamp:
- 10/26/07 05:48:32 (1 year ago)
- Location:
- branches/3.0
- Files:
-
- 11 modified
-
setup.py (modified) (3 diffs)
-
tests/test_message.py (modified) (1 diff)
-
turbomail/__init__.py (modified) (2 diffs)
-
turbomail/control.py (modified) (3 diffs)
-
turbomail/extensions/smime.py (modified) (1 diff)
-
turbomail/managers/demand.py (modified) (1 diff)
-
turbomail/managers/immediate.py (modified) (3 diffs)
-
turbomail/message.py (modified) (14 diffs)
-
turbomail/providers/debug.py (modified) (2 diffs)
-
turbomail/providers/smtp.py (modified) (7 diffs)
-
turbomail/release.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
branches/3.0/setup.py
r44 r52 28 28 ], 29 29 extras_require = { 30 'turbogears': ["TurboGears>=1.0"] 30 'turbogears': ["TurboGears>=1.0"], 31 'smime': ["M2Crypto>=0.18"] 31 32 }, 32 33 zip_safe=True, … … 50 51 ], 51 52 'turbomail.managers': [ 52 #"demand = turbomail.managers.demand",53 "demand = turbomail.managers.demand", 53 54 # "polling = turbomail.managers.polling", 54 55 "immediate = turbomail.managers.immediate" 55 56 ], 56 57 'turbomail.providers': [ 57 #"smtp = turbomail.providers.smtp",58 "smtp = turbomail.providers.smtp", 58 59 # "sendmail = turbomail.providers.sendmail" 59 60 # "disk = turbomail.providers.disk", … … 62 63 'turbomail.extensions': [ 63 64 "utf8qp = turbomail.extensions.utf8qp", 64 #"smime = turbomail.extensions.smime",65 "smime = turbomail.extensions.smime", 65 66 # "gpg = turbomail.extensions.gpg", 66 67 ] -
branches/3.0/tests/test_message.py
r36 r52 30 30 31 31 def test_message_string(self): 32 self.failUnless("To: Recipient <recipient@example.com>" in str(self.message)) 33 self.failUnless("From: Sender <sender@example.com>" in str(self.message)) 32 34 self.failUnless("Subject: Test message subject." in str(self.message)) 33 35 self.failUnless("\n\nThis is a test message plain text body." in str(self.message)) 36 37 def test_recipients_collection(self): 38 self.message.cc.append("copied@example.com") 39 self.assertEqual(self.message.recipients.addresses, ["recipient@example.com", "copied@example.com"]) -
branches/3.0/turbomail/__init__.py
r44 r52 20 20 from turbomail.util import send 21 21 22 import pkg_resources 23 22 24 __all__ = ['exceptions', 'extensions', 'managers', 'providers', 'release', 'manager', 'provider', 'config', 'Message', 'interface', 'send'] 23 25 … … 26 28 provider = None 27 29 config = {} 30 -
branches/3.0/turbomail/control.py
r40 r52 67 67 # Load the requested pool manager. 68 68 manager = turbomail.config.get("mail.manager", "immediate") 69 turbomail.manager = self.load_single_entry("turbomail.managers", manager).load()70 if not turbomail.manager:71 turbomail.config. set("mail.on", False)69 controller = self.load_single_entry("turbomail.managers", manager) 70 if not controller: 71 turbomail.config.update({"mail.on": False}) 72 72 log.error("Unable to locate %s manager, TurboMail disabled." % manager) 73 self.stop(force=True) 73 74 return 75 turbomail.manager = controller.load() 74 76 turbomail.manager.start() 75 77 76 78 # Load the requested mail provider. 77 79 provider = turbomail.config.get("mail.provider", "debug") 78 turbomail.provider = self.load_single_entry("turbomail.providers", provider).load()79 if not turbomail.provider:80 turbomail.config. set("mail.on", False)80 controller = self.load_single_entry("turbomail.providers", provider) 81 if not controller: 82 turbomail.config.update({"mail.on": False}) 81 83 log.error("Unable to locate %s provider, TurboMail disabled." % provider) 84 self.stop(force=True) 82 85 return 86 turbomail.provider = controller.load() 83 87 turbomail.provider.start() 84 88 … … 94 98 self.running = True 95 99 96 def stop(self ):97 if not self.running : return100 def stop(self, force=False): 101 if not self.running and not force: return 98 102 99 103 log.info("TurboMail extension shutting down.") … … 104 108 if turbomail.config.get("mail." + entrypoint.name + ".on", False): 105 109 ext = entrypoint.load() 106 if hasattr(ext, " unload"): ext.unload()110 if hasattr(ext, "stop"): ext.stop() 107 111 108 112 # Unload the provider and manager. 109 turbomail.provider.stop() 110 turbomail.manager.stop() 113 if turbomail.provider and hasattr(turbomail.provider, "stop"): turbomail.provider.stop() 114 if turbomail.manager and hasattr(turbomail.manager, "stop"): turbomail.manager.stop() 115 116 turbomail.provider = None 117 turbomail.manager = None 111 118 112 119 self.running = False -
branches/3.0/turbomail/extensions/smime.py
r42 r52 1 # encoding: utf-8 2 3 """TurboMail extension API.""" 4 5 import logging 6 log = logging.getLogger("turbomail.utf8qp") 7 8 import turbomail, os 9 from email.mime.multipart import MIMEMultipart 10 from email.mime.application import MIMEApplication 11 12 __all__ = ['start'] 13 14 15 16 class Message(turbomail.Message): 17 def __init__(self, **kw): 18 super(Message, self).__init__(**kw) 19 20 self.sign = kw.get("sign", turbomail.config.get("mail.message.sign", False)) 21 self.encrypt = kw.get("encrypt", turbomail.config.get("mail.message.encrypt", False)) 22 self.force = kw.get("force", turbomail.config.get("mail.smime.force", False)) 23 24 def process_mime_message(self, message): 25 newmessage = message 26 27 if self.sign: 28 from M2Crypto import BIO, Rand, SMIME 29 30 # Make a MemoryBuffer of the message. 31 buf = BIO.MemoryBuffer(str(message)) 32 33 # Seed the PRNG. 34 Rand.load_file('randpool.dat', -1) 35 36 # Instantiate an SMIME object; set it up; sign the buffer. 37 s = SMIME.SMIME() 38 s.load_key( 39 os.path.join(turbomail.config.get("mail.smime.keystore", "./"), str(self.author.addresses[0]) + '.key'), 40 os.path.join(turbomail.config.get("mail.smime.keystore", "./"), str(self.author.addresses[0]) + '.x509'), 41 ) 42 p7 = s.sign(buf) 43 44 # I'm assuming -a-lot- here. There's got to be a better way to do this, but the M2Crypto lib sucks ass from this PoV. 45 newmessage = MIMEMultipart("signed", protocol='application/x-pkcs7-signature', micalg="sha1") 46 newmessage.attach(message) 47 48 buf = BIO.MemoryBuffer() 49 p7.write_der(buf) 50 signature = MIMEApplication(buf.read(), "x-pkcs7-signature") 51 signature.set_param("name", "smime.p7s") 52 del signature['Content-Disposition'] 53 signature.add_header("Content-Disposition", "attachment", filename="smime.p7s") 54 55 newmessage.attach(signature) 56 57 # Save the PRNG's state. 58 Rand.save_file('randpool.dat') 59 60 return newmessage -
branches/3.0/turbomail/managers/demand.py
r40 r52 1 # encoding: utf-8 2 3 """TurboMail extension API.""" 4 5 import logging 6 log = logging.getLogger("turbomail.manager") 7 8 import turbomail 9 from turbomail.api import Manager 10 from turbomail.exceptions import ProviderExhaustedException 11 12 import math, copy 13 from Queue import Queue, Empty 14 from threading import Event, Thread 15 from turbomail.dispatch import Dispatch 16 17 __all__ = ['load'] 18 19 20 def load(): 21 return DemandManager() 22 23 24 class DemandManager(Manager): 25 name = "Demand" 26 version = "1.0" 27 url = "http://www.python-turbomail.org/wiki/DemandManager" 28 29 def __init__(self): 30 log.info("Demand manager starting up.") 31 32 super(DemandManager, self).__init__() 33 34 self.pool = 0 35 self.queue = Queue() 36 self.finished = Event() 37 38 self.threads = turbomail.config.get("mail.demand.threads", 4) # Maximum number of threads to create. 39 self.divisor = turbomail.config.get("mail.demand.divisor", 10) # Estimate the number of required threads by dividing the queue size by this. 40 self.timeout = turbomail.config.get("mail.demand.timeout", 60) 41 42 log.info("Demand manager ready.") 43 44 def optimum(self): 45 return min(self.threads, math.ceil(self.queue.qsize() / float(self.divisor))) 46 47 optimum = property(optimum) 48 49 def stop(self): 50 log.info("Demand manager shutting down.") 51 self.finished.set() 52 53 def spawn(self): 54 thread = Thread(target=self.wrapper) 55 thread.start() 56 self.pool += 1 57 58 def deliver(self, message): 59 log.info("Adding message %s to the queue for background delivery." % message.id) 60 self.queue.put(copy.deepcopy(message)) 61 message._processed = True 62 message._dirty = True 63 64 if not self.queue.empty() and self.pool < self.optimum: 65 tospawn = int(self.optimum - self.pool) 66 log.debug("Spawning %d thread%s." % (tospawn, tospawn != 1 and "s" or "")) 67 for i in range(tospawn): 68 self.spawn() 69 70 def wrapper(self): 71 log.debug("Mail queue worker starting up.") 72 73 self.worker() 74 75 self.pool -= 1 76 log.debug("Mail queue worker finished.") 77 78 def worker(self): 79 log.debug("Requesting new provider instance.") 80 provider = turbomail.provider.new() 81 if not provider: raise ManagerException, "Unable to allocate new provider." 82 83 while True: 84 try: 85 message = self.queue.get(True, self.timeout) 86 provider.deliver(message) 87 88 except Empty: 89 log.debug("Worker death from starvation.") 90 break 91 92 except ProviderExhaustedException: 93 log.debug("Worker death from provider exhaustion - spawning child.") 94 self.deliver(message) 95 self.spawn() 96 break 97 98 except: 99 log.exception("Delivery of message %s failed." % message.id) 100 break 101 102 else: 103 log.info("Delivery of message %s successful or deferred." % message.id) 104 -
branches/3.0/turbomail/managers/immediate.py
r43 r52 18 18 19 19 class ImmediateManager(Manager): 20 name = "Immediate" 21 version = "1.0" 22 url = "http://www.python-turbomail.org/wiki/ImmediateManager" 23 20 24 def __init__(self): 21 25 log.info("Immediate manager starting up.") … … 24 28 25 29 self.provider = None 30 31 log.info("Immediate manager ready.") 26 32 27 33 def deliver(self, message): … … 45 51 log.debug("Provider exhausted.") 46 52 self.provider = None 53 self.deliver(message) 47 54 48 55 except: -
branches/3.0/turbomail/message.py
r41 r52 4 4 5 5 import turbomail 6 from turbomail import release 6 7 from turbomail.util import AddressList 7 from turbomail.release import version8 8 import re, os, email 9 9 … … 31 31 This allows you to define your own message to be delivered.""" 32 32 33 def __init__(self, sender=None, recipients=[], message=None): 33 def __init__(self, id=None, sender=None, recipients=[], message=None): 34 self.id = id 34 35 self.sender = sender 35 36 self.recipients = recipients 36 37 self.message = message 38 39 if not self.id: self.id = uuid() 40 41 super(StubMessage, self).__init__() 37 42 38 43 def __str__(self): … … 58 63 59 64 def configget(name, key, default=None): 60 pass65 return kw.get(name, turbomail.config.get(key, default)) 61 66 62 67 self.date = kw.get("date", formatdate(localtime=True)) 63 68 64 self._senders = AddressList(kw.get("sender", turbomail.config.get("mail.message.sender", None))) 65 self._senders = AddressList(kw.get("senders", turbomail.config.get("mail.message.senders", self._senders))) 66 self._envelope = AddressList(kw.get("envelope", turbomail.config.get("mail.message.envelope", None))) 67 self._reply = AddressList(kw.get("reply", turbomail.config.get("mail.message.reply", None))) 69 if 'authors' in kw: kw['author'] == kw['authors'] 70 if 'senders' in kw: kw['sender'] == kw['senders'] 71 72 self._author = AddressList(configget("from", "mail.message.author")) 73 self._sender = AddressList(configget("sender", "mail.message.sender")) 74 self._reply = AddressList(configget("reply", "mail.message.reply")) 68 75 self._to = AddressList(kw.get("to", None)) 69 self._cc = AddressList( kw.get("cc", turbomail.config.get("mail.message.cc", None)))70 self._bcc = AddressList( kw.get("bcc", turbomail.config.get("mail.message.bcc", None)))71 self._disposition = AddressList( kw.get("disposition", turbomail.config.get("mail.message.disposition", None)))72 73 self.organization = kw.get("organization", turbomail.config.get("mail.message.organization", None))74 self.encoding = kw.get("encoding", turbomail.config.get("mail.encoding", 'us-ascii'))75 self.priority = kw.get("priority", turbomail.config.get("mail.message.priority", None))76 self._cc = AddressList(configget("cc", "mail.message.cc")) 77 self._bcc = AddressList(configget("bcc", "mail.message.bcc")) 78 self._disposition = AddressList(configget("disposition", "mail.message.disposition")) 79 80 self.organization = configget("organization", "mail.message.organization") 81 self.encoding = configget("encoding", "mail.encoding", 'us-ascii') 82 self.priority = configget("priority", "mail.message.priority") 76 83 self.subject = kw.get("subject", None) 77 84 self.plain = kw.get("plain", None) … … 79 86 self.attachments = kw.get("attachments", []) 80 87 self.embedded = kw.get("embedded", []) 81 self.headers = kw.get("headers", turbomail.config.get("mail.message.headers", []))82 self.tries = kw.get("tries", turbomail.config.get("mail.tries", 3))88 self.headers = configget("headers", "mail.message.headers", []) 89 self.tries = configget("tries", "mail.tries", 3) 83 90 84 91 self._id = kw.get("id", None) … … 94 101 return self.mime.as_string() 95 102 96 def _get_sender(self): 97 if self._envelope: 98 return self._envelope.addresses[0] 99 return self._senders.addresses[0] 100 101 sender = AddressList.protected('_senders') 102 senders = AddressList.protected('_senders') 103 envelope = AddressList.protected('_envelope') 103 author = AddressList.protected('_author') 104 authors = AddressList.protected('_author') 105 sender = AddressList.protected('_sender') 106 senders = AddressList.protected('_sender') 104 107 reply = AddressList.protected('_reply') 105 108 to = AddressList.protected('_to') … … 114 117 115 118 return self._id 119 116 120 id = property(id) 117 121 122 def envelope(self): 123 if self.sender and self.senders != self.author: 124 return AddressList(self.senders) 125 return AddressList(self.author) 126 127 envelope = property(envelope) 128 118 129 def recipients(self): 119 return [isinstance(i, tuple) and i[1] or i for i in self.to + self.cc + self.bcc] 130 return AddressList(self.to + self.cc + self.bcc) 131 120 132 recipients = property(recipients) 121 133 … … 123 135 """Produce the final MIME message.""" 124 136 125 assert self. senders, "You must specify a sender."137 assert self.author, "You must specify an author." 126 138 assert self.subject, "You must specify a subject." 127 139 assert self.to or self.cc or self.bcc, "You must specify at least one recipient." … … 134 146 135 147 plain = MIMEText(self._callable(self.plain).encode(self.encoding), 'plain', self.encoding) 136 rich = self.rich and MIMEText(self._callable(self.rich).encode(self.encoding), 'html', self.encoding) or None 137 148 149 rich = None 150 if self.rich: 151 rich = MIMEText(self._callable(self.rich).encode(self.encoding), 'html', self.encoding) 152 138 153 def generate_mime(): 139 154 if not rich: return plain 140 155 141 156 message = MIMEMultipart('alternative') 142 157 message.attach(plain) 143 158 144 159 if not self.embedded: 145 160 message.attach(rich) 146 161 147 162 else: 148 163 embedded = MIMEMultipart('related') … … 150 165 for attachment in self.embedded: embedded.attach(attachment) 151 166 message.attach(embedded) 152 167 153 168 return message 154 169 155 170 message = generate_mime() 156 171 157 172 if self.attachments: 158 173 attachments = MIMEMultipart() … … 160 175 for attachment in self.attachments: attachments.attach(attachment) 161 176 message = attachments 162 177 178 if hasattr(self, "process_mime_message"): 179 message = self.process_mime_message(message) 180 163 181 headers = [ 164 ('Sender', self.sender), # AddressList165 ('From', self. senders), # AddressList166 ('Reply-To', self.reply), # AddressList182 ('Sender', self.sender), 183 ('From', self.author), 184 ('Reply-To', self.reply), 167 185 ('Subject', self.subject), 168 186 ('Date', self.date), 169 ('To', self.to), # AddressList170 ('Cc', self.cc), # AddressList171 ('Disposition-Notification-To', self.disposition), # AddressList187 ('To', self.to), 188 ('Cc', self.cc), 189 ('Disposition-Notification-To', self.disposition), 172 190 ('Organization', self.organization), 173 191 ('X-Priority', self.priority), 174 ('X-Mailer', "TurboMail <http://www.python-turbomail.org/>"),175 ('X-TurboMail-Version', version),176 ('X-TurboMail-Message-GUID', self.id),177 ('X-TurboMail-Extensions', "manager.demand v.1.0, provider.smtp v.1.0")178 192 ] 179 193 194 if turbomail.config.get("mail.brand", True): 195 headers.extend([ 196 ('X-Mailer', "%s <%s>" % (release.name, release.url)), 197 ('X-TurboMail-Version', release.version), 198 ('X-TurboMail-Message-GUID', self.id), 199 ]) 200 201 if turbomail.provider: 202 headers.extend([ 203 ('X-TurboMail-Provider', "%s <%s>" % (turbomail.provider.name, turbomail.provider.url)), 204 ('X-TurboMail-Provider-Version', turbomail.provider.version), 205 ]) 206 207 if turbomail.manager: 208 headers.extend([ 209 ('X-TurboMail-Manager', "%s <%s>" % (turbomail.manager.name, turbomail.manager.url)), 210 ('X-TurboMail-Manager-Version', turbomail.manager.version), 211 ]) 212 180 213 headers.extend(self.headers) 181 214 182 215 for header in headers: 183 216 if isinstance(header, (tuple, list)): … … 188 221 elif isinstance(header, dict): 189 222 message.add_header(**header) 190 223 191 224 self._mime = message 192 225 self._processed = True … … 194 227 195 228 return message 229 196 230 mime = property(mime)<
