[Zope] Fix for lots of MailHost problems!

Stephen Pitts smpitts@midsouth.rr.com
Sun, 23 Jan 2000 19:02:22 -0600


--mYCpIKhGyMATD0i+
Content-Type: text/plain; charset=us-ascii

I was having lots of trouble with <dtml-sendmail>, and I investigated
the code for MailHost. Instead of using the excellent smtplib module
(that ships with Python) to send EMail, the folks at DC decided to write
their own, using sockets.

The attatched patch replaces 100+ lines of socket-based code with 2 line
calls to smtplib. I think that this will improve the reliability of
MailHost dramatically. As a side effect, the timeout and localhost
properties of MailHosts are gone.

So far, the basic <dtml-sendmail> tag works perfectly. Someone try this
out and tell me what you think.
-- 
Stephen Pitts
smpitts@midsouth.rr.com
webmaster - http://www.mschess.org

--mYCpIKhGyMATD0i+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="mailhost.smtplib.patch"

Only in lib/python/Products/MailHost/: .MailHost.py.swp
diff -ru ../Zope-2.1.2-src/lib/python/Products/MailHost/MailHost.py lib/python/Products/MailHost/MailHost.py
--- ../Zope-2.1.2-src/lib/python/Products/MailHost/MailHost.py	Wed Nov  3 08:42:15 1999
+++ lib/python/Products/MailHost/MailHost.py	Sun Jan 23 18:14:56 2000
@@ -85,7 +85,7 @@
 """SMTP mail objects"""
 
 from Globals import Persistent, HTMLFile, HTML, MessageDialog
-from socket import *; from select import select
+from smtplib import SMTP
 from AccessControl.Role import RoleManager
 from operator import truth
 import Acquisition, sys, string, types, mimetools
@@ -98,15 +98,13 @@
 smtpError = "SMTP Error"
 MailHostError = "MailHost Error"
 
-addForm=HTMLFile('addMailHost_form', globals(), localhost=gethostname())
-def add(self, id, title='', smtp_host=None, 
-        localhost='localhost', smtp_port=25, timeout=1.0, REQUEST=None):
+addForm=HTMLFile('addMailHost_form', globals())
+def add(self, id, title='', smtp_host=None, smtp_port=25, REQUEST=None):
     ' add a MailHost into the system '
     i=MailHost()            #create new mail host
     i.id=id                 #give it id
     i.title=title           #title
-    i._init(localHost=localhost, smtpHost=smtp_host, smtpPort=smtp_port,
-            timeout=timeout)
+    i._init(smtpHost=smtp_host, smtpPort=smtp_port)
     self._setObject(id,i)   #register it
     if REQUEST: return self.manage_main(self,REQUEST)
 
@@ -136,20 +134,15 @@
         'nothing yet'
         pass
 
-    def _init(self, localHost, smtpHost, smtpPort, timeout=1):
-        self.localHost=localHost
+    def _init(self, smtpHost, smtpPort):
         self.smtpHost=smtpHost
         self.smtpPort=smtpPort
-        self.timeout=timeout
 
-    def manage_makeChanges(self,title,localHost,smtpHost,smtpPort,
-                           timeout, REQUEST=None):
+    def manage_makeChanges(self,title,smtpHost,smtpPort, REQUEST=None):
         'make the changes'
         self.title=title
-        self.localHost=localHost
         self.smtpHost=smtpHost
         self.smtpPort=smtpPort
