Merge branch 'next' of github.com:OfflineIMAP/offlineimap into next
This commit is contained in:
commit
9b911faa58
10
COPYING
10
COPYING
@ -65,7 +65,7 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
@ -120,7 +120,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@ -178,7 +178,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@ -235,7 +235,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@ -288,7 +288,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
|
14
MAINTAINERS
14
MAINTAINERS
@ -10,10 +10,22 @@ Eygene Ryabinkin
|
||||
email: rea at freebsd.org
|
||||
github: konvpalto
|
||||
|
||||
Sebastian Spaeth
|
||||
email: sebastian at sspaeth.de
|
||||
github: spaetz
|
||||
|
||||
Nicolas Sebrecht
|
||||
email: nicolas.s-dev at laposte.net
|
||||
github: nicolas33
|
||||
|
||||
MAILING LIST MAINTAINERS
|
||||
========================
|
||||
|
||||
Eygene Ryabinkin
|
||||
email: rea at freebsd.org
|
||||
|
||||
Sebastian Spaeth
|
||||
email: sebastian at sspaeth.de
|
||||
github: spaetz
|
||||
|
||||
Nicolas Sebrecht
|
||||
email: nicolas.s-dev at laposte.net
|
||||
|
@ -13,57 +13,6 @@ This document contains assorted guidelines for programmers that want
|
||||
to hack OfflineIMAP.
|
||||
|
||||
|
||||
------------------
|
||||
Exception handling
|
||||
------------------
|
||||
|
||||
OfflineIMAP on many occasions re-raises various exceptions and often
|
||||
changes exception type to `OfflineImapError`. This is not a problem
|
||||
per se, but you must always remember that we need to preserve original
|
||||
tracebacks. This is not hard if you follow these simple rules.
|
||||
|
||||
For re-raising original exceptions, just use::
|
||||
|
||||
raise
|
||||
|
||||
from inside your exception handling code.
|
||||
|
||||
If you need to change exception type, or its argument, or whatever,
|
||||
use this three-argument form::
|
||||
|
||||
raise YourExceptionClass(argum, ents), None, sys.exc_info()[2]
|
||||
|
||||
In this form, you're creating an instance of new exception, so ``raise``
|
||||
will deduce its ``type`` and ``value`` parameters from the first argument,
|
||||
thus the second expression passed to ``raise`` is always ``None``.
|
||||
And the third one is the traceback object obtained from the thread-safe
|
||||
``exc_info()`` function.
|
||||
|
||||
In fact, if you hadn't already imported the whole ``sys`` module, it will
|
||||
be better to import just ``exc_info()``::
|
||||
|
||||
from sys import exc_info
|
||||
|
||||
and raise like this::
|
||||
|
||||
raise YourExceptionClass(argum, ents), None, exc_info()[2]
|
||||
|
||||
since this is the historically-preferred style in the OfflineIMAP code.
|
||||
.. -*- coding: utf-8 -*-
|
||||
.. _OfflineIMAP: https://github.com/OfflineIMAP/offlineimap
|
||||
.. _OLI_git_repo: git://github.com/OfflineIMAP/offlineimap.git
|
||||
|
||||
=================================
|
||||
Coding guidelines for OfflineIMAP
|
||||
=================================
|
||||
|
||||
.. contents::
|
||||
.. .. sectnum::
|
||||
|
||||
This document contains assorted guidelines for programmers that want
|
||||
to hack OfflineIMAP.
|
||||
|
||||
|
||||
------------------
|
||||
Exception handling
|
||||
------------------
|
||||
|
@ -23,7 +23,8 @@ from offlineimap import __version__,__author__
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo']
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
|
||||
'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
|
||||
autoclass_content = "both"
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
|
701
offlineimap.conf
701
offlineimap.conf
File diff suppressed because it is too large
Load Diff
@ -33,15 +33,21 @@ except:
|
||||
|
||||
# FIXME: spaghetti code alert!
|
||||
def getaccountlist(customconfig):
|
||||
# Account names in a list.
|
||||
return customconfig.getsectionlist('Account')
|
||||
|
||||
# FIXME: spaghetti code alert!
|
||||
def AccountListGenerator(customconfig):
|
||||
"""Returns a list of instanciated Account class, one per account name."""
|
||||
|
||||
return [Account(customconfig, accountname)
|
||||
for accountname in getaccountlist(customconfig)]
|
||||
|
||||
# FIXME: spaghetti code alert!
|
||||
def AccountHashGenerator(customconfig):
|
||||
"""Returns a dict of instanciated Account class with the account name as
|
||||
key."""
|
||||
|
||||
retval = {}
|
||||
for item in AccountListGenerator(customconfig):
|
||||
retval[item.getname()] = item
|
||||
@ -54,9 +60,10 @@ class Account(CustomConfig.ConfigHelperMixin):
|
||||
Most of the time you will actually want to use the derived
|
||||
:class:`accounts.SyncableAccount` which contains all functions used
|
||||
for syncing an account."""
|
||||
#signal gets set when we should stop looping
|
||||
|
||||
# Signal gets set when we should stop looping.
|
||||
abort_soon_signal = Event()
|
||||
#signal gets set on CTRL-C/SIGTERM
|
||||
# Signal gets set on CTRL-C/SIGTERM.
|
||||
abort_NOW_signal = Event()
|
||||
|
||||
def __init__(self, config, name):
|
||||
@ -66,6 +73,7 @@ class Account(CustomConfig.ConfigHelperMixin):
|
||||
|
||||
:param name: A string denoting the name of the Account
|
||||
as configured"""
|
||||
|
||||
self.config = config
|
||||
self.name = name
|
||||
self.metadatadir = config.getmetadatadir()
|
||||
@ -257,8 +265,8 @@ class SyncableAccount(Account):
|
||||
raise
|
||||
self.ui.error(e, exc_info()[2])
|
||||
except Exception as e:
|
||||
self.ui.error(e, exc_info()[2], msg = "While attempting to sync"
|
||||
" account '%s'" % self)
|
||||
self.ui.error(e, exc_info()[2], msg="While attempting to sync"
|
||||
" account '%s'"% self)
|
||||
else:
|
||||
# after success sync, reset the looping counter to 3
|
||||
if self.refreshperiod:
|
||||
@ -483,3 +491,7 @@ def syncfolder(account, remotefolder, quick):
|
||||
ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \
|
||||
(account, remotefolder.getvisiblename(),
|
||||
traceback.format_exc()))
|
||||
finally:
|
||||
for folder in ["statusfolder", "localfolder", "remotefolder"]:
|
||||
if folder in locals():
|
||||
locals()[folder].dropmessagelistcache()
|
||||
|
@ -247,6 +247,9 @@ class BaseFolder(object):
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def dropmessagelistcache(self):
|
||||
raise NotImplementedException
|
||||
|
||||
def getmessagelist(self):
|
||||
"""Gets the current message list.
|
||||
You must call cachemessagelist() before calling this function!"""
|
||||
@ -438,6 +441,11 @@ class BaseFolder(object):
|
||||
- headername: name of the header to add
|
||||
- headervalue: value of the header to add
|
||||
|
||||
.. note::
|
||||
|
||||
The following documentation will not get displayed correctly after being
|
||||
processed by Sphinx. View the source of this method to read it.
|
||||
|
||||
This has to deal with strange corner cases where the header is
|
||||
missing or empty. Here are illustrations for all the cases,
|
||||
showing where the header gets inserted and what the end result
|
||||
|
@ -40,6 +40,8 @@ CRLF = '\r\n'
|
||||
|
||||
class IMAPFolder(BaseFolder):
|
||||
def __init__(self, imapserver, name, repository):
|
||||
# FIXME: decide if unquoted name is from the responsability of the
|
||||
# caller or not, but not both.
|
||||
name = imaputil.dequote(name)
|
||||
self.sep = imapserver.delim
|
||||
super(IMAPFolder, self).__init__(name, repository)
|
||||
@ -248,6 +250,8 @@ class IMAPFolder(BaseFolder):
|
||||
rtime = imaplibutil.Internaldate2epoch(messagestr)
|
||||
self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime}
|
||||
|
||||
def dropmessagelistcache(self):
|
||||
self.messagelist = None
|
||||
|
||||
# Interface from BaseFolder
|
||||
def getmessagelist(self):
|
||||
|
@ -161,6 +161,8 @@ class LocalStatusFolder(BaseFolder):
|
||||
self.readstatus(file)
|
||||
file.close()
|
||||
|
||||
def dropmessagelistcache(self):
|
||||
self.messagelist = None
|
||||
|
||||
def save(self):
|
||||
"""Save changed data to disk. For this backend it is the same as saveall."""
|
||||
|
@ -203,6 +203,9 @@ class LocalStatusSQLiteFolder(BaseFolder):
|
||||
self.messagelist[uid]['labels'] = labels
|
||||
self.messagelist[uid]['mtime'] = row[2]
|
||||
|
||||
def dropmessagelistcache(self):
|
||||
self.messagelist = None
|
||||
|
||||
# Interface from LocalStatusFolder
|
||||
def save(self):
|
||||
pass
|
||||
|
@ -220,6 +220,9 @@ class MaildirFolder(BaseFolder):
|
||||
if self.messagelist is None:
|
||||
self.messagelist = self._scanfolder()
|
||||
|
||||
def dropmessagelistcache(self):
|
||||
self.messagelist = None
|
||||
|
||||
# Interface from BaseFolder
|
||||
def getmessagelist(self):
|
||||
return self.messagelist
|
||||
|
@ -125,6 +125,9 @@ class MappedIMAPFolder(IMAPFolder):
|
||||
finally:
|
||||
self.maplock.release()
|
||||
|
||||
def dropmessagelistcache(self):
|
||||
self._mb.dropmessagelistcache()
|
||||
|
||||
# Interface from BaseFolder
|
||||
def uidexists(self, ruid):
|
||||
"""Checks if the (remote) UID exists in this Folder"""
|
||||
|
@ -40,6 +40,7 @@ class OfflineImap:
|
||||
oi = OfflineImap()
|
||||
oi.run()
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
"""Parse the commandline and invoke everything"""
|
||||
# next line also sets self.config and self.ui
|
||||
@ -321,6 +322,8 @@ class OfflineImap:
|
||||
pass
|
||||
|
||||
try:
|
||||
# Honor CLI --account option, only.
|
||||
# Accounts to sync are put into syncaccounts variable.
|
||||
activeaccounts = self.config.get("general", "accounts")
|
||||
if options.accounts:
|
||||
activeaccounts = options.accounts
|
||||
|
@ -31,10 +31,10 @@ class LocalEval:
|
||||
if path is not None:
|
||||
# FIXME: limit opening files owned by current user with rights set
|
||||
# to fixed mode 644.
|
||||
file = open(path, 'r')
|
||||
foo = open(path, 'r')
|
||||
module = imp.load_module(
|
||||
'<none>',
|
||||
file,
|
||||
foo,
|
||||
path,
|
||||
('', 'r', imp.PY_SOURCE))
|
||||
for attr in dir(module):
|
||||
|
@ -39,6 +39,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
|
||||
self.mapdir = os.path.join(self.uiddir, 'UIDMapping')
|
||||
if not os.path.exists(self.mapdir):
|
||||
os.mkdir(self.mapdir, 0o700)
|
||||
# FIXME: self.uiddir variable name is lying about itself.
|
||||
self.uiddir = os.path.join(self.uiddir, 'FolderValidity')
|
||||
if not os.path.exists(self.uiddir):
|
||||
os.mkdir(self.uiddir, 0o700)
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Maildir repository support
|
||||
# Copyright (C) 2002 John Goerzen
|
||||
# Copyright (C) 2002-2015 John Goerzen & contributors
|
||||
# <jgoerzen@complete.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@ -23,8 +23,8 @@ class GmailMaildirRepository(MaildirRepository):
|
||||
def __init__(self, reposname, account):
|
||||
"""Initialize a MaildirRepository object. Takes a path name
|
||||
to the directory holding all the Maildir directories."""
|
||||
super(GmailMaildirRepository, self).__init__(reposname, account)
|
||||
|
||||
super(GmailMaildirRepository, self).__init__(reposname, account)
|
||||
|
||||
def getfoldertype(self):
|
||||
return GmailMaildirFolder
|
||||
|
@ -301,6 +301,8 @@ class IMAPRepository(BaseRepository):
|
||||
return None
|
||||
|
||||
def getfolder(self, foldername):
|
||||
"""Return instance of OfflineIMAP representative folder."""
|
||||
|
||||
return self.getfoldertype()(self.imapserver, foldername, self)
|
||||
|
||||
def getfoldertype(self):
|
||||
@ -314,6 +316,8 @@ class IMAPRepository(BaseRepository):
|
||||
self.folders = None
|
||||
|
||||
def getfolders(self):
|
||||
"""Return a list of instances of OfflineIMAP representative folder."""
|
||||
|
||||
if self.folders != None:
|
||||
return self.folders
|
||||
retval = []
|
||||
@ -326,13 +330,13 @@ class IMAPRepository(BaseRepository):
|
||||
listresult = listfunction(directory = self.imapserver.reference)[1]
|
||||
finally:
|
||||
self.imapserver.releaseconnection(imapobj)
|
||||
for string in listresult:
|
||||
if string == None or \
|
||||
(isinstance(string, basestring) and string == ''):
|
||||
for s in listresult:
|
||||
if s == None or \
|
||||
(isinstance(s, basestring) and s == ''):
|
||||
# Bug in imaplib: empty strings in results from
|
||||
# literals. TODO: still relevant?
|
||||
continue
|
||||
flags, delim, name = imaputil.imapsplit(string)
|
||||
flags, delim, name = imaputil.imapsplit(s)
|
||||
flaglist = [x.lower() for x in imaputil.flagsplit(flags)]
|
||||
if '\\noselect' in flaglist:
|
||||
continue
|
||||
@ -353,9 +357,8 @@ class IMAPRepository(BaseRepository):
|
||||
self.ui.error(e, exc_info()[2],
|
||||
'Invalid folderinclude:')
|
||||
continue
|
||||
retval.append(self.getfoldertype()(self.imapserver,
|
||||
foldername,
|
||||
self))
|
||||
retval.append(self.getfoldertype()(
|
||||
self.imapserver, foldername, self))
|
||||
finally:
|
||||
self.imapserver.releaseconnection(imapobj)
|
||||
|
||||
|
@ -94,7 +94,8 @@ class LocalStatusRepository(BaseRepository):
|
||||
self.forgetfolders()
|
||||
|
||||
def getfolder(self, foldername):
|
||||
"""Return the Folder() object for a foldername"""
|
||||
"""Return the Folder() object for a foldername."""
|
||||
|
||||
if foldername in self._folders:
|
||||
return self._folders[foldername]
|
||||
|
||||
|
@ -170,8 +170,8 @@ class MaildirRepository(BaseRepository):
|
||||
self.debug(" skip this entry (not a directory)")
|
||||
# Not a directory -- not a folder.
|
||||
continue
|
||||
# extension can be None.
|
||||
if extension:
|
||||
# extension can be None which fails.
|
||||
foldername = os.path.join(extension, dirname)
|
||||
else:
|
||||
foldername = dirname
|
||||
|
Loading…
Reference in New Issue
Block a user