OfflineIMAP: upgrade to 6.3.4

- removed applied upstream patch;
 - added patch for MANUAL.rst that eliminates RST -> HTML
   translation error.

Relevant changelog entries since 6.3.3:

OfflineIMAP v6.3.4 (2011-08-10)
===============================

* CHANGE: Handle when UID can't be found on saved messages.

OfflineIMAP v6.3.4-rc4 (2011-07-27)
===================================

* CHANGE: Manual improved.

OfflineIMAP v6.3.4-rc3 (2011-07-07)
===================================

* NEW FEATURE: Added StartTLS support, it will automatically be used
  if the server supports it.

* BUG FIX: We protect more robustly against asking for inexistent
  messages from the IMAP server, when someone else deletes or moves
  messages while we sync.

OfflineIMAP v6.3.4-rc2 (2011-06-15)
===================================

* NEW FEATURE: Implement experimental IDLE feature.

* CHANGE: Maildirs use less memory while syncing.

* BUG FIX: Saving to Maildirs now checks for file existence without
  race conditions.

* BUG FIX: A bug in the underlying imap library has been fixed
  that could potentially lead to data loss if the server interrupted
  responses with unexpected but legal server status responses.
  This would mainly occur in folders with many thousands of emails.
  Upgrading from the previous release is strongly recommended.

NOTE: the latter bug fix was already integrated into FreeBSD port
version 6.3.3_1.

OfflineIMAP v6.3.4-rc1 (2011-05-16)
===================================

* NEW FEATURE: Begin sphinx-based documentation for the code.

* NEW FEATURE: Enable 1-way synchronization by settting a
  [Repository ...] to readonly = True. When e.g. using offlineimap
  for backup purposes you can thus make sure that no changes in your
  backup trickle back into the main IMAP server.

* NEW FEATURE: Optional: experimental SQLite-based backend for the
  LocalStatus cache. Plain text remains the default.

* CHANGE: Start a enhanced error handling background system. This is
  designed to not stop a whole sync process on all errors (not much
  used, yet).

* CHANGE: Documentation improvements: the FAQ wins new entries and add
  a new HACKING file for developers.

* CHANGE: Lot of code cleanups.

* CHANGE: Reduced our sync logic from 4 passes to 3 passes
  (integrating upload of "new" and "existing" messages into one
  function). This should result in a slight speedup.

* CHANGE: No whitespace is stripped from comma-separated arguments
  passed via the -f option.

* CHANGE: Give more detailed error when encountering a corrupt UID
  mapping file.

* BUG FIX: Drop connection if synchronization failed. This is needed
  if resuming the system from suspend mode gives a wrong connection.

* BUG FIX: Fix the offlineimap crash when invoking debug option 'thread'.

* BUG FIX: Make 'thread' command line option work.

PR: ports/159815
Approved-by: sergei (maintainer timeout)
Feature-safe: yes
This commit is contained in:
Eygene Ryabinkin 2011-09-11 20:20:55 +00:00
parent c5c68361eb
commit 94b08c8e01
Notes: svn2git 2021-03-31 03:12:20 +00:00
svn path=/head/; revision=281639
5 changed files with 35 additions and 869 deletions

View file

@ -6,13 +6,12 @@
#
PORTNAME= offlineimap
DISTVERSION= 6.3.3
PORTREVISION= 1
DISTVERSION= 6.3.4
CATEGORIES= mail python
MASTER_SITES= http://download.github.com/ \
http://dist.codelabs.ru/fbsd/offlineimap/ \
LOCAL/rea/offlineimap
DISTNAME= ${GITHUB_USER}-${PORTNAME}-v${DISTVERSION}-0-g3b09695
DISTNAME= ${GITHUB_USER}-${PORTNAME}-v${DISTVERSION}-${GITHUB_VER_SUFX}
MAINTAINER= sergei@FreeBSD.org
COMMENT= Powerful IMAP/Maildir synchronization and reader support
@ -24,10 +23,12 @@ LICENSE_FILE_GPLv2=${WRKSRC}/COPYING
OPTIONS= HTMLDOCS "Generate HTML documentation" Off
WRKSRC= ${WRKDIR}/${GITHUB_USER}-${PORTNAME}-cf325d5
WRKSRC= ${WRKDIR}/${GITHUB_USER}-${PORTNAME}-${GITHUB_SRC_SUFX}
SUB_FILES= pkg-message
GITHUB_USER= nicolas33
GITHUB_VER_SUFX= 0-g61e50b3
GITHUB_SRC_SUFX= 94450e9
USE_PYTHON= yes
USE_PYDISTUTILS= yes

View file

