]>
git.proxmox.com Git - mirror_edk2.git/blob - AppPkg/Applications/Python/Python-2.7.2/Lib/email/_parseaddr.py
9375d2dee9973bdcb845d4240660734c929820e7
1 # Copyright (C) 2002-2007 Python Software Foundation
2 # Contact: email-sig@python.org
4 """Email address parsing code.
6 Lifted directly from rfc822.py. This should eventually be rewritten.
23 _monthnames
= ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
24 'aug', 'sep', 'oct', 'nov', 'dec',
25 'january', 'february', 'march', 'april', 'may', 'june', 'july',
26 'august', 'september', 'october', 'november', 'december']
28 _daynames
= ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
30 # The timezone table does not include the military time zones defined
31 # in RFC822, other than Z. According to RFC1123, the description in
32 # RFC822 gets the signs wrong, so we can't rely on any such time
33 # zones. RFC1123 recommends that numeric timezone indicators be used
34 # instead of timezone names.
36 _timezones
= {'UT':0, 'UTC':0, 'GMT':0, 'Z':0,
37 'AST': -400, 'ADT': -300, # Atlantic (used in Canada)
38 'EST': -500, 'EDT': -400, # Eastern
39 'CST': -600, 'CDT': -500, # Central
40 'MST': -700, 'MDT': -600, # Mountain
41 'PST': -800, 'PDT': -700 # Pacific
45 def parsedate_tz(data
):
46 """Convert a date string to a time tuple.
48 Accounts for military timezones.
51 # The FWS after the comma after the day-of-week is optional, so search and
53 if data
[0].endswith(',') or data
[0].lower() in _daynames
:
54 # There's a dayname here. Skip it
57 i
= data
[0].rfind(',')
59 data
[0] = data
[0][i
+1:]
60 if len(data
) == 3: # RFC 850 date, deprecated
61 stuff
= data
[0].split('-')
63 data
= stuff
+ data
[1:]
68 data
[3:] = [s
[:i
], s
[i
+1:]]
70 data
.append('') # Dummy tz
74 [dd
, mm
, yy
, tm
, tz
] = data
76 if mm
not in _monthnames
:
77 dd
, mm
= mm
, dd
.lower()
78 if mm
not in _monthnames
:
80 mm
= _monthnames
.index(mm
) + 1
90 if not yy
[0].isdigit():
110 # Check for a yy specified in two-digit format, then convert it to the
111 # appropriate four-digit format, according to the POSIX standard. RFC 822
112 # calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822)
113 # mandates a 4-digit yy. For more information, see the documentation for
116 # The year is between 1969 and 1999 (inclusive).
119 # The year is between 2000 and 2068 (inclusive).
125 tzoffset
= _timezones
[tz
]
131 # Convert a timezone offset into seconds ; -0500 -> -18000
138 tzoffset
= tzsign
* ( (tzoffset
//100)*3600 + (tzoffset
% 100)*60)
139 # Daylight Saving Time flag is set to -1, since DST is unknown.
140 return yy
, mm
, dd
, thh
, tmm
, tss
, 0, 1, -1, tzoffset
144 """Convert a time string to a time tuple."""
145 t
= parsedate_tz(data
)
146 if isinstance(t
, tuple):
153 """Turn a 10-tuple as returned by parsedate_tz() into a UTC timestamp."""
155 # No zone info, so localtime is better assumption than GMT
156 return time
.mktime(data
[:8] + (-1,))
158 t
= time
.mktime(data
[:8] + (0,))
159 return t
- data
[9] - time
.timezone
163 """Prepare string to be used in a quoted string.
165 Turns backslash and double quote characters into quoted pairs. These
166 are the only characters that need to be quoted inside a quoted string.
167 Does not add the surrounding double quotes.
169 return str.replace('\\', '\\\\').replace('"', '\\"')
173 """Address parser class by Ben Escoto.
175 To understand what this class does, it helps to have a copy of RFC 2822 in
178 Note: this class interface is deprecated and may be removed in the future.
179 Use rfc822.AddressList instead.
182 def __init__(self
, field
):
183 """Initialize a new instance.
185 `field' is an unparsed address header field, containing
186 one or more addresses.
188 self
.specials
= '()<>@,:;.\"[]'
192 self
.FWS
= self
.LWS
+ self
.CR
193 self
.atomends
= self
.specials
+ self
.LWS
+ self
.CR
194 # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it
195 # is obsolete syntax. RFC 2822 requires that we recognize obsolete
196 # syntax, so allow dots in phrases.
197 self
.phraseends
= self
.atomends
.replace('.', '')
199 self
.commentlist
= []
202 """Parse up to the start of the next address."""
203 while self
.pos
< len(self
.field
):
204 if self
.field
[self
.pos
] in self
.LWS
+ '\n\r':
206 elif self
.field
[self
.pos
] == '(':
207 self
.commentlist
.append(self
.getcomment())
211 def getaddrlist(self
):
212 """Parse all addresses.
214 Returns a list containing all of the addresses.
217 while self
.pos
< len(self
.field
):
218 ad
= self
.getaddress()
222 result
.append(('', ''))
225 def getaddress(self
):
226 """Parse the next address."""
227 self
.commentlist
= []
231 oldcl
= self
.commentlist
232 plist
= self
.getphraselist()
237 if self
.pos
>= len(self
.field
):
238 # Bad email address technically, no domain.
240 returnlist
= [(SPACE
.join(self
.commentlist
), plist
[0])]
242 elif self
.field
[self
.pos
] in '.@':
243 # email address is just an addrspec
244 # this isn't very efficient since we start over
246 self
.commentlist
= oldcl
247 addrspec
= self
.getaddrspec()
248 returnlist
= [(SPACE
.join(self
.commentlist
), addrspec
)]
250 elif self
.field
[self
.pos
] == ':':
254 fieldlen
= len(self
.field
)
256 while self
.pos
< len(self
.field
):
258 if self
.pos
< fieldlen
and self
.field
[self
.pos
] == ';':
261 returnlist
= returnlist
+ self
.getaddress()
263 elif self
.field
[self
.pos
] == '<':
264 # Address is a phrase then a route addr
265 routeaddr
= self
.getrouteaddr()
268 returnlist
= [(SPACE
.join(plist
) + ' (' +
269 ' '.join(self
.commentlist
) + ')', routeaddr
)]
271 returnlist
= [(SPACE
.join(plist
), routeaddr
)]
275 returnlist
= [(SPACE
.join(self
.commentlist
), plist
[0])]
276 elif self
.field
[self
.pos
] in self
.specials
:
280 if self
.pos
< len(self
.field
) and self
.field
[self
.pos
] == ',':
284 def getrouteaddr(self
):
285 """Parse a route address (Return-path value).
287 This method just skips all the route stuff and returns the addrspec.
289 if self
.field
[self
.pos
] != '<':
296 while self
.pos
< len(self
.field
):
300 elif self
.field
[self
.pos
] == '>':
303 elif self
.field
[self
.pos
] == '@':
306 elif self
.field
[self
.pos
] == ':':
309 adlist
= self
.getaddrspec()
316 def getaddrspec(self
):
317 """Parse an RFC 2822 addr-spec."""
321 while self
.pos
< len(self
.field
):
322 if self
.field
[self
.pos
] == '.':
325 elif self
.field
[self
.pos
] == '"':
326 aslist
.append('"%s"' % quote(self
.getquote()))
327 elif self
.field
[self
.pos
] in self
.atomends
:
330 aslist
.append(self
.getatom())
333 if self
.pos
>= len(self
.field
) or self
.field
[self
.pos
] != '@':
334 return EMPTYSTRING
.join(aslist
)
339 return EMPTYSTRING
.join(aslist
) + self
.getdomain()
342 """Get the complete domain name from an address."""
344 while self
.pos
< len(self
.field
):
345 if self
.field
[self
.pos
] in self
.LWS
:
347 elif self
.field
[self
.pos
] == '(':
348 self
.commentlist
.append(self
.getcomment())
349 elif self
.field
[self
.pos
] == '[':
350 sdlist
.append(self
.getdomainliteral())
351 elif self
.field
[self
.pos
] == '.':
354 elif self
.field
[self
.pos
] in self
.atomends
:
357 sdlist
.append(self
.getatom())
358 return EMPTYSTRING
.join(sdlist
)
360 def getdelimited(self
, beginchar
, endchars
, allowcomments
=True):
361 """Parse a header fragment delimited by special characters.
363 `beginchar' is the start character for the fragment.
364 If self is not looking at an instance of `beginchar' then
365 getdelimited returns the empty string.
367 `endchars' is a sequence of allowable end-delimiting characters.
368 Parsing stops when one of these is encountered.
370 If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed
371 within the parsed fragment.
373 if self
.field
[self
.pos
] != beginchar
:
379 while self
.pos
< len(self
.field
):
381 slist
.append(self
.field
[self
.pos
])
383 elif self
.field
[self
.pos
] in endchars
:
386 elif allowcomments
and self
.field
[self
.pos
] == '(':
387 slist
.append(self
.getcomment())
388 continue # have already advanced pos from getcomment
389 elif self
.field
[self
.pos
] == '\\':
392 slist
.append(self
.field
[self
.pos
])
395 return EMPTYSTRING
.join(slist
)
398 """Get a quote-delimited fragment from self's field."""
399 return self
.getdelimited('"', '"\r', False)
401 def getcomment(self
):
402 """Get a parenthesis-delimited fragment from self's field."""
403 return self
.getdelimited('(', ')\r', True)
405 def getdomainliteral(self
):
406 """Parse an RFC 2822 domain-literal."""
407 return '[%s]' % self
.getdelimited('[', ']\r', False)
409 def getatom(self
, atomends
=None):
410 """Parse an RFC 2822 atom.
412 Optional atomends specifies a different set of end token delimiters
413 (the default is to use self.atomends). This is used e.g. in
414 getphraselist() since phrase endings must not include the `.' (which
415 is legal in phrases)."""
418 atomends
= self
.atomends
420 while self
.pos
< len(self
.field
):
421 if self
.field
[self
.pos
] in atomends
:
424 atomlist
.append(self
.field
[self
.pos
])
427 return EMPTYSTRING
.join(atomlist
)
429 def getphraselist(self
):
430 """Parse a sequence of RFC 2822 phrases.
432 A phrase is a sequence of words, which are in turn either RFC 2822
433 atoms or quoted-strings. Phrases are canonicalized by squeezing all
434 runs of continuous whitespace into one space.
438 while self
.pos
< len(self
.field
):
439 if self
.field
[self
.pos
] in self
.FWS
:
441 elif self
.field
[self
.pos
] == '"':
442 plist
.append(self
.getquote())
443 elif self
.field
[self
.pos
] == '(':
444 self
.commentlist
.append(self
.getcomment())
445 elif self
.field
[self
.pos
] in self
.phraseends
:
448 plist
.append(self
.getatom(self
.phraseends
))
452 class AddressList(AddrlistClass
):
453 """An AddressList encapsulates a list of parsed RFC 2822 addresses."""
454 def __init__(self
, field
):
455 AddrlistClass
.__init
__(self
, field
)
457 self
.addresslist
= self
.getaddrlist()
459 self
.addresslist
= []
462 return len(self
.addresslist
)
464 def __add__(self
, other
):
466 newaddr
= AddressList(None)
467 newaddr
.addresslist
= self
.addresslist
[:]
468 for x
in other
.addresslist
:
469 if not x
in self
.addresslist
:
470 newaddr
.addresslist
.append(x
)
473 def __iadd__(self
, other
):
474 # Set union, in-place
475 for x
in other
.addresslist
:
476 if not x
in self
.addresslist
:
477 self
.addresslist
.append(x
)
480 def __sub__(self
, other
):
482 newaddr
= AddressList(None)
483 for x
in self
.addresslist
:
484 if not x
in other
.addresslist
:
485 newaddr
.addresslist
.append(x
)
488 def __isub__(self
, other
):
489 # Set difference, in-place
490 for x
in other
.addresslist
:
491 if x
in self
.addresslist
:
492 self
.addresslist
.remove(x
)
495 def __getitem__(self
, index
):
496 # Make indexing, slices, and 'in' work
497 return self
.addresslist
[index
]