]> code.delx.au - offlineimap/blob - offlineimap/repository/Base.py
Initial stab at showing list of folders that aren't being synced
[offlineimap] / offlineimap / repository / Base.py
1 # Base repository support
2 # Copyright (C) 2002-2007 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; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 from offlineimap import CustomConfig
20 from offlineimap.ui import UIBase
21 import os.path
22
23 def LoadRepository(name, account, reqtype):
24 from offlineimap.repository.Gmail import GmailRepository
25 from offlineimap.repository.IMAP import IMAPRepository, MappedIMAPRepository
26 from offlineimap.repository.Maildir import MaildirRepository
27 if reqtype == 'remote':
28 # For now, we don't support Maildirs on the remote side.
29 typemap = {'IMAP': IMAPRepository,
30 'Gmail': GmailRepository}
31 elif reqtype == 'local':
32 typemap = {'IMAP': MappedIMAPRepository,
33 'Maildir': MaildirRepository}
34 else:
35 raise ValueError, "Request type %s not supported" % reqtype
36 config = account.getconfig()
37 repostype = config.get('Repository ' + name, 'type').strip()
38 return typemap[repostype](name, account)
39
40 class BaseRepository(CustomConfig.ConfigHelperMixin):
41 def __init__(self, reposname, account):
42 self.account = account
43 self.config = account.getconfig()
44 self.name = reposname
45 self.localeval = account.getlocaleval()
46 self.accountname = self.account.getname()
47 self.uiddir = os.path.join(self.config.getmetadatadir(), 'Repository-' + self.name)
48 if not os.path.exists(self.uiddir):
49 os.mkdir(self.uiddir, 0700)
50 self.mapdir = os.path.join(self.uiddir, 'UIDMapping')
51 if not os.path.exists(self.mapdir):
52 os.mkdir(self.mapdir, 0700)
53 self.uiddir = os.path.join(self.uiddir, 'FolderValidity')
54 if not os.path.exists(self.uiddir):
55 os.mkdir(self.uiddir, 0700)
56
57 # The 'restoreatime' config parameter only applies to local Maildir
58 # mailboxes.
59 def restore_atime(self):
60 if self.config.get('Repository ' + self.name, 'type').strip() != \
61 'Maildir':
62 return
63
64 if not self.config.has_option('Repository ' + self.name, 'restoreatime') or not self.config.getboolean('Repository ' + self.name, 'restoreatime'):
65 return
66
67 return self.restore_folder_atimes()
68
69 def connect(self):
70 """Establish a connection to the remote, if necessary. This exists
71 so that IMAP connections can all be established up front, gathering
72 passwords as needed. It was added in order to support the
73 error recovery -- we need to connect first outside of the error
74 trap in order to validate the password, and that's the point of
75 this function."""
76 pass
77
78 def holdordropconnections(self):
79 pass
80
81 def dropconnections(self):
82 pass
83
84 def getaccount(self):
85 return self.account
86
87 def getname(self):
88 return self.name
89
90 def getuiddir(self):
91 return self.uiddir
92
93 def getmapdir(self):
94 return self.mapdir
95
96 def getaccountname(self):
97 return self.accountname
98
99 def getsection(self):
100 return 'Repository ' + self.name
101
102 def getconfig(self):
103 return self.config
104
105 def getlocaleval(self):
106 return self.account.getlocaleval()
107
108 def getfolders(self):
109 """Returns a list of ALL folders on this server."""
110 return []
111
112 def forgetfolders(self):
113 """Forgets the cached list of folders, if any. Useful to run
114 after a sync run."""
115 pass
116
117 def getsep(self):
118 raise NotImplementedError
119
120 def makefolder(self, foldername):
121 raise NotImplementedError
122
123 def deletefolder(self, foldername):
124 raise NotImplementedError
125
126 def getfolder(self, foldername):
127 raise NotImplementedError
128
129 def syncfoldersto(self, dest, copyfolders):
130 """Syncs the folders in this repository to those in dest.
131 It does NOT sync the contents of those folders.
132
133 For every time dest.makefolder() is called, also call makefolder()
134 on each folder in copyfolders."""
135 src = self
136 srcfolders = src.getfolders()
137 destfolders = dest.getfolders()
138
139 # Create hashes with the names, but convert the source folders
140 # to the dest folder's sep.
141
142 srchash = {}
143 for folder in srcfolders:
144 srchash[folder.getvisiblename().replace(src.getsep(), dest.getsep())] = \
145 folder
146 desthash = {}
147 for folder in destfolders:
148 desthash[folder.getvisiblename()] = folder
149
150 #
151 # Find new folders.
152 #
153
154 for key in srchash.keys():
155 if not key in desthash:
156 dest.makefolder(key)
157 for copyfolder in copyfolders:
158 copyfolder.makefolder(key.replace(dest.getsep(), copyfolder.getsep()))
159
160 #
161 # Find deleted folders.
162 #
163 # We don't delete folders right now.
164
165 #for key in desthash.keys():
166 # if not key in srchash:
167 # dest.deletefolder(key)
168
169
170 ##### Find any folders that aren't being synced
171 ignoredfolders = []
172 for key in desthash.keys():
173 if not key in srchash:
174 ignoredfolders.append(key)
175
176 ignoredfolders.sort()
177 if ignoredfolders != []:
178 UIBase.getglobalui().warn("Found local folders that are not being synced: %s" % (", ".join(ignoredfolders)))
179
180 ##### Keepalive
181
182 def startkeepalive(self):
183 """The default implementation will do nothing."""
184 pass
185
186 def stopkeepalive(self, abrupt = 0):
187 """Stop keep alive. If abrupt is 1, stop it but don't bother waiting
188 for the threads to terminate."""
189 pass
190