14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Copyright (C) 2002-2007 Python Software Foundation 24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Contact: email-sig@python.org 34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""Email address parsing code. 54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmLifted directly from rfc822.py. This should eventually be rewritten. 74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm""" 84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm__all__ = [ 104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'mktime_tz', 114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'parsedate', 124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'parsedate_tz', 134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'quote', 144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ] 154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport time 174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmSPACE = ' ' 194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmEMPTYSTRING = '' 204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCOMMASPACE = ', ' 214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Parse a date field 234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'aug', 'sep', 'oct', 'nov', 'dec', 254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'january', 'february', 'march', 'april', 'may', 'june', 'july', 264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'august', 'september', 'october', 'november', 'december'] 274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] 294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# The timezone table does not include the military time zones defined 314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# in RFC822, other than Z. According to RFC1123, the description in 324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# RFC822 gets the signs wrong, so we can't rely on any such time 334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# zones. RFC1123 recommends that numeric timezone indicators be used 344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# instead of timezone names. 354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0, 374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'AST': -400, 'ADT': -300, # Atlantic (used in Canada) 384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'EST': -500, 'EDT': -400, # Eastern 394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'CST': -600, 'CDT': -500, # Central 404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'MST': -700, 'MDT': -600, # Mountain 414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'PST': -800, 'PDT': -700 # Pacific 424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm } 434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parsedate_tz(data): 464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Convert a date string to a time tuple. 474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Accounts for military timezones. 494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data = data.split() 514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # The FWS after the comma after the day-of-week is optional, so search and 524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # adjust for this. 534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if data[0].endswith(',') or data[0].lower() in _daynames: 544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # There's a dayname here. Skip it 554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm del data[0] 564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = data[0].rfind(',') 584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if i >= 0: 594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data[0] = data[0][i+1:] 604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(data) == 3: # RFC 850 date, deprecated 614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm stuff = data[0].split('-') 624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(stuff) == 3: 634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data = stuff + data[1:] 644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(data) == 4: 654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s = data[3] 664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = s.find('+') 674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if i > 0: 684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data[3:] = [s[:i], s[i+1:]] 694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data.append('') # Dummy tz 714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(data) < 5: 724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data = data[:5] 744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm [dd, mm, yy, tm, tz] = data 754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm mm = mm.lower() 764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if mm not in _monthnames: 774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm dd, mm = mm, dd.lower() 784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if mm not in _monthnames: 794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm mm = _monthnames.index(mm) + 1 814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if mm > 12: 824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm mm -= 12 834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if dd[-1] == ',': 844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm dd = dd[:-1] 854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = yy.find(':') 864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if i > 0: 874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm yy, tm = tm, yy 884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if yy[-1] == ',': 894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm yy = yy[:-1] 904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not yy[0].isdigit(): 914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm yy, tz = tz, yy 924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if tm[-1] == ',': 934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tm = tm[:-1] 944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tm = tm.split(':') 954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(tm) == 2: 964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm [thh, tmm] = tm 974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tss = '0' 984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif len(tm) == 3: 994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm [thh, tmm, tss] = tm 1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm yy = int(yy) 1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm dd = int(dd) 1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm thh = int(thh) 1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tmm = int(tmm) 1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tss = int(tss) 1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except ValueError: 1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Check for a yy specified in two-digit format, then convert it to the 1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # appropriate four-digit format, according to the POSIX standard. RFC 822 1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822) 1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # mandates a 4-digit yy. For more information, see the documentation for 1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # the time module. 1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if yy < 100: 1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # The year is between 1969 and 1999 (inclusive). 1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if yy > 68: 1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm yy += 1900 1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # The year is between 2000 and 2068 (inclusive). 1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm yy += 2000 1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tzoffset = None 1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tz = tz.upper() 1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if tz in _timezones: 1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tzoffset = _timezones[tz] 1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tzoffset = int(tz) 1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except ValueError: 1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm pass 1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Convert a timezone offset into seconds ; -0500 -> -18000 1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if tzoffset: 1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if tzoffset < 0: 1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tzsign = -1 1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tzoffset = -tzoffset 1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tzsign = 1 1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60) 1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Daylight Saving Time flag is set to -1, since DST is unknown. 1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset 1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parsedate(data): 1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Convert a time string to a time tuple.""" 1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm t = parsedate_tz(data) 1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if isinstance(t, tuple): 1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return t[:9] 1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return t 1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef mktime_tz(data): 1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Turn a 10-tuple as returned by parsedate_tz() into a UTC timestamp.""" 1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if data[9] is None: 1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # No zone info, so localtime is better assumption than GMT 1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return time.mktime(data[:8] + (-1,)) 1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm t = time.mktime(data[:8] + (0,)) 1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return t - data[9] - time.timezone 1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef quote(str): 1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Prepare string to be used in a quoted string. 1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Turns backslash and double quote characters into quoted pairs. These 1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm are the only characters that need to be quoted inside a quoted string. 1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Does not add the surrounding double quotes. 1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return str.replace('\\', '\\\\').replace('"', '\\"') 1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass AddrlistClass: 1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Address parser class by Ben Escoto. 1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm To understand what this class does, it helps to have a copy of RFC 2822 in 1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm front of you. 1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Note: this class interface is deprecated and may be removed in the future. 1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Use rfc822.AddressList instead. 1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, field): 1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Initialize a new instance. 1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm `field' is an unparsed address header field, containing 1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm one or more addresses. 1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.specials = '()<>@,:;.\"[]' 1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos = 0 1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.LWS = ' \t' 1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.CR = '\r\n' 1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.FWS = self.LWS + self.CR 1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.atomends = self.specials + self.LWS + self.CR 1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it 1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # is obsolete syntax. RFC 2822 requires that we recognize obsolete 1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # syntax, so allow dots in phrases. 1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.phraseends = self.atomends.replace('.', '') 1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.field = field 1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.commentlist = [] 2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def gotonext(self): 2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse up to the start of the next address.""" 2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.field[self.pos] in self.LWS + '\n\r': 2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '(': 2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.commentlist.append(self.getcomment()) 2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getaddrlist(self): 2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse all addresses. 2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Returns a list containing all of the addresses. 2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm result = [] 2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ad = self.getaddress() 2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if ad: 2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm result += ad 2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm result.append(('', '')) 2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return result 2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getaddress(self): 2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse the next address.""" 2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.commentlist = [] 2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm oldpos = self.pos 2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm oldcl = self.commentlist 2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm plist = self.getphraselist() 2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = [] 2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.pos >= len(self.field): 2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Bad email address technically, no domain. 2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if plist: 2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = [(SPACE.join(self.commentlist), plist[0])] 2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] in '.@': 2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # email address is just an addrspec 2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # this isn't very efficient since we start over 2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos = oldpos 2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.commentlist = oldcl 2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm addrspec = self.getaddrspec() 2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = [(SPACE.join(self.commentlist), addrspec)] 2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == ':': 2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # address is a group 2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = [] 2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fieldlen = len(self.field) 2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.pos < fieldlen and self.field[self.pos] == ';': 2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = returnlist + self.getaddress() 2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '<': 2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Address is a phrase then a route addr 2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm routeaddr = self.getrouteaddr() 2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.commentlist: 2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = [(SPACE.join(plist) + ' (' + 2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ' '.join(self.commentlist) + ')', routeaddr)] 2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = [(SPACE.join(plist), routeaddr)] 2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if plist: 2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm returnlist = [(SPACE.join(self.commentlist), plist[0])] 2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] in self.specials: 2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 2804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.pos < len(self.field) and self.field[self.pos] == ',': 2814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 2824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return returnlist 2834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getrouteaddr(self): 2854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse a route address (Return-path value). 2864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm This method just skips all the route stuff and returns the addrspec. 2884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 2894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.field[self.pos] != '<': 2904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 2914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm expectroute = False 2934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 2944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 2954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm adlist = '' 2964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 2974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if expectroute: 2984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.getdomain() 2994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm expectroute = False 3004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '>': 3014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 3034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '@': 3044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm expectroute = True 3064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == ':': 3074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 3094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm adlist = self.getaddrspec() 3104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 3124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 3134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return adlist 3154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getaddrspec(self): 3174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse an RFC 2822 addr-spec.""" 3184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm aslist = [] 3194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 3214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 3224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.field[self.pos] == '.': 3234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm aslist.append('.') 3244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '"': 3264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm aslist.append('"%s"' % quote(self.getquote())) 3274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] in self.atomends: 3284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 3294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 3304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm aslist.append(self.getatom()) 3314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 3324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.pos >= len(self.field) or self.field[self.pos] != '@': 3344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return EMPTYSTRING.join(aslist) 3354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm aslist.append('@') 3374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.gotonext() 3394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return EMPTYSTRING.join(aslist) + self.getdomain() 3404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getdomain(self): 3424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Get the complete domain name from an address.""" 3434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sdlist = [] 3444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 3454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.field[self.pos] in self.LWS: 3464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '(': 3484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.commentlist.append(self.getcomment()) 3494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '[': 3504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sdlist.append(self.getdomainliteral()) 3514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '.': 3524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sdlist.append('.') 3544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] in self.atomends: 3554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 3564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 3574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sdlist.append(self.getatom()) 3584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return EMPTYSTRING.join(sdlist) 3594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getdelimited(self, beginchar, endchars, allowcomments=True): 3614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse a header fragment delimited by special characters. 3624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm `beginchar' is the start character for the fragment. 3644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm If self is not looking at an instance of `beginchar' then 3654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm getdelimited returns the empty string. 3664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm `endchars' is a sequence of allowable end-delimiting characters. 3684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Parsing stops when one of these is encountered. 3694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed 3714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm within the parsed fragment. 3724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 3734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.field[self.pos] != beginchar: 3744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return '' 3754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm slist = [''] 3774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm quote = False 3784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 3804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if quote: 3814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm slist.append(self.field[self.pos]) 3824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm quote = False 3834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] in endchars: 3844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 3864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif allowcomments and self.field[self.pos] == '(': 3874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm slist.append(self.getcomment()) 3884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue # have already advanced pos from getcomment 3894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '\\': 3904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm quote = True 3914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 3924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm slist.append(self.field[self.pos]) 3934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 3944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return EMPTYSTRING.join(slist) 3964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getquote(self): 3984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Get a quote-delimited fragment from self's field.""" 3994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.getdelimited('"', '"\r', False) 4004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getcomment(self): 4024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Get a parenthesis-delimited fragment from self's field.""" 4034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.getdelimited('(', ')\r', True) 4044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getdomainliteral(self): 4064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse an RFC 2822 domain-literal.""" 4074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return '[%s]' % self.getdelimited('[', ']\r', False) 4084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getatom(self, atomends=None): 4104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse an RFC 2822 atom. 4114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Optional atomends specifies a different set of end token delimiters 4134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm (the default is to use self.atomends). This is used e.g. in 4144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm getphraselist() since phrase endings must not include the `.' (which 4154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm is legal in phrases).""" 4164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm atomlist = [''] 4174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if atomends is None: 4184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm atomends = self.atomends 4194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 4214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.field[self.pos] in atomends: 4224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 4234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 4244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm atomlist.append(self.field[self.pos]) 4254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 4264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return EMPTYSTRING.join(atomlist) 4284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getphraselist(self): 4304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Parse a sequence of RFC 2822 phrases. 4314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm A phrase is a sequence of words, which are in turn either RFC 2822 4334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm atoms or quoted-strings. Phrases are canonicalized by squeezing all 4344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm runs of continuous whitespace into one space. 4354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 4364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm plist = [] 4374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while self.pos < len(self.field): 4394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.field[self.pos] in self.FWS: 4404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.pos += 1 4414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '"': 4424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm plist.append(self.getquote()) 4434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] == '(': 4444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.commentlist.append(self.getcomment()) 4454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif self.field[self.pos] in self.phraseends: 4464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 4474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 4484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm plist.append(self.getatom(self.phraseends)) 4494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return plist 4514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass AddressList(AddrlistClass): 4534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """An AddressList encapsulates a list of parsed RFC 2822 addresses.""" 4544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, field): 4554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm AddrlistClass.__init__(self, field) 4564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if field: 4574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.addresslist = self.getaddrlist() 4584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 4594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.addresslist = [] 4604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __len__(self): 4624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return len(self.addresslist) 4634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __add__(self, other): 4654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Set union 4664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm newaddr = AddressList(None) 4674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm newaddr.addresslist = self.addresslist[:] 4684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for x in other.addresslist: 4694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not x in self.addresslist: 4704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm newaddr.addresslist.append(x) 4714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return newaddr 4724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __iadd__(self, other): 4744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Set union, in-place 4754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for x in other.addresslist: 4764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not x in self.addresslist: 4774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.addresslist.append(x) 4784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self 4794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __sub__(self, other): 4814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Set difference 4824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm newaddr = AddressList(None) 4834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for x in self.addresslist: 4844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not x in other.addresslist: 4854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm newaddr.addresslist.append(x) 4864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return newaddr 4874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __isub__(self, other): 4894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Set difference, in-place 4904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for x in other.addresslist: 4914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if x in self.addresslist: 4924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.addresslist.remove(x) 4934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self 4944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __getitem__(self, index): 4964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Make indexing, slices, and 'in' work 4974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.addresslist[index] 498