mirror of
https://github.com/python/cpython.git
synced 2025-10-10 00:43:41 +00:00
apply patch item #416253
This commit is contained in:
parent
34d9705943
commit
15e5d5344d
1 changed files with 115 additions and 38 deletions
153
Lib/imaplib.py
153
Lib/imaplib.py
|
@ -14,8 +14,9 @@ Public functions: Internaldate2tuple
|
||||||
#
|
#
|
||||||
# Authentication code contributed by Donn Cave <donn@u.washington.edu> June 1998.
|
# Authentication code contributed by Donn Cave <donn@u.washington.edu> June 1998.
|
||||||
# String method conversion by ESR, February 2001.
|
# String method conversion by ESR, February 2001.
|
||||||
|
# GET/SETACL contributed by Anthony Baxter <anthony@interlink.com.au> April 2001.
|
||||||
|
|
||||||
__version__ = "2.40"
|
__version__ = "2.47"
|
||||||
|
|
||||||
import binascii, re, socket, time, random, sys
|
import binascii, re, socket, time, random, sys
|
||||||
|
|
||||||
|
@ -44,21 +45,24 @@ Commands = {
|
||||||
'EXAMINE': ('AUTH', 'SELECTED'),
|
'EXAMINE': ('AUTH', 'SELECTED'),
|
||||||
'EXPUNGE': ('SELECTED',),
|
'EXPUNGE': ('SELECTED',),
|
||||||
'FETCH': ('SELECTED',),
|
'FETCH': ('SELECTED',),
|
||||||
|
'GETACL': ('AUTH', 'SELECTED'),
|
||||||
'LIST': ('AUTH', 'SELECTED'),
|
'LIST': ('AUTH', 'SELECTED'),
|
||||||
'LOGIN': ('NONAUTH',),
|
'LOGIN': ('NONAUTH',),
|
||||||
'LOGOUT': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
|
'LOGOUT': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
|
||||||
'LSUB': ('AUTH', 'SELECTED'),
|
'LSUB': ('AUTH', 'SELECTED'),
|
||||||
|
'NAMESPACE': ('AUTH', 'SELECTED'),
|
||||||
'NOOP': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
|
'NOOP': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
|
||||||
'PARTIAL': ('SELECTED',),
|
'PARTIAL': ('SELECTED',),
|
||||||
'RENAME': ('AUTH', 'SELECTED'),
|
'RENAME': ('AUTH', 'SELECTED'),
|
||||||
'SEARCH': ('SELECTED',),
|
'SEARCH': ('SELECTED',),
|
||||||
'SELECT': ('AUTH', 'SELECTED'),
|
'SELECT': ('AUTH', 'SELECTED'),
|
||||||
|
'SETACL': ('AUTH', 'SELECTED'),
|
||||||
|
'SORT': ('SELECTED',),
|
||||||
'STATUS': ('AUTH', 'SELECTED'),
|
'STATUS': ('AUTH', 'SELECTED'),
|
||||||
'STORE': ('SELECTED',),
|
'STORE': ('SELECTED',),
|
||||||
'SUBSCRIBE': ('AUTH', 'SELECTED'),
|
'SUBSCRIBE': ('AUTH', 'SELECTED'),
|
||||||
'UID': ('SELECTED',),
|
'UID': ('SELECTED',),
|
||||||
'UNSUBSCRIBE': ('AUTH', 'SELECTED'),
|
'UNSUBSCRIBE': ('AUTH', 'SELECTED'),
|
||||||
'NAMESPACE': ('AUTH', 'SELECTED'),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Patterns to match server responses
|
# Patterns to match server responses
|
||||||
|
@ -155,6 +159,7 @@ class IMAP4:
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
if self.debug >= 1:
|
if self.debug >= 1:
|
||||||
|
_mesg('imaplib version %s' % __version__)
|
||||||
_mesg('new IMAP4 connection, tag=%s' % self.tagpre)
|
_mesg('new IMAP4 connection, tag=%s' % self.tagpre)
|
||||||
|
|
||||||
self.welcome = self._get_response()
|
self.welcome = self._get_response()
|
||||||
|
@ -187,21 +192,57 @@ class IMAP4:
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
# Allow UPPERCASE variants of IMAP4 command methods.
|
# Allow UPPERCASE variants of IMAP4 command methods.
|
||||||
if Commands.has_key(attr):
|
if Commands.has_key(attr):
|
||||||
return eval("self.%s" % attr.lower())
|
return getattr(self, attr.lower())
|
||||||
raise AttributeError("Unknown IMAP4 command: '%s'" % attr)
|
raise AttributeError("Unknown IMAP4 command: '%s'" % attr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Public methods
|
# Overridable methods
|
||||||
|
|
||||||
|
|
||||||
def open(self, host, port):
|
def open(self, host, port):
|
||||||
"""Setup 'self.sock' and 'self.file'."""
|
"""Setup connection to remote server on "host:port".
|
||||||
|
This connection will be used by the routines:
|
||||||
|
read, readline, send, shutdown.
|
||||||
|
"""
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.sock.connect((self.host, self.port))
|
self.sock.connect((self.host, self.port))
|
||||||
self.file = self.sock.makefile('r')
|
self.file = self.sock.makefile('r')
|
||||||
|
|
||||||
|
|
||||||
|
def read(self, size):
|
||||||
|
"""Read 'size' bytes from remote."""
|
||||||
|
return self.file.read(size)
|
||||||
|
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
"""Read line from remote."""
|
||||||
|
return self.file.readline()
|
||||||
|
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
"""Send data to remote."""
|
||||||
|
self.sock.send(data)
|
||||||
|
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""Close I/O established in "open"."""
|
||||||
|
self.file.close()
|
||||||
|
self.sock.close()
|
||||||
|
|
||||||
|
|
||||||
|
def socket(self):
|
||||||
|
"""Return socket instance used to connect to IMAP4 server.
|
||||||
|
|
||||||
|
socket = <instance>.socket()
|
||||||
|
"""
|
||||||
|
return self.sock
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Utility methods
|
||||||
|
|
||||||
|
|
||||||
def recent(self):
|
def recent(self):
|
||||||
"""Return most recent 'RECENT' responses if any exist,
|
"""Return most recent 'RECENT' responses if any exist,
|
||||||
else prompt server for an update using the 'NOOP' command.
|
else prompt server for an update using the 'NOOP' command.
|
||||||
|
@ -229,14 +270,6 @@ class IMAP4:
|
||||||
return self._untagged_response(code, [None], code.upper())
|
return self._untagged_response(code, [None], code.upper())
|
||||||
|
|
||||||
|
|
||||||
def socket(self):
|
|
||||||
"""Return socket instance used to connect to IMAP4 server.
|
|
||||||
|
|
||||||
socket = <instance>.socket()
|
|
||||||
"""
|
|
||||||
return self.sock
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# IMAP4 commands
|
# IMAP4 commands
|
||||||
|
|
||||||
|
@ -368,6 +401,15 @@ class IMAP4:
|
||||||
return self._untagged_response(typ, dat, name)
|
return self._untagged_response(typ, dat, name)
|
||||||
|
|
||||||
|
|
||||||
|
def getacl(self, mailbox):
|
||||||
|
"""Get the ACLs for a mailbox.
|
||||||
|
|
||||||
|
(typ, [data]) = <instance>.getacl(mailbox)
|
||||||
|
"""
|
||||||
|
typ, dat = self._simple_command('GETACL', mailbox)
|
||||||
|
return self._untagged_response(typ, dat, 'ACL')
|
||||||
|
|
||||||
|
|
||||||
def list(self, directory='""', pattern='*'):
|
def list(self, directory='""', pattern='*'):
|
||||||
"""List mailbox names in directory matching pattern.
|
"""List mailbox names in directory matching pattern.
|
||||||
|
|
||||||
|
@ -406,8 +448,7 @@ class IMAP4:
|
||||||
self.state = 'LOGOUT'
|
self.state = 'LOGOUT'
|
||||||
try: typ, dat = self._simple_command('LOGOUT')
|
try: typ, dat = self._simple_command('LOGOUT')
|
||||||
except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]]
|
except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]]
|
||||||
self.file.close()
|
self.shutdown()
|
||||||
self.sock.close()
|
|
||||||
if self.untagged_responses.has_key('BYE'):
|
if self.untagged_responses.has_key('BYE'):
|
||||||
return 'BYE', self.untagged_responses['BYE']
|
return 'BYE', self.untagged_responses['BYE']
|
||||||
return typ, dat
|
return typ, dat
|
||||||
|
@ -425,6 +466,16 @@ class IMAP4:
|
||||||
return self._untagged_response(typ, dat, name)
|
return self._untagged_response(typ, dat, name)
|
||||||
|
|
||||||
|
|
||||||
|
def namespace(self):
|
||||||
|
""" Returns IMAP namespaces ala rfc2342
|
||||||
|
|
||||||
|
(typ, [data, ...]) = <instance>.namespace()
|
||||||
|
"""
|
||||||
|
name = 'NAMESPACE'
|
||||||
|
typ, dat = self._simple_command(name)
|
||||||
|
return self._untagged_response(typ, dat, name)
|
||||||
|
|
||||||
|
|
||||||
def noop(self):
|
def noop(self):
|
||||||
"""Send NOOP command.
|
"""Send NOOP command.
|
||||||
|
|
||||||
|
@ -465,8 +516,9 @@ class IMAP4:
|
||||||
"""
|
"""
|
||||||
name = 'SEARCH'
|
name = 'SEARCH'
|
||||||
if charset:
|
if charset:
|
||||||
charset = 'CHARSET ' + charset
|
typ, dat = apply(self._simple_command, (name, 'CHARSET', charset) + criteria)
|
||||||
typ, dat = apply(self._simple_command, (name, charset) + criteria)
|
else:
|
||||||
|
typ, dat = apply(self._simple_command, (name,) + criteria)
|
||||||
return self._untagged_response(typ, dat, name)
|
return self._untagged_response(typ, dat, name)
|
||||||
|
|
||||||
|
|
||||||
|
@ -500,14 +552,36 @@ class IMAP4:
|
||||||
return typ, self.untagged_responses.get('EXISTS', [None])
|
return typ, self.untagged_responses.get('EXISTS', [None])
|
||||||
|
|
||||||
|
|
||||||
|
def setacl(self, mailbox, who, what):
|
||||||
|
"""Set a mailbox acl.
|
||||||
|
|
||||||
|
(typ, [data]) = <instance>.create(mailbox, who, what)
|
||||||
|
"""
|
||||||
|
return self._simple_command('SETACL', mailbox, who, what)
|
||||||
|
|
||||||
|
|
||||||
|
def sort(self, sort_criteria, charset, *search_criteria):
|
||||||
|
"""IMAP4rev1 extension SORT command.
|
||||||
|
|
||||||
|
(typ, [data]) = <instance>.sort(sort_criteria, charset, search_criteria, ...)
|
||||||
|
"""
|
||||||
|
name = 'SORT'
|
||||||
|
#if not name in self.capabilities: # Let the server decide!
|
||||||
|
# raise self.error('unimplemented extension command: %s' % name)
|
||||||
|
if (sort_criteria[0],sort_criteria[-1]) != ('(',')'):
|
||||||
|
sort_criteria = '(%s)' % sort_criteria
|
||||||
|
typ, dat = apply(self._simple_command, (name, sort_criteria, charset) + search_criteria)
|
||||||
|
return self._untagged_response(typ, dat, name)
|
||||||
|
|
||||||
|
|
||||||
def status(self, mailbox, names):
|
def status(self, mailbox, names):
|
||||||
"""Request named status conditions for mailbox.
|
"""Request named status conditions for mailbox.
|
||||||
|
|
||||||
(typ, [data]) = <instance>.status(mailbox, names)
|
(typ, [data]) = <instance>.status(mailbox, names)
|
||||||
"""
|
"""
|
||||||
name = 'STATUS'
|
name = 'STATUS'
|
||||||
if self.PROTOCOL_VERSION == 'IMAP4':
|
#if self.PROTOCOL_VERSION == 'IMAP4': # Let the server decide!
|
||||||
raise self.error('%s unimplemented in IMAP4 (obtain IMAP4rev1 server, or re-code)' % name)
|
# raise self.error('%s unimplemented in IMAP4 (obtain IMAP4rev1 server, or re-code)' % name)
|
||||||
typ, dat = self._simple_command(name, mailbox, names)
|
typ, dat = self._simple_command(name, mailbox, names)
|
||||||
return self._untagged_response(typ, dat, name)
|
return self._untagged_response(typ, dat, name)
|
||||||
|
|
||||||
|
@ -547,8 +621,8 @@ class IMAP4:
|
||||||
% (command, self.state))
|
% (command, self.state))
|
||||||
name = 'UID'
|
name = 'UID'
|
||||||
typ, dat = apply(self._simple_command, (name, command) + args)
|
typ, dat = apply(self._simple_command, (name, command) + args)
|
||||||
if command == 'SEARCH':
|
if command in ('SEARCH', 'SORT'):
|
||||||
name = 'SEARCH'
|
name = command
|
||||||
else:
|
else:
|
||||||
name = 'FETCH'
|
name = 'FETCH'
|
||||||
return self._untagged_response(typ, dat, name)
|
return self._untagged_response(typ, dat, name)
|
||||||
|
@ -566,18 +640,19 @@ class IMAP4:
|
||||||
"""Allow simple extension commands
|
"""Allow simple extension commands
|
||||||
notified by server in CAPABILITY response.
|
notified by server in CAPABILITY response.
|
||||||
|
|
||||||
|
Assumes command is legal in current state.
|
||||||
|
|
||||||
(typ, [data]) = <instance>.xatom(name, arg, ...)
|
(typ, [data]) = <instance>.xatom(name, arg, ...)
|
||||||
|
|
||||||
|
Returns response appropriate to extension command `name'.
|
||||||
"""
|
"""
|
||||||
if name[0] != 'X' or not name in self.capabilities:
|
name = name.upper()
|
||||||
raise self.error('unknown extension command: %s' % name)
|
#if not name in self.capabilities: # Let the server decide!
|
||||||
|
# raise self.error('unknown extension command: %s' % name)
|
||||||
|
if not Commands.has_key(name):
|
||||||
|
Commands[name] = (self.state,)
|
||||||
return apply(self._simple_command, (name,) + args)
|
return apply(self._simple_command, (name,) + args)
|
||||||
|
|
||||||
def namespace(self):
|
|
||||||
""" Returns IMAP namespaces ala rfc2342
|
|
||||||
"""
|
|
||||||
name = 'NAMESPACE'
|
|
||||||
typ, dat = self._simple_command(name)
|
|
||||||
return self._untagged_response(typ, dat, name)
|
|
||||||
|
|
||||||
|
|
||||||
# Private methods
|
# Private methods
|
||||||
|
@ -640,8 +715,8 @@ class IMAP4:
|
||||||
_log('> %s' % data)
|
_log('> %s' % data)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sock.send('%s%s' % (data, CRLF))
|
self.send('%s%s' % (data, CRLF))
|
||||||
except socket.error, val:
|
except (socket.error, OSError), val:
|
||||||
raise self.abort('socket error: %s' % val)
|
raise self.abort('socket error: %s' % val)
|
||||||
|
|
||||||
if literal is None:
|
if literal is None:
|
||||||
|
@ -664,9 +739,9 @@ class IMAP4:
|
||||||
_mesg('write literal size %s' % len(literal))
|
_mesg('write literal size %s' % len(literal))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sock.send(literal)
|
self.send(literal)
|
||||||
self.sock.send(CRLF)
|
self.send(CRLF)
|
||||||
except socket.error, val:
|
except (socket.error, OSError), val:
|
||||||
raise self.abort('socket error: %s' % val)
|
raise self.abort('socket error: %s' % val)
|
||||||
|
|
||||||
if not literator:
|
if not literator:
|
||||||
|
@ -741,7 +816,7 @@ class IMAP4:
|
||||||
if __debug__:
|
if __debug__:
|
||||||
if self.debug >= 4:
|
if self.debug >= 4:
|
||||||
_mesg('read literal size %s' % size)
|
_mesg('read literal size %s' % size)
|
||||||
data = self.file.read(size)
|
data = self.read(size)
|
||||||
|
|
||||||
# Store response with literal as tuple
|
# Store response with literal as tuple
|
||||||
|
|
||||||
|
@ -789,7 +864,7 @@ class IMAP4:
|
||||||
|
|
||||||
def _get_line(self):
|
def _get_line(self):
|
||||||
|
|
||||||
line = self.file.readline()
|
line = self.readline()
|
||||||
if not line:
|
if not line:
|
||||||
raise self.abort('socket error: EOF')
|
raise self.abort('socket error: EOF')
|
||||||
|
|
||||||
|
@ -1059,7 +1134,7 @@ if __name__ == '__main__':
|
||||||
host = args[0]
|
host = args[0]
|
||||||
|
|
||||||
USER = getpass.getuser()
|
USER = getpass.getuser()
|
||||||
PASSWD = getpass.getpass("IMAP password for %s on %s:" % (USER, host or "localhost"))
|
PASSWD = getpass.getpass("IMAP password for %s on %s: " % (USER, host or "localhost"))
|
||||||
|
|
||||||
test_mesg = 'From: %s@localhost\nSubject: IMAP4 test\n\ndata...\n' % USER
|
test_mesg = 'From: %s@localhost\nSubject: IMAP4 test\n\ndata...\n' % USER
|
||||||
test_seq1 = (
|
test_seq1 = (
|
||||||
|
@ -1073,6 +1148,7 @@ if __name__ == '__main__':
|
||||||
('search', (None, 'SUBJECT', 'test')),
|
('search', (None, 'SUBJECT', 'test')),
|
||||||
('partial', ('1', 'RFC822', 1, 1024)),
|
('partial', ('1', 'RFC822', 1, 1024)),
|
||||||
('store', ('1', 'FLAGS', '(\Deleted)')),
|
('store', ('1', 'FLAGS', '(\Deleted)')),
|
||||||
|
('namespace', ()),
|
||||||
('expunge', ()),
|
('expunge', ()),
|
||||||
('recent', ()),
|
('recent', ()),
|
||||||
('close', ()),
|
('close', ()),
|
||||||
|
@ -1090,13 +1166,14 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
def run(cmd, args):
|
def run(cmd, args):
|
||||||
_mesg('%s %s' % (cmd, args))
|
_mesg('%s %s' % (cmd, args))
|
||||||
typ, dat = apply(eval('M.%s' % cmd), args)
|
typ, dat = apply(getattr(M, cmd), args)
|
||||||
_mesg('%s => %s %s' % (cmd, typ, dat))
|
_mesg('%s => %s %s' % (cmd, typ, dat))
|
||||||
return dat
|
return dat
|
||||||
|
|
||||||
try:
|
try:
|
||||||
M = IMAP4(host)
|
M = IMAP4(host)
|
||||||
_mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
|
_mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
|
||||||
|
_mesg('CAPABILITIES = %s' % `M.capabilities`)
|
||||||
|
|
||||||
for cmd,args in test_seq1:
|
for cmd,args in test_seq1:
|
||||||
run(cmd, args)
|
run(cmd, args)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue