2002-06-19 08:08:59 +02:00
|
|
|
# Maildir repository support
|
|
|
|
# Copyright (C) 2002 John Goerzen
|
|
|
|
# <jgoerzen@complete.org>
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
2003-04-16 21:23:45 +02:00
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
2002-06-19 08:08:59 +02:00
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
2006-08-12 06:15:55 +02:00
|
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2002-06-19 08:08:59 +02:00
|
|
|
|
|
|
|
from Base import BaseRepository
|
2011-03-11 22:13:21 +01:00
|
|
|
from offlineimap import folder
|
2011-01-05 17:00:56 +01:00
|
|
|
from offlineimap.ui import getglobalui
|
2011-08-29 15:33:58 +02:00
|
|
|
from offlineimap.error import OfflineImapError
|
2002-06-19 08:08:59 +02:00
|
|
|
import os
|
New restoreatime patch from Ben Kibbey
From: Ben Kibbey
Subject: Re: Removed restoratime from OfflineIMAP
On Wed, May 03, 2006 at 10:08:35PM -0500, John Goerzen wrote:
> Hi Ben,
>
> Thanks for your restoreatime patch.
>
> However, I have received this bug report:
>
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=365933
>
> After looking at the problem, here's what's going on.
>
> The person is using IMAP as the local repository as well.
>
> You really need to move the atime save and restore code from accounts.py
> into the repository/Maildir.py. Then, for any new call you add to the
> Maildir repository (that will be called from outside Maildir.py), you
> need to add a corresponding default function to repository/Base.py, and
> also make sure that on folders (such as IMAP) where atime restoration
> makes no sense, no error is generated.
>
> Let me know if that doesn't make sense to you. If you get it fixed, I'd
> be happy to re-apply it to a future version of OfflineIMAP.
>
> -- John Goerzen
>
Attached is a new diff that should work though not really tested
(v4.0.14). In repository/Base.py restore_atime() will call
self.restore_folder_atimes() only if the folder type is Maildir. Let me
know if it has any more problems.
2006-09-06 03:33:07 +02:00
|
|
|
from stat import *
|
2002-06-19 08:08:59 +02:00
|
|
|
|
|
|
|
class MaildirRepository(BaseRepository):
|
2003-04-18 04:18:34 +02:00
|
|
|
def __init__(self, reposname, account):
|
2002-06-19 08:08:59 +02:00
|
|
|
"""Initialize a MaildirRepository object. Takes a path name
|
|
|
|
to the directory holding all the Maildir directories."""
|
2003-04-18 04:18:34 +02:00
|
|
|
BaseRepository.__init__(self, reposname, account)
|
2002-06-19 08:08:59 +02:00
|
|
|
|
2003-04-18 04:18:34 +02:00
|
|
|
self.root = self.getlocalroot()
|
2002-06-19 08:08:59 +02:00
|
|
|
self.folders = None
|
2011-01-05 17:00:56 +01:00
|
|
|
self.ui = getglobalui()
|
2002-08-08 22:15:30 +02:00
|
|
|
self.debug("MaildirRepository initialized, sep is " + repr(self.getsep()))
|
2011-08-16 10:55:13 +02:00
|
|
|
self.folder_atimes = []
|
New restoreatime patch from Ben Kibbey
From: Ben Kibbey
Subject: Re: Removed restoratime from OfflineIMAP
On Wed, May 03, 2006 at 10:08:35PM -0500, John Goerzen wrote:
> Hi Ben,
>
> Thanks for your restoreatime patch.
>
> However, I have received this bug report:
>
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=365933
>
> After looking at the problem, here's what's going on.
>
> The person is using IMAP as the local repository as well.
>
> You really need to move the atime save and restore code from accounts.py
> into the repository/Maildir.py. Then, for any new call you add to the
> Maildir repository (that will be called from outside Maildir.py), you
> need to add a corresponding default function to repository/Base.py, and
> also make sure that on folders (such as IMAP) where atime restoration
> makes no sense, no error is generated.
>
> Let me know if that doesn't make sense to you. If you get it fixed, I'd
> be happy to re-apply it to a future version of OfflineIMAP.
>
> -- John Goerzen
>
Attached is a new diff that should work though not really tested
(v4.0.14). In repository/Base.py restore_atime() will call
self.restore_folder_atimes() only if the folder type is Maildir. Let me
know if it has any more problems.
2006-09-06 03:33:07 +02:00
|
|
|
|
2008-03-27 22:19:35 +01:00
|
|
|
# Create the top-level folder if it doesn't exist
|
|
|
|
if not os.path.isdir(self.root):
|
|
|
|
os.mkdir(self.root, 0700)
|
|
|
|
|
New restoreatime patch from Ben Kibbey
From: Ben Kibbey
Subject: Re: Removed restoratime from OfflineIMAP
On Wed, May 03, 2006 at 10:08:35PM -0500, John Goerzen wrote:
> Hi Ben,
>
> Thanks for your restoreatime patch.
>
> However, I have received this bug report:
>
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=365933
>
> After looking at the problem, here's what's going on.
>
> The person is using IMAP as the local repository as well.
>
> You really need to move the atime save and restore code from accounts.py
> into the repository/Maildir.py. Then, for any new call you add to the
> Maildir repository (that will be called from outside Maildir.py), you
> need to add a corresponding default function to repository/Base.py, and
> also make sure that on folders (such as IMAP) where atime restoration
> makes no sense, no error is generated.
>
> Let me know if that doesn't make sense to you. If you get it fixed, I'd
> be happy to re-apply it to a future version of OfflineIMAP.
>
> -- John Goerzen
>
Attached is a new diff that should work though not really tested
(v4.0.14). In repository/Base.py restore_atime() will call
self.restore_folder_atimes() only if the folder type is Maildir. Let me
know if it has any more problems.
2006-09-06 03:33:07 +02:00
|
|
|
def _append_folder_atimes(self, foldername):
|
2011-09-15 15:06:30 +02:00
|
|
|
"""Store the atimes of a folder's new|cur in self.folder_atimes"""
|
2011-08-16 10:55:13 +02:00
|
|
|
p = os.path.join(self.root, foldername)
|
|
|
|
new = os.path.join(p, 'new')
|
|
|
|
cur = os.path.join(p, 'cur')
|
2011-09-15 15:06:31 +02:00
|
|
|
atimes = (p, os.path.getatime(new), os.path.getatime(cur))
|
|
|
|
self.folder_atimes.append(atimes)
|
New restoreatime patch from Ben Kibbey
From: Ben Kibbey
Subject: Re: Removed restoratime from OfflineIMAP
On Wed, May 03, 2006 at 10:08:35PM -0500, John Goerzen wrote:
> Hi Ben,
>
> Thanks for your restoreatime patch.
>
> However, I have received this bug report:
>
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=365933
>
> After looking at the problem, here's what's going on.
>
> The person is using IMAP as the local repository as well.
>
> You really need to move the atime save and restore code from accounts.py
> into the repository/Maildir.py. Then, for any new call you add to the
> Maildir repository (that will be called from outside Maildir.py), you
> need to add a corresponding default function to repository/Base.py, and
> also make sure that on folders (such as IMAP) where atime restoration
> makes no sense, no error is generated.
>
> Let me know if that doesn't make sense to you. If you get it fixed, I'd
> be happy to re-apply it to a future version of OfflineIMAP.
>
> -- John Goerzen
>
Attached is a new diff that should work though not really tested
(v4.0.14). In repository/Base.py restore_atime() will call
self.restore_folder_atimes() only if the folder type is Maildir. Let me
know if it has any more problems.
2006-09-06 03:33:07 +02:00
|
|
|
|
2011-09-15 15:06:30 +02:00
|
|
|
def restore_atime(self):
|
|
|
|
"""Sets folders' atime back to their values after a sync
|
|
|
|
|
|
|
|
Controlled by the 'restoreatime' config parameter."""
|
|
|
|
if not self.getconfboolean('restoreatime', False):
|
2011-09-15 15:06:31 +02:00
|
|
|
return # not configured to restore
|
New restoreatime patch from Ben Kibbey
From: Ben Kibbey
Subject: Re: Removed restoratime from OfflineIMAP
On Wed, May 03, 2006 at 10:08:35PM -0500, John Goerzen wrote:
> Hi Ben,
>
> Thanks for your restoreatime patch.
>
> However, I have received this bug report:
>
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=365933
>
> After looking at the problem, here's what's going on.
>
> The person is using IMAP as the local repository as well.
>
> You really need to move the atime save and restore code from accounts.py
> into the repository/Maildir.py. Then, for any new call you add to the
> Maildir repository (that will be called from outside Maildir.py), you
> need to add a corresponding default function to repository/Base.py, and
> also make sure that on folders (such as IMAP) where atime restoration
> makes no sense, no error is generated.
>
> Let me know if that doesn't make sense to you. If you get it fixed, I'd
> be happy to re-apply it to a future version of OfflineIMAP.
>
> -- John Goerzen
>
Attached is a new diff that should work though not really tested
(v4.0.14). In repository/Base.py restore_atime() will call
self.restore_folder_atimes() only if the folder type is Maildir. Let me
know if it has any more problems.
2006-09-06 03:33:07 +02:00
|
|
|
|
2011-09-15 15:06:31 +02:00
|
|
|
for (dirpath, new_atime, cur_atime) in self.folder_atimes:
|
|
|
|
new_dir = os.path.join(dirpath, 'new')
|
|
|
|
cur_dir = os.path.join(dirpath, 'cur')
|
|
|
|
os.utime(new_dir, (new_atime, os.path.getmtime(new_dir)))
|
|
|
|
os.utime(cur_dir, (cur_atime, os.path.getmtime(cur_dir)))
|
2002-08-08 22:15:30 +02:00
|
|
|
|
2003-04-18 04:18:34 +02:00
|
|
|
def getlocalroot(self):
|
|
|
|
return os.path.expanduser(self.getconf('localfolders'))
|
|
|
|
|
2002-08-08 22:15:30 +02:00
|
|
|
def debug(self, msg):
|
|
|
|
self.ui.debug('maildir', msg)
|
2002-06-19 08:08:59 +02:00
|
|
|
|
2002-06-19 08:16:19 +02:00
|
|
|
def getsep(self):
|
2003-04-18 04:18:34 +02:00
|
|
|
return self.getconf('sep', '.').strip()
|
2002-06-20 04:55:24 +02:00
|
|
|
|
|
|
|
def makefolder(self, foldername):
|
2011-06-06 11:37:25 +02:00
|
|
|
"""Create new Maildir folder if necessary
|
|
|
|
|
2011-09-30 08:58:08 +02:00
|
|
|
This will not update the list cached in getfolders(). You will
|
|
|
|
need to invoke :meth:`forgetfolders` to force new caching when
|
|
|
|
you are done creating folders yourself.
|
|
|
|
|
2011-06-06 11:37:25 +02:00
|
|
|
:param foldername: A relative mailbox name. The maildir will be
|
|
|
|
created in self.root+'/'+foldername. All intermediate folder
|
|
|
|
levels will be created if they do not exist yet. 'cur',
|
|
|
|
'tmp', and 'new' subfolders will be created in the maildir.
|
|
|
|
"""
|
|
|
|
self.debug("makefolder called with arg '%s'" % (foldername))
|
|
|
|
full_path = os.path.abspath(os.path.join(self.root, foldername))
|
|
|
|
|
|
|
|
# sanity tests
|
2002-08-08 04:41:52 +02:00
|
|
|
if self.getsep() == '/':
|
2011-06-06 11:37:25 +02:00
|
|
|
for component in foldername.split('/'):
|
|
|
|
assert not component in ['new', 'cur', 'tmp'],\
|
|
|
|
"When using nested folders (/ as a Maildir separator), "\
|
|
|
|
"folder names may not contain 'new', 'cur', 'tmp'."
|
|
|
|
assert foldername.find('../') == -1, "Folder names may not contain ../"
|
2002-08-08 04:44:37 +02:00
|
|
|
assert not foldername.startswith('/'), "Folder names may not begin with /"
|
2002-08-08 05:01:31 +02:00
|
|
|
|
2011-06-06 11:37:25 +02:00
|
|
|
# If we're using hierarchical folders, it's possible that
|
|
|
|
# sub-folders may be created before higher-up ones.
|
|
|
|
self.debug("makefolder: calling makedirs '%s'" % full_path)
|
|
|
|
try:
|
|
|
|
os.makedirs(full_path, 0700)
|
|
|
|
except OSError, e:
|
|
|
|
if e.errno == 17 and os.path.isdir(full_path):
|
|
|
|
self.debug("makefolder: '%s' already a directory" % foldername)
|
|
|
|
else:
|
|
|
|
raise
|
2002-06-20 04:55:24 +02:00
|
|
|
for subdir in ['cur', 'new', 'tmp']:
|
2011-06-06 11:37:25 +02:00
|
|
|
try:
|
|
|
|
os.mkdir(os.path.join(full_path, subdir), 0700)
|
|
|
|
except OSError, e:
|
|
|
|
if e.errno == 17 and os.path.isdir(full_path):
|
|
|
|
self.debug("makefolder: '%s' already has subdir %s" %
|
2011-06-22 21:38:19 +02:00
|
|
|
(foldername, subdir))
|
2011-06-06 11:37:25 +02:00
|
|
|
else:
|
|
|
|
raise
|
2002-06-20 04:55:24 +02:00
|
|
|
|
|
|
|
def deletefolder(self, foldername):
|
2002-08-08 22:15:30 +02:00
|
|
|
self.ui.warn("NOT YET IMPLEMENTED: DELETE FOLDER %s" % foldername)
|
2002-06-20 04:55:24 +02:00
|
|
|
|
|
|
|
def getfolder(self, foldername):
|
2011-08-29 15:33:58 +02:00
|
|
|
"""Return a Folder instance of this Maildir
|
|
|
|
|
|
|
|
If necessary, scan and cache all foldernames to make sure that
|
|
|
|
we only return existing folders and that 2 calls with the same
|
|
|
|
name will return the same object."""
|
|
|
|
# getfolders() will scan and cache the values *if* necessary
|
|
|
|
folders = self.getfolders()
|
|
|
|
for folder in folders:
|
|
|
|
if foldername == folder.name:
|
|
|
|
return folder
|
|
|
|
raise OfflineImapError("getfolder() asked for a nonexisting "
|
|
|
|
"folder '%s'." % foldername,
|
|
|
|
OfflineImapError.ERROR.FOLDER)
|
|
|
|
|
2002-08-08 04:57:52 +02:00
|
|
|
def _getfolders_scandir(self, root, extension = None):
|
2011-06-16 17:09:29 +02:00
|
|
|
"""Recursively scan folder 'root'; return a list of MailDirFolder
|
|
|
|
|
|
|
|
:param root: (absolute) path to Maildir root
|
|
|
|
:param extension: (relative) subfolder to examine within root"""
|
2002-08-08 22:15:30 +02:00
|
|
|
self.debug("_GETFOLDERS_SCANDIR STARTING. root = %s, extension = %s" \
|
|
|
|
% (root, extension))
|
2002-06-19 08:08:59 +02:00
|
|
|
retval = []
|
2002-08-08 04:57:52 +02:00
|
|
|
|
|
|
|
# Configure the full path to this repository -- "toppath"
|
2011-06-16 17:09:29 +02:00
|
|
|
if extension:
|
2002-08-08 04:57:52 +02:00
|
|
|
toppath = os.path.join(root, extension)
|
2011-06-16 17:09:29 +02:00
|
|
|
else:
|
|
|
|
toppath = root
|
2002-08-08 22:15:30 +02:00
|
|
|
self.debug(" toppath = %s" % toppath)
|
|
|
|
|
2011-06-06 11:37:25 +02:00
|
|
|
# Iterate over directories in top & top itself.
|
2011-09-19 14:14:44 +02:00
|
|
|
for dirname in os.listdir(toppath) + ['']:
|
2002-08-08 22:15:30 +02:00
|
|
|
self.debug(" *** top of loop")
|
|
|
|
self.debug(" dirname = %s" % dirname)
|
2011-06-06 11:37:25 +02:00
|
|
|
if dirname in ['cur', 'new', 'tmp']:
|
2002-08-08 22:15:30 +02:00
|
|
|
self.debug(" skipping this dir (Maildir special)")
|
2002-08-08 04:57:52 +02:00
|
|
|
# Bypass special files.
|
|
|
|
continue
|
|
|
|
fullname = os.path.join(toppath, dirname)
|
2002-08-08 22:15:30 +02:00
|
|
|
self.debug(" fullname = %s" % fullname)
|
2002-06-19 08:08:59 +02:00
|
|
|
if not os.path.isdir(fullname):
|
2002-08-08 22:15:30 +02:00
|
|
|
self.debug(" skipping this entry (not a directory)")
|
2002-08-08 04:57:52 +02:00
|
|
|
# Not a directory -- not a folder.
|
2002-06-19 08:08:59 +02:00
|
|
|
continue
|
2002-08-09 03:53:57 +02:00
|
|
|
foldername = dirname
|
2011-12-01 17:01:43 +01:00
|
|
|
if extension and dirname != '':
|
2002-08-09 03:53:57 +02:00
|
|
|
foldername = os.path.join(extension, dirname)
|
2002-08-09 03:53:05 +02:00
|
|
|
if (os.path.isdir(os.path.join(fullname, 'cur')) and
|
|
|
|
os.path.isdir(os.path.join(fullname, 'new')) and
|
|
|
|
os.path.isdir(os.path.join(fullname, 'tmp'))):
|
|
|
|
# This directory has maildir stuff -- process
|
2011-06-16 17:09:29 +02:00
|
|
|
self.debug(" This is maildir folder '%s'." % foldername)
|
2011-08-29 15:33:58 +02:00
|
|
|
if self.getconfboolean('restoreatime', False):
|
2011-08-16 10:55:13 +02:00
|
|
|
self._append_folder_atimes(foldername)
|
2011-06-16 17:09:29 +02:00
|
|
|
retval.append(folder.Maildir.MaildirFolder(self.root,
|
|
|
|
foldername,
|
|
|
|
self.getsep(),
|
2011-09-16 10:54:24 +02:00
|
|
|
self))
|
2011-08-29 15:09:16 +02:00
|
|
|
# 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
|
|
|
|
|
2011-09-19 14:14:44 +02:00
|
|
|
if self.getsep() == '/' and dirname != '':
|
2011-06-16 17:09:29 +02:00
|
|
|
# Recursively check sub-directories for folders too.
|
2002-08-08 05:27:55 +02:00
|
|
|
retval.extend(self._getfolders_scandir(root, foldername))
|
2002-08-08 22:18:45 +02:00
|
|
|
self.debug("_GETFOLDERS_SCANDIR RETURNING %s" % \
|
|
|
|
repr([x.getname() for x in retval]))
|
2002-06-19 08:08:59 +02:00
|
|
|
return retval
|
2011-12-01 17:01:43 +01:00
|
|
|
|
2002-08-08 04:57:52 +02:00
|
|
|
def getfolders(self):
|
|
|
|
if self.folders == None:
|
|
|
|
self.folders = self._getfolders_scandir(self.root)
|
|
|
|
return self.folders
|
|
|
|
|