-        self.timeout=timeout
         if REQUEST: return MessageDialog(
             title  ='Changed %s' % self.__name__,
             message='%s has been updated' % self.id,
@@ -163,18 +156,15 @@
         mtemplate = getattr(self, messageTemplate)
         messageText = mtemplate(self, trueself.REQUEST)
         messageText=_encode(messageText, encode)
-        headers, message = decapitate(messageText)
+        headers = extractheaders(messageText)
         if mto: headers['to'] = mto
         if mfrom: headers['from'] = mfrom
         for requiredHeader in ('to', 'from'):
             if not headers.has_key(requiredHeader):
                 raise MailHostError,"Message missing SMTP Header '%s'"\
                       % requiredHeader
-        Send(trueself.smtpHost, trueself.smtpPort, 
-             trueself.localHost, trueself.timeout, 
-             headers['from'], headers['to'],
-             headers['subject'] or 'No Subject', messageText
-             )
+		mailserver = SMTP(trueself.smtpHost, trueself.smtpPort)
+		mailserver.sendmail(headers['from'], headers['to'], messageText)
 
         if not statusTemplate: return "SEND OK"
 
@@ -186,7 +176,7 @@
 
     def send(self, messageText, mto=None, mfrom=None, subject=None,
              encode=None):
-        headers, message = decapitate(messageText)
+        headers = extractheaders(messageText)
         
         if not headers['subject']:
             messageText="subject: %s\n%s" % (subject or '[No Subject]',
@@ -203,14 +193,12 @@
                 raise MailHostError,"Message missing SMTP Header '%s'"\
                 % requiredHeader
         messageText=_encode(messageText, encode)
-        sm=SendMail(self.smtpHost, self.smtpPort, self.localHost, self.timeout)
-        sm.send(mfrom=headers['from'], mto=headers['to'],
-                subj=headers['subject'] or 'No Subject',
-                body=messageText)
+        smtpserver = SMTP(self.smtpHost, self.smtpPort)
+        smtpserver.sendmail(headers['from'],headers['to'], messageText)
 
     def scheduledSend(self, messageText, mto=None, mfrom=None, subject=None,
                       encode=None):
-        headers, message = decapitate(messageText)
+        headers = extractheaders(messageText)
 
         if not headers['subject']:
             messageText="subject: %s\n%s" % (subject or '[No Subject]',
@@ -227,91 +215,17 @@
                 raise MailHostError,"Message missing SMTP Header '%s'"\
                 % requiredHeader
         messageText=_encode(messageText, encode)
-        Send(self.smtpHost, self.smtpPort, self.localHost, self.timeout,
-             headers['from'], headers['to'],
-             headers['subject'] or 'No Subject', messageText
-             )
+        smtpserver = SMTP(self.smtpHost, self.smtpPort)
+        smtpserver.sendmail(headers['from'], headers['to'], messageText)
 
     def simple_send(self, mto, mfrom, subject, body):
-        body="subject: %s\n\n%s" % (subject, body)
-        SendMail(self.smtpHost, self.smtpPort, self.localHost,
-                 self.timeout).send( 
-                     mfrom=mfrom, mto=mto, subj=subject, body=body
-                     )
+        body="from: %s\nto: %s\nsubject: %s\n\n%s" % (mfrom, mto, subject, body)
+        mailserver = SMTP(self.smtphost, self.smtpport)
+        mailserver.sendmail(mfrom, mto, body)
 
 class MailHost(Persistent, MailBase):
     "persistent version"
 
-def Send(host, port, localhost, timeout, from_, to, subject, body):
-    SendMail(host, port, localhost, timeout).send(from_, to, subject, body)
-        
-class SendMail:
-    singledots=re.compile('^\.$', re.M)
-    
-    def __init__(self, smtpHost, smtpPort, localHost="localhost", timeout=1):
-        self.conn = socket(AF_INET, SOCK_STREAM)
-        self.conn.connect(smtpHost, smtpPort)
-        self.timeout=timeout
-        self.fd=self.conn.fileno()
-        self.conn.send("helo "+localHost+"\015\012")
-        while 1:
-            if not self._check(): break
-
-    def __del__(self):
-        self._close()
-
-    def getLine(self):
-        line=''
-        tm=self.timeout
-        while 1:
-            if not select([self.fd],[],[],tm)[0]:       #check the socket
-                break
-            data=self.conn.recv(1)
-            if (not data) or (data == '\n'):
-                break
-            line=line+data
-        return line
-
-    def _check(self, lev='250'):
-        line = self.getLine()
-        if not line: return 0
-        try:
-            code=string.atoi(line[:3])
-        except:
-            raise smtpError, \
-                  "Cannot convert line from SMTP: %s" % line
-        if code > 400:
-            raise smtpError, \
-                  "Recieved error code %s from SMTP: %s"\
-                  % (code, line)
-        return 1
-
-    def send(self, mfrom, mto, subj='No Subject', body='Blank Message'):
-        self.conn.send("mail from:<%s>\015\012" % mfrom)
-        self._check()
-        if type(mto) in [types.ListType, types.TupleType]:
-            for person in mto:
-                self.conn.send("rcpt to:<%s>\015\012" % person)
-                self._check()
-        else:
-            self.conn.send("rcpt to:<%s>\015\012" % mto)
-            self._check()
-        self.conn.send("data\015\012")
-        self._check()
-        body=self.singledots.sub('..', body)
-        body=string.replace(body, '\r\n', '\n')
-        body=string.replace(body, '\r', '\n')
-        body=string.replace(body, '\n', '\015\012')
-        self.conn.send(body)
-        self.conn.send("\015\012.\015\012")
-        self._check('354')
-
-    def _close(self):
-        self.conn.send("quit\015\012")
-        self.conn.close()
-
-
-
 def _encode(body, encode=None):
     if encode is None:
         return body
@@ -329,8 +243,8 @@
     return newmfile.getvalue()
 
 
-def decapitate(message):
-    # split message into headers / body
+def extractheaders(message):
+    # return headers of message
     mfile=StringIO(message)
     mo=rfc822.Message(mfile)
 
@@ -345,5 +259,4 @@
     
     hd['from']=mo.getaddr('from')[1]
     hd['subject']=mo.getheader('subject') or "No Subject"
-
-    return hd, mfile.read()
+    return hd
Only in lib/python/Products/MailHost/: MailHost.pyc
Only in lib/python/Products/MailHost/: SendMailTag.pyc
Only in lib/python/Products/MailHost/: __init__.pyc
diff -ru ../Zope-2.1.2-src/lib/python/Products/MailHost/addMailHost_form.dtml lib/python/Products/MailHost/addMailHost_form.dtml
--- ../Zope-2.1.2-src/lib/python/Products/MailHost/addMailHost_form.dtml	Wed Nov  3 12:56:32 1999
+++ lib/python/Products/MailHost/addMailHost_form.dtml	Sun Jan 23 18:11:55 2000
@@ -36,12 +36,6 @@
   </TD>
 </TR>
 <TR VALIGN="TOP">
-  <TH ALIGN="LEFT">Local Host</TH>
-  <TD ALIGN="LEFT">
-    <INPUT TYPE="TEXT" NAME="localhost" SIZE="40" VALUE="<dtml-var localhost html_quote>">
-  </TD>
-</TR>
-<TR VALIGN="TOP">
   <TH ALIGN="LEFT">SMTP Host</TH>
   <TD ALIGN="LEFT">
     <INPUT TYPE="TEXT" NAME="smtp_host" SIZE="40">
@@ -51,12 +45,6 @@
   <TH ALIGN="LEFT">SMTP Port</TH>
   <TD ALIGN="LEFT">
     <INPUT TYPE="TEXT" NAME="smtp_port:int" SIZE="4" VALUE="25">
-  </TD>
-</TR>
-<TR VALIGN="TOP">
-  <TH ALIGN="LEFT">Max. Timeout</TH>
-  <TD ALIGN="LEFT">
-    <INPUT TYPE="TEXT" NAME="timeout:float" SIZE="4" VALUE="1">
   </TD>
 </TR>
 <TR VALIGN="TOP">
diff -ru ../Zope-2.1.2-src/lib/python/Products/MailHost/manageMailHost.dtml lib/python/Products/MailHost/manageMailHost.dtml
--- ../Zope-2.1.2-src/lib/python/Products/MailHost/manageMailHost.dtml	Wed Nov  3 12:56:32 1999
+++ lib/python/Products/MailHost/manageMailHost.dtml	Sun Jan 23 18:14:29 2000
@@ -19,12 +19,6 @@
   </TD>
 </TR>
 <TR VALIGN="TOP">
-  <TH ALIGN="LEFT">Local Host</TH>
-  <TD ALIGN="LEFT">
-    <INPUT TYPE="TEXT" NAME="localHost" SIZE="40" VALUE="<dtml-var localHost html_quote>">
-  </TD>
-</TR>
-<TR VALIGN="TOP">
   <TH ALIGN="LEFT">SMTP Host</TH>
   <TD ALIGN="LEFT">
     <INPUT TYPE="TEXT" NAME="smtpHost" SIZE="40" VALUE="<dtml-var smtpHost html_quote>">
@@ -35,12 +29,6 @@
   <TD ALIGN="LEFT">
     <INPUT TYPE="TEXT" NAME="smtpPort:int" SIZE="4" VALUE="<dtml-var smtpPort html_quote>">
   </TD>
-</TR>
-<TR VALIGN="TOP">
- <TH ALIGN="LEFT">Max. Timeout</TH>
- <TD ALIGN="LEFT">
-   <INPUT TYPE="TEXT" NAME="timeout:float" SIZE="4" VALUE="<dtml-var timeout html_quote>">
- </TD>
 </TR>
 
 <TR VALIGN="TOP">

--mYCpIKhGyMATD0i+--