apply patch item #416253

This commit is contained in:
Piers Lauder 2001-07-20 10:52:06 +00:00
parent 34d9705943
commit 15e5d5344d

View file

@ -14,8 +14,9 @@
# #
# 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 @@
'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 @@ def __init__(self, host = '', port = IMAP4_PORT):
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 @@ def __init__(self, host = '', port = IMAP4_PORT):
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 @@ def response(self, code):
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 @@ def fetch(self, message_set, message_parts):
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 @@ def logout(self):
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 @@ def lsub(self, directory='""', pattern='*'):
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 @@ def search(self, charset, *criteria):
""" """
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 @@ def select(self, mailbox='INBOX', readonly=None):
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 @@ def uid(self, command, *args):
% (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 @@ def xatom(self, name, *args):
"""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 @@ def _command(self, name, *args):
_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 @@ def _command(self, name, *args):
_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 @@ def _get_response(self):
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 @@ def _get_tagged_response(self, tag):
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')
@ -1073,6 +1148,7 @@ def print_log():
('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 @@ def print_log():
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)