Merge branch 'next'
This commit is contained in:
commit
a73b4b3465
@ -4,17 +4,35 @@ ChangeLog
|
||||
|
||||
:website: http://offlineimap.org
|
||||
|
||||
|
||||
WIP (add new stuff for the next release)
|
||||
========================================
|
||||
|
||||
OfflineIMAP v6.5.5-rc1 (2012-09-05)
|
||||
===================================
|
||||
|
||||
* Bump version number
|
||||
|
||||
OfflineIMAP v6.5.5-rc1 (2012-09-05)
|
||||
===================================
|
||||
|
||||
* Don't create folders if readonly is enabled.
|
||||
* Learn to deal with readonly folders to properly detect this condition and act
|
||||
accordingly. One example is Gmail's "Chats" folder that is read-only,
|
||||
but contains logs of the quick chats. (E. Ryabinkin)
|
||||
* Fix str.format() calls for Python 2.6 (D. Logie)
|
||||
* Remove APPENDUID hack, previously introduced to fix Gmail, no longer
|
||||
necessary, it might have been breaking things. (J. Wiegley)
|
||||
* Improve regex that could lead to 'NoneType' object has no attribute 'group'
|
||||
(D. Franke)
|
||||
* Improved error throwing on repository misconfiguration
|
||||
|
||||
OfflineIMAP v6.5.4 (2012-06-02)
|
||||
=================================
|
||||
===============================
|
||||
|
||||
* bump bundled imaplib2 library 2.29 --> 2.33
|
||||
* Actually perform the SSL fingerprint check (reported by J. Cook)
|
||||
* Curses UI, don't use colors after we shut down curses already (C.Höger)
|
||||
* Document that '%' needs encoding as '%%' in *.conf
|
||||
* Document that '%' needs encoding as '%%' in configuration files.
|
||||
* Fix crash when IMAP.quickchanged() led to an Error (reported by sharat87)
|
||||
* Implement the createfolders setting to disable folder propagation (see docs)
|
||||
|
||||
@ -60,7 +78,7 @@ OfflineIMAP v6.5.3 (2012-04-02)
|
||||
* Improve compatability of the curses UI with python 2.6
|
||||
|
||||
OfflineIMAP v6.5.2.1 (2012-04-04)
|
||||
=====================================
|
||||
=================================
|
||||
|
||||
* Fix python2.6 compatibility with the TTYUI backend (crash)
|
||||
|
||||
@ -116,7 +134,7 @@ Smallish bug fixes that deserve to be put out.
|
||||
* Add filter information to the filter list in --info output
|
||||
|
||||
OfflineIMAP v6.5.1.1 (2012-01-07) - "Das machine control is nicht fur gerfinger-poken und mittengrabben"
|
||||
==================================================================================================================
|
||||
========================================================================================================
|
||||
|
||||
Blinkenlights UI 6.5.0 regression fixes only.
|
||||
|
||||
|
19
MAINTAINERS
Normal file
19
MAINTAINERS
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
Official maintainers
|
||||
====================
|
||||
|
||||
Dmitrijs Ledkovs
|
||||
email: xnox at debian.org
|
||||
github: xnox
|
||||
|
||||
Eygene Ryabinkin
|
||||
email: rea at freebsd.org
|
||||
github: konvpalto
|
||||
|
||||
Nicolas Sebrecht
|
||||
email: nicolas.s-dev at laposte.net
|
||||
github: nicolas33
|
||||
|
||||
Sebastian Spaeth
|
||||
email: sebastian at sspaeth.de
|
||||
github: spaetz
|
12
README
12
README
@ -10,7 +10,9 @@ messages on each computer, and changes you make one place will be visible on all
|
||||
other systems. For instance, you can delete a message on your home computer, and
|
||||
it will appear deleted on your work computer as well. OfflineIMAP is also useful
|
||||
if you want to use a mail reader that does not have IMAP support, has poor IMAP
|
||||
support, or does not provide disconnected operation. It's homepage at http://offlineimap.org contains more information, source code, and online documentation.
|
||||
support, or does not provide disconnected operation. It's homepage at
|
||||
http://offlineimap.org contains more information, source code, and online
|
||||
documentation.
|
||||
|
||||
OfflineIMAP does not require additional python dependencies beyond python >=2.6
|
||||
(although python-sqlite is strongly recommended).
|
||||
@ -87,9 +89,13 @@ Mailing list & bug reporting
|
||||
----------------------------
|
||||
|
||||
The user discussion, development and all exciting stuff take place in the
|
||||
OfflineImap mailing list at http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project. You do not need to subscribe to send emails.
|
||||
OfflineImap mailing list at
|
||||
http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project. You do not
|
||||
need to subscribe to send emails.
|
||||
|
||||
Bugs, issues and contributions should be reported to the mailing list. Bugs can also be reported in the issue tracker at https://github.com/spaetz/offlineimap/issues.
|
||||
Bugs, issues and contributions should be reported to the mailing list. Bugs can
|
||||
also be reported in the issue tracker at
|
||||
https://github.com/OfflineIMAP/offlineimap/issues.
|
||||
|
||||
Configuration Examples
|
||||
======================
|
||||
|
@ -1,6 +1,6 @@
|
||||
.. -*- coding: utf-8 -*-
|
||||
.. _OfflineIMAP: https://github.com/spaetz/offlineimap
|
||||
.. _OLI_git_repo: git://github.com/spaetz/offlineimap.git
|
||||
.. _OfflineIMAP: https://github.com/OfflineIMAP/offlineimap
|
||||
.. _OLI_git_repo: git://github.com/OfflineIMAP/offlineimap.git
|
||||
|
||||
============
|
||||
Installation
|
||||
@ -37,29 +37,40 @@ In order to use `OfflineIMAP`_, you need to have these conditions satisfied:
|
||||
Installation
|
||||
------------
|
||||
|
||||
Installing OfflineImap should usually be quite easy, as you can simply unpack and run OfflineImap in place if you wish to do so. There are a number of options though:
|
||||
Installing OfflineImap should usually be quite easy, as you can simply unpack
|
||||
and run OfflineImap in place if you wish to do so. There are a number of options
|
||||
though:
|
||||
|
||||
#. system-wide :ref:`installation via your distribution package manager <inst_pkg_man>`
|
||||
#. system-wide or single user :ref:`installation from the source package <inst_src_tar>`
|
||||
#. system-wide or single user :ref:`installation from a git checkout <inst_git>`
|
||||
|
||||
Having installed OfflineImap, you will need to configure it, to be actually useful. Please check the :ref:`Configuration` section in the :doc:`MANUAL` for more information on the configuration step.
|
||||
Having installed OfflineImap, you will need to configure it, to be actually
|
||||
useful. Please check the :ref:`Configuration` section in the :doc:`MANUAL` for
|
||||
more information on the configuration step.
|
||||
|
||||
.. _inst_pkg_man:
|
||||
|
||||
System-Wide Installation via distribution
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The easiest way to install OfflineIMAP is via your distribution's package manager. OfflineImap is available under the name `offlineimap` in most Linux and BSD distributions.
|
||||
The easiest way to install OfflineIMAP is via your distribution's package
|
||||
manager. OfflineImap is available under the name `offlineimap` in most Linux and
|
||||
BSD distributions.
|
||||
|
||||
|
||||
.. _inst_src_tar:
|
||||
|
||||
Installation from source package
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Download the latest source archive from our `download page <https://github.com/spaetz/offlineimap/downloads>`_. Simply click the "Download as .zip" or "Download as .tar.gz" buttons to get the latest "stable" code from the master branch. If you prefer command line, you will want to use:
|
||||
wget https://github.com/spaetz/offlineimap/tarball/master
|
||||
|
||||
Unpack and continue with the :ref:`system-wide installation <system_wide_inst>` or the :ref:`single-user installation <single_user_inst>` section.
|
||||
Download the latest source archive from our `download page
|
||||
<https://github.com/spaetz/offlineimap/downloads>`_. Simply click the "Download
|
||||
as .zip" or "Download as .tar.gz" buttons to get the latest "stable" code from
|
||||
the master branch. If you prefer command line, you will want to use: wget
|
||||
https://github.com/spaetz/offlineimap/tarball/master
|
||||
|
||||
Unpack and continue with the :ref:`system-wide installation <system_wide_inst>`
|
||||
or the :ref:`single-user installation <single_user_inst>` section.
|
||||
|
||||
|
||||
.. _inst_git:
|
||||
@ -78,7 +89,9 @@ checkout a particular release like this::
|
||||
cd offlineimap
|
||||
git checkout v6.5.2.1
|
||||
|
||||
You have now a source tree available and proceed with either the :ref:`system-wide installation <system_wide_inst>` or the :ref:`single-user installation <single_user_inst>`.
|
||||
You have now a source tree available and proceed with either the
|
||||
:ref:`system-wide installation <system_wide_inst>` or the :ref:`single-user
|
||||
installation <single_user_inst>`.
|
||||
|
||||
|
||||
.. _system_wide_inst:
|
||||
|
@ -1,7 +1,7 @@
|
||||
__all__ = ['OfflineImap']
|
||||
|
||||
__productname__ = 'OfflineIMAP'
|
||||
__version__ = "6.5.4"
|
||||
__version__ = "6.5.5-rc2"
|
||||
__copyright__ = "Copyright 2002-2012 John Goerzen & contributors"
|
||||
__author__ = "John Goerzen"
|
||||
__author_email__= "john@complete.org"
|
||||
|
@ -217,6 +217,7 @@ class SyncableAccount(Account):
|
||||
|
||||
def syncrunner(self):
|
||||
self.ui.registerthread(self)
|
||||
try:
|
||||
accountmetadata = self.getaccountmeta()
|
||||
if not os.path.exists(accountmetadata):
|
||||
os.mkdir(accountmetadata, 0o700)
|
||||
@ -224,6 +225,11 @@ class SyncableAccount(Account):
|
||||
self.remoterepos = Repository(self, 'remote')
|
||||
self.localrepos = Repository(self, 'local')
|
||||
self.statusrepos = Repository(self, 'status')
|
||||
except OfflineImapError as e:
|
||||
self.ui.error(e, exc_info()[2])
|
||||
if e.severity >= OfflineImapError.ERROR.CRITICAL:
|
||||
raise
|
||||
return
|
||||
|
||||
# Loop account sync if needed (bail out after 3 failures)
|
||||
looping = 3
|
||||
@ -255,6 +261,12 @@ class SyncableAccount(Account):
|
||||
if looping and self.sleeper() >= 2:
|
||||
looping = 0
|
||||
|
||||
def get_local_folder(self, remotefolder):
|
||||
"""Return the corresponding local folder for a given remotefolder"""
|
||||
return self.localrepos.getfolder(
|
||||
remotefolder.getvisiblename().
|
||||
replace(self.remoterepos.getsep(), self.localrepos.getsep()))
|
||||
|
||||
def sync(self):
|
||||
"""Synchronize the account once, then return
|
||||
|
||||
@ -289,7 +301,6 @@ class SyncableAccount(Account):
|
||||
# folder delimiter etc)
|
||||
remoterepos.getfolders()
|
||||
localrepos.getfolders()
|
||||
statusrepos.getfolders()
|
||||
|
||||
remoterepos.sync_folder_structure(localrepos, statusrepos)
|
||||
# replicate the folderstructure between REMOTE to LOCAL
|
||||
@ -300,10 +311,16 @@ class SyncableAccount(Account):
|
||||
for remotefolder in remoterepos.getfolders():
|
||||
# check for CTRL-C or SIGTERM
|
||||
if Account.abort_NOW_signal.is_set(): break
|
||||
|
||||
if not remotefolder.sync_this:
|
||||
self.ui.debug('', "Not syncing filtered remote folder '%s'"
|
||||
self.ui.debug('', "Not syncing filtered folder '%s'"
|
||||
"[%s]" % (remotefolder, remoterepos))
|
||||
continue # Filtered out remote folder
|
||||
continue # Ignore filtered folder
|
||||
localfolder = self.get_local_folder(remotefolder)
|
||||
if not localfolder.sync_this:
|
||||
self.ui.debug('', "Not syncing filtered folder '%s'"
|
||||
"[%s]" % (localfolder, localfolder.repository))
|
||||
continue # Ignore filtered folder
|
||||
thread = InstanceLimitedThread(\
|
||||
instancename = 'FOLDER_' + self.remoterepos.getname(),
|
||||
target = syncfolder,
|
||||
@ -367,17 +384,8 @@ def syncfolder(account, remotefolder, quick):
|
||||
ui.registerthread(account)
|
||||
try:
|
||||
# Load local folder.
|
||||
localfolder = localrepos.\
|
||||
getfolder(remotefolder.getvisiblename().\
|
||||
replace(remoterepos.getsep(), localrepos.getsep()))
|
||||
localfolder = account.get_local_folder(remotefolder)
|
||||
|
||||
#Filtered folders on the remote side will not invoke this
|
||||
#function, but we need to NOOP if the local folder is filtered
|
||||
#out too:
|
||||
if not localfolder.sync_this:
|
||||
ui.debug('', "Not syncing filtered local folder '%s'" \
|
||||
% localfolder)
|
||||
return
|
||||
# Write the mailboxes
|
||||
mbnames.add(account.name, localfolder.getname())
|
||||
|
||||
@ -457,15 +465,8 @@ def syncfolder(account, remotefolder, quick):
|
||||
if e.severity > OfflineImapError.ERROR.FOLDER:
|
||||
raise
|
||||
else:
|
||||
#if the initial localfolder assignement bailed out, the localfolder var will not be available, so we need
|
||||
ui.error(e, exc_info()[2], msg = "Aborting sync, folder '%s' "
|
||||
"[acc: '%s']" % (
|
||||
remotefolder.getvisiblename().\
|
||||
replace(remoterepos.getsep(), localrepos.getsep()),
|
||||
account))
|
||||
# we reconstruct foldername above rather than using
|
||||
# localfolder, as the localfolder var is not
|
||||
# available if assignment fails.
|
||||
"[acc: '%s']" % (localfolder, account))
|
||||
except Exception as e:
|
||||
ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \
|
||||
(account, remotefolder.getvisiblename(),
|
||||
|
@ -31,9 +31,12 @@ class BaseFolder(object):
|
||||
:para name: Path & name of folder minus root or reference
|
||||
:para repository: Repository() in which the folder is.
|
||||
"""
|
||||
self.sync_this = True
|
||||
"""Should this folder be included in syncing?"""
|
||||
self.ui = getglobalui()
|
||||
"""Should this folder be included in syncing?"""
|
||||
self._sync_this = repository.should_sync_folder(name)
|
||||
if not self._sync_this:
|
||||
self.ui.debug('', "Filtering out '%s'[%s] due to folderfilter" \
|
||||
% (name, repository))
|
||||
# Top level dir name is always ''
|
||||
self.name = name if not name == self.getsep() else ''
|
||||
self.repository = repository
|
||||
@ -57,6 +60,11 @@ class BaseFolder(object):
|
||||
"""Account name as string"""
|
||||
return self.repository.accountname
|
||||
|
||||
@property
|
||||
def sync_this(self):
|
||||
"""Should this folder be synced or is it e.g. filtered out?"""
|
||||
return self._sync_this
|
||||
|
||||
def suggeststhreads(self):
|
||||
"""Returns true if this folder suggests using threads for actions;
|
||||
false otherwise. Probably only IMAP will return true."""
|
||||
@ -386,7 +394,7 @@ class BaseFolder(object):
|
||||
self.getmessageuidlist())
|
||||
num_to_copy = len(copylist)
|
||||
if num_to_copy and self.repository.account.dryrun:
|
||||
self.ui.info("[DRYRUN] Copy {} messages from {}[{}] to {}".format(
|
||||
self.ui.info("[DRYRUN] Copy {0} messages from {1}[{2}] to {3}".format(
|
||||
num_to_copy, self, self.repository, dstfolder.repository))
|
||||
return
|
||||
for num, uid in enumerate(copylist):
|
||||
|
@ -575,9 +575,8 @@ class IMAPFolder(BaseFolder):
|
||||
(typ,dat) = imapobj.check()
|
||||
assert(typ == 'OK')
|
||||
|
||||
# get the new UID. Test for APPENDUID response even if the
|
||||
# server claims to not support it, as e.g. Gmail does :-(
|
||||
if use_uidplus or imapobj._get_untagged_response('APPENDUID', True):
|
||||
# get the new UID, do we use UIDPLUS?
|
||||
if use_uidplus:
|
||||
# get new UID from the APPENDUID response, it could look
|
||||
# like OK [APPENDUID 38505 3955] APPEND completed with
|
||||
# 38505 bein folder UIDvalidity and 3955 the new UID.
|
||||
@ -585,7 +584,7 @@ class IMAPFolder(BaseFolder):
|
||||
# often seems to return [None], even though we have
|
||||
# data. TODO
|
||||
resp = imapobj._get_untagged_response('APPENDUID')
|
||||
if resp == [None]:
|
||||
if resp == [None] or resp is None:
|
||||
self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
|
||||
"appending a message.")
|
||||
return 0
|
||||
|
@ -85,8 +85,7 @@ class LocalStatusFolder(BaseFolder):
|
||||
file.close()
|
||||
|
||||
def save(self):
|
||||
self.savelock.acquire()
|
||||
try:
|
||||
with self.savelock:
|
||||
file = open(self.filename + ".tmp", "wt")
|
||||
file.write(magicline + "\n")
|
||||
for msg in self.messagelist.values():
|
||||
@ -104,9 +103,6 @@ class LocalStatusFolder(BaseFolder):
|
||||
os.fsync(fd)
|
||||
os.close(fd)
|
||||
|
||||
finally:
|
||||
self.savelock.release()
|
||||
|
||||
def getmessagelist(self):
|
||||
return self.messagelist
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
from threading import Lock
|
||||
from offlineimap import OfflineImapError
|
||||
from .IMAP import IMAPFolder
|
||||
import os.path
|
||||
|
||||
|
@ -23,7 +23,7 @@ from offlineimap.ui import getglobalui
|
||||
|
||||
# find the first quote in a string
|
||||
quotere = re.compile(
|
||||
r"""(?P<quote>"(?:\\"|[^"])*") # Quote, possibly containing encoded
|
||||
r"""(?P<quote>"[^\"\\]*(?:\\"|[^"])*") # Quote, possibly containing encoded
|
||||
# quotation mark
|
||||
\s*(?P<rest>.*)$ # Whitespace & remainder of string""",
|
||||
re.VERBOSE)
|
||||
|
@ -129,12 +129,17 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
|
||||
def getsep(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def should_sync_folder(self, fname):
|
||||
"""Should this folder be synced?"""
|
||||
return fname in self.folderincludes or self.folderfilter(fname)
|
||||
|
||||
def get_create_folders(self):
|
||||
"""Is folder creation enabled on this repository?
|
||||
|
||||
It is disabled by either setting the whole repository
|
||||
'readonly' or by using the 'createfolders' setting."""
|
||||
return self._readonly or self.getconfboolean('createfolders', True)
|
||||
return (not self._readonly) and \
|
||||
self.getconfboolean('createfolders', True)
|
||||
|
||||
def makefolder(self, foldername):
|
||||
"""Create a new folder"""
|
||||
@ -201,7 +206,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
|
||||
# Does nametrans back&forth lead to identical names?
|
||||
# 1) would src repo filter out the new folder name? In this
|
||||
# case don't create it on it:
|
||||
if not self.folderfilter(dst_name_t):
|
||||
if not self.should_sync_folder(dst_name_t):
|
||||
self.ui.debug('', "Not creating folder '%s' (repository '%s"
|
||||
"') as it would be filtered out on that repository." %
|
||||
(dst_name_t, self))
|
||||
|
@ -287,11 +287,6 @@ class IMAPRepository(BaseRepository):
|
||||
foldername = imaputil.dequote(name)
|
||||
retval.append(self.getfoldertype()(self.imapserver, foldername,
|
||||
self))
|
||||
# filter out the folder?
|
||||
if not self.folderfilter(foldername):
|
||||
self.ui.debug('imap', "Filtering out '%s'[%s] due to folderfilt"
|
||||
"er" % (foldername, self))
|
||||
retval[-1].sync_this = False
|
||||
# Add all folderincludes
|
||||
if len(self.folderincludes):
|
||||
imapobj = self.imapserver.acquireconnection()
|
||||
|
@ -43,8 +43,8 @@ class LocalStatusRepository(BaseRepository):
|
||||
if not os.path.exists(self.root):
|
||||
os.mkdir(self.root, 0o700)
|
||||
|
||||
# self._folders is a list of LocalStatusFolders()
|
||||
self._folders = None
|
||||
# self._folders is a dict of name:LocalStatusFolders()
|
||||
self._folders = {}
|
||||
|
||||
def getsep(self):
|
||||
return '.'
|
||||
@ -79,23 +79,27 @@ class LocalStatusRepository(BaseRepository):
|
||||
file.close()
|
||||
os.rename(filename + ".tmp", filename)
|
||||
# Invalidate the cache.
|
||||
self._folders = None
|
||||
self._folders = {}
|
||||
|
||||
def getfolder(self, foldername):
|
||||
"""Return the Folder() object for a foldername"""
|
||||
return self.LocalStatusFolderClass(foldername, self)
|
||||
if foldername in self._folders:
|
||||
return self._folders[foldername]
|
||||
|
||||
folder = self.LocalStatusFolderClass(foldername, self)
|
||||
self._folders[foldername] = folder
|
||||
return folder
|
||||
|
||||
def getfolders(self):
|
||||
"""Returns a list of all cached folders."""
|
||||
if self._folders != None:
|
||||
return self._folders
|
||||
"""Returns a list of all cached folders.
|
||||
|
||||
self._folders = []
|
||||
for folder in os.listdir(self.root):
|
||||
self._folders.append(self.getfolder(folder))
|
||||
return self._folders
|
||||
Does nothing for this backend. We mangle the folder file names
|
||||
(see getfolderfilename) so we can not derive folder names from
|
||||
the file names that we have available. TODO: need to store a
|
||||
list of folder names somehow?"""
|
||||
pass
|
||||
|
||||
def forgetfolders(self):
|
||||
"""Forgets the cached list of folders, if any. Useful to run
|
||||
after a sync run."""
|
||||
self._folders = None
|
||||
self._folders = {}
|
||||
|
@ -181,11 +181,6 @@ class MaildirRepository(BaseRepository):
|
||||
foldername,
|
||||
self.getsep(),
|
||||
self))
|
||||
# filter out the folder?
|
||||
if not self.folderfilter(foldername):
|
||||
self.debug("Filtering out '%s'[%s] due to folderfilt"
|
||||
"er" % (foldername, self))
|
||||
retval[-1].sync_this = False
|
||||
|
||||
if self.getsep() == '/' and dirname != '':
|
||||
# Recursively check sub-directories for folders too.
|
||||
|
@ -15,10 +15,17 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
try:
|
||||
from configparser import NoSectionError
|
||||
except ImportError: #python2
|
||||
from ConfigParser import NoSectionError
|
||||
|
||||
from offlineimap.repository.IMAP import IMAPRepository, MappedIMAPRepository
|
||||
from offlineimap.repository.Gmail import GmailRepository
|
||||
from offlineimap.repository.Maildir import MaildirRepository
|
||||
from offlineimap.repository.LocalStatus import LocalStatusRepository
|
||||
from offlineimap.error import OfflineImapError
|
||||
|
||||
|
||||
class Repository(object):
|
||||
"""Abstract class that returns the correct Repository type
|
||||
@ -47,17 +54,26 @@ class Repository(object):
|
||||
return LocalStatusRepository(name, account)
|
||||
|
||||
else:
|
||||
raise ValueError("Request type %s not supported" % reqtype)
|
||||
errstr = "Repository type %s not supported" % reqtype
|
||||
raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO)
|
||||
|
||||
# Get repository type
|
||||
config = account.getconfig()
|
||||
try:
|
||||
repostype = config.get('Repository ' + name, 'type').strip()
|
||||
except NoSectionError as e:
|
||||
errstr = ("Could not find section '%s' in configuration. Required "
|
||||
"for account '%s'." % ('Repository %s' % name, account))
|
||||
raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO)
|
||||
|
||||
try:
|
||||
repo = typemap[repostype]
|
||||
except KeyError:
|
||||
raise ValueError("'%s' repository not supported for %s repositories"
|
||||
"." % (repostype, reqtype))
|
||||
return repo(name, account)
|
||||
errstr = "'%s' repository not supported for '%s' repositories." \
|
||||
% (repostype, reqtype)
|
||||
raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO)
|
||||
|
||||
return repo(name, account)
|
||||
|
||||
def __init__(self, account, reqtype):
|
||||
"""Load the correct Repository type and return that. The
|
||||
|
@ -88,7 +88,6 @@ class UIBase(object):
|
||||
def setlogfile(self, logfile):
|
||||
"""Create file handler which logs to file"""
|
||||
fh = logging.FileHandler(logfile, 'at')
|
||||
#fh.setLevel(logging.DEBUG)
|
||||
file_formatter = logging.Formatter("%(asctime)s %(levelname)s: "
|
||||
"%(message)s", '%Y-%m-%d %H:%M:%S')
|
||||
fh.setFormatter(file_formatter)
|
||||
@ -98,9 +97,7 @@ class UIBase(object):
|
||||
msg = "OfflineImap %s starting...\n Python: %s Platform: %s\n "\
|
||||
"Args: %s" % (offlineimap.__version__, p_ver, sys.platform,
|
||||
" ".join(sys.argv))
|
||||
record = logging.LogRecord('OfflineImap', logging.INFO, __file__,
|
||||
None, msg, None, None)
|
||||
fh.emit(record)
|
||||
self.logger.info(msg)
|
||||
|
||||
def _msg(self, msg):
|
||||
"""Display a message."""
|
||||
@ -301,7 +298,7 @@ class UIBase(object):
|
||||
def makefolder(self, repo, foldername):
|
||||
"""Called when a folder is created"""
|
||||
prefix = "[DRYRUN] " if self.dryrun else ""
|
||||
self.info("{}Creating folder {}[{}]".format(
|
||||
self.info("{0}Creating folder {1}[{2}]".format(
|
||||
prefix, foldername, repo))
|
||||
|
||||
def syncingfolder(self, srcrepos, srcfolder, destrepos, destfolder):
|
||||
@ -346,7 +343,7 @@ class UIBase(object):
|
||||
def deletingmessages(self, uidlist, destlist):
|
||||
ds = self.folderlist(destlist)
|
||||
prefix = "[DRYRUN] " if self.dryrun else ""
|
||||
self.info("{}Deleting {} messages ({}) in {}".format(
|
||||
self.info("{0}Deleting {1} messages ({2}) in {3}".format(
|
||||
prefix, len(uidlist),
|
||||
offlineimap.imaputil.uid_sequence(uidlist), ds))
|
||||
|
||||
@ -474,7 +471,7 @@ class UIBase(object):
|
||||
|
||||
def callhook(self, msg):
|
||||
if self.dryrun:
|
||||
self.info("[DRYRUN] {}".format(msg))
|
||||
self.info("[DRYRUN] {0}".format(msg))
|
||||
else:
|
||||
self.info(msg)
|
||||
|
||||
|
@ -127,7 +127,7 @@ class OLITestLib():
|
||||
reponame: All on `reponame` or all IMAP-type repositories if None"""
|
||||
config = cls.get_default_config()
|
||||
if reponame:
|
||||
sections = ['Repository {}'.format(reponame)]
|
||||
sections = ['Repository {0}'.format(reponame)]
|
||||
else:
|
||||
sections = [r for r in config.sections() \
|
||||
if r.startswith('Repository')]
|
||||
@ -162,8 +162,8 @@ class OLITestLib():
|
||||
dirs = [d for d in dirs if d.startswith(b'INBOX.OLItest')]
|
||||
for folder in dirs:
|
||||
res_t, data = imapobj.delete(b'\"'+folder+b'\"')
|
||||
assert res_t == 'OK', "Folder deletion of {} failed with error"\
|
||||
":\n{} {}".format(folder.decode('utf-8'), res_t, data)
|
||||
assert res_t == 'OK', "Folder deletion of {0} failed with error"\
|
||||
":\n{1} {2}".format(folder.decode('utf-8'), res_t, data)
|
||||
imapobj.logout()
|
||||
|
||||
@classmethod
|
||||
@ -197,7 +197,7 @@ class OLITestLib():
|
||||
Use some default content if not given"""
|
||||
assert cls.testdir != None
|
||||
while True: # Loop till we found a unique filename
|
||||
mailfile = '{}:2,'.format(random.randint(0,999999999))
|
||||
mailfile = '{0}:2,'.format(random.randint(0,999999999))
|
||||
mailfilepath = os.path.join(cls.testdir, 'mail',
|
||||
folder, 'new', mailfile)
|
||||
if not os.path.isfile(mailfilepath):
|
||||
|
@ -67,7 +67,7 @@ class TestBasicFunctions(unittest.TestCase):
|
||||
self.assertEqual(res, "")
|
||||
boxes, mails = OLITestLib.count_maildir_mails('')
|
||||
self.assertTrue((boxes, mails)==(0,0), msg="Expected 0 folders and 0 "
|
||||
"mails, but sync led to {} folders and {} mails".format(
|
||||
"mails, but sync led to {0} folders and {1} mails".format(
|
||||
boxes, mails))
|
||||
|
||||
def test_02_createdir(self):
|
||||
@ -82,7 +82,7 @@ class TestBasicFunctions(unittest.TestCase):
|
||||
self.assertEqual(res, "")
|
||||
boxes, mails = OLITestLib.count_maildir_mails('')
|
||||
self.assertTrue((boxes, mails)==(2,0), msg="Expected 2 folders and 0 "
|
||||
"mails, but sync led to {} folders and {} mails".format(
|
||||
"mails, but sync led to {0} folders and {1} mails".format(
|
||||
boxes, mails))
|
||||
|
||||
def test_03_nametransmismatch(self):
|
||||
@ -101,7 +101,7 @@ class TestBasicFunctions(unittest.TestCase):
|
||||
mismatch = "ERROR: INFINITE FOLDER CREATION DETECTED!" in res
|
||||
self.assertEqual(mismatch, True, msg="Mismatching nametrans rules did "
|
||||
"NOT trigger an 'infinite folder generation' error. Output was:\n"
|
||||
"{}".format(res))
|
||||
"{0}".format(res))
|
||||
# Write out default config file again
|
||||
OLITestLib.write_config_file()
|
||||
|
||||
@ -121,12 +121,12 @@ class TestBasicFunctions(unittest.TestCase):
|
||||
self.assertEqual(res, "")
|
||||
boxes, mails = OLITestLib.count_maildir_mails('')
|
||||
self.assertTrue((boxes, mails)==(1,1), msg="Expected 1 folders and 1 "
|
||||
"mails, but sync led to {} folders and {} mails".format(
|
||||
"mails, but sync led to {0} folders and {1} mails".format(
|
||||
boxes, mails))
|
||||
# The local Mail should have been assigned a proper UID now, check!
|
||||
uids = OLITestLib.get_maildir_uids('INBOX.OLItest')
|
||||
self.assertFalse (None in uids, msg = "All mails should have been "+ \
|
||||
"assigned the IMAP's UID number, but {} messages had no valid ID "\
|
||||
"assigned the IMAP's UID number, but {0} messages had no valid ID "\
|
||||
.format(len([None for x in uids if x==None])))
|
||||
|
||||
def test_05_createfolders(self):
|
||||
|
Loading…
Reference in New Issue
Block a user