]> code.delx.au - offlineimap/blob - offlineimap/head/offlineimap/repository/Maildir.py
/offlineimap/head: changeset 360
[offlineimap] / offlineimap / head / offlineimap / repository / Maildir.py
1 # Maildir repository support
2 # Copyright (C) 2002 John Goerzen
3 # <jgoerzen@complete.org>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18 from Base import BaseRepository
19 from offlineimap import folder, imaputil
20 from offlineimap.ui import UIBase
21 from mailbox import Maildir
22 import os
23
24 class MaildirRepository(BaseRepository):
25 def __init__(self, root, accountname, config):
26 """Initialize a MaildirRepository object. Takes a path name
27 to the directory holding all the Maildir directories."""
28
29 self.root = root
30 self.folders = None
31 self.accountname = accountname
32 self.config = config
33 self.ui = UIBase.getglobalui()
34 self.debug("MaildirRepository initialized, sep is " + repr(self.getsep()))
35
36 def debug(self, msg):
37 self.ui.debug('maildir', msg)
38
39 def getsep(self):
40 if self.config.has_option(self.accountname, 'sep'):
41 return self.config.get(self.accountname, 'sep').strip()
42 else:
43 return '.'
44
45 def makefolder(self, foldername):
46 self.debug("makefolder called with arg " + repr(foldername))
47 # Do the chdir thing so the call to makedirs does not make the
48 # self.root directory (we'd prefer to raise an error in that case),
49 # but will make the (relative) paths underneath it. Need to use
50 # makedirs to support a / separator.
51 if self.getsep() == '/':
52 for invalid in ['new', 'cur', 'tmp', 'offlineimap.uidvalidity']:
53 for component in foldername.split('/'):
54 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'."
55
56 assert foldername.find('./') == -1, "Folder names may not contain ../"
57 assert not foldername.startswith('/'), "Folder names may not begin with /"
58
59 oldcwd = os.getcwd()
60 os.chdir(self.root)
61
62 # If we're using hierarchical folders, it's possible that sub-folders
63 # may be created before higher-up ones. If this is the case,
64 # makedirs will fail because the higher-up dir already exists.
65 # So, check to see if this is indeed the case.
66
67 if self.getsep() == '/' and os.path.isdir(foldername):
68 self.debug("makefolder: %s already is a directory" % foldername)
69 # Already exists. Sanity-check that it's not a Maildir.
70 for subdir in ['cur', 'new', 'tmp']:
71 assert not os.path.isdir(os.path.join(foldername, subdir)), \
72 "Tried to create folder %s but it already had dir %s" %\
73 (foldername, subdir)
74 else:
75 self.debug("makefolder: calling makedirs %s" % foldername)
76 os.makedirs(foldername, 0700)
77 self.debug("makefolder: creating cur, new, tmp")
78 for subdir in ['cur', 'new', 'tmp']:
79 os.mkdir(os.path.join(foldername, subdir), 0700)
80 # Invalidate the cache
81 self.folders = None
82 os.chdir(oldcwd)
83
84 def deletefolder(self, foldername):
85 self.ui.warn("NOT YET IMPLEMENTED: DELETE FOLDER %s" % foldername)
86
87 def getfolder(self, foldername):
88 return folder.Maildir.MaildirFolder(self.root, foldername,
89 self.getsep(), self, self.accountname)
90
91 def _getfolders_scandir(self, root, extension = None):
92 self.debug("_GETFOLDERS_SCANDIR STARTING. root = %s, extension = %s" \
93 % (root, extension))
94 # extension willl only be non-None when called recursively when
95 # getsep() returns '/'.
96 retval = []
97
98 # Configure the full path to this repository -- "toppath"
99
100 if extension == None:
101 toppath = root
102 else:
103 toppath = os.path.join(root, extension)
104
105 self.debug(" toppath = %s" % toppath)
106
107 # Iterate over directories in top.
108 for dirname in os.listdir(toppath):
109 self.debug(" *** top of loop")
110 self.debug(" dirname = %s" % dirname)
111 if dirname in ['cur', 'new', 'tmp', 'offlineimap.uidvalidity']:
112 self.debug(" skipping this dir (Maildir special)")
113 # Bypass special files.
114 continue
115 fullname = os.path.join(toppath, dirname)
116 self.debug(" fullname = %s" % fullname)
117 if not os.path.isdir(fullname):
118 self.debug(" skipping this entry (not a directory)")
119 # Not a directory -- not a folder.
120 continue
121 foldername = dirname
122 if extension != None:
123 foldername = os.path.join(extension, dirname)
124 if (os.path.isdir(os.path.join(fullname, 'cur')) and
125 os.path.isdir(os.path.join(fullname, 'new')) and
126 os.path.isdir(os.path.join(fullname, 'tmp'))):
127 # This directory has maildir stuff -- process
128 self.debug(" This is a maildir folder.")
129
130 self.debug(" foldername = %s" % foldername)
131
132 retval.append(folder.Maildir.MaildirFolder(self.root, foldername,
133 self.getsep(), self, self.accountname))
134 if self.getsep() == '/':
135 # Check sub-directories for folders.
136 retval.extend(self._getfolders_scandir(root, foldername))
137 self.debug("_GETFOLDERS_SCANDIR RETURNING %s" % \
138 repr([x.getname() for x in retval]))
139 return retval
140
141 def getfolders(self):
142 if self.folders == None:
143 self.folders = self._getfolders_scandir(self.root)
144 return self.folders
145