Step 2 of SVN to arch tree conversion
This commit is contained in:
145
offlineimap/repository/Base.py
Normal file
145
offlineimap/repository/Base.py
Normal file
@ -0,0 +1,145 @@
|
||||
# Base repository support
|
||||
# Copyright (C) 2002, 2003 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# 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
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
from offlineimap import CustomConfig
|
||||
import os.path
|
||||
|
||||
def LoadRepository(name, account, reqtype):
|
||||
from offlineimap.repository.IMAP import IMAPRepository, MappedIMAPRepository
|
||||
from offlineimap.repository.Maildir import MaildirRepository
|
||||
if reqtype == 'remote':
|
||||
# For now, we don't support Maildirs on the remote side.
|
||||
typemap = {'IMAP': IMAPRepository}
|
||||
elif reqtype == 'local':
|
||||
typemap = {'IMAP': MappedIMAPRepository,
|
||||
'Maildir': MaildirRepository}
|
||||
else:
|
||||
raise ValueError, "Request type %s not supported" % reqtype
|
||||
config = account.getconfig()
|
||||
repostype = config.get('Repository ' + name, 'type').strip()
|
||||
return typemap[repostype](name, account)
|
||||
|
||||
class BaseRepository(CustomConfig.ConfigHelperMixin):
|
||||
def __init__(self, reposname, account):
|
||||
self.account = account
|
||||
self.config = account.getconfig()
|
||||
self.name = reposname
|
||||
self.localeval = account.getlocaleval()
|
||||
self.accountname = self.account.getname()
|
||||
self.uiddir = os.path.join(self.config.getmetadatadir(), 'Repository-' + self.name)
|
||||
if not os.path.exists(self.uiddir):
|
||||
os.mkdir(self.uiddir, 0700)
|
||||
self.mapdir = os.path.join(self.uiddir, 'UIDMapping')
|
||||
if not os.path.exists(self.mapdir):
|
||||
os.mkdir(self.mapdir, 0700)
|
||||
self.uiddir = os.path.join(self.uiddir, 'FolderValidity')
|
||||
if not os.path.exists(self.uiddir):
|
||||
os.mkdir(self.uiddir, 0700)
|
||||
|
||||
def holdordropconnections(self):
|
||||
pass
|
||||
|
||||
def dropconnections(self):
|
||||
pass
|
||||
|
||||
def getaccount(self):
|
||||
return self.account
|
||||
|
||||
def getname(self):
|
||||
return self.name
|
||||
|
||||
def getuiddir(self):
|
||||
return self.uiddir
|
||||
|
||||
def getmapdir(self):
|
||||
return self.mapdir
|
||||
|
||||
def getaccountname(self):
|
||||
return self.accountname
|
||||
|
||||
def getsection(self):
|
||||
return 'Repository ' + self.name
|
||||
|
||||
def getconfig(self):
|
||||
return self.config
|
||||
|
||||
def getlocaleval(self):
|
||||
return self.account.getlocaleval()
|
||||
|
||||
def getfolders(self):
|
||||
"""Returns a list of ALL folders on this server."""
|
||||
return []
|
||||
|
||||
def getsep(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def makefolder(self, foldername):
|
||||
raise NotImplementedError
|
||||
|
||||
def deletefolder(self, foldername):
|
||||
raise NotImplementedError
|
||||
|
||||
def getfolder(self, foldername):
|
||||
raise NotImplementedError
|
||||
|
||||
def syncfoldersto(self, dest):
|
||||
"""Syncs the folders in this repository to those in dest.
|
||||
It does NOT sync the contents of those folders."""
|
||||
src = self
|
||||
srcfolders = src.getfolders()
|
||||
destfolders = dest.getfolders()
|
||||
|
||||
# Create hashes with the names, but convert the source folders
|
||||
# to the dest folder's sep.
|
||||
|
||||
srchash = {}
|
||||
for folder in srcfolders:
|
||||
srchash[folder.getvisiblename().replace(src.getsep(), dest.getsep())] = \
|
||||
folder
|
||||
desthash = {}
|
||||
for folder in destfolders:
|
||||
desthash[folder.getvisiblename()] = folder
|
||||
|
||||
#
|
||||
# Find new folders.
|
||||
#
|
||||
|
||||
for key in srchash.keys():
|
||||
if not key in desthash:
|
||||
dest.makefolder(key)
|
||||
|
||||
#
|
||||
# Find deleted folders.
|
||||
#
|
||||
# We don't delete folders right now.
|
||||
|
||||
#for key in desthash.keys():
|
||||
# if not key in srchash:
|
||||
# dest.deletefolder(key)
|
||||
|
||||
##### Keepalive
|
||||
|
||||
def startkeepalive(self):
|
||||
"""The default implementation will do nothing."""
|
||||
pass
|
||||
|
||||
def stopkeepalive(self, abrupt = 0):
|
||||
"""Stop keep alive. If abrupt is 1, stop it but don't bother waiting
|
||||
for the threads to terminate."""
|
||||
pass
|
||||
|
191
offlineimap/repository/IMAP.py
Normal file
191
offlineimap/repository/IMAP.py
Normal file
@ -0,0 +1,191 @@
|
||||
# IMAP 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# 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
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
from Base import BaseRepository
|
||||
from offlineimap import folder, imaputil, imapserver
|
||||
from offlineimap.folder.UIDMaps import MappedIMAPFolder
|
||||
from offlineimap.threadutil import ExitNotifyThread
|
||||
import re, types, os
|
||||
from threading import *
|
||||
|
||||
class IMAPRepository(BaseRepository):
|
||||
def __init__(self, reposname, account):
|
||||
"""Initialize an IMAPRepository object."""
|
||||
BaseRepository.__init__(self, reposname, account)
|
||||
self.imapserver = imapserver.ConfigedIMAPServer(self)
|
||||
self.folders = None
|
||||
self.nametrans = lambda foldername: foldername
|
||||
self.folderfilter = lambda foldername: 1
|
||||
self.folderincludes = []
|
||||
self.foldersort = cmp
|
||||
localeval = self.localeval
|
||||
if self.config.has_option(self.getsection(), 'nametrans'):
|
||||
self.nametrans = localeval.eval(self.getconf('nametrans'),
|
||||
{'re': re})
|
||||
if self.config.has_option(self.getsection(), 'folderfilter'):
|
||||
self.folderfilter = localeval.eval(self.getconf('folderfilter'),
|
||||
{'re': re})
|
||||
if self.config.has_option(self.getsection(), 'folderincludes'):
|
||||
self.folderincludes = localeval.eval(self.getconf('folderincludes'),
|
||||
{'re': re})
|
||||
if self.config.has_option(self.getsection(), 'foldersort'):
|
||||
self.foldersort = localeval.eval(self.getconf('foldersort'),
|
||||
{'re': re})
|
||||
|
||||
def startkeepalive(self):
|
||||
keepalivetime = self.getkeepalive()
|
||||
if not keepalivetime: return
|
||||
self.kaevent = Event()
|
||||
self.kathread = ExitNotifyThread(target = self.imapserver.keepalive,
|
||||
name = "Keep alive " + self.getname(),
|
||||
args = (keepalivetime, self.kaevent))
|
||||
self.kathread.setDaemon(1)
|
||||
self.kathread.start()
|
||||
|
||||
def stopkeepalive(self, abrupt = 0):
|
||||
if not hasattr(self, 'kaevent'):
|
||||
# Keepalive is not active.
|
||||
return
|
||||
|
||||
self.kaevent.set()
|
||||
if not abrupt:
|
||||
self.kathread.join()
|
||||
del self.kathread
|
||||
del self.kaevent
|
||||
|
||||
def holdordropconnections(self):
|
||||
if not self.getholdconnectionopen():
|
||||
self.dropconnections()
|
||||
|
||||
def dropconnections(self):
|
||||
self.imapserver.close()
|
||||
|
||||
def getholdconnectionopen(self):
|
||||
return self.getconfboolean("holdconnectionopen", 0)
|
||||
|
||||
def getkeepalive(self):
|
||||
return self.getconfint("keepalive", 0)
|
||||
|
||||
def getsep(self):
|
||||
return self.imapserver.delim
|
||||
|
||||
def gethost(self):
|
||||
return self.getconf('remotehost')
|
||||
|
||||
def getuser(self):
|
||||
return self.getconf('remoteuser')
|
||||
|
||||
def getport(self):
|
||||
return self.getconfint('remoteport', None)
|
||||
|
||||
def getssl(self):
|
||||
return self.getconfboolean('ssl', 0)
|
||||
|
||||
def getpreauthtunnel(self):
|
||||
return self.getconf('preauthtunnel', None)
|
||||
|
||||
def getreference(self):
|
||||
return self.getconf('reference', '""')
|
||||
|
||||
def getmaxconnections(self):
|
||||
return self.getconfint('maxconnections', 1)
|
||||
|
||||
def getexpunge(self):
|
||||
return self.getconfboolean('expunge', 1)
|
||||
|
||||
def getpassword(self):
|
||||
password = self.getconf('remotepass', None)
|
||||
if password != None:
|
||||
return password
|
||||
passfile = self.getconf('remotepassfile', None)
|
||||
if passfile != None:
|
||||
fd = open(os.path.expanduser(passfile))
|
||||
password = fd.readline().strip()
|
||||
fd.close()
|
||||
return password
|
||||
return None
|
||||
|
||||
def getfolder(self, foldername):
|
||||
return self.getfoldertype()(self.imapserver, foldername,
|
||||
self.nametrans(foldername),
|
||||
self.accountname, self)
|
||||
|
||||
def getfoldertype(self):
|
||||
return folder.IMAP.IMAPFolder
|
||||
|
||||
def getfolders(self):
|
||||
if self.folders != None:
|
||||
return self.folders
|
||||
retval = []
|
||||
imapobj = self.imapserver.acquireconnection()
|
||||
try:
|
||||
listresult = imapobj.list(directory = self.imapserver.reference)[1]
|
||||
finally:
|
||||
self.imapserver.releaseconnection(imapobj)
|
||||
for string in listresult:
|
||||
if string == None or \
|
||||
(type(string) == types.StringType and string == ''):
|
||||
# Bug in imaplib: empty strings in results from
|
||||
# literals.
|
||||
continue
|
||||
flags, delim, name = imaputil.imapsplit(string)
|
||||
flaglist = [x.lower() for x in imaputil.flagsplit(flags)]
|
||||
if '\\noselect' in flaglist:
|
||||
continue
|
||||
foldername = imaputil.dequote(name)
|
||||
if not self.folderfilter(foldername):
|
||||
continue
|
||||
retval.append(self.getfoldertype()(self.imapserver, foldername,
|
||||
self.nametrans(foldername),
|
||||
self.accountname, self))
|
||||
if len(self.folderincludes):
|
||||
imapobj = self.imapserver.acquireconnection()
|
||||
try:
|
||||
for foldername in self.folderincludes:
|
||||
try:
|
||||
imapobj.select(foldername, readonly = 1)
|
||||
except ValueError:
|
||||
continue
|
||||
retval.append(self.getfoldertype()(self.imapserver,
|
||||
foldername,
|
||||
self.nametrans(foldername),
|
||||
self.accountname, self))
|
||||
finally:
|
||||
self.imapserver.releaseconnection(imapobj)
|
||||
|
||||
retval.sort(lambda x, y: self.foldersort(x.getvisiblename(), y.getvisiblename()))
|
||||
self.folders = retval
|
||||
return retval
|
||||
|
||||
def makefolder(self, foldername):
|
||||
#if self.getreference() != '""':
|
||||
# newname = self.getreference() + self.getsep() + foldername
|
||||
#else:
|
||||
# newname = foldername
|
||||
newname = foldername
|
||||
imapobj = self.imapserver.acquireconnection()
|
||||
try:
|
||||
result = imapobj.create(newname)
|
||||
if result[0] != 'OK':
|
||||
raise RuntimeError, "Repository %s could not create folder %s: %s" % (self.getname(), foldername, str(result))
|
||||
finally:
|
||||
self.imapserver.releaseconnection(imapobj)
|
||||
|
||||
class MappedIMAPRepository(IMAPRepository):
|
||||
def getfoldertype(self):
|
||||
return MappedIMAPFolder
|
60
offlineimap/repository/LocalStatus.py
Normal file
60
offlineimap/repository/LocalStatus.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Local status cache 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# 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
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
from Base import BaseRepository
|
||||
from offlineimap import folder
|
||||
import os, re
|
||||
|
||||
class LocalStatusRepository(BaseRepository):
|
||||
def __init__(self, reposname, account):
|
||||
BaseRepository.__init__(self, reposname, account)
|
||||
self.directory = os.path.join(account.getaccountmeta(), 'LocalStatus')
|
||||
if not os.path.exists(self.directory):
|
||||
os.mkdir(self.directory, 0700)
|
||||
self.folders = None
|
||||
|
||||
def getsep(self):
|
||||
return '.'
|
||||
|
||||
def getfolderfilename(self, foldername):
|
||||
foldername = re.sub('/\.$', '/dot', foldername)
|
||||
foldername = re.sub('^\.$', 'dot', foldername)
|
||||
return os.path.join(self.directory, foldername)
|
||||
|
||||
def makefolder(self, foldername):
|
||||
# "touch" the file.
|
||||
file = open(self.getfolderfilename(foldername), "ab")
|
||||
file.close()
|
||||
# Invalidate the cache.
|
||||
self.folders = None
|
||||
|
||||
def getfolders(self):
|
||||
retval = []
|
||||
for folder in os.listdir(self.directory):
|
||||
retval.append(folder.LocalStatus.LocalStatusFolder(self.directory,
|
||||
folder, self, self.accountname))
|
||||
return retval
|
||||
|
||||
def getfolder(self, foldername):
|
||||
return folder.LocalStatus.LocalStatusFolder(self.directory, foldername,
|
||||
self, self.accountname)
|
||||
|
||||
|
||||
|
||||
|
||||
|
146
offlineimap/repository/Maildir.py
Normal file
146
offlineimap/repository/Maildir.py
Normal file
@ -0,0 +1,146 @@
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# 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
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
from Base import BaseRepository
|
||||
from offlineimap import folder, imaputil
|
||||
from offlineimap.ui import UIBase
|
||||
from mailbox import Maildir
|
||||
import os
|
||||
|
||||
class MaildirRepository(BaseRepository):
|
||||
def __init__(self, reposname, account):
|
||||
"""Initialize a MaildirRepository object. Takes a path name
|
||||
to the directory holding all the Maildir directories."""
|
||||
BaseRepository.__init__(self, reposname, account)
|
||||
|
||||
self.root = self.getlocalroot()
|
||||
self.folders = None
|
||||
self.ui = UIBase.getglobalui()
|
||||
self.debug("MaildirRepository initialized, sep is " + repr(self.getsep()))
|
||||
|
||||
def getlocalroot(self):
|
||||
return os.path.expanduser(self.getconf('localfolders'))
|
||||
|
||||
def debug(self, msg):
|
||||
self.ui.debug('maildir', msg)
|
||||
|
||||
def getsep(self):
|
||||
return self.getconf('sep', '.').strip()
|
||||
|
||||
def makefolder(self, foldername):
|
||||
self.debug("makefolder called with arg " + repr(foldername))
|
||||
# Do the chdir thing so the call to makedirs does not make the
|
||||
# self.root directory (we'd prefer to raise an error in that case),
|
||||
# but will make the (relative) paths underneath it. Need to use
|
||||
# makedirs to support a / separator.
|
||||
if self.getsep() == '/':
|
||||
for invalid in ['new', 'cur', 'tmp', 'offlineimap.uidvalidity']:
|
||||
for component in foldername.split('/'):
|
||||
assert component != invalid, "When using nested folders (/ as a separator in the account config), your folder names may not contain 'new', 'cur', 'tmp', or 'offlineimap.uidvalidity'."
|
||||
|
||||
assert foldername.find('./') == -1, "Folder names may not contain ../"
|
||||
assert not foldername.startswith('/'), "Folder names may not begin with /"
|
||||
|
||||
oldcwd = os.getcwd()
|
||||
os.chdir(self.root)
|
||||
|
||||
# If we're using hierarchical folders, it's possible that sub-folders
|
||||
# may be created before higher-up ones. If this is the case,
|
||||
# makedirs will fail because the higher-up dir already exists.
|
||||
# So, check to see if this is indeed the case.
|
||||
|
||||
if (self.getsep() == '/' or self.getconfboolean('existsok', 0)) \
|
||||
and os.path.isdir(foldername):
|
||||
self.debug("makefolder: %s already is a directory" % foldername)
|
||||
# Already exists. Sanity-check that it's not a Maildir.
|
||||
for subdir in ['cur', 'new', 'tmp']:
|
||||
assert not os.path.isdir(os.path.join(foldername, subdir)), \
|
||||
"Tried to create folder %s but it already had dir %s" %\
|
||||
(foldername, subdir)
|
||||
else:
|
||||
self.debug("makefolder: calling makedirs %s" % foldername)
|
||||
os.makedirs(foldername, 0700)
|
||||
self.debug("makefolder: creating cur, new, tmp")
|
||||
for subdir in ['cur', 'new', 'tmp']:
|
||||
os.mkdir(os.path.join(foldername, subdir), 0700)
|
||||
# Invalidate the cache
|
||||
self.folders = None
|
||||
os.chdir(oldcwd)
|
||||
|
||||
def deletefolder(self, foldername):
|
||||
self.ui.warn("NOT YET IMPLEMENTED: DELETE FOLDER %s" % foldername)
|
||||
|
||||
def getfolder(self, foldername):
|
||||
return folder.Maildir.MaildirFolder(self.root, foldername,
|
||||
self.getsep(), self, self.accountname)
|
||||
|
||||
def _getfolders_scandir(self, root, extension = None):
|
||||
self.debug("_GETFOLDERS_SCANDIR STARTING. root = %s, extension = %s" \
|
||||
% (root, extension))
|
||||
# extension willl only be non-None when called recursively when
|
||||
# getsep() returns '/'.
|
||||
retval = []
|
||||
|
||||
# Configure the full path to this repository -- "toppath"
|
||||
|
||||
if extension == None:
|
||||
toppath = root
|
||||
else:
|
||||
toppath = os.path.join(root, extension)
|
||||
|
||||
self.debug(" toppath = %s" % toppath)
|
||||
|
||||
# Iterate over directories in top.
|
||||
for dirname in os.listdir(toppath) + ['.']:
|
||||
self.debug(" *** top of loop")
|
||||
self.debug(" dirname = %s" % dirname)
|
||||
if dirname in ['cur', 'new', 'tmp', 'offlineimap.uidvalidity']:
|
||||
self.debug(" skipping this dir (Maildir special)")
|
||||
# Bypass special files.
|
||||
continue
|
||||
fullname = os.path.join(toppath, dirname)
|
||||
self.debug(" fullname = %s" % fullname)
|
||||
if not os.path.isdir(fullname):
|
||||
self.debug(" skipping this entry (not a directory)")
|
||||
# Not a directory -- not a folder.
|
||||
continue
|
||||
foldername = dirname
|
||||
if extension != None:
|
||||
foldername = os.path.join(extension, dirname)
|
||||
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
|
||||
self.debug(" This is a maildir folder.")
|
||||
|
||||
self.debug(" foldername = %s" % foldername)
|
||||
|
||||
retval.append(folder.Maildir.MaildirFolder(self.root, foldername,
|
||||
self.getsep(), self, self.accountname))
|
||||
if self.getsep() == '/' and dirname != '.':
|
||||
# Check sub-directories for folders.
|
||||
retval.extend(self._getfolders_scandir(root, foldername))
|
||||
self.debug("_GETFOLDERS_SCANDIR RETURNING %s" % \
|
||||
repr([x.getname() for x in retval]))
|
||||
return retval
|
||||
|
||||
def getfolders(self):
|
||||
if self.folders == None:
|
||||
self.folders = self._getfolders_scandir(self.root)
|
||||
return self.folders
|
||||
|
1
offlineimap/repository/__init__.py
Normal file
1
offlineimap/repository/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
__all__ = ['IMAP', 'Base', 'Maildir', 'LocalStatus']
|
Reference in New Issue
Block a user