]> code.delx.au - offlineimap/blob - offlineimap/folder/LocalStatus.py
Commit ever 100 updateflags requests
[offlineimap] / offlineimap / folder / LocalStatus.py
1 # Local status cache virtual folder
2 # Copyright (C) 2002 - 2008 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 Base import BaseFolder
20 import os, threading
21
22 from pysqlite2 import dbapi2 as sqlite
23
24 magicline = "OFFLINEIMAP LocalStatus CACHE DATA - DO NOT MODIFY - FORMAT 1"
25 newmagicline = "OFFLINEIMAP LocalStatus NOW IN SQLITE, DO NOT MODIFY"
26
27 class LocalStatusFolder(BaseFolder):
28 def __deinit__(self):
29 self.save()
30 self.cursor.close()
31 self.connection.close()
32
33 def __init__(self, root, name, repository, accountname):
34 self.name = name
35 self.root = root
36 self.sep = '.'
37 self.filename = os.path.join(root, name)
38 self.filename = repository.getfolderfilename(name)
39 self.messagelist = {}
40 self.repository = repository
41 self.savelock = threading.Lock()
42 self.doautosave = 1
43 self.accountname = accountname
44 self.count = 0 # used for batching operations before commit
45 BaseFolder.__init__(self)
46 self.dbfilename = self.filename + '.sqlite'
47
48 # MIGRATE
49 if os.path.exists(self.filename):
50 self.connection = sqlite.connect(self.dbfilename)
51 self.cursor = self.connection.cursor()
52 self.cursor.execute('CREATE TABLE status (id INTEGER PRIMARY KEY, flags VARCHAR(50))')
53 if self.isnewfolder():
54 self.messagelist = {}
55 return
56 file = open(self.filename, "rt")
57 self.messagelist = {}
58 line = file.readline().strip()
59 assert(line == magicline)
60 for line in file.xreadlines():
61 line = line.strip()
62 uid, flags = line.split(':')
63 uid = long(uid)
64 flags = [x for x in flags]
65 flags.sort()
66 flags = ''.join(flags)
67 self.cursor.execute('INSERT INTO status (id,flags) VALUES (?,?)',
68 (uid,flags))
69 file.close()
70 self.connection.commit()
71 os.rename(self.filename, self.filename + ".old")
72 self.cursor.close()
73 self.connection.close()
74
75 # create new
76 if not os.path.exists(self.dbfilename):
77 self.connection = sqlite.connect(self.dbfilename)
78 self.cursor = self.connection.cursor()
79 self.cursor.execute('CREATE TABLE status (id INTEGER PRIMARY KEY, flags VARCHAR(50))')
80 else:
81 self.connection = sqlite.connect(self.dbfilename)
82 self.cursor = self.connection.cursor()
83
84
85
86 def getaccountname(self):
87 return self.accountname
88
89 def storesmessages(self):
90 return 0
91
92 def isnewfolder(self):
93 return not os.path.exists(self.dbfilename)
94
95 def getname(self):
96 return self.name
97
98 def getroot(self):
99 return self.root
100
101 def getsep(self):
102 return self.sep
103
104 def getfullname(self):
105 return self.filename
106
107 def deletemessagelist(self):
108 if not self.isnewfolder():
109 self.cursor.close()
110 self.connection.close()
111 os.unlink(self.dbfilename)
112
113 def cachemessagelist(self):
114 return
115
116 def autosave(self):
117 if self.doautosave:
118 self.save()
119
120 def save(self):
121 self.connection.commit()
122
123 def getmessagelist(self):
124 if self.isnewfolder():
125 self.messagelist = {}
126 return
127
128 self.messagelist = {}
129 self.cursor.execute('SELECT id,flags from status')
130 for row in self.cursor:
131 flags = [x for x in row[1]]
132 self.messagelist[row[0]] = {'uid': row[0], 'flags': flags}
133
134 return self.messagelist
135
136 def uidexists(self,uid):
137 self.cursor.execute('SELECT id FROM status WHERE id=:id',{'id': uid})
138 for row in self.cursor:
139 if(row[0]==uid):
140 return 1
141 return 0
142
143 def getmessageuidlist(self):
144 self.cursor.execute('SELECT id from status')
145 r = []
146 for row in self.cursor:
147 r.append(row[0])
148 return r
149
150 def getmessagecount(self):
151 self.cursor.execute('SELECT count(id) from status');
152 row = self.cursor.fetchone()
153 return row[0]
154
155 def savemessage(self, uid, content, flags, rtime):
156 if uid < 0:
157 # We cannot assign a uid.
158 return uid
159
160 if self.uidexists(uid): # already have it
161 self.savemessageflags(uid, flags)
162 return uid
163
164 self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime}
165 flags.sort()
166 flags = ''.join(flags)
167 self.cursor.execute('INSERT INTO status (id,flags) VALUES (?,?)',
168 (uid,flags))
169 self.autosave()
170 return uid
171
172 def getmessageflags(self, uid):
173 self.cursor.execute('SELECT flags FROM status WHERE id=:id',
174 {'id': uid})
175 for row in self.cursor:
176 flags = [x for x in row[0]]
177 return flags
178 return flags
179
180 def getmessagetime(self, uid):
181 return self.messagelist[uid]['time']
182
183 def savemessageflags(self, uid, flags):
184 self.messagelist[uid] = {'uid': uid, 'flags': flags}
185 flags.sort()
186 flags = ''.join(flags)
187 self.cursor.execute('UPDATE status SET flags=? WHERE id=?',(flags,uid))
188 self.count = self.count + 1
189 if self.count == 100:
190 self.autosave()
191 self.count = 0
192
193 def deletemessage(self, uid):
194 if not self.uidexists(uid):
195 return
196
197 if uid in self.messagelist:
198 del(self.messagelist[uid])
199
200 self.cursor.execute('DELETE FROM status WHERE id=:id', {'id': uid})
201 return