programming:python:smtprelaytest
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | programming:python:smtprelaytest [2008/01/07 17:56] (current) – created crustymonkey | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== SMTPRelayTest ====== | ||
+ | ===== What is it? ===== | ||
+ | '' | ||
+ | |||
+ | ===== The Script ===== | ||
+ | You can either download it using {{programming: | ||
+ | <code python> | ||
+ | # | ||
+ | |||
+ | __cvsversion__ = '$Id: smtprelaytest.py, | ||
+ | __author__ = 'Jay Deiman' | ||
+ | |||
+ | import smtplib , os , sys , getopt , socket , re | ||
+ | |||
+ | class TerminalController: | ||
+ | """ | ||
+ | Author of the TerminalController class: Edward Loper | ||
+ | Code copied from: http:// | ||
+ | | ||
+ | A class that can be used to portably generate formatted output to | ||
+ | a terminal. | ||
+ | | ||
+ | `TerminalController` defines a set of instance variables whose | ||
+ | values are initialized to the control sequence necessary to | ||
+ | perform a given action. | ||
+ | output to the terminal: | ||
+ | |||
+ | >>> | ||
+ | >>> | ||
+ | |||
+ | Alternatively, | ||
+ | ' | ||
+ | |||
+ | >>> | ||
+ | >>> | ||
+ | |||
+ | If the terminal doesn' | ||
+ | the corresponding instance variable will be set to '' | ||
+ | result, the above code will still work on terminals that do not | ||
+ | support color, except that their output will not be colored. | ||
+ | Also, this means that you can test whether the terminal supports a | ||
+ | given action by simply testing the truth value of the | ||
+ | corresponding instance variable: | ||
+ | |||
+ | >>> | ||
+ | >>> | ||
+ | ... print 'This terminal supports clearning the screen.' | ||
+ | |||
+ | Finally, if the width and height of the terminal are known, then | ||
+ | they will be stored in the `COLS` and `LINES` attributes. | ||
+ | """ | ||
+ | # Cursor movement: | ||
+ | BOL = '' | ||
+ | UP = '' | ||
+ | DOWN = '' | ||
+ | LEFT = '' | ||
+ | RIGHT = '' | ||
+ | |||
+ | # Deletion: | ||
+ | CLEAR_SCREEN = '' | ||
+ | CLEAR_EOL = '' | ||
+ | CLEAR_BOL = '' | ||
+ | CLEAR_EOS = '' | ||
+ | |||
+ | # Output modes: | ||
+ | BOLD = '' | ||
+ | BLINK = '' | ||
+ | DIM = '' | ||
+ | REVERSE = '' | ||
+ | NORMAL = '' | ||
+ | |||
+ | # Cursor display: | ||
+ | HIDE_CURSOR = '' | ||
+ | SHOW_CURSOR = '' | ||
+ | |||
+ | # Terminal size: | ||
+ | COLS = None #: Width of the terminal (None for unknown) | ||
+ | LINES = None #: Height of the terminal (None for unknown) | ||
+ | |||
+ | # Foreground colors: | ||
+ | BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' | ||
+ | | ||
+ | # Background colors: | ||
+ | BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' | ||
+ | BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' | ||
+ | | ||
+ | _STRING_CAPABILITIES = """ | ||
+ | BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 | ||
+ | CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold | ||
+ | BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 | ||
+ | HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""" | ||
+ | _COLORS = """ | ||
+ | _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE" | ||
+ | |||
+ | def __init__(self, | ||
+ | """ | ||
+ | Create a `TerminalController` and initialize its attributes | ||
+ | with appropriate values for the current terminal. | ||
+ | `term_stream` is the stream that will be used for terminal | ||
+ | output; if this stream is not a tty, then the terminal is | ||
+ | assumed to be a dumb terminal (i.e., have no capabilities). | ||
+ | """ | ||
+ | # Curses isn't available on all platforms | ||
+ | try: import curses | ||
+ | except: return | ||
+ | |||
+ | # If the stream isn't a tty, then assume it has no capabilities. | ||
+ | if not term_stream.isatty(): | ||
+ | |||
+ | # Check the terminal type. If we fail, then assume that the | ||
+ | # terminal has no capabilities. | ||
+ | try: curses.setupterm() | ||
+ | except: return | ||
+ | |||
+ | # Look up numeric capabilities. | ||
+ | self.COLS = curses.tigetnum(' | ||
+ | self.LINES = curses.tigetnum(' | ||
+ | | ||
+ | # Look up string capabilities. | ||
+ | for capability in self._STRING_CAPABILITIES: | ||
+ | (attrib, cap_name) = capability.split(' | ||
+ | setattr(self, | ||
+ | |||
+ | # Colors | ||
+ | set_fg = self._tigetstr(' | ||
+ | if set_fg: | ||
+ | for i,color in zip(range(len(self._COLORS)), | ||
+ | setattr(self, | ||
+ | set_fg_ansi = self._tigetstr(' | ||
+ | if set_fg_ansi: | ||
+ | for i,color in zip(range(len(self._ANSICOLORS)), | ||
+ | setattr(self, | ||
+ | set_bg = self._tigetstr(' | ||
+ | if set_bg: | ||
+ | for i,color in zip(range(len(self._COLORS)), | ||
+ | setattr(self, | ||
+ | set_bg_ansi = self._tigetstr(' | ||
+ | if set_bg_ansi: | ||
+ | for i,color in zip(range(len(self._ANSICOLORS)), | ||
+ | setattr(self, | ||
+ | |||
+ | def _tigetstr(self, | ||
+ | # String capabilities can include " | ||
+ | # For any modern terminal, we should be able to just ignore | ||
+ | # these, so strip them out. | ||
+ | import curses | ||
+ | cap = curses.tigetstr(cap_name) or '' | ||
+ | return re.sub(r' | ||
+ | |||
+ | def render(self, | ||
+ | """ | ||
+ | Replace each $-substitutions in the given template string with | ||
+ | the corresponding terminal control string (if it's defined) or | ||
+ | '' | ||
+ | """ | ||
+ | return re.sub(r' | ||
+ | |||
+ | def _render_sub(self, | ||
+ | s = match.group() | ||
+ | if s == ' | ||
+ | else: return getattr(self, | ||
+ | |||
+ | # Functions | ||
+ | def getHostName(): | ||
+ | fqdn = re.compile(r' | ||
+ | res = socket.gethostbyaddr(' | ||
+ | if fqdn.match(res[0]): | ||
+ | return res[0] | ||
+ | for name in res[1]: | ||
+ | if fqdn.match(name): | ||
+ | return name | ||
+ | |||
+ | def usage(exitCode=0): | ||
+ | print '%s -r <remote host> [-p < | ||
+ | '-c < | ||
+ | print """ | ||
+ | -h, | ||
+ | -s, | ||
+ | -r, | ||
+ | -p, | ||
+ | -t, | ||
+ | the hostname if not supplied | ||
+ | -c, | ||
+ | -f, | ||
+ | | ||
+ | """ | ||
+ | sys.exit(exitCode) | ||
+ | | ||
+ | def gAndR(code): | ||
+ | code = int(code) | ||
+ | if code >= 200 and code < 300: | ||
+ | return " | ||
+ | else: | ||
+ | return " | ||
+ | | ||
+ | # Config vars | ||
+ | remoteHost = '' | ||
+ | remotePort = 25 | ||
+ | heloHost = getHostName() | ||
+ | mRecip = '' | ||
+ | mFrom = ' | ||
+ | useTls = False | ||
+ | |||
+ | # Get the command line opts | ||
+ | shortOpts = ' | ||
+ | longOpts = [' | ||
+ | ' | ||
+ | try: | ||
+ | optList , junk = getopt.getopt(sys.argv[1: | ||
+ | except getopt.GetoptError , e: | ||
+ | print e | ||
+ | usage(1) | ||
+ | for opt , val in optList: | ||
+ | if opt in (' | ||
+ | usage() | ||
+ | elif opt in (' | ||
+ | useTls = True | ||
+ | elif opt in (' | ||
+ | remoteHost = val | ||
+ | elif opt in (' | ||
+ | remotePort = int(val) | ||
+ | elif opt in (' | ||
+ | heloHost = val | ||
+ | elif opt in (' | ||
+ | mRecip = val | ||
+ | elif opt in (' | ||
+ | mFrom = val | ||
+ | # If we don't have a remoteHost or recipient, usage and exit | ||
+ | if not remoteHost or not mRecip: | ||
+ | usage(1) | ||
+ | |||
+ | # Now, establish the connection and try the relay | ||
+ | t = TerminalController() | ||
+ | s = smtplib.SMTP() | ||
+ | print ' | ||
+ | resp = s.connect(remoteHost , remotePort) | ||
+ | print t.render(' | ||
+ | print ' | ||
+ | resp = s.helo(heloHost) | ||
+ | print t.render(' | ||
+ | if useTls: | ||
+ | print ' | ||
+ | resp = s.starttls() | ||
+ | print t.render(' | ||
+ | mailFrom = 'MAIL FROM: < | ||
+ | print ' | ||
+ | s.send(mailFrom) | ||
+ | resp = s.getreply() | ||
+ | print t.render(' | ||
+ | mailTo = 'RCPT TO: < | ||
+ | print ' | ||
+ | s.send(mailTo) | ||
+ | resp = s.getreply() | ||
+ | print t.render(' | ||
+ | print ' | ||
+ | s.send(' | ||
+ | resp = s.getreply() | ||
+ | print t.render(' | ||
+ | s.close() | ||
+ | </ | ||
+ | |||
+ | ===== Usage ===== | ||
+ | Using '' | ||
+ | < | ||
+ | $ ./ | ||
+ | smtprelaytest.py -r <remote host> [-p < | ||
+ | |||
+ | -h, | ||
+ | -s, | ||
+ | -r, | ||
+ | -p, | ||
+ | -t, | ||
+ | the hostname if not supplied | ||
+ | -c, | ||
+ | -f, | ||
+ | | ||
+ | </ | ||
+ | |||
+ | A typical lookup, with TLS support would look like this: | ||
+ | < | ||
+ | # ./ | ||
+ | Connecting to 127.0.0.1: | ||
+ | 220 mail.splitstreams.com ESMTP Postfix | ||
+ | HELOing as localhost.splitstreams.com | ||
+ | 250 mail.splitstreams.com | ||
+ | Starting a TLS connection | ||
+ | 220 2.0.0 Ready to start TLS | ||
+ | Sending: MAIL FROM: < | ||
+ | 250 2.1.0 Ok | ||
+ | Sending: RCPT TO: < | ||
+ | 250 2.1.5 Ok | ||
+ | Sending: QUIT | ||
+ | 221 2.0.0 Bye | ||
+ | </ |
programming/python/smtprelaytest.txt · Last modified: 2008/01/07 17:56 by crustymonkey