Edit File by line
/home/barbar84/public_h.../wp-conte.../plugins/sujqvwi/ExeBy/smexe_ro.../lib64/python2....
File: rfc822.py
"""RFC 2822 message manipulation.
[0] Fix | Delete
[1] Fix | Delete
Note: This is only a very rough sketch of a full RFC-822 parser; in particular
[2] Fix | Delete
the tokenizing of addresses does not adhere to all the quoting rules.
[3] Fix | Delete
[4] Fix | Delete
Note: RFC 2822 is a long awaited update to RFC 822. This module should
[5] Fix | Delete
conform to RFC 2822, and is thus mis-named (it's not worth renaming it). Some
[6] Fix | Delete
effort at RFC 2822 updates have been made, but a thorough audit has not been
[7] Fix | Delete
performed. Consider any RFC 2822 non-conformance to be a bug.
[8] Fix | Delete
[9] Fix | Delete
RFC 2822: http://www.faqs.org/rfcs/rfc2822.html
[10] Fix | Delete
RFC 822 : http://www.faqs.org/rfcs/rfc822.html (obsolete)
[11] Fix | Delete
[12] Fix | Delete
Directions for use:
[13] Fix | Delete
[14] Fix | Delete
To create a Message object: first open a file, e.g.:
[15] Fix | Delete
[16] Fix | Delete
fp = open(file, 'r')
[17] Fix | Delete
[18] Fix | Delete
You can use any other legal way of getting an open file object, e.g. use
[19] Fix | Delete
sys.stdin or call os.popen(). Then pass the open file object to the Message()
[20] Fix | Delete
constructor:
[21] Fix | Delete
[22] Fix | Delete
m = Message(fp)
[23] Fix | Delete
[24] Fix | Delete
This class can work with any input object that supports a readline method. If
[25] Fix | Delete
the input object has seek and tell capability, the rewindbody method will
[26] Fix | Delete
work; also illegal lines will be pushed back onto the input stream. If the
[27] Fix | Delete
input object lacks seek but has an `unread' method that can push back a line
[28] Fix | Delete
of input, Message will use that to push back illegal lines. Thus this class
[29] Fix | Delete
can be used to parse messages coming from a buffered stream.
[30] Fix | Delete
[31] Fix | Delete
The optional `seekable' argument is provided as a workaround for certain stdio
[32] Fix | Delete
libraries in which tell() discards buffered data before discovering that the
[33] Fix | Delete
lseek() system call doesn't work. For maximum portability, you should set the
[34] Fix | Delete
seekable argument to zero to prevent that initial \code{tell} when passing in
[35] Fix | Delete
an unseekable object such as a file object created from a socket object. If
[36] Fix | Delete
it is 1 on entry -- which it is by default -- the tell() method of the open
[37] Fix | Delete
file object is called once; if this raises an exception, seekable is reset to
[38] Fix | Delete
0. For other nonzero values of seekable, this test is not made.
[39] Fix | Delete
[40] Fix | Delete
To get the text of a particular header there are several methods:
[41] Fix | Delete
[42] Fix | Delete
str = m.getheader(name)
[43] Fix | Delete
str = m.getrawheader(name)
[44] Fix | Delete
[45] Fix | Delete
where name is the name of the header, e.g. 'Subject'. The difference is that
[46] Fix | Delete
getheader() strips the leading and trailing whitespace, while getrawheader()
[47] Fix | Delete
doesn't. Both functions retain embedded whitespace (including newlines)
[48] Fix | Delete
exactly as they are specified in the header, and leave the case of the text
[49] Fix | Delete
unchanged.
[50] Fix | Delete
[51] Fix | Delete
For addresses and address lists there are functions
[52] Fix | Delete
[53] Fix | Delete
realname, mailaddress = m.getaddr(name)
[54] Fix | Delete
list = m.getaddrlist(name)
[55] Fix | Delete
[56] Fix | Delete
where the latter returns a list of (realname, mailaddr) tuples.
[57] Fix | Delete
[58] Fix | Delete
There is also a method
[59] Fix | Delete
[60] Fix | Delete
time = m.getdate(name)
[61] Fix | Delete
[62] Fix | Delete
which parses a Date-like field and returns a time-compatible tuple,
[63] Fix | Delete
i.e. a tuple such as returned by time.localtime() or accepted by
[64] Fix | Delete
time.mktime().
[65] Fix | Delete
[66] Fix | Delete
See the class definition for lower level access methods.
[67] Fix | Delete
[68] Fix | Delete
There are also some utility functions here.
[69] Fix | Delete
"""
[70] Fix | Delete
# Cleanup and extensions by Eric S. Raymond <esr@thyrsus.com>
[71] Fix | Delete
[72] Fix | Delete
import time
[73] Fix | Delete
[74] Fix | Delete
from warnings import warnpy3k
[75] Fix | Delete
warnpy3k("in 3.x, rfc822 has been removed in favor of the email package",
[76] Fix | Delete
stacklevel=2)
[77] Fix | Delete
[78] Fix | Delete
__all__ = ["Message","AddressList","parsedate","parsedate_tz","mktime_tz"]
[79] Fix | Delete
[80] Fix | Delete
_blanklines = ('\r\n', '\n') # Optimization for islast()
[81] Fix | Delete
[82] Fix | Delete
[83] Fix | Delete
class Message:
[84] Fix | Delete
"""Represents a single RFC 2822-compliant message."""
[85] Fix | Delete
[86] Fix | Delete
def __init__(self, fp, seekable = 1):
[87] Fix | Delete
"""Initialize the class instance and read the headers."""
[88] Fix | Delete
if seekable == 1:
[89] Fix | Delete
# Exercise tell() to make sure it works
[90] Fix | Delete
# (and then assume seek() works, too)
[91] Fix | Delete
try:
[92] Fix | Delete
fp.tell()
[93] Fix | Delete
except (AttributeError, IOError):
[94] Fix | Delete
seekable = 0
[95] Fix | Delete
self.fp = fp
[96] Fix | Delete
self.seekable = seekable
[97] Fix | Delete
self.startofheaders = None
[98] Fix | Delete
self.startofbody = None
[99] Fix | Delete
#
[100] Fix | Delete
if self.seekable:
[101] Fix | Delete
try:
[102] Fix | Delete
self.startofheaders = self.fp.tell()
[103] Fix | Delete
except IOError:
[104] Fix | Delete
self.seekable = 0
[105] Fix | Delete
#
[106] Fix | Delete
self.readheaders()
[107] Fix | Delete
#
[108] Fix | Delete
if self.seekable:
[109] Fix | Delete
try:
[110] Fix | Delete
self.startofbody = self.fp.tell()
[111] Fix | Delete
except IOError:
[112] Fix | Delete
self.seekable = 0
[113] Fix | Delete
[114] Fix | Delete
def rewindbody(self):
[115] Fix | Delete
"""Rewind the file to the start of the body (if seekable)."""
[116] Fix | Delete
if not self.seekable:
[117] Fix | Delete
raise IOError, "unseekable file"
[118] Fix | Delete
self.fp.seek(self.startofbody)
[119] Fix | Delete
[120] Fix | Delete
def readheaders(self):
[121] Fix | Delete
"""Read header lines.
[122] Fix | Delete
[123] Fix | Delete
Read header lines up to the entirely blank line that terminates them.
[124] Fix | Delete
The (normally blank) line that ends the headers is skipped, but not
[125] Fix | Delete
included in the returned list. If a non-header line ends the headers,
[126] Fix | Delete
(which is an error), an attempt is made to backspace over it; it is
[127] Fix | Delete
never included in the returned list.
[128] Fix | Delete
[129] Fix | Delete
The variable self.status is set to the empty string if all went well,
[130] Fix | Delete
otherwise it is an error message. The variable self.headers is a
[131] Fix | Delete
completely uninterpreted list of lines contained in the header (so
[132] Fix | Delete
printing them will reproduce the header exactly as it appears in the
[133] Fix | Delete
file).
[134] Fix | Delete
"""
[135] Fix | Delete
self.dict = {}
[136] Fix | Delete
self.unixfrom = ''
[137] Fix | Delete
self.headers = lst = []
[138] Fix | Delete
self.status = ''
[139] Fix | Delete
headerseen = ""
[140] Fix | Delete
firstline = 1
[141] Fix | Delete
startofline = unread = tell = None
[142] Fix | Delete
if hasattr(self.fp, 'unread'):
[143] Fix | Delete
unread = self.fp.unread
[144] Fix | Delete
elif self.seekable:
[145] Fix | Delete
tell = self.fp.tell
[146] Fix | Delete
while 1:
[147] Fix | Delete
if tell:
[148] Fix | Delete
try:
[149] Fix | Delete
startofline = tell()
[150] Fix | Delete
except IOError:
[151] Fix | Delete
startofline = tell = None
[152] Fix | Delete
self.seekable = 0
[153] Fix | Delete
line = self.fp.readline()
[154] Fix | Delete
if not line:
[155] Fix | Delete
self.status = 'EOF in headers'
[156] Fix | Delete
break
[157] Fix | Delete
# Skip unix From name time lines
[158] Fix | Delete
if firstline and line.startswith('From '):
[159] Fix | Delete
self.unixfrom = self.unixfrom + line
[160] Fix | Delete
continue
[161] Fix | Delete
firstline = 0
[162] Fix | Delete
if headerseen and line[0] in ' \t':
[163] Fix | Delete
# It's a continuation line.
[164] Fix | Delete
lst.append(line)
[165] Fix | Delete
x = (self.dict[headerseen] + "\n " + line.strip())
[166] Fix | Delete
self.dict[headerseen] = x.strip()
[167] Fix | Delete
continue
[168] Fix | Delete
elif self.iscomment(line):
[169] Fix | Delete
# It's a comment. Ignore it.
[170] Fix | Delete
continue
[171] Fix | Delete
elif self.islast(line):
[172] Fix | Delete
# Note! No pushback here! The delimiter line gets eaten.
[173] Fix | Delete
break
[174] Fix | Delete
headerseen = self.isheader(line)
[175] Fix | Delete
if headerseen:
[176] Fix | Delete
# It's a legal header line, save it.
[177] Fix | Delete
lst.append(line)
[178] Fix | Delete
self.dict[headerseen] = line[len(headerseen)+1:].strip()
[179] Fix | Delete
continue
[180] Fix | Delete
elif headerseen is not None:
[181] Fix | Delete
# An empty header name. These aren't allowed in HTTP, but it's
[182] Fix | Delete
# probably a benign mistake. Don't add the header, just keep
[183] Fix | Delete
# going.
[184] Fix | Delete
continue
[185] Fix | Delete
else:
[186] Fix | Delete
# It's not a header line; throw it back and stop here.
[187] Fix | Delete
if not self.dict:
[188] Fix | Delete
self.status = 'No headers'
[189] Fix | Delete
else:
[190] Fix | Delete
self.status = 'Non-header line where header expected'
[191] Fix | Delete
# Try to undo the read.
[192] Fix | Delete
if unread:
[193] Fix | Delete
unread(line)
[194] Fix | Delete
elif tell:
[195] Fix | Delete
self.fp.seek(startofline)
[196] Fix | Delete
else:
[197] Fix | Delete
self.status = self.status + '; bad seek'
[198] Fix | Delete
break
[199] Fix | Delete
[200] Fix | Delete
def isheader(self, line):
[201] Fix | Delete
"""Determine whether a given line is a legal header.
[202] Fix | Delete
[203] Fix | Delete
This method should return the header name, suitably canonicalized.
[204] Fix | Delete
You may override this method in order to use Message parsing on tagged
[205] Fix | Delete
data in RFC 2822-like formats with special header formats.
[206] Fix | Delete
"""
[207] Fix | Delete
i = line.find(':')
[208] Fix | Delete
if i > -1:
[209] Fix | Delete
return line[:i].lower()
[210] Fix | Delete
return None
[211] Fix | Delete
[212] Fix | Delete
def islast(self, line):
[213] Fix | Delete
"""Determine whether a line is a legal end of RFC 2822 headers.
[214] Fix | Delete
[215] Fix | Delete
You may override this method if your application wants to bend the
[216] Fix | Delete
rules, e.g. to strip trailing whitespace, or to recognize MH template
[217] Fix | Delete
separators ('--------'). For convenience (e.g. for code reading from
[218] Fix | Delete
sockets) a line consisting of \\r\\n also matches.
[219] Fix | Delete
"""
[220] Fix | Delete
return line in _blanklines
[221] Fix | Delete
[222] Fix | Delete
def iscomment(self, line):
[223] Fix | Delete
"""Determine whether a line should be skipped entirely.
[224] Fix | Delete
[225] Fix | Delete
You may override this method in order to use Message parsing on tagged
[226] Fix | Delete
data in RFC 2822-like formats that support embedded comments or
[227] Fix | Delete
free-text data.
[228] Fix | Delete
"""
[229] Fix | Delete
return False
[230] Fix | Delete
[231] Fix | Delete
def getallmatchingheaders(self, name):
[232] Fix | Delete
"""Find all header lines matching a given header name.
[233] Fix | Delete
[234] Fix | Delete
Look through the list of headers and find all lines matching a given
[235] Fix | Delete
header name (and their continuation lines). A list of the lines is
[236] Fix | Delete
returned, without interpretation. If the header does not occur, an
[237] Fix | Delete
empty list is returned. If the header occurs multiple times, all
[238] Fix | Delete
occurrences are returned. Case is not important in the header name.
[239] Fix | Delete
"""
[240] Fix | Delete
name = name.lower() + ':'
[241] Fix | Delete
n = len(name)
[242] Fix | Delete
lst = []
[243] Fix | Delete
hit = 0
[244] Fix | Delete
for line in self.headers:
[245] Fix | Delete
if line[:n].lower() == name:
[246] Fix | Delete
hit = 1
[247] Fix | Delete
elif not line[:1].isspace():
[248] Fix | Delete
hit = 0
[249] Fix | Delete
if hit:
[250] Fix | Delete
lst.append(line)
[251] Fix | Delete
return lst
[252] Fix | Delete
[253] Fix | Delete
def getfirstmatchingheader(self, name):
[254] Fix | Delete
"""Get the first header line matching name.
[255] Fix | Delete
[256] Fix | Delete
This is similar to getallmatchingheaders, but it returns only the
[257] Fix | Delete
first matching header (and its continuation lines).
[258] Fix | Delete
"""
[259] Fix | Delete
name = name.lower() + ':'
[260] Fix | Delete
n = len(name)
[261] Fix | Delete
lst = []
[262] Fix | Delete
hit = 0
[263] Fix | Delete
for line in self.headers:
[264] Fix | Delete
if hit:
[265] Fix | Delete
if not line[:1].isspace():
[266] Fix | Delete
break
[267] Fix | Delete
elif line[:n].lower() == name:
[268] Fix | Delete
hit = 1
[269] Fix | Delete
if hit:
[270] Fix | Delete
lst.append(line)
[271] Fix | Delete
return lst
[272] Fix | Delete
[273] Fix | Delete
def getrawheader(self, name):
[274] Fix | Delete
"""A higher-level interface to getfirstmatchingheader().
[275] Fix | Delete
[276] Fix | Delete
Return a string containing the literal text of the header but with the
[277] Fix | Delete
keyword stripped. All leading, trailing and embedded whitespace is
[278] Fix | Delete
kept in the string, however. Return None if the header does not
[279] Fix | Delete
occur.
[280] Fix | Delete
"""
[281] Fix | Delete
[282] Fix | Delete
lst = self.getfirstmatchingheader(name)
[283] Fix | Delete
if not lst:
[284] Fix | Delete
return None
[285] Fix | Delete
lst[0] = lst[0][len(name) + 1:]
[286] Fix | Delete
return ''.join(lst)
[287] Fix | Delete
[288] Fix | Delete
def getheader(self, name, default=None):
[289] Fix | Delete
"""Get the header value for a name.
[290] Fix | Delete
[291] Fix | Delete
This is the normal interface: it returns a stripped version of the
[292] Fix | Delete
header value for a given header name, or None if it doesn't exist.
[293] Fix | Delete
This uses the dictionary version which finds the *last* such header.
[294] Fix | Delete
"""
[295] Fix | Delete
return self.dict.get(name.lower(), default)
[296] Fix | Delete
get = getheader
[297] Fix | Delete
[298] Fix | Delete
def getheaders(self, name):
[299] Fix | Delete
"""Get all values for a header.
[300] Fix | Delete
[301] Fix | Delete
This returns a list of values for headers given more than once; each
[302] Fix | Delete
value in the result list is stripped in the same way as the result of
[303] Fix | Delete
getheader(). If the header is not given, return an empty list.
[304] Fix | Delete
"""
[305] Fix | Delete
result = []
[306] Fix | Delete
current = ''
[307] Fix | Delete
have_header = 0
[308] Fix | Delete
for s in self.getallmatchingheaders(name):
[309] Fix | Delete
if s[0].isspace():
[310] Fix | Delete
if current:
[311] Fix | Delete
current = "%s\n %s" % (current, s.strip())
[312] Fix | Delete
else:
[313] Fix | Delete
current = s.strip()
[314] Fix | Delete
else:
[315] Fix | Delete
if have_header:
[316] Fix | Delete
result.append(current)
[317] Fix | Delete
current = s[s.find(":") + 1:].strip()
[318] Fix | Delete
have_header = 1
[319] Fix | Delete
if have_header:
[320] Fix | Delete
result.append(current)
[321] Fix | Delete
return result
[322] Fix | Delete
[323] Fix | Delete
def getaddr(self, name):
[324] Fix | Delete
"""Get a single address from a header, as a tuple.
[325] Fix | Delete
[326] Fix | Delete
An example return value:
[327] Fix | Delete
('Guido van Rossum', 'guido@cwi.nl')
[328] Fix | Delete
"""
[329] Fix | Delete
# New, by Ben Escoto
[330] Fix | Delete
alist = self.getaddrlist(name)
[331] Fix | Delete
if alist:
[332] Fix | Delete
return alist[0]
[333] Fix | Delete
else:
[334] Fix | Delete
return (None, None)
[335] Fix | Delete
[336] Fix | Delete
def getaddrlist(self, name):
[337] Fix | Delete
"""Get a list of addresses from a header.
[338] Fix | Delete
[339] Fix | Delete
Retrieves a list of addresses from a header, where each address is a
[340] Fix | Delete
tuple as returned by getaddr(). Scans all named headers, so it works
[341] Fix | Delete
properly with multiple To: or Cc: headers for example.
[342] Fix | Delete
"""
[343] Fix | Delete
raw = []
[344] Fix | Delete
for h in self.getallmatchingheaders(name):
[345] Fix | Delete
if h[0] in ' \t':
[346] Fix | Delete
raw.append(h)
[347] Fix | Delete
else:
[348] Fix | Delete
if raw:
[349] Fix | Delete
raw.append(', ')
[350] Fix | Delete
i = h.find(':')
[351] Fix | Delete
if i > 0:
[352] Fix | Delete
addr = h[i+1:]
[353] Fix | Delete
raw.append(addr)
[354] Fix | Delete
alladdrs = ''.join(raw)
[355] Fix | Delete
a = AddressList(alladdrs)
[356] Fix | Delete
return a.addresslist
[357] Fix | Delete
[358] Fix | Delete
def getdate(self, name):
[359] Fix | Delete
"""Retrieve a date field from a header.
[360] Fix | Delete
[361] Fix | Delete
Retrieves a date field from the named header, returning a tuple
[362] Fix | Delete
compatible with time.mktime().
[363] Fix | Delete
"""
[364] Fix | Delete
try:
[365] Fix | Delete
data = self[name]
[366] Fix | Delete
except KeyError:
[367] Fix | Delete
return None
[368] Fix | Delete
return parsedate(data)
[369] Fix | Delete
[370] Fix | Delete
def getdate_tz(self, name):
[371] Fix | Delete
"""Retrieve a date field from a header as a 10-tuple.
[372] Fix | Delete
[373] Fix | Delete
The first 9 elements make up a tuple compatible with time.mktime(),
[374] Fix | Delete
and the 10th is the offset of the poster's time zone from GMT/UTC.
[375] Fix | Delete
"""
[376] Fix | Delete
try:
[377] Fix | Delete
data = self[name]
[378] Fix | Delete
except KeyError:
[379] Fix | Delete
return None
[380] Fix | Delete
return parsedate_tz(data)
[381] Fix | Delete
[382] Fix | Delete
[383] Fix | Delete
# Access as a dictionary (only finds *last* header of each type):
[384] Fix | Delete
[385] Fix | Delete
def __len__(self):
[386] Fix | Delete
"""Get the number of headers in a message."""
[387] Fix | Delete
return len(self.dict)
[388] Fix | Delete
[389] Fix | Delete
def __getitem__(self, name):
[390] Fix | Delete
"""Get a specific header, as from a dictionary."""
[391] Fix | Delete
return self.dict[name.lower()]
[392] Fix | Delete
[393] Fix | Delete
def __setitem__(self, name, value):
[394] Fix | Delete
"""Set the value of a header.
[395] Fix | Delete
[396] Fix | Delete
Note: This is not a perfect inversion of __getitem__, because any
[397] Fix | Delete
changed headers get stuck at the end of the raw-headers list rather
[398] Fix | Delete
than where the altered header was.
[399] Fix | Delete
"""
[400] Fix | Delete
del self[name] # Won't fail if it doesn't exist
[401] Fix | Delete
self.dict[name.lower()] = value
[402] Fix | Delete
text = name + ": " + value
[403] Fix | Delete
for line in text.split("\n"):
[404] Fix | Delete
self.headers.append(line + "\n")
[405] Fix | Delete
[406] Fix | Delete
def __delitem__(self, name):
[407] Fix | Delete
"""Delete all occurrences of a specific header, if it is present."""
[408] Fix | Delete
name = name.lower()
[409] Fix | Delete
if not name in self.dict:
[410] Fix | Delete
return
[411] Fix | Delete
del self.dict[name]
[412] Fix | Delete
name = name + ':'
[413] Fix | Delete
n = len(name)
[414] Fix | Delete
lst = []
[415] Fix | Delete
hit = 0
[416] Fix | Delete
for i in range(len(self.headers)):
[417] Fix | Delete
line = self.headers[i]
[418] Fix | Delete
if line[:n].lower() == name:
[419] Fix | Delete
hit = 1
[420] Fix | Delete
elif not line[:1].isspace():
[421] Fix | Delete
hit = 0
[422] Fix | Delete
if hit:
[423] Fix | Delete
lst.append(i)
[424] Fix | Delete
for i in reversed(lst):
[425] Fix | Delete
del self.headers[i]
[426] Fix | Delete
[427] Fix | Delete
def setdefault(self, name, default=""):
[428] Fix | Delete
lowername = name.lower()
[429] Fix | Delete
if lowername in self.dict:
[430] Fix | Delete
return self.dict[lowername]
[431] Fix | Delete
else:
[432] Fix | Delete
text = name + ": " + default
[433] Fix | Delete
for line in text.split("\n"):
[434] Fix | Delete
self.headers.append(line + "\n")
[435] Fix | Delete
self.dict[lowername] = default
[436] Fix | Delete
return default
[437] Fix | Delete
[438] Fix | Delete
def has_key(self, name):
[439] Fix | Delete
"""Determine whether a message contains the named header."""
[440] Fix | Delete
return name.lower() in self.dict
[441] Fix | Delete
[442] Fix | Delete
def __contains__(self, name):
[443] Fix | Delete
"""Determine whether a message contains the named header."""
[444] Fix | Delete
return name.lower() in self.dict
[445] Fix | Delete
[446] Fix | Delete
def __iter__(self):
[447] Fix | Delete
return iter(self.dict)
[448] Fix | Delete
[449] Fix | Delete
def keys(self):
[450] Fix | Delete
"""Get all of a message's header field names."""
[451] Fix | Delete
return self.dict.keys()
[452] Fix | Delete
[453] Fix | Delete
def values(self):
[454] Fix | Delete
"""Get all of a message's header field values."""
[455] Fix | Delete
return self.dict.values()
[456] Fix | Delete
[457] Fix | Delete
def items(self):
[458] Fix | Delete
"""Get all of a message's headers.
[459] Fix | Delete
[460] Fix | Delete
Returns a list of name, value tuples.
[461] Fix | Delete
"""
[462] Fix | Delete
return self.dict.items()
[463] Fix | Delete
[464] Fix | Delete
def __str__(self):
[465] Fix | Delete
return ''.join(self.headers)
[466] Fix | Delete
[467] Fix | Delete
[468] Fix | Delete
# Utility functions
[469] Fix | Delete
# -----------------
[470] Fix | Delete
[471] Fix | Delete
# XXX Should fix unquote() and quote() to be really conformant.
[472] Fix | Delete
# XXX The inverses of the parse functions may also be useful.
[473] Fix | Delete
[474] Fix | Delete
[475] Fix | Delete
def unquote(s):
[476] Fix | Delete
"""Remove quotes from a string."""
[477] Fix | Delete
if len(s) > 1:
[478] Fix | Delete
if s.startswith('"') and s.endswith('"'):
[479] Fix | Delete
return s[1:-1].replace('\\\\', '\\').replace('\\"', '"')
[480] Fix | Delete
if s.startswith('<') and s.endswith('>'):
[481] Fix | Delete
return s[1:-1]
[482] Fix | Delete
return s
[483] Fix | Delete
[484] Fix | Delete
[485] Fix | Delete
def quote(s):
[486] Fix | Delete
"""Add quotes around a string."""
[487] Fix | Delete
return s.replace('\\', '\\\\').replace('"', '\\"')
[488] Fix | Delete
[489] Fix | Delete
[490] Fix | Delete
def parseaddr(address):
[491] Fix | Delete
"""Parse an address into a (realname, mailaddr) tuple."""
[492] Fix | Delete
a = AddressList(address)
[493] Fix | Delete
lst = a.addresslist
[494] Fix | Delete
if not lst:
[495] Fix | Delete
return (None, None)
[496] Fix | Delete
return lst[0]
[497] Fix | Delete
[498] Fix | Delete
[499] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function