@ -1,2 +1,2 @@
SHA256 (nicolas33-offlineimap-v6.3.3-0-g3b09695.tar.gz) = 762591144f13423da6020ba840e2eb216c4ed45839f1881ac90d9e3dd57f4eca
SIZE (nicolas33-offlineimap-v6.3.3-0-g3b09695.tar.gz) = 117153
SHA256 (nicolas33-offlineimap-v6.3.4-0-g61e50b3.tar.gz) = a2ac69a58605338e971658e930e095e4c2611383837a9378b88c0749cd374333
SIZE (nicolas33-offlineimap-v6.3.4-0-g61e50b3.tar.gz) = 137313

View file

@ -1,863 +0,0 @@
commit 520e39d35536172d03b22d012cf4d8828687f2ff
Author: Sebastian Spaeth <Sebastian@SSpaeth.de>
Date: Thu Jun 9 15:26:14 2011 +0200
Update imaplib2 to 2.24
In some cases we had offlineimap trying to delete emails that shouldn't
be deleted. E.g. https://bugzilla.redhat.com/show_bug.cgi?id=708898.
It turns out that imaplib2 does not like FETCH responses that are
interrupted by other unsolicited server responses, e.g.
* OK Searched 43% of the mailbox, ETA 0:12\r\n
Bump imaplib2 to a version that can cope with these (legal) responses by
the IMAP server.
Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
diff --git offlineimap/imaplib2.py offlineimap/imaplib2.py
index be34025..ec6cd0d 100644
--- offlineimap/imaplib2.py
+++ offlineimap/imaplib2.py
@@ -17,9 +17,9 @@ Public functions: Internaldate2Time
__all__ = ("IMAP4", "IMAP4_SSL", "IMAP4_stream",
"Internaldate2Time", "ParseFlags", "Time2Internaldate")
-__version__ = "2.22"
+__version__ = "2.24"
__release__ = "2"
-__revision__ = "22"
+__revision__ = "24"
__credits__ = """
Authentication code contributed by Donn Cave <donn@u.washington.edu> June 1998.
String method conversion by ESR, February 2001.
@@ -36,9 +36,11 @@ Improved untagged responses handling suggested by Dave Baggett <dave@baggett.org
Improved thread naming, and 0 read detection contributed by Grant Edwards <grant.b.edwards@gmail.com> June 2010.
Improved timeout handling contributed by Ivan Vovnenko <ivovnenko@gmail.com> October 2010.
Timeout handling further improved by Ethan Glasser-Camp <glasse@cs.rpi.edu> December 2010.
-Time2Internaldate() patch to match RFC2060 specification of English month names from http://bugs.python.org/issue11024 March 2011."""
+Time2Internaldate() patch to match RFC2060 specification of English month names from bugs.python.org/issue11024 March 2011.
+starttls() bug fixed with the help of Sebastian Spaeth <sebastian@sspaeth.de> April 2011.
+Threads now set the "daemon" flag (suggested by offlineimap-project)."""
__author__ = "Piers Lauder <piers@janeelix.com>"
-__URL__ = "http://janeelix.com/piers/python/imaplib2"
+__URL__ = "http://imaplib2.sourceforge.net"
__license__ = "Python License"
import binascii, errno, os, Queue, random, re, select, socket, sys, time, threading, zlib
@@ -55,6 +57,9 @@ IMAP4_SSL_PORT = 993
IDLE_TIMEOUT_RESPONSE = '* IDLE TIMEOUT\r\n'
IDLE_TIMEOUT = 60*29 # Don't stay in IDLE state longer
READ_POLL_TIMEOUT = 30 # Without this timeout interrupted network connections can hang reader
+READ_SIZE = 32768 # Consume all available in socket
+
+DFLT_DEBUG_BUF_LVL = 3 # Level above which the logging output goes directly to stderr
AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first
@@ -133,6 +138,7 @@ class Request(object):
"""Private class to represent a request awaiting response."""
def __init__(self, parent, name=None, callback=None, cb_arg=None):
+ self.parent = parent
self.name = name
self.callback = callback # Function called to process result
self.callback_arg = cb_arg # Optional arg passed to "callback"
@@ -147,12 +153,16 @@ class Request(object):
def abort(self, typ, val):
+ """Called whenever we abort a command
+
+ Sets self.aborted reason, and deliver()s nothing"""
self.aborted = (typ, val)
self.deliver(None)
def get_response(self, exc_fmt=None):
self.callback = None
+ if __debug__: self.parent._log(3, '%s:%s.ready.wait' % (self.name, self.tag))
self.ready.wait()
if self.aborted is not None:
@@ -171,6 +181,7 @@ class Request(object):
self.response = response
self.ready.set()
+ if __debug__: self.parent._log(3, '%s:%s.ready.set' % (self.name, self.tag))
@@ -180,14 +191,15 @@ class IMAP4(object):
"""Threaded IMAP4 client class.
Instantiate with:
- IMAP4(host=None, port=None, debug=None, debug_file=None, identifier=None, timeout=None)
+ IMAP4(host=None, port=None, debug=None, debug_file=None, identifier=None, timeout=None, debug_buf_lvl=None)
- host - host's name (default: localhost);
- port - port number (default: standard IMAP4 port);
- debug - debug level (default: 0 - no debug);
- debug_file - debug stream (default: sys.stderr);
- identifier - thread identifier prefix (default: host);
- timeout - timeout in seconds when expecting a command response (default: no timeout).
+ host - host's name (default: localhost);
+ port - port number (default: standard IMAP4 port);
+ debug - debug level (default: 0 - no debug);
+ debug_file - debug stream (default: sys.stderr);
+ identifier - thread identifier prefix (default: host);
+ timeout - timeout in seconds when expecting a command response (default: no timeout),
+ debug_buf_lvl - debug level at which buffering is turned off.
All IMAP4rev1 commands are supported by methods of the same name.
@@ -267,7 +279,7 @@ class IMAP4(object):
untagged_status_cre = re.compile(r'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?')
- def __init__(self, host=None, port=None, debug=None, debug_file=None, identifier=None, timeout=None):
+ def __init__(self, host=None, port=None, debug=None, debug_file=None, identifier=None, timeout=None, debug_buf_lvl=None):
self.state = NONAUTH # IMAP4 protocol state
self.literal = None # A literal argument to a command
@@ -295,7 +307,7 @@ class IMAP4(object):
+ self.tagpre
+ r'\d+) (?P<type>[A-Z]+) (?P<data>.*)')
- if __debug__: self._init_debug(debug, debug_file)
+ if __debug__: self._init_debug(debug, debug_file, debug_buf_lvl)
self.resp_timeout = timeout # Timeout waiting for command response
@@ -303,6 +315,7 @@ class IMAP4(object):
self.read_poll_timeout = timeout
else:
self.read_poll_timeout = READ_POLL_TIMEOUT
+ self.read_size = READ_SIZE
# Open socket to server.
@@ -321,20 +334,26 @@ class IMAP4(object):
if self.identifier:
self.identifier += ' '
- self.Terminate = False
+ self.Terminate = self.TerminateReader = False
self.state_change_free = threading.Event()
self.state_change_pending = threading.Lock()
self.commands_lock = threading.Lock()
+ """commands_lock prevents self.untagged_responses to be
+ manipulated concurrently"""
+ self.idle_lock = threading.Lock()
self.ouq = Queue.Queue(10)
self.inq = Queue.Queue()
self.wrth = threading.Thread(target=self._writer)
+ self.wrth.setDaemon(True)
self.wrth.start()
self.rdth = threading.Thread(target=self._reader)
+ self.rdth.setDaemon(True)
self.rdth.start()
self.inth = threading.Thread(target=self._handler)
+ self.inth.setDaemon(True)
self.inth.start()
# Get server welcome message,
@@ -387,8 +406,8 @@ class IMAP4(object):
This connection will be used by the routines:
read, send, shutdown, socket."""
- self.host = host is not None and host or ''
- self.port = port is not None and port or IMAP4_PORT
+ self.host = self._choose_nonull_or_dflt('', host)
+ self.port = self._choose_nonull_or_dflt(IMAP4_PORT, port)
self.sock = self.open_socket()
self.read_fd = self.sock.fileno()
@@ -490,7 +509,7 @@ class IMAP4(object):
self.start_compressing()
if __debug__: self._log(1, 'Enabled COMPRESS=DEFLATE')
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def pop_untagged_responses(self):
@@ -515,16 +534,9 @@ class IMAP4(object):
else list of RECENT responses, most recent last."""
name = 'RECENT'
-
- data = []
- while True:
- dat = self._get_untagged_response(name)
- if not dat:
- break
- data += dat
-
- if data:
- return self._deliver_dat(name, data, kw)
+ typ, dat = self._untagged_response(None, [None], name)
+ if dat != [None]:
+ return self._deliver_dat(typ, dat, kw)
kw['untagged_response'] = name
return self.noop(**kw) # Prod server for response
@@ -564,7 +576,7 @@ class IMAP4(object):
try:
return self._simple_command(name, mailbox, flags, date_time, **kw)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def authenticate(self, mechanism, authobject, **kw):
@@ -592,7 +604,7 @@ class IMAP4(object):
self.state = AUTH
if __debug__: self._log(1, 'state => AUTH')
finally:
- self.state_change_pending.release()
+ self._release_state_change()
return self._deliver_dat(typ, dat, kw)
@@ -626,7 +638,7 @@ class IMAP4(object):
finally:
self.state = AUTH
if __debug__: self._log(1, 'state => AUTH')
- self.state_change_pending.release()
+ self._release_state_change()
return self._deliver_dat(typ, dat, kw)
@@ -753,7 +765,7 @@ class IMAP4(object):
try:
return self._simple_command(name, **kw)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def list(self, directory='""', pattern='*', **kw):
@@ -782,7 +794,7 @@ class IMAP4(object):
self.state = AUTH
if __debug__: self._log(1, 'state => AUTH')
finally:
- self.state_change_pending.release()
+ self._release_state_change()
return self._deliver_dat(typ, dat, kw)
@@ -810,14 +822,15 @@ class IMAP4(object):
if __debug__: self._log(1, 'state => LOGOUT')
try:
- typ, dat = self._simple_command('LOGOUT')
- except:
- typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]]
- if __debug__: self._log(1, dat)
-
- self._close_threads()
+ try:
+ typ, dat = self._simple_command('LOGOUT')
+ except:
+ typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]]
+ if __debug__: self._log(1, dat)
- self.state_change_pending.release()
+ self._close_threads()
+ finally:
+ self._release_state_change()
if __debug__: self._log(1, 'connection closed')
@@ -882,7 +895,7 @@ class IMAP4(object):
try:
return self._simple_command('PROXYAUTH', user, **kw)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def rename(self, oldmailbox, newmailbox, **kw):
@@ -937,7 +950,7 @@ class IMAP4(object):
self.state = SELECTED
if __debug__: self._log(1, 'state => SELECTED')
finally:
- self.state_change_pending.release()
+ self._release_state_change()
if self._get_untagged_response('READ-ONLY', leave=True) and not readonly:
if __debug__: self._dump_ur(1)
@@ -953,7 +966,7 @@ class IMAP4(object):
try:
return self._simple_command('SETACL', mailbox, who, what, **kw)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def setannotation(self, *args, **kw):
@@ -972,7 +985,7 @@ class IMAP4(object):
try:
return self._simple_command('SETQUOTA', root, limits, **kw)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def sort(self, sort_criteria, charset, *search_criteria, **kw):
@@ -998,23 +1011,46 @@ class IMAP4(object):
if hasattr(self, '_tls_established') and self._tls_established:
raise self.abort('TLS session already established')
+ # Must now shutdown reader thread after next response, and restart after changing read_fd
+
+ self.read_size = 1 # Don't consume TLS handshake
+ self.TerminateReader = True
+
try:
typ, dat = self._simple_command(name)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
+ self.rdth.join()
+ self.TerminateReader = False
+ self.read_size = READ_SIZE
+
+ if typ != 'OK':
+ # Restart reader thread and error
+ self.rdth = threading.Thread(target=self._reader)
+ self.rdth.setDaemon(True)
+ self.rdth.start()
+ raise self.error("Couldn't establish TLS session: %s" % dat)
+
+ try:
+ try:
+ import ssl
+ self.sock = ssl.wrap_socket(self.sock, keyfile, certfile)
+ except ImportError:
+ self.sock = socket.ssl(self.sock, keyfile, certfile)
- if typ == 'OK':
- import ssl
- self.sock = ssl.wrap_socket(self.sock, keyfile, certfile)
self.read_fd = self.sock.fileno()
+ finally:
+ # Restart reader thread
+ self.rdth = threading.Thread(target=self._reader)
+ self.rdth.setDaemon(True)
+ self.rdth.start()
- typ, dat = self.capability()
- if dat == [None]:
- raise self.error('no CAPABILITY response from server')
- self.capabilities = tuple(dat[-1].upper().split())
- self._tls_established = True
- else:
- raise self.error("Couldn't establish TLS session: %s" % dat)
+ typ, dat = self.capability()
+ if dat == [None]:
+ raise self.error('no CAPABILITY response from server')
+ self.capabilities = tuple(dat[-1].upper().split())
+
+ self._tls_established = True
typ, dat = self._untagged_response(typ, dat, name)
return self._deliver_dat(typ, dat, kw)
@@ -1046,7 +1082,7 @@ class IMAP4(object):
try:
return self._simple_command('SUBSCRIBE', mailbox, **kw)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def thread(self, threading_algorithm, charset, *search_criteria, **kw):
@@ -1081,7 +1117,7 @@ class IMAP4(object):
try:
return self._simple_command('UNSUBSCRIBE', mailbox, **kw)
finally:
- self.state_change_pending.release()
+ self._release_state_change()
def xatom(self, name, *args, **kw):
@@ -1096,8 +1132,7 @@ class IMAP4(object):
try:
return self._simple_command(name, *args, **kw)
finally:
- if self.state_change_pending.locked():
- self.state_change_pending.release()
+ self._release_state_change()
@@ -1105,34 +1140,29 @@ class IMAP4(object):
def _append_untagged(self, typ, dat):
+ """Append new untagged response
- # Append new 'dat' to end of last untagged response if same 'typ',
- # else append new response.
-
+ Append new 'dat' to end of last untagged response if same 'typ',
+ else append new response."""
if dat is None: dat = ''
+ ur_data = []
- self.commands_lock.acquire()
+ self.commands_lock.acquire() # protect untagged_responses
- if self.untagged_responses:
- urn, urd = self.untagged_responses[-1]
- if urn != typ:
- urd = None
+ if self.untagged_responses and self.untagged_responses[-1][0] == typ:
+ # last respons is of type 'typ', get ur_data for appending
+ ur_data = self.untagged_responses[-1][1]
else:
- urd = None
-
- if urd is None:
- urd = []
- self.untagged_responses.append([typ, urd])
-
- urd.append(dat)
+ # need to create new untagged response of this type
+ self.untagged_responses.append([typ, ur_data])
+ ur_data.append(dat)
self.commands_lock.release()
-
- if __debug__: self._log(5, 'untagged_responses[%s] %s += ["%s"]' % (typ, len(urd)-1, dat))
+ if __debug__: self._log(5, 'untagged_responses[%s] %s += ["%s"]' % (typ, len(ur_data)-1, dat))
def _check_bye(self):
-
+ """raise Exception if untagged responses contains a 'BYE'"""
bye = self._get_untagged_response('BYE', leave=True)
if bye:
raise self.abort(bye[-1])
@@ -1152,6 +1182,16 @@ class IMAP4(object):
return self._quote(arg)
+ def _choose_nonull_or_dflt(self, dflt, *args):
+ dflttyp = type(dflt)
+ for arg in args:
+ if arg is not None:
+ if type(arg) is dflttyp:
+ return arg
+ if __debug__: self._log(1, 'bad arg type is %s, expecting %s' % (type(arg), dflttyp))
+ return dflt
+
+
def _command(self, name, *args, **kw):
if Commands[name][CMD_VAL_ASYNC]:
@@ -1161,12 +1201,14 @@ class IMAP4(object):
if __debug__: self._log(1, '[%s] %s %s' % (cmdtyp, name, args))
+ if __debug__: self._log(3, 'state_change_pending.acquire')
self.state_change_pending.acquire()
self._end_idle()
if cmdtyp == 'async':
self.state_change_pending.release()
+ if __debug__: self._log(3, 'state_change_pending.release')
else:
# Need to wait for all async commands to complete
self._check_bye()
@@ -1178,9 +1220,9 @@ class IMAP4(object):
need_event = False
self.commands_lock.release()
if need_event:
- if __debug__: self._log(4, 'sync command %s waiting for empty commands Q' % name)
+ if __debug__: self._log(3, 'sync command %s waiting for empty commands Q' % name)
self.state_change_free.wait()
- if __debug__: self._log(4, 'sync command %s proceeding' % name)
+ if __debug__: self._log(3, 'sync command %s proceeding' % name)
if self.state not in Commands[name][CMD_VAL_STATES]:
self.literal = None
@@ -1232,7 +1274,7 @@ class IMAP4(object):
# Wait for continuation response
ok, data = crqb.get_response('command: %s => %%s' % name)
- if __debug__: self._log(3, 'continuation => %s, %s' % (ok, data))
+ if __debug__: self._log(4, 'continuation => %s, %s' % (ok, data))
# NO/BAD response?
@@ -1316,18 +1358,25 @@ class IMAP4(object):
def _end_idle(self):
+ self.idle_lock.acquire()
irqb = self.idle_rqb
if irqb is None:
+ self.idle_lock.release()
return
self.idle_rqb = None
self.idle_timeout = None
+ self.idle_lock.release()
irqb.data = 'DONE%s' % CRLF
self.ouq.put(irqb)
if __debug__: self._log(2, 'server IDLE finished')
def _get_untagged_response(self, name, leave=False):
+ """Return an untagged response of type 'name'
+ :param leave: If leave (default: False) is True, we keep the
+ fetched responsem; otherwise it will be deleted. Returns
+ None if no such response found."""
self.commands_lock.acquire()
for i, (typ, dat) in enumerate(self.untagged_responses):
@@ -1434,7 +1483,7 @@ class IMAP4(object):
self._append_untagged(typ, dat)
- if typ != 'OK':
+ if typ != 'OK': # NO, BYE, IDLE
self._end_idle()
# Bracketed response information?
@@ -1460,14 +1509,22 @@ class IMAP4(object):
return '"%s"' % arg.replace('\\', '\\\\').replace('"', '\\"')
+ def _release_state_change(self):
+
+ if self.state_change_pending.locked():
+ self.state_change_pending.release()
+ if __debug__: self._log(3, 'state_change_pending.release')
+
+
def _request_pop(self, name, data):
- if __debug__: self._log(4, '_request_pop(%s, %s)' % (name, data))
self.commands_lock.acquire()
rqb = self.tagged_commands.pop(name)
if not self.tagged_commands:
+ if __debug__: self._log(3, 'state_change_free.set')
self.state_change_free.set()
self.commands_lock.release()
+ if __debug__: self._log(4, '_request_pop(%s, %s) = %s' % (name, data, rqb.tag))
rqb.deliver(data)
@@ -1479,7 +1536,7 @@ class IMAP4(object):
tag = rqb.tag
self.tagged_commands[tag] = rqb
self.commands_lock.release()
- if __debug__: self._log(4, '_request_push(%s, %s, %s)' % (tag, name, `kw`))
+ if __debug__: self._log(4, '_request_push(%s, %s, %s) = %s' % (tag, name, `kw`, rqb.tag))
return rqb
@@ -1493,12 +1550,28 @@ class IMAP4(object):
def _untagged_response(self, typ, dat, name):
-
+ """Returns an untagged response for 'name' of type 'typ'
+
+ :param typ: 'OK, 'NO', etc... which will be used for the type of
+ the response.
+ :param dat: The fallback data to be used in case `typ` is
+ 'NO'. Otherwise the data from the existing untagged
+ responses will be searched for data to be returned. If there
+ is no such response, we return `[None]` as data.
+ :param name: The name of the response.
+ :returns: (typ, data)
+ """
if typ == 'NO':
return typ, dat
data = self._get_untagged_response(name)
if not data:
return typ, [None]
+ while True:
+ dat = self._get_untagged_response(name)
+ if not dat:
+ break
+ data += dat
+ if __debug__: self._log(4, '_untagged_response(%s, ?, %s) => %s' % (typ, name, data))
return typ, data
@@ -1588,6 +1661,7 @@ class IMAP4(object):
rqb.abort(typ, val)
self.state_change_free.set()
self.commands_lock.release()
+ if __debug__: self._log(3, 'state_change_free.set')
if __debug__: self._log(1, 'finished')
@@ -1615,9 +1689,10 @@ class IMAP4(object):
poll.register(self.read_fd, select.POLLIN)
rxzero = 0
+ terminate = False
read_poll_timeout = self.read_poll_timeout * 1000 # poll() timeout is in millisecs
- while not self.Terminate:
+ while not (terminate or self.Terminate):
if self.state == LOGOUT:
timeout = 1
else:
@@ -1631,7 +1706,7 @@ class IMAP4(object):
fd,state = r[0]
if state & select.POLLIN:
- data = self.read(32768) # Drain ssl buffer if present
+ data = self.read(self.read_size) # Drain ssl buffer if present
start = 0
dlen = len(data)
if __debug__: self._log(5, 'rcvd %s' % dlen)
@@ -1652,6 +1727,8 @@ class IMAP4(object):
'', stop, line_part + data[start:stop]
if __debug__: self._log(4, '< %s' % line)
self.inq.put(line)
+ if self.TerminateReader:
+ terminate = True
if state & ~(select.POLLIN):
raise IOError(poll_error(state))
@@ -1682,20 +1759,20 @@ class IMAP4(object):
line_part = ''
rxzero = 0
- read_poll_timeout = self.read_poll_timeout
+ terminate = False
- while not self.Terminate:
+ while not (terminate or self.Terminate):
if self.state == LOGOUT:
timeout = 1
else:
- timeout = read_poll_timeout
+ timeout = self.read_poll_timeout
try:
r,w,e = select.select([self.read_fd], [], [], timeout)
if __debug__: self._log(5, 'select => %s, %s, %s' % (r,w,e))
if not r: # Timeout
continue
- data = self.read(32768) # Drain ssl buffer if present
+ data = self.read(self.read_size) # Drain ssl buffer if present
start = 0
dlen = len(data)
if __debug__: self._log(5, 'rcvd %s' % dlen)
@@ -1716,6 +1793,8 @@ class IMAP4(object):
'', stop, line_part + data[start:stop]
if __debug__: self._log(4, '< %s' % line)
self.inq.put(line)
+ if self.TerminateReader:
+ terminate = True
except:
reason = 'socket error: %s - %s' % sys.exc_info()[:2]
if __debug__:
@@ -1766,9 +1845,10 @@ class IMAP4(object):
if __debug__:
- def _init_debug(self, debug=None, debug_file=None):
- self.debug = debug is not None and debug or Debug is not None and Debug or 0
- self.debug_file = debug_file is not None and debug_file or sys.stderr
+ def _init_debug(self, debug=None, debug_file=None, debug_buf_lvl=None):
+ self.debug = self._choose_nonull_or_dflt(0, debug, Debug)
+ self.debug_file = self._choose_nonull_or_dflt(sys.stderr, debug_file)
+ self.debug_buf_lvl = self._choose_nonull_or_dflt(DFLT_DEBUG_BUF_LVL, debug_buf_lvl)
self.debug_lock = threading.Lock()
self._cmd_log_len = 20
@@ -1776,7 +1856,7 @@ class IMAP4(object):
self._cmd_log = {} # Last `_cmd_log_len' interactions
if self.debug:
self._mesg('imaplib2 version %s' % __version__)
- self._mesg('imaplib2 debug level %s' % self.debug)
+ self._mesg('imaplib2 debug level %s, buffer level %s' % (self.debug, self.debug_buf_lvl))
def _dump_ur(self, lvl):
@@ -1803,11 +1883,12 @@ class IMAP4(object):
tn = threading.currentThread().getName()
- if lvl == 1 or self.debug >= 4:
+ if lvl <= 1 or self.debug > self.debug_buf_lvl:
self.debug_lock.acquire()
self._mesg(line, tn)
self.debug_lock.release()
- return
+ if lvl != 1:
+ return
# Keep log of last `_cmd_log_len' interactions for debugging.
self.debug_lock.acquire()
@@ -1824,8 +1905,11 @@ class IMAP4(object):
if tn is None:
tn = threading.currentThread().getName()
tm = time.strftime('%M:%S', time.localtime(secs))
- self.debug_file.write(' %s.%02d %s %s\n' % (tm, (secs*100)%100, tn, s))
- self.debug_file.flush()
+ try:
+ self.debug_file.write(' %s.%02d %s %s\n' % (tm, (secs*100)%100, tn, s))
+ self.debug_file.flush()
+ finally:
+ pass
def _print_log(self):
@@ -1865,10 +1949,10 @@ class IMAP4_SSL(IMAP4):
"""
- def __init__(self, host=None, port=None, keyfile=None, certfile=None, debug=None, debug_file=None, identifier=None, timeout=None):
+ def __init__(self, host=None, port=None, keyfile=None, certfile=None, debug=None, debug_file=None, identifier=None, timeout=None, debug_buf_lvl=None):
self.keyfile = keyfile
self.certfile = certfile
- IMAP4.__init__(self, host, port, debug, debug_file, identifier, timeout)
+ IMAP4.__init__(self, host, port, debug, debug_file, identifier, timeout, debug_buf_lvl)
def open(self, host=None, port=None):
@@ -1878,15 +1962,15 @@ class IMAP4_SSL(IMAP4):
This connection will be used by the routines:
read, send, shutdown, socket, ssl."""
- self.host = host is not None and host or ''
- self.port = port is not None and port or IMAP4_SSL_PORT
+ self.host = self._choose_nonull_or_dflt('', host)
+ self.port = self._choose_nonull_or_dflt(IMAP4_SSL_PORT, port)
self.sock = self.open_socket()
try:
- import ssl
- self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
+ import ssl
+ self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
except ImportError:
- self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
+ self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
self.read_fd = self.sock.fileno()
@@ -1949,14 +2033,14 @@ class IMAP4_stream(IMAP4):
"""
- def __init__(self, command, debug=None, debug_file=None, identifier=None, timeout=None):
+ def __init__(self, command, debug=None, debug_file=None, identifier=None, timeout=None, debug_buf_lvl=None):
self.command = command
self.host = command
self.port = None
self.sock = None
self.writefile, self.readfile = None, None
self.read_fd = None
- IMAP4.__init__(self, None, None, debug, debug_file, identifier, timeout)
+ IMAP4.__init__(self, None, None, debug, debug_file, identifier, timeout, debug_buf_lvl)
def open(self, host=None, port=None):
@@ -2055,12 +2139,14 @@ class _IdleCont(object):
def __init__(self, parent, timeout):
self.parent = parent
- self.timeout = timeout is not None and timeout or IDLE_TIMEOUT
+ self.timeout = parent._choose_nonull_or_dflt(IDLE_TIMEOUT, timeout)
self.parent.idle_timeout = self.timeout + time.time()
def process(self, data, rqb):
+ self.parent.idle_lock.acquire()
self.parent.idle_rqb = rqb
self.parent.idle_timeout = self.timeout + time.time()
+ self.parent.idle_lock.release()
if __debug__: self.parent._log(2, 'server IDLE started, timeout in %.2f secs' % self.timeout)
return None
@@ -2175,10 +2261,11 @@ if __name__ == '__main__':
except getopt.error, val:
optlist, args = (), ()
- debug, port, stream_command, keyfile, certfile = (None,)*5
+ debug, debug_buf_lvl, port, stream_command, keyfile, certfile = (None,)*6
for opt,val in optlist:
if opt == '-d':
debug = int(val)
+ debug_buf_lvl = debug - 1
elif opt == '-l':
try:
keyfile,certfile = val.split(':')
@@ -2235,31 +2322,33 @@ if __name__ == '__main__':
cmd, args = cb_arg
if error is not None:
AsyncError = error
- M._mesg('[cb] ERROR %s %.100s => %s' % (cmd, args, error))
+ M._log(0, '[cb] ERROR %s %.100s => %s' % (cmd, args, error))
return
typ, dat = response
- M._mesg('[cb] %s %.100s => %s %.100s' % (cmd, args, typ, dat))
+ M._log(0, '[cb] %s %.100s => %s %.100s' % (cmd, args, typ, dat))
if typ == 'NO':
AsyncError = (Exception, dat[0])
def run(cmd, args, cb=True):
if AsyncError:
+ M._log(1, 'AsyncError')
M.logout()
typ, val = AsyncError
raise typ(val)
- M._mesg('%s %.100s' % (cmd, args))
+ if not M.debug: M._log(0, '%s %.100s' % (cmd, args))
try:
if cb:
typ, dat = getattr(M, cmd)(callback=responder, cb_arg=(cmd, args), *args)
- if M.debug:
- M._mesg('%s %.100s => %s %.100s' % (cmd, args, typ, dat))
+ M._log(1, '%s %.100s => %s %.100s' % (cmd, args, typ, dat))
else:
typ, dat = getattr(M, cmd)(*args)
- M._mesg('%s %.100s => %s %.100s' % (cmd, args, typ, dat))
+ M._log(1, '%s %.100s => %s %.100s' % (cmd, args, typ, dat))
except:
+ M._log(1, '%s - %s' % sys.exc_info()[:2])
M.logout()
raise
if typ == 'NO':
+ M._log(1, 'NO')
M.logout()
raise Exception(dat[0])
return dat
@@ -2270,15 +2359,15 @@ if __name__ == '__main__':
if keyfile is not None:
if not keyfile: keyfile = None
if not certfile: certfile = None
- M = IMAP4_SSL(host=host, port=port, keyfile=keyfile, certfile=certfile, debug=debug, identifier='', timeout=10)
+ M = IMAP4_SSL(host=host, port=port, keyfile=keyfile, certfile=certfile, debug=debug, identifier='', timeout=10, debug_buf_lvl=debug_buf_lvl)
elif stream_command:
- M = IMAP4_stream(stream_command, debug=debug, identifier='', timeout=10)
+ M = IMAP4_stream(stream_command, debug=debug, identifier='', timeout=10, debug_buf_lvl=debug_buf_lvl)
else:
- M = IMAP4(host=host, port=port, debug=debug, identifier='', timeout=10)
+ M = IMAP4(host=host, port=port, debug=debug, identifier='', timeout=10, debug_buf_lvl=debug_buf_lvl)
if M.state != 'AUTH': # Login needed
PASSWD = getpass.getpass("IMAP password for %s on %s: " % (USER, host or "localhost"))
test_seq1.insert(0, ('login', (USER, PASSWD)))
- M._mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
+ M._log(0, 'PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
if 'COMPRESS=DEFLATE' in M.capabilities:
M.enable_compression()

View file

@ -0,0 +1,22 @@
Fixes wrong title level.
--- docs/MANUAL.rst.orig 2011-08-15 14:21:24.000000000 +0400
+++ docs/MANUAL.rst 2011-08-15 14:22:03.000000000 +0400
@@ -380,7 +380,7 @@
default.
Certificate checking
-^^^^^^^^^^^^^^^^^^^^
+--------------------
Unfortunately, by default we will not verify the certificate of an IMAP
TLS/SSL server we connect to, so connecting by SSL is no guarantee
@@ -397,7 +397,7 @@
certificate and CA certificate.
StartTLS
-^^^^^^^^
+--------
If you have not configured your account to connect via SSL anyway,
OfflineImap will still attempt to set up an SSL connection via the

View file

@ -15,6 +15,9 @@ bin/offlineimap
%%PYTHON_SITELIBDIR%%/offlineimap/folder/LocalStatus.py
%%PYTHON_SITELIBDIR%%/offlineimap/folder/LocalStatus.pyc
%%PYTHON_SITELIBDIR%%/offlineimap/folder/LocalStatus.pyo
%%PYTHON_SITELIBDIR%%/offlineimap/folder/LocalStatusSQLite.py
%%PYTHON_SITELIBDIR%%/offlineimap/folder/LocalStatusSQLite.pyc
%%PYTHON_SITELIBDIR%%/offlineimap/folder/LocalStatusSQLite.pyo
%%PYTHON_SITELIBDIR%%/offlineimap/folder/Maildir.py
%%PYTHON_SITELIBDIR%%/offlineimap/folder/Maildir.pyc
%%PYTHON_SITELIBDIR%%/offlineimap/folder/Maildir.pyo
@ -75,6 +78,9 @@ bin/offlineimap
%%PYTHON_SITELIBDIR%%/offlineimap/accounts.py
%%PYTHON_SITELIBDIR%%/offlineimap/accounts.pyc
%%PYTHON_SITELIBDIR%%/offlineimap/accounts.pyo
%%PYTHON_SITELIBDIR%%/offlineimap/error.py
%%PYTHON_SITELIBDIR%%/offlineimap/error.pyc
%%PYTHON_SITELIBDIR%%/offlineimap/error.pyo
%%PYTHON_SITELIBDIR%%/offlineimap/imaplib2.py
%%PYTHON_SITELIBDIR%%/offlineimap/imaplib2.pyc
%%PYTHON_SITELIBDIR%%/offlineimap/imaplib2.pyo