# Copyright (C) 2002-2007 Python Software Foundation# Contact: email-sig@python.org"""Email address parsing code.Lifted directly from rfc822.py. This should eventually be rewritten."""__all__ = ['mktime_tz','parsedate','parsedate_tz','quote',]import time, calendarSPACE = ' 'EMPTYSTRING = ''COMMASPACE = ', '# Parse a date field_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul','aug', 'sep', 'oct', 'nov', 'dec','january', 'february', 'march', 'april', 'may', 'june', 'july','august', 'september', 'october', 'november', 'december']_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']# The timezone table does not include the military time zones defined# in RFC822, other than Z. According to RFC1123, the description in# RFC822 gets the signs wrong, so we can't rely on any such time# zones. RFC1123 recommends that numeric timezone indicators be used# instead of timezone names._timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0,'AST': -400, 'ADT': -300, # Atlantic (used in Canada)'EST': -500, 'EDT': -400, # Eastern'CST': -600, 'CDT': -500, # Central'MST': -700, 'MDT': -600, # Mountain'PST': -800, 'PDT': -700 # Pacific}def parsedate_tz(data):"""Convert a date string to a time tuple.Accounts for military timezones."""res = _parsedate_tz(data)if not res:returnif res[9] is None:res[9] = 0return tuple(res)def _parsedate_tz(data):"""Convert date to extended time tuple.The last (additional) element is the time zone offset in seconds, except ifthe timezone was specified as -0000. In that case the last element isNone. This indicates a UTC timestamp that explicitly declaims knowledge ofthe source timezone, as opposed to a +0000 timestamp that indicates thesource timezone really was UTC."""if not data:returndata = data.split()# The FWS after the comma after the day-of-week is optional, so search and# adjust for this.if data[0].endswith(',') or data[0].lower() in _daynames:# There's a dayname here. Skip itdel data[0]else:i = data[0].rfind(',')if i >= 0:data[0] = data[0][i+1:]if len(data) == 3: # RFC 850 date, deprecatedstuff = data[0].split('-')if len(stuff) == 3:data = stuff + data[1:]if len(data) == 4:s = data[3]i = s.find('+')if i == -1:i = s.find('-')if i > 0:data[3:] = [s[:i], s[i:]]else:data.append('') # Dummy tzif len(data) < 5:return Nonedata = data[:5][dd, mm, yy, tm, tz] = datamm = mm.lower()if mm not in _monthnames:dd, mm = mm, dd.lower()if mm not in _monthnames:return Nonemm = _monthnames.index(mm) + 1if mm > 12:mm -= 12if dd[-1] == ',':dd = dd[:-1]i = yy.find(':')if i > 0:yy, tm = tm, yyif yy[-1] == ',':yy = yy[:-1]if not yy[0].isdigit():yy, tz = tz, yyif tm[-1] == ',':tm = tm[:-1]tm = tm.split(':')if len(tm) == 2:[thh, tmm] = tmtss = '0'elif len(tm) == 3:[thh, tmm, tss] = tmelif len(tm) == 1 and '.' in tm[0]:# Some non-compliant MUAs use '.' to separate time elements.tm = tm[0].split('.')if len(tm) == 2:[thh, tmm] = tmtss = 0elif len(tm) == 3:[thh, tmm, tss] = tmelse:return Nonetry:yy = int(yy)dd = int(dd)thh = int(thh)tmm = int(tmm)tss = int(tss)except ValueError:return None# Check for a yy specified in two-digit format, then convert it to the# appropriate four-digit format, according to the POSIX standard. RFC 822# calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822)# mandates a 4-digit yy. For more information, see the documentation for# the time module.if yy < 100:# The year is between 1969 and 1999 (inclusive).if yy > 68:yy += 1900# The year is between 2000 and 2068 (inclusive).else:yy += 2000tzoffset = Nonetz = tz.upper()if tz in _timezones:tzoffset = _timezones[tz]else:try:tzoffset = int(tz)except ValueError:passif tzoffset==0 and tz.startswith('-'):tzoffset = None# Convert a timezone offset into seconds ; -0500 -> -18000if tzoffset:if tzoffset < 0:tzsign = -1tzoffset = -tzoffsetelse:tzsign = 1tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60)# Daylight Saving Time flag is set to -1, since DST is unknown.return [yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset]def parsedate(data):"""Convert a time string to a time tuple."""t = parsedate_tz(data)if isinstance(t, tuple):return t[:9]else:return tdef mktime_tz(data):"""Turn a 10-tuple as returned by parsedate_tz() into a POSIX timestamp."""if data[9] is None:# No zone info, so localtime is better assumption than GMTreturn time.mktime(data[:8] + (-1,))else:t = calendar.timegm(data)return t - data[9]def quote(str):"""Prepare string to be used in a quoted string.Turns backslash and double quote characters into quoted pairs. Theseare the only characters that need to be quoted inside a quoted string.Does not add the surrounding double quotes."""return str.replace('\\', '\\\\').replace('"', '\\"')class AddrlistClass:"""Address parser class by Ben Escoto.To understand what this class does, it helps to have a copy of RFC 2822 infront of you.Note: this class interface is deprecated and may be removed in the future.Use email.utils.AddressList instead."""def __init__(self, field):"""Initialize a new instance.`field' is an unparsed address header field, containingone or more addresses."""self.specials = '()<>@,:;.\"[]'self.pos = 0self.LWS = ' \t'self.CR = '\r\n'self.FWS = self.LWS + self.CRself.atomends = self.specials + self.LWS + self.CR# Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it# is obsolete syntax. RFC 2822 requires that we recognize obsolete# syntax, so allow dots in phrases.self.phraseends = self.atomends.replace('.', '')self.field = fieldself.commentlist = []def gotonext(self):"""Skip white space and extract comments."""wslist = []while self.pos < len(self.field):if self.field[self.pos] in self.LWS + '\n\r':if self.field[self.pos] not in '\n\r':wslist.append(self.field[self.pos])self.pos += 1elif self.field[self.pos] == '(':self.commentlist.append(self.getcomment())else:breakreturn EMPTYSTRING.join(wslist)def getaddrlist(self):"""Parse all addresses.Returns a list containing all of the addresses."""result = []while self.pos < len(self.field):ad = self.getaddress()if ad:result += adelse:result.append(('', ''))return resultdef getaddress(self):"""Parse the next address."""self.commentlist = []self.gotonext()oldpos = self.posoldcl = self.commentlistplist = self.getphraselist()self.gotonext()returnlist = []if self.pos >= len(self.field):# Bad email address technically, no domain.if plist:returnlist = [(SPACE.join(self.commentlist), plist[0])]elif self.field[self.pos] in '.@':# email address is just an addrspec# this isn't very efficient since we start overself.pos = oldposself.commentlist = oldcladdrspec = self.getaddrspec()returnlist = [(SPACE.join(self.commentlist), addrspec)]elif self.field[self.pos] == ':':# address is a groupreturnlist = []fieldlen = len(self.field)self.pos += 1while self.pos < len(self.field):self.gotonext()if self.pos < fieldlen and self.field[self.pos] == ';':self.pos += 1breakreturnlist = returnlist + self.getaddress()elif self.field[self.pos] == '<':# Address is a phrase then a route addrrouteaddr = self.getrouteaddr()if self.commentlist:returnlist = [(SPACE.join(plist) + ' (' +' '.join(self.commentlist) + ')', routeaddr)]else:returnlist = [(SPACE.join(plist), routeaddr)]else:if plist:returnlist = [(SPACE.join(self.commentlist), plist[0])]elif self.field[self.pos] in self.specials:self.pos += 1self.gotonext()if self.pos < len(self.field) and self.field[self.pos] == ',':self.pos += 1return returnlistdef getrouteaddr(self):"""Parse a route address (Return-path value).This method just skips all the route stuff and returns the addrspec."""if self.field[self.pos] != '<':returnexpectroute = Falseself.pos += 1self.gotonext()adlist = ''while self.pos < len(self.field):if expectroute:self.getdomain()expectroute = Falseelif self.field[self.pos] == '>':self.pos += 1breakelif self.field[self.pos] == '@':self.pos += 1expectroute = Trueelif self.field[self.pos] == ':':self.pos += 1else:adlist = self.getaddrspec()self.pos += 1breakself.gotonext()return adlistdef getaddrspec(self):"""Parse an RFC 2822 addr-spec."""aslist = []self.gotonext()while self.pos < len(self.field):preserve_ws = Trueif self.field[self.pos] == '.':if aslist and not aslist[-1].strip():aslist.pop()aslist.append('.')self.pos += 1preserve_ws = Falseelif self.field[self.pos] == '"':aslist.append('"%s"' % quote(self.getquote()))elif self.field[self.pos] in self.atomends:if aslist and not aslist[-1].strip():aslist.pop()breakelse:aslist.append(self.getatom())ws = self.gotonext()if preserve_ws and ws:aslist.append(ws)if self.pos >= len(self.field) or self.field[self.pos] != '@':return EMPTYSTRING.join(aslist)aslist.append('@')self.pos += 1self.gotonext()domain = self.getdomain()if not domain:# Invalid domain, return an empty address instead of returning a# local part to denote failed parsing.return EMPTYSTRINGreturn EMPTYSTRING.join(aslist) + domaindef getdomain(self):"""Get the complete domain name from an address."""sdlist = []while self.pos < len(self.field):if self.field[self.pos] in self.LWS:self.pos += 1elif self.field[self.pos] == '(':self.commentlist.append(self.getcomment())elif self.field[self.pos] == '[':sdlist.append(self.getdomainliteral())elif self.field[self.pos] == '.':self.pos += 1sdlist.append('.')elif self.field[self.pos] == '@':# bpo-34155: Don't parse domains with two `@` like# `a@malicious.org@important.com`.return EMPTYSTRINGelif self.field[self.pos] in self.atomends:breakelse:sdlist.append(self.getatom())return EMPTYSTRING.join(sdlist)def getdelimited(self, beginchar, endchars, allowcomments=True):"""Parse a header fragment delimited by special characters.`beginchar' is the start character for the fragment.If self is not looking at an instance of `beginchar' thengetdelimited returns the empty string.`endchars' is a sequence of allowable end-delimiting characters.Parsing stops when one of these is encountered.If `allowcomments' is non-zero, embedded RFC 2822 comments are allowedwithin the parsed fragment."""if self.field[self.pos] != beginchar:return ''slist = ['']quote = Falseself.pos += 1while self.pos < len(self.field):if quote:slist.append(self.field[self.pos])quote = Falseelif self.field[self.pos] in endchars:self.pos += 1breakelif allowcomments and self.field[self.pos] == '(':slist.append(self.getcomment())continue # have already advanced pos from getcommentelif self.field[self.pos] == '\\':quote = Trueelse:slist.append(self.field[self.pos])self.pos += 1return EMPTYSTRING.join(slist)def getquote(self):"""Get a quote-delimited fragment from self's field."""return self.getdelimited('"', '"\r', False)def getcomment(self):"""Get a parenthesis-delimited fragment from self's field."""return self.getdelimited('(', ')\r', True)def getdomainliteral(self):"""Parse an RFC 2822 domain-literal."""return '[%s]' % self.getdelimited('[', ']\r', False)def getatom(self, atomends=None):"""Parse an RFC 2822 atom.Optional atomends specifies a different set of end token delimiters(the default is to use self.atomends). This is used e.g. ingetphraselist() since phrase endings must not include the `.' (whichis legal in phrases)."""atomlist = ['']if atomends is None:atomends = self.atomendswhile self.pos < len(self.field):if self.field[self.pos] in atomends:breakelse:atomlist.append(self.field[self.pos])self.pos += 1return EMPTYSTRING.join(atomlist)def getphraselist(self):"""Parse a sequence of RFC 2822 phrases.A phrase is a sequence of words, which are in turn either RFC 2822atoms or quoted-strings. Phrases are canonicalized by squeezing allruns of continuous whitespace into one space."""plist = []while self.pos < len(self.field):if self.field[self.pos] in self.FWS:self.pos += 1elif self.field[self.pos] == '"':plist.append(self.getquote())elif self.field[self.pos] == '(':self.commentlist.append(self.getcomment())elif self.field[self.pos] in self.phraseends:breakelse:plist.append(self.getatom(self.phraseends))return plistclass AddressList(AddrlistClass):"""An AddressList encapsulates a list of parsed RFC 2822 addresses."""def __init__(self, field):AddrlistClass.__init__(self, field)if field:self.addresslist = self.getaddrlist()else:self.addresslist = []def __len__(self):return len(self.addresslist)def __add__(self, other):# Set unionnewaddr = AddressList(None)newaddr.addresslist = self.addresslist[:]for x in other.addresslist:if not x in self.addresslist:newaddr.addresslist.append(x)return newaddrdef __iadd__(self, other):# Set union, in-placefor x in other.addresslist:if not x in self.addresslist:self.addresslist.append(x)return selfdef __sub__(self, other):# Set differencenewaddr = AddressList(None)for x in self.addresslist:if not x in other.addresslist:newaddr.addresslist.append(x)return newaddrdef __isub__(self, other):# Set difference, in-placefor x in other.addresslist:if x in self.addresslist:self.addresslist.remove(x)return selfdef __getitem__(self, index):# Make indexing, slices, and 'in' workreturn self.addresslist[index]
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。