1edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep"""RFC 2822 message manipulation. 2edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 3edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepNote: This is only a very rough sketch of a full RFC-822 parser; in particular 4edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepthe tokenizing of addresses does not adhere to all the quoting rules. 5edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 6edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepNote: RFC 2822 is a long awaited update to RFC 822. This module should 7edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepconform to RFC 2822, and is thus mis-named (it's not worth renaming it). Some 8edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepeffort at RFC 2822 updates have been made, but a thorough audit has not been 9edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepperformed. Consider any RFC 2822 non-conformance to be a bug. 10edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 11edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep RFC 2822: http://www.faqs.org/rfcs/rfc2822.html 12edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep RFC 822 : http://www.faqs.org/rfcs/rfc822.html (obsolete) 13edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 14edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepDirections for use: 15edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 16edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepTo create a Message object: first open a file, e.g.: 17edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 18edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep fp = open(file, 'r') 19edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 20edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepYou can use any other legal way of getting an open file object, e.g. use 21edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepsys.stdin or call os.popen(). Then pass the open file object to the Message() 22edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepconstructor: 23edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 24edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep m = Message(fp) 25edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 26edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepThis class can work with any input object that supports a readline method. If 27edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepthe input object has seek and tell capability, the rewindbody method will 28edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepwork; also illegal lines will be pushed back onto the input stream. If the 29edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepinput object lacks seek but has an `unread' method that can push back a line 30edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepof input, Message will use that to push back illegal lines. Thus this class 31edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepcan be used to parse messages coming from a buffered stream. 32edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 33edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepThe optional `seekable' argument is provided as a workaround for certain stdio 34edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoeplibraries in which tell() discards buffered data before discovering that the 35edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoeplseek() system call doesn't work. For maximum portability, you should set the 36edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepseekable argument to zero to prevent that initial \code{tell} when passing in 37edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepan unseekable object such as a file object created from a socket object. If 38edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepit is 1 on entry -- which it is by default -- the tell() method of the open 39edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepfile object is called once; if this raises an exception, seekable is reset to 40edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep0. For other nonzero values of seekable, this test is not made. 41edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 42edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepTo get the text of a particular header there are several methods: 43edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 44edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep str = m.getheader(name) 45edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep str = m.getrawheader(name) 46edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 47edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepwhere name is the name of the header, e.g. 'Subject'. The difference is that 48edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepgetheader() strips the leading and trailing whitespace, while getrawheader() 49edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdoesn't. Both functions retain embedded whitespace (including newlines) 50edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepexactly as they are specified in the header, and leave the case of the text 51edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepunchanged. 52edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 53edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepFor addresses and address lists there are functions 54edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 55edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep realname, mailaddress = m.getaddr(name) 56edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep list = m.getaddrlist(name) 57edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 58edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepwhere the latter returns a list of (realname, mailaddr) tuples. 59edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 60edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepThere is also a method 61edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 62edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep time = m.getdate(name) 63edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 64edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepwhich parses a Date-like field and returns a time-compatible tuple, 65edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepi.e. a tuple such as returned by time.localtime() or accepted by 66edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoeptime.mktime(). 67edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 68edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepSee the class definition for lower level access methods. 69edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 70edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander StoepThere are also some utility functions here. 71edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep""" 72edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# Cleanup and extensions by Eric S. Raymond <esr@thyrsus.com> 73edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 74edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepimport time 75edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 76edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepfrom warnings import warnpy3k 77edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepwarnpy3k("in 3.x, rfc822 has been removed in favor of the email package", 78edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep stacklevel=2) 79edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 80edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep__all__ = ["Message","AddressList","parsedate","parsedate_tz","mktime_tz"] 81edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 82edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep_blanklines = ('\r\n', '\n') # Optimization for islast() 83edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 84edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 85edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepclass Message: 86edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Represents a single RFC 2822-compliant message.""" 87edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 88edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __init__(self, fp, seekable = 1): 89edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Initialize the class instance and read the headers.""" 90edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if seekable == 1: 91edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Exercise tell() to make sure it works 92edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # (and then assume seek() works, too) 93edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 94edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep fp.tell() 95edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except (AttributeError, IOError): 96edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep seekable = 0 97edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.fp = fp 98edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.seekable = seekable 99edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.startofheaders = None 100edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.startofbody = None 101edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # 102edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.seekable: 103edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 104edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.startofheaders = self.fp.tell() 105edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except IOError: 106edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.seekable = 0 107edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # 108edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.readheaders() 109edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # 110edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.seekable: 111edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 112edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.startofbody = self.fp.tell() 113edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except IOError: 114edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.seekable = 0 115edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 116edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def rewindbody(self): 117edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Rewind the file to the start of the body (if seekable).""" 118edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not self.seekable: 119edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep raise IOError, "unseekable file" 120edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.fp.seek(self.startofbody) 121edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 122edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def readheaders(self): 123edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Read header lines. 124edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 125edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Read header lines up to the entirely blank line that terminates them. 126edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep The (normally blank) line that ends the headers is skipped, but not 127edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep included in the returned list. If a non-header line ends the headers, 128edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep (which is an error), an attempt is made to backspace over it; it is 129edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep never included in the returned list. 130edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 131edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep The variable self.status is set to the empty string if all went well, 132edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep otherwise it is an error message. The variable self.headers is a 133edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep completely uninterpreted list of lines contained in the header (so 134edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep printing them will reproduce the header exactly as it appears in the 135edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep file). 136edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 137edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.dict = {} 138edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.unixfrom = '' 139edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.headers = lst = [] 140edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.status = '' 141edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep headerseen = "" 142edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep firstline = 1 143edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep startofline = unread = tell = None 144edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if hasattr(self.fp, 'unread'): 145edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep unread = self.fp.unread 146edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.seekable: 147edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tell = self.fp.tell 148edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while 1: 149edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if tell: 150edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 151edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep startofline = tell() 152edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except IOError: 153edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep startofline = tell = None 154edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.seekable = 0 155edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep line = self.fp.readline() 156edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not line: 157edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.status = 'EOF in headers' 158edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 159edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Skip unix From name time lines 160edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if firstline and line.startswith('From '): 161edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.unixfrom = self.unixfrom + line 162edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep continue 163edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep firstline = 0 164edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if headerseen and line[0] in ' \t': 165edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # It's a continuation line. 166edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst.append(line) 167edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep x = (self.dict[headerseen] + "\n " + line.strip()) 168edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.dict[headerseen] = x.strip() 169edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep continue 170edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.iscomment(line): 171edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # It's a comment. Ignore it. 172edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep continue 173edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.islast(line): 174edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Note! No pushback here! The delimiter line gets eaten. 175edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 176edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep headerseen = self.isheader(line) 177edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if headerseen: 178edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # It's a legal header line, save it. 179edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst.append(line) 180edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.dict[headerseen] = line[len(headerseen)+1:].strip() 181edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep continue 182edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 183edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # It's not a header line; throw it back and stop here. 184edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not self.dict: 185edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.status = 'No headers' 186edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 187edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.status = 'Non-header line where header expected' 188edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Try to undo the read. 189edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if unread: 190edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep unread(line) 191edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif tell: 192edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.fp.seek(startofline) 193edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 194edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.status = self.status + '; bad seek' 195edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 196edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 197edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def isheader(self, line): 198edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Determine whether a given line is a legal header. 199edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 200edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep This method should return the header name, suitably canonicalized. 201edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep You may override this method in order to use Message parsing on tagged 202edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data in RFC 2822-like formats with special header formats. 203edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 204edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep i = line.find(':') 205edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if i > 0: 206edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return line[:i].lower() 207edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 208edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 209edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def islast(self, line): 210edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Determine whether a line is a legal end of RFC 2822 headers. 211edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 212edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep You may override this method if your application wants to bend the 213edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep rules, e.g. to strip trailing whitespace, or to recognize MH template 214edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep separators ('--------'). For convenience (e.g. for code reading from 215edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep sockets) a line consisting of \\r\\n also matches. 216edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 217edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return line in _blanklines 218edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 219edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def iscomment(self, line): 220edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Determine whether a line should be skipped entirely. 221edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 222edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep You may override this method in order to use Message parsing on tagged 223edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data in RFC 2822-like formats that support embedded comments or 224edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep free-text data. 225edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 226edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return False 227edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 228edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getallmatchingheaders(self, name): 229edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Find all header lines matching a given header name. 230edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 231edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Look through the list of headers and find all lines matching a given 232edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep header name (and their continuation lines). A list of the lines is 233edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returned, without interpretation. If the header does not occur, an 234edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep empty list is returned. If the header occurs multiple times, all 235edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep occurrences are returned. Case is not important in the header name. 236edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 237edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep name = name.lower() + ':' 238edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep n = len(name) 239edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst = [] 240edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 0 241edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for line in self.headers: 242edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if line[:n].lower() == name: 243edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 1 244edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif not line[:1].isspace(): 245edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 0 246edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if hit: 247edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst.append(line) 248edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return lst 249edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 250edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getfirstmatchingheader(self, name): 251edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get the first header line matching name. 252edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 253edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep This is similar to getallmatchingheaders, but it returns only the 254edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep first matching header (and its continuation lines). 255edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 256edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep name = name.lower() + ':' 257edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep n = len(name) 258edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst = [] 259edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 0 260edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for line in self.headers: 261edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if hit: 262edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not line[:1].isspace(): 263edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 264edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif line[:n].lower() == name: 265edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 1 266edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if hit: 267edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst.append(line) 268edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return lst 269edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 270edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getrawheader(self, name): 271edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """A higher-level interface to getfirstmatchingheader(). 272edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 273edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Return a string containing the literal text of the header but with the 274edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep keyword stripped. All leading, trailing and embedded whitespace is 275edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep kept in the string, however. Return None if the header does not 276edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep occur. 277edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 278edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 279edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst = self.getfirstmatchingheader(name) 280edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not lst: 281edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 282edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst[0] = lst[0][len(name) + 1:] 283edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ''.join(lst) 284edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 285edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getheader(self, name, default=None): 286edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get the header value for a name. 287edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 288edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep This is the normal interface: it returns a stripped version of the 289edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep header value for a given header name, or None if it doesn't exist. 290edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep This uses the dictionary version which finds the *last* such header. 291edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 292edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.dict.get(name.lower(), default) 293edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep get = getheader 294edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 295edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getheaders(self, name): 296edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get all values for a header. 297edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 298edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep This returns a list of values for headers given more than once; each 299edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep value in the result list is stripped in the same way as the result of 300edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep getheader(). If the header is not given, return an empty list. 301edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 302edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep result = [] 303edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep current = '' 304edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep have_header = 0 305edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for s in self.getallmatchingheaders(name): 306edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if s[0].isspace(): 307edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if current: 308edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep current = "%s\n %s" % (current, s.strip()) 309edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 310edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep current = s.strip() 311edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 312edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if have_header: 313edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep result.append(current) 314edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep current = s[s.find(":") + 1:].strip() 315edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep have_header = 1 316edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if have_header: 317edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep result.append(current) 318edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return result 319edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 320edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getaddr(self, name): 321edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get a single address from a header, as a tuple. 322edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 323edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep An example return value: 324edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep ('Guido van Rossum', 'guido@cwi.nl') 325edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 326edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # New, by Ben Escoto 327edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep alist = self.getaddrlist(name) 328edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if alist: 329edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return alist[0] 330edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 331edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return (None, None) 332edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 333edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getaddrlist(self, name): 334edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get a list of addresses from a header. 335edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 336edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Retrieves a list of addresses from a header, where each address is a 337edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tuple as returned by getaddr(). Scans all named headers, so it works 338edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep properly with multiple To: or Cc: headers for example. 339edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 340edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep raw = [] 341edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for h in self.getallmatchingheaders(name): 342edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if h[0] in ' \t': 343edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep raw.append(h) 344edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 345edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if raw: 346edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep raw.append(', ') 347edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep i = h.find(':') 348edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if i > 0: 349edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep addr = h[i+1:] 350edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep raw.append(addr) 351edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep alladdrs = ''.join(raw) 352edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep a = AddressList(alladdrs) 353edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return a.addresslist 354edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 355edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getdate(self, name): 356edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Retrieve a date field from a header. 357edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 358edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Retrieves a date field from the named header, returning a tuple 359edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep compatible with time.mktime(). 360edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 361edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 362edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data = self[name] 363edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except KeyError: 364edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 365edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return parsedate(data) 366edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 367edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getdate_tz(self, name): 368edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Retrieve a date field from a header as a 10-tuple. 369edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 370edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep The first 9 elements make up a tuple compatible with time.mktime(), 371edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep and the 10th is the offset of the poster's time zone from GMT/UTC. 372edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 373edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 374edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data = self[name] 375edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except KeyError: 376edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 377edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return parsedate_tz(data) 378edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 379edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 380edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Access as a dictionary (only finds *last* header of each type): 381edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 382edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __len__(self): 383edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get the number of headers in a message.""" 384edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return len(self.dict) 385edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 386edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __getitem__(self, name): 387edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get a specific header, as from a dictionary.""" 388edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.dict[name.lower()] 389edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 390edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __setitem__(self, name, value): 391edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Set the value of a header. 392edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 393edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Note: This is not a perfect inversion of __getitem__, because any 394edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep changed headers get stuck at the end of the raw-headers list rather 395edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep than where the altered header was. 396edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 397edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep del self[name] # Won't fail if it doesn't exist 398edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.dict[name.lower()] = value 399edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep text = name + ": " + value 400edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for line in text.split("\n"): 401edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.headers.append(line + "\n") 402edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 403edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __delitem__(self, name): 404edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Delete all occurrences of a specific header, if it is present.""" 405edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep name = name.lower() 406edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not name in self.dict: 407edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return 408edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep del self.dict[name] 409edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep name = name + ':' 410edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep n = len(name) 411edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst = [] 412edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 0 413edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for i in range(len(self.headers)): 414edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep line = self.headers[i] 415edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if line[:n].lower() == name: 416edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 1 417edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif not line[:1].isspace(): 418edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hit = 0 419edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if hit: 420edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst.append(i) 421edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for i in reversed(lst): 422edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep del self.headers[i] 423edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 424edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def setdefault(self, name, default=""): 425edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lowername = name.lower() 426edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if lowername in self.dict: 427edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.dict[lowername] 428edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 429edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep text = name + ": " + default 430edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for line in text.split("\n"): 431edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.headers.append(line + "\n") 432edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.dict[lowername] = default 433edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return default 434edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 435edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def has_key(self, name): 436edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Determine whether a message contains the named header.""" 437edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return name.lower() in self.dict 438edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 439edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __contains__(self, name): 440edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Determine whether a message contains the named header.""" 441edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return name.lower() in self.dict 442edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 443edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __iter__(self): 444edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return iter(self.dict) 445edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 446edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def keys(self): 447edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get all of a message's header field names.""" 448edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.dict.keys() 449edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 450edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def values(self): 451edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get all of a message's header field values.""" 452edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.dict.values() 453edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 454edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def items(self): 455edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get all of a message's headers. 456edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 457edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Returns a list of name, value tuples. 458edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 459edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.dict.items() 460edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 461edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __str__(self): 462edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ''.join(self.headers) 463edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 464edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 465edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# Utility functions 466edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# ----------------- 467edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 468edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# XXX Should fix unquote() and quote() to be really conformant. 469edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# XXX The inverses of the parse functions may also be useful. 470edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 471edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 472edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef unquote(s): 473edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Remove quotes from a string.""" 474edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if len(s) > 1: 475edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if s.startswith('"') and s.endswith('"'): 476edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return s[1:-1].replace('\\\\', '\\').replace('\\"', '"') 477edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if s.startswith('<') and s.endswith('>'): 478edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return s[1:-1] 479edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return s 480edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 481edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 482edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef quote(s): 483edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Add quotes around a string.""" 484edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return s.replace('\\', '\\\\').replace('"', '\\"') 485edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 486edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 487edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef parseaddr(address): 488edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse an address into a (realname, mailaddr) tuple.""" 489edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep a = AddressList(address) 490edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep lst = a.addresslist 491edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not lst: 492edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return (None, None) 493edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return lst[0] 494edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 495edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 496edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepclass AddrlistClass: 497edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Address parser class by Ben Escoto. 498edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 499edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep To understand what this class does, it helps to have a copy of 500edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep RFC 2822 in front of you. 501edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 502edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep http://www.faqs.org/rfcs/rfc2822.html 503edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 504edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Note: this class interface is deprecated and may be removed in the future. 505edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Use rfc822.AddressList instead. 506edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 507edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 508edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __init__(self, field): 509edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Initialize a new instance. 510edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 511edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep `field' is an unparsed address header field, containing one or more 512edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep addresses. 513edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 514edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.specials = '()<>@,:;.\"[]' 515edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos = 0 516edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.LWS = ' \t' 517edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.CR = '\r\n' 518edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.atomends = self.specials + self.LWS + self.CR 519edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it 520edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # is obsolete syntax. RFC 2822 requires that we recognize obsolete 521edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # syntax, so allow dots in phrases. 522edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.phraseends = self.atomends.replace('.', '') 523edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.field = field 524edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.commentlist = [] 525edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 526edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def gotonext(self): 527edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse up to the start of the next address.""" 528edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 529edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.field[self.pos] in self.LWS + '\n\r': 530edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos = self.pos + 1 531edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '(': 532edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.commentlist.append(self.getcomment()) 533edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: break 534edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 535edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getaddrlist(self): 536edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse all addresses. 537edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 538edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Returns a list containing all of the addresses. 539edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 540edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep result = [] 541edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep ad = self.getaddress() 542edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while ad: 543edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep result += ad 544edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep ad = self.getaddress() 545edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return result 546edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 547edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getaddress(self): 548edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse the next address.""" 549edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.commentlist = [] 550edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 551edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 552edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep oldpos = self.pos 553edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep oldcl = self.commentlist 554edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep plist = self.getphraselist() 555edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 556edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 557edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returnlist = [] 558edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 559edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.pos >= len(self.field): 560edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Bad email address technically, no domain. 561edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if plist: 562edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returnlist = [(' '.join(self.commentlist), plist[0])] 563edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 564edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] in '.@': 565edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # email address is just an addrspec 566edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # this isn't very efficient since we start over 567edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos = oldpos 568edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.commentlist = oldcl 569edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep addrspec = self.getaddrspec() 570edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returnlist = [(' '.join(self.commentlist), addrspec)] 571edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 572edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == ':': 573edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # address is a group 574edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returnlist = [] 575edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 576edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep fieldlen = len(self.field) 577edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 578edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 579edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 580edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.pos < fieldlen and self.field[self.pos] == ';': 581edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 582edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 583edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returnlist = returnlist + self.getaddress() 584edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 585edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '<': 586edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Address is a phrase then a route addr 587edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep routeaddr = self.getrouteaddr() 588edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 589edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.commentlist: 590edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returnlist = [(' '.join(plist) + ' (' + \ 591edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep ' '.join(self.commentlist) + ')', routeaddr)] 592edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: returnlist = [(' '.join(plist), routeaddr)] 593edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 594edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 595edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if plist: 596edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep returnlist = [(' '.join(self.commentlist), plist[0])] 597edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] in self.specials: 598edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 599edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 600edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 601edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.pos < len(self.field) and self.field[self.pos] == ',': 602edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 603edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return returnlist 604edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 605edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getrouteaddr(self): 606edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse a route address (Return-path value). 607edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 608edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep This method just skips all the route stuff and returns the addrspec. 609edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 610edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.field[self.pos] != '<': 611edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return 612edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 613edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep expectroute = 0 614edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 615edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 616edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep adlist = "" 617edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 618edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if expectroute: 619edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.getdomain() 620edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep expectroute = 0 621edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '>': 622edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 623edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 624edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '@': 625edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 626edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep expectroute = 1 627edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == ':': 628edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 629edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 630edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep adlist = self.getaddrspec() 631edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 632edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 633edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 634edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 635edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return adlist 636edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 637edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getaddrspec(self): 638edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse an RFC 2822 addr-spec.""" 639edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep aslist = [] 640edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 641edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 642edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 643edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.field[self.pos] == '.': 644edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep aslist.append('.') 645edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 646edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '"': 647edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep aslist.append('"%s"' % self.getquote()) 648edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] in self.atomends: 649edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 650edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: aslist.append(self.getatom()) 651edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 652edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 653edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.pos >= len(self.field) or self.field[self.pos] != '@': 654edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ''.join(aslist) 655edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 656edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep aslist.append('@') 657edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 658edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.gotonext() 659edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ''.join(aslist) + self.getdomain() 660edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 661edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getdomain(self): 662edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get the complete domain name from an address.""" 663edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep sdlist = [] 664edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 665edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.field[self.pos] in self.LWS: 666edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 667edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '(': 668edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.commentlist.append(self.getcomment()) 669edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '[': 670edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep sdlist.append(self.getdomainliteral()) 671edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '.': 672edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 673edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep sdlist.append('.') 674edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] in self.atomends: 675edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 676edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: sdlist.append(self.getatom()) 677edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ''.join(sdlist) 678edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 679edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getdelimited(self, beginchar, endchars, allowcomments = 1): 680edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse a header fragment delimited by special characters. 681edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 682edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep `beginchar' is the start character for the fragment. If self is not 683edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep looking at an instance of `beginchar' then getdelimited returns the 684edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep empty string. 685edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 686edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep `endchars' is a sequence of allowable end-delimiting characters. 687edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Parsing stops when one of these is encountered. 688edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 689edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed 690edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep within the parsed fragment. 691edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 692edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.field[self.pos] != beginchar: 693edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return '' 694edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 695edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep slist = [''] 696edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep quote = 0 697edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 698edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 699edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if quote == 1: 700edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep slist.append(self.field[self.pos]) 701edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep quote = 0 702edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] in endchars: 703edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 704edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 705edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif allowcomments and self.field[self.pos] == '(': 706edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep slist.append(self.getcomment()) 707edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep continue # have already advanced pos from getcomment 708edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '\\': 709edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep quote = 1 710edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 711edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep slist.append(self.field[self.pos]) 712edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 713edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 714edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ''.join(slist) 715edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 716edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getquote(self): 717edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get a quote-delimited fragment from self's field.""" 718edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.getdelimited('"', '"\r', 0) 719edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 720edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getcomment(self): 721edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Get a parenthesis-delimited fragment from self's field.""" 722edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.getdelimited('(', ')\r', 1) 723edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 724edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getdomainliteral(self): 725edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse an RFC 2822 domain-literal.""" 726edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return '[%s]' % self.getdelimited('[', ']\r', 0) 727edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 728edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getatom(self, atomends=None): 729edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse an RFC 2822 atom. 730edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 731edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Optional atomends specifies a different set of end token delimiters 732edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep (the default is to use self.atomends). This is used e.g. in 733edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep getphraselist() since phrase endings must not include the `.' (which 734edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep is legal in phrases).""" 735edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep atomlist = [''] 736edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if atomends is None: 737edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep atomends = self.atomends 738edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 739edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 740edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.field[self.pos] in atomends: 741edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 742edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: atomlist.append(self.field[self.pos]) 743edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 744edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 745edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ''.join(atomlist) 746edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 747edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def getphraselist(self): 748edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Parse a sequence of RFC 2822 phrases. 749edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 750edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep A phrase is a sequence of words, which are in turn either RFC 2822 751edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep atoms or quoted-strings. Phrases are canonicalized by squeezing all 752edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep runs of continuous whitespace into one space. 753edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 754edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep plist = [] 755edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 756edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while self.pos < len(self.field): 757edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if self.field[self.pos] in self.LWS: 758edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.pos += 1 759edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '"': 760edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep plist.append(self.getquote()) 761edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] == '(': 762edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.commentlist.append(self.getcomment()) 763edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif self.field[self.pos] in self.phraseends: 764edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep break 765edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 766edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep plist.append(self.getatom(self.phraseends)) 767edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 768edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return plist 769edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 770edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepclass AddressList(AddrlistClass): 771edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """An AddressList encapsulates a list of parsed RFC 2822 addresses.""" 772edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __init__(self, field): 773edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep AddrlistClass.__init__(self, field) 774edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if field: 775edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.addresslist = self.getaddrlist() 776edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 777edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.addresslist = [] 778edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 779edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __len__(self): 780edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return len(self.addresslist) 781edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 782edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __str__(self): 783edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return ", ".join(map(dump_address_pair, self.addresslist)) 784edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 785edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __add__(self, other): 786edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Set union 787edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep newaddr = AddressList(None) 788edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep newaddr.addresslist = self.addresslist[:] 789edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for x in other.addresslist: 790edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not x in self.addresslist: 791edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep newaddr.addresslist.append(x) 792edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return newaddr 793edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 794edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __iadd__(self, other): 795edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Set union, in-place 796edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for x in other.addresslist: 797edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not x in self.addresslist: 798edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.addresslist.append(x) 799edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self 800edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 801edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __sub__(self, other): 802edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Set difference 803edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep newaddr = AddressList(None) 804edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for x in self.addresslist: 805edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not x in other.addresslist: 806edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep newaddr.addresslist.append(x) 807edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return newaddr 808edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 809edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __isub__(self, other): 810edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Set difference, in-place 811edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep for x in other.addresslist: 812edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if x in self.addresslist: 813edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep self.addresslist.remove(x) 814edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self 815edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 816edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep def __getitem__(self, index): 817edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Make indexing, slices, and 'in' work 818edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return self.addresslist[index] 819edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 820edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef dump_address_pair(pair): 821edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Dump a (name, address) pair in a canonicalized form.""" 822edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if pair[0]: 823edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return '"' + pair[0] + '" <' + pair[1] + '>' 824edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 825edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return pair[1] 826edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 827edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# Parse a date field 828edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 829edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 830edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'aug', 'sep', 'oct', 'nov', 'dec', 831edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'january', 'february', 'march', 'april', 'may', 'june', 'july', 832edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'august', 'september', 'october', 'november', 'december'] 833edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] 834edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 835edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# The timezone table does not include the military time zones defined 836edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# in RFC822, other than Z. According to RFC1123, the description in 837edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# RFC822 gets the signs wrong, so we can't rely on any such time 838edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# zones. RFC1123 recommends that numeric timezone indicators be used 839edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# instead of timezone names. 840edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 841edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep_timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0, 842edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'AST': -400, 'ADT': -300, # Atlantic (used in Canada) 843edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'EST': -500, 'EDT': -400, # Eastern 844edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'CST': -600, 'CDT': -500, # Central 845edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'MST': -700, 'MDT': -600, # Mountain 846edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 'PST': -800, 'PDT': -700 # Pacific 847edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep } 848edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 849edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 850edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef parsedate_tz(data): 851edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Convert a date string to a time tuple. 852edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 853edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Accounts for military timezones. 854edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 855edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not data: 856edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 857edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data = data.split() 858edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if data[0][-1] in (',', '.') or data[0].lower() in _daynames: 859edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # There's a dayname here. Skip it 860edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep del data[0] 861edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 862edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # no space after the "weekday,"? 863edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep i = data[0].rfind(',') 864edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if i >= 0: 865edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data[0] = data[0][i+1:] 866edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if len(data) == 3: # RFC 850 date, deprecated 867edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep stuff = data[0].split('-') 868edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if len(stuff) == 3: 869edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data = stuff + data[1:] 870edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if len(data) == 4: 871edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep s = data[3] 872edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep i = s.find('+') 873edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if i > 0: 874edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data[3:] = [s[:i], s[i+1:]] 875edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 876edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data.append('') # Dummy tz 877edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if len(data) < 5: 878edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 879edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep data = data[:5] 880edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep [dd, mm, yy, tm, tz] = data 881edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep mm = mm.lower() 882edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not mm in _monthnames: 883edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep dd, mm = mm, dd.lower() 884edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not mm in _monthnames: 885edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 886edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep mm = _monthnames.index(mm)+1 887edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if mm > 12: mm = mm - 12 888edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if dd[-1] == ',': 889edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep dd = dd[:-1] 890edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep i = yy.find(':') 891edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if i > 0: 892edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep yy, tm = tm, yy 893edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if yy[-1] == ',': 894edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep yy = yy[:-1] 895edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if not yy[0].isdigit(): 896edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep yy, tz = tz, yy 897edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if tm[-1] == ',': 898edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tm = tm[:-1] 899edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tm = tm.split(':') 900edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if len(tm) == 2: 901edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep [thh, tmm] = tm 902edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tss = '0' 903edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep elif len(tm) == 3: 904edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep [thh, tmm, tss] = tm 905edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 906edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 907edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 908edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep yy = int(yy) 909edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep dd = int(dd) 910edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep thh = int(thh) 911edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tmm = int(tmm) 912edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tss = int(tss) 913edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except ValueError: 914edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return None 915edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tzoffset = None 916edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tz = tz.upper() 917edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if tz in _timezones: 918edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tzoffset = _timezones[tz] 919edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 920edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep try: 921edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tzoffset = int(tz) 922edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep except ValueError: 923edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep pass 924edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # Convert a timezone offset into seconds ; -0500 -> -18000 925edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if tzoffset: 926edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if tzoffset < 0: 927edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tzsign = -1 928edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tzoffset = -tzoffset 929edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 930edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tzsign = 1 931edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60) 932edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return (yy, mm, dd, thh, tmm, tss, 0, 1, 0, tzoffset) 933edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 934edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 935edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef parsedate(data): 936edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Convert a time string to a time tuple.""" 937edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep t = parsedate_tz(data) 938edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if t is None: 939edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return t 940edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return t[:9] 941edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 942edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 943edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef mktime_tz(data): 944edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Turn a 10-tuple as returned by parsedate_tz() into a UTC timestamp.""" 945edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if data[9] is None: 946edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep # No zone info, so localtime is better assumption than GMT 947edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return time.mktime(data[:8] + (-1,)) 948edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 949edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep t = time.mktime(data[:8] + (0,)) 950edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return t - data[9] - time.timezone 951edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 952edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepdef formatdate(timeval=None): 953edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """Returns time format preferred for Internet standards. 954edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 955edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 956edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 957edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep According to RFC 1123, day and month names must always be in 958edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep English. If not for that, this code could use strftime(). It 959edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep can't because strftime() honors the locale and could generated 960edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep non-English names. 961edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep """ 962edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if timeval is None: 963edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep timeval = time.time() 964edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep timeval = time.gmtime(timeval) 965edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep return "%s, %02d %s %04d %02d:%02d:%02d GMT" % ( 966edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")[timeval[6]], 967edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep timeval[2], 968edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep ("Jan", "Feb", "Mar", "Apr", "May", "Jun", 969edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")[timeval[1]-1], 970edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep timeval[0], timeval[3], timeval[4], timeval[5]) 971edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 972edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 973edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# When used as script, run a small test program. 974edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# The first command line argument must be a filename containing one 975edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep# message in RFC-822 format. 976edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep 977edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoepif __name__ == '__main__': 978edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep import sys, os 979edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep file = os.path.join(os.environ['HOME'], 'Mail/inbox/1') 980edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if sys.argv[1:]: file = sys.argv[1] 981edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep f = open(file, 'r') 982edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep m = Message(f) 983edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'From:', m.getaddr('from') 984edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'To:', m.getaddrlist('to') 985edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'Subject:', m.getheader('subject') 986edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'Date:', m.getheader('date') 987edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep date = m.getdate_tz('date') 988edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep tz = date[-1] 989edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep date = time.localtime(mktime_tz(date)) 990edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if date: 991edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'ParsedDate:', time.asctime(date), 992edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hhmmss = tz 993edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hhmm, ss = divmod(hhmmss, 60) 994edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep hh, mm = divmod(hhmm, 60) 995edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print "%+03d%02d" % (hh, mm), 996edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if ss: print ".%02d" % ss, 997edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 998edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep else: 999edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'ParsedDate:', None 1000edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep m.rewindbody() 1001edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep n = 0 1002edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep while f.readline(): 1003edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep n += 1 1004edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'Lines:', n 1005edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print '-'*70 1006edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'len =', len(m) 1007edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if 'Date' in m: print 'Date =', m['Date'] 1008edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep if 'X-Nonsense' in m: pass 1009edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'keys =', m.keys() 1010edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'values =', m.values() 1011edbb763a2b63074cd468a5d33a17908b2cc0654Jeff Vander Stoep print 'items =', m.items() 1012