PageRenderTime 31ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/Pre_Barrys_Archiver_Changes/mailman/Mailman/smtplib.py

#
Python | 169 lines | 128 code | 13 blank | 28 comment | 7 complexity | 6a2b80943e482b351e4bf1e84adcfa1a MD5 | raw file
Possible License(s): GPL-2.0
  1. # Copyright (C) 1998 by the Free Software Foundation, Inc.
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  16. # A quick hack. Talk to the SMTP port.
  17. # Right now this isn't very functional.
  18. # A lot of functionality was borrowed directly from ftplib...
  19. # John Viega (viega@list.org)
  20. # Adapted dan ohnesorg's hack to use DSN. We gotta start using the
  21. # official smtplib - as of 1.5.2 it'll have esmtp, we won't have to dabble
  22. # in this shit. Ken.
  23. # >>> from smtplib import *
  24. # >>> s = SmtpConnection('list.org')
  25. # >>> s.helo('adder.cs.virginia.edu')
  26. # >>> s.send(to='viega@list.org', frm='jtv2j@cs.virginia.edu', text='hello, world!')
  27. # >>> s.quit()
  28. from socket import *
  29. import string, types
  30. from Mailman.Logging.Utils import LogStdErr
  31. LogStdErr("error", "smtplib")
  32. SMTP_PORT = 25
  33. CRLF = '\r\n'
  34. # Exception raised when an error or invalid response is received
  35. error_reply = 'smtplib.error_reply' # unexpected [123]xx reply
  36. error_temp = 'smtplib.error_temp' # 4xx errors
  37. error_perm = 'smtplib.error_perm' # 5xx errors
  38. error_proto = 'smtplib.error_proto' # response does not begin with [1-5]
  39. class SmtpConnection:
  40. def __init__(self, host=''):
  41. self.host = host
  42. self._file = None
  43. self.connect()
  44. self.DSN_support = 0
  45. def connect(self):
  46. self._sock = socket(AF_INET, SOCK_STREAM)
  47. self._sock.connect(self.host, SMTP_PORT)
  48. self._file = self._sock.makefile('r')
  49. self.getresp()
  50. def helo(self, host):
  51. """First try an esmtp EHLO, falling back to old HELO if necessary.
  52. We also determine (a bit haphazardly) whether DSN is supported."""
  53. self._sock.send('EHLO %s\r\n' % host)
  54. try:
  55. resp = self.getresp()
  56. except error_perm:
  57. self._sock.send('HELO %s\r\n' % host)
  58. resp = self.getresp()
  59. self.DSN_support = (string.find(resp, '-DSN') != -1)
  60. def quit(self):
  61. self._sock.send('QUIT\r\n')
  62. self.getresp()
  63. # text should be \n at eol, we'll add the \r.
  64. def send(self, to, frm, text, headers=None):
  65. if headers:
  66. hlines = string.split(headers, '\n')
  67. lines = string.split(text, '\n')
  68. self._sock.send('MAIL FROM: <%s>\r\n' % frm)
  69. self.getresp()
  70. valid_recipients = []
  71. if self.DSN_support:
  72. rcpt_form = 'RCPT TO: <%s> NOTIFY=failure\r\n'
  73. else:
  74. rcpt_form = 'RCPT TO: <%s>\r\n'
  75. if type(to) == types.StringType:
  76. self._sock.send(rcpt_form % to)
  77. self.getresp()
  78. valid_recipients.append(rcpt_form % to)
  79. else:
  80. for item in to:
  81. self._sock.send(rcpt_form % item)
  82. lastresp = self.getresp(impunity=1)
  83. if type(lastresp) == type(""):
  84. # XXX klm 07/23/1998 Invalid recipients on local host
  85. # are a special problem, since they are recognized
  86. # and refused immediately by, eg, sendmail, rather than
  87. # being queued. So we do not get the benefit of a
  88. # bounce message for, eg, disabling the recipient, and
  89. # it would knock out delivery to other recipients if we
  90. # didn't take this precaution.
  91. valid_recipients.append(item)
  92. if not valid_recipients:
  93. return
  94. self._sock.send('DATA\r\n')
  95. self.getresp()
  96. if headers:
  97. for line in hlines:
  98. self._sock.send(line + '\r\n')
  99. self._sock.send('\r\n')
  100. for line in lines:
  101. if line == '.': line = '..'
  102. self._sock.send(line + '\r\n')
  103. self._sock.send('.\r\n')
  104. self.getresp()
  105. # Private crap from here down.
  106. def getline(self):
  107. line = self._file.readline()
  108. if not line: raise EOFError
  109. if line[-2:] == CRLF: line = line[:-2]
  110. elif line[-1:] in CRLF: line = line[:-1]
  111. return line
  112. # Internal: get a response from the server, which may possibly
  113. # consist of multiple lines. Return a single string with no
  114. # trailing CRLF. If the response consists of multiple lines,
  115. # these are separated by '\n' characters in the string
  116. def getmultiline(self):
  117. line = self.getline()
  118. if line[3:4] == '-':
  119. code = line[:3]
  120. while 1:
  121. nextline = self.getline()
  122. line = line + ('\n' + nextline)
  123. if nextline[:3] == code and \
  124. nextline[3:4] <> '-':
  125. break
  126. return line
  127. def getresp(self, impunity=0):
  128. """Get response, raising suitable exception for errors.
  129. If option 'impunity' is set, on failure the exception and message
  130. that would have been raised are instead returned."""
  131. resp = self.getmultiline()
  132. self.lastresp = resp[:3]
  133. c = resp[:1]
  134. bad = None
  135. if c == '4':
  136. bad = error_temp
  137. if c == '5':
  138. bad = error_perm
  139. if c not in '123':
  140. bad = error_proto
  141. if bad:
  142. if impunity:
  143. return bad, resp
  144. else:
  145. raise bad, resp
  146. return resp