summaryrefslogtreecommitdiff
path: root/Lib
diff options
context:
space:
mode:
authorChristian Heimes <christian@cheimes.de>2008-11-05 19:39:50 +0000
committerChristian Heimes <christian@cheimes.de>2008-11-05 19:39:50 +0000
commitfb5faf02853f5f56fe96f00e8020a6fabe8ac390 (patch)
treea67ba6e8f279b1e34a5b2def4f89d203cb47e976 /Lib
parentecc42a2b82858763818484572dccc7e88a3bdda1 (diff)
downloadcpython-git-fb5faf02853f5f56fe96f00e8020a6fabe8ac390.tar.gz
Issue #1210: Fixed imaplib
Patch by Victor Stinner, reviewed by Barry Warsaw.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/imaplib.py187
1 files changed, 77 insertions, 110 deletions
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
index 156b15db3d..6469df2dcf 100644
--- a/Lib/imaplib.py
+++ b/Lib/imaplib.py
@@ -22,14 +22,14 @@ Public functions: Internaldate2tuple
__version__ = "2.58"
-import binascii, os, random, re, socket, sys, time
+import binascii, random, re, socket, subprocess, sys, time
__all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple",
"Int2AP", "ParseFlags", "Time2Internaldate"]
# Globals
-CRLF = '\r\n'
+CRLF = b'\r\n'
Debug = 0
IMAP4_PORT = 143
IMAP4_SSL_PORT = 993
@@ -81,19 +81,19 @@ Commands = {
# Patterns to match server responses
-Continuation = re.compile(r'\+( (?P<data>.*))?')
-Flags = re.compile(r'.*FLAGS \((?P<flags>[^\)]*)\)')
-InternalDate = re.compile(r'.*INTERNALDATE "'
- r'(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9])'
- r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
- r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
- r'"')
-Literal = re.compile(r'.*{(?P<size>\d+)}$', re.ASCII)
-MapCRLF = re.compile(r'\r\n|\r|\n')
-Response_code = re.compile(r'\[(?P<type>[A-Z-]+)( (?P<data>[^\]]*))?\]')
-Untagged_response = re.compile(r'\* (?P<type>[A-Z-]+)( (?P<data>.*))?')
+Continuation = re.compile(br'\+( (?P<data>.*))?')
+Flags = re.compile(br'.*FLAGS \((?P<flags>[^\)]*)\)')
+InternalDate = re.compile(br'.*INTERNALDATE "'
+ br'(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9])'
+ br' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
+ br' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
+ br'"')
+Literal = re.compile(br'.*{(?P<size>\d+)}$', re.ASCII)
+MapCRLF = re.compile(br'\r\n|\r|\n')
+Response_code = re.compile(br'\[(?P<type>[A-Z-]+)( (?P<data>[^\]]*))?\]')
+Untagged_response = re.compile(br'\* (?P<type>[A-Z-]+)( (?P<data>.*))?')
Untagged_status = re.compile(
- r'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?', re.ASCII)
+ br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?', re.ASCII)
@@ -147,7 +147,7 @@ class IMAP4:
class abort(error): pass # Service errors - close and retry
class readonly(abort): pass # Mailbox status changed to READ-ONLY
- mustquote = re.compile(r"[^\w!#$%&'*+,.:;<=>?^`|~-]", re.ASCII)
+ mustquote = re.compile(br"[^\w!#$%&'*+,.:;<=>?^`|~-]", re.ASCII)
def __init__(self, host = '', port = IMAP4_PORT):
self.debug = Debug
@@ -167,9 +167,9 @@ class IMAP4:
# and compile tagged response matcher.
self.tagpre = Int2AP(random.randint(4096, 65535))
- self.tagre = re.compile(r'(?P<tag>'
+ self.tagre = re.compile(br'(?P<tag>'
+ self.tagpre
- + r'\d+) (?P<type>[A-Z]+) (?P<data>.*)', re.ASCII)
+ + br'\d+) (?P<type>[A-Z]+) (?P<data>.*)', re.ASCII)
# Get server welcome message,
# request and store CAPABILITY response.
@@ -193,7 +193,9 @@ class IMAP4:
typ, dat = self.capability()
if dat == [None]:
raise self.error('no CAPABILITY response from server')
- self.capabilities = tuple(dat[-1].upper().split())
+ dat = str(dat[-1], "ASCII")
+ dat = dat.upper()
+ self.capabilities = tuple(dat.split())
if __debug__:
if self.debug >= 3:
@@ -219,6 +221,11 @@ class IMAP4:
# Overridable methods
+ def _create_socket(self):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((self.host, self.port))
+ return sock
+
def open(self, host = '', port = IMAP4_PORT):
"""Setup connection to remote server on "host:port"
(default: localhost:standard IMAP4 port).
@@ -227,14 +234,21 @@ class IMAP4:
"""
self.host = host
self.port = port
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.connect((host, port))
+ self.sock = self._create_socket()
self.file = self.sock.makefile('rb')
def read(self, size):
"""Read 'size' bytes from remote."""
- return self.file.read(size)
+ chunks = []
+ read = 0
+ while read < size:
+ data = self.file.read(min(size-read, 4096))
+ if not data:
+ break
+ read += len(data)
+ chunks.append(data)
+ return b''.join(chunks)
def readline(self):
@@ -791,12 +805,12 @@ class IMAP4:
def _append_untagged(self, typ, dat):
-
- if dat is None: dat = ''
+ if dat is None:
+ dat = b''
ur = self.untagged_responses
if __debug__:
if self.debug >= 5:
- self._mesg('untagged_responses[%s] %s += ["%s"]' %
+ self._mesg('untagged_responses[%s] %s += ["%r"]' %
(typ, len(ur.get(typ,'')), dat))
if typ in ur:
ur[typ].append(dat)
@@ -828,10 +842,14 @@ class IMAP4:
raise self.readonly('mailbox status changed to READ-ONLY')
tag = self._new_tag()
- data = '%s %s' % (tag, name)
+ name = bytes(name, 'ASCII')
+ data = tag + b' ' + name
for arg in args:
if arg is None: continue
- data = '%s %s' % (data, self._checkquote(arg))
+ if isinstance(arg, str):
+ arg = bytes(arg, "ASCII")
+ #data = data + b' ' + self._checkquote(arg)
+ data = data + b' ' + arg
literal = self.literal
if literal is not None:
@@ -840,16 +858,16 @@ class IMAP4:
literator = literal
else:
literator = None
- data = '%s {%s}' % (data, len(literal))
+ data = data + bytes(' {%s}' % len(literal), 'ASCII')
if __debug__:
if self.debug >= 4:
- self._mesg('> %s' % data)
+ self._mesg('> %r' % data)
else:
- self._log('> %s' % data)
+ self._log('> %r' % data)
try:
- self.send('%s%s' % (data, CRLF))
+ self.send(data + CRLF)
except (socket.error, OSError) as val:
raise self.abort('socket error: %s' % val)
@@ -915,6 +933,7 @@ class IMAP4:
raise self.abort('unexpected tagged response: %s' % resp)
typ = self.mo.group('type')
+ typ = str(typ, 'ASCII')
dat = self.mo.group('data')
self.tagged_commands[tag] = (typ, [dat])
else:
@@ -936,9 +955,10 @@ class IMAP4:
raise self.abort("unexpected response: '%s'" % resp)
typ = self.mo.group('type')
+ typ = str(typ, 'ascii')
dat = self.mo.group('data')
- if dat is None: dat = '' # Null untagged response
- if dat2: dat = dat + ' ' + dat2
+ if dat is None: dat = b'' # Null untagged response
+ if dat2: dat = dat + b' ' + dat2
# Is there a literal to come?
@@ -965,11 +985,13 @@ class IMAP4:
# Bracketed response information?
if typ in ('OK', 'NO', 'BAD') and self._match(Response_code, dat):
- self._append_untagged(self.mo.group('type'), self.mo.group('data'))
+ typ = self.mo.group('type')
+ typ = str(typ, "ASCII")
+ self._append_untagged(typ, self.mo.group('data'))
if __debug__:
if self.debug >= 1 and typ in ('NO', 'BAD', 'BYE'):
- self._mesg('%s response: %s' % (typ, dat))
+ self._mesg('%s response: %r' % (typ, dat))
return resp
@@ -1007,9 +1029,9 @@ class IMAP4:
line = line[:-2]
if __debug__:
if self.debug >= 4:
- self._mesg('< %s' % line)
+ self._mesg('< %r' % line)
else:
- self._log('< %s' % line)
+ self._log('< %r' % line)
return line
@@ -1021,13 +1043,13 @@ class IMAP4:
self.mo = cre.match(s)
if __debug__:
if self.mo is not None and self.debug >= 5:
- self._mesg("\tmatched r'%s' => %r" % (cre.pattern, self.mo.groups()))
+ self._mesg("\tmatched r'%r' => %r" % (cre.pattern, self.mo.groups()))
return self.mo is not None
def _new_tag(self):
- tag = '%s%s' % (self.tagpre, self.tagnum)
+ tag = self.tagpre + bytes(str(self.tagnum), 'ASCII')
self.tagnum = self.tagnum + 1
self.tagged_commands[tag] = None
return tag
@@ -1038,8 +1060,6 @@ class IMAP4:
# Must quote command args if non-alphanumeric chars present,
# and not already quoted.
- if type(arg) is not type(''):
- return arg
if len(arg) >= 2 and (arg[0],arg[-1]) in (('(',')'),('"','"')):
return arg
if arg and self.mustquote.search(arg) is None:
@@ -1049,10 +1069,10 @@ class IMAP4:
def _quote(self, arg):
- arg = arg.replace('\\', '\\\\')
- arg = arg.replace('"', '\\"')
+ arg = arg.replace(b'\\', b'\\\\')
+ arg = arg.replace(b'"', b'\\"')
- return '"%s"' % arg
+ return b'"' + arg + b'"'
def _simple_command(self, name, *args):
@@ -1061,7 +1081,6 @@ class IMAP4:
def _untagged_response(self, typ, dat, name):
-
if typ == 'NO':
return typ, dat
if not name in self.untagged_responses:
@@ -1137,73 +1156,17 @@ else:
self.certfile = certfile
IMAP4.__init__(self, host, port)
+ def _create_socket(self):
+ sock = IMAP4._create_socket(self)
+ return ssl.wrap_socket(sock, self.keyfile, self.certfile)
- def open(self, host = '', port = IMAP4_SSL_PORT):
+ def open(self, host='', port=IMAP4_SSL_PORT):
"""Setup connection to remote server on "host:port".
(default: localhost:standard IMAP4 SSL port).
This connection will be used by the routines:
read, readline, send, shutdown.
"""
- self.host = host
- self.port = port
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect((host, port))
- self.sock = ssl.wrap_socket(sock, self.keyfile, self.certfile)
- self.file = self.sock.makefile('rb')
-
-
- def read(self, size):
- """Read 'size' bytes from remote."""
- # sslobj.read() sometimes returns < size bytes
- chunks = []
- read = 0
- while read < size:
- data = self.sslobj.read(min(size-read, 16384))
- read += len(data)
- chunks.append(data)
-
- return b''.join(chunks)
-
-
- def readline(self):
- """Read line from remote."""
- line = []
- while 1:
- char = self.sslobj.read(1)
- line.append(char)
- if char == b"\n": return b''.join(line)
-
-
- def send(self, data):
- """Send data to remote."""
- bytes = len(data)
- while bytes > 0:
- sent = self.sslobj.write(data)
- if sent == bytes:
- break # avoid copy
- data = data[sent:]
- bytes = bytes - sent
-
-
- def shutdown(self):
- """Close I/O established in "open"."""
- self.sock.close()
-
-
- def socket(self):
- """Return socket instance used to connect to IMAP4 server.
-
- socket = <instance>.socket()
- """
- return self.sock
-
-
- def ssl(self):
- """Return SSLObject instance used to communicate with the IMAP4 server.
-
- ssl = ssl.wrap_socket(<instance>.socket)
- """
- return self.sock
+ IMAP4.open(self, host, port)
__all__.append("IMAP4_SSL")
@@ -1214,7 +1177,7 @@ class IMAP4_stream(IMAP4):
Instantiate with: IMAP4_stream(command)
- where "command" is a string that can be passed to os.popen2()
+ where "command" is a string that can be passed to subprocess.Popen()
for more documentation see the docstring of the parent class IMAP4.
"""
@@ -1234,8 +1197,11 @@ class IMAP4_stream(IMAP4):
self.port = None
self.sock = None
self.file = None
- self.writefile, self.readfile = os.popen2(self.command)
-
+ self.process = subprocess.Popen(self.command,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ shell=True, close_fds=True)
+ self.writefile = self.process.stdin
+ self.readfile = self.process.stdout
def read(self, size):
"""Read 'size' bytes from remote."""
@@ -1257,6 +1223,7 @@ class IMAP4_stream(IMAP4):
"""Close I/O established in "open"."""
self.readfile.close()
self.writefile.close()
+ self.process.wait()
@@ -1355,11 +1322,11 @@ def Int2AP(num):
"""Convert integer to A-P string representation."""
- val = ''; AP = 'ABCDEFGHIJKLMNOP'
+ val = b''; AP = b'ABCDEFGHIJKLMNOP'
num = int(abs(num))
while num:
num, mod = divmod(num, 16)
- val = AP[mod] + val
+ val = AP[mod:mod+1] + val
return val