]> code.delx.au - offlineimap/blob - head/offlineimap/folder/Maildir.py
/head: changeset 39
[offlineimap] / head / offlineimap / folder / Maildir.py
1 # Maildir folder 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; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 from Base import BaseFolder
20 from offlineimap import imaputil
21 import os.path, os, re, time, socket
22
23 class MaildirFolder(BaseFolder):
24 def __init__(self, root, name):
25 self.name = name
26 self.root = root
27 self.sep = '.'
28 self.uidfilename = os.path.join(self.getfullname(), "offlineimap.uidvalidity")
29 self.messagelist = None
30
31 def getfullname(self):
32 return os.path.join(self.getroot(), self.getname())
33
34 def getuidvalidity(self):
35 if not os.path.exists(self.uidfilename):
36 return None
37 file = open(self.uidfilename, "rt")
38 retval = long(file.readline().strip())
39 file.close()
40 return retval
41
42 def saveuidvalidity(self, newval):
43 file = open(self.uidfilename, "wt")
44 file.write("%d\n" % newval)
45 file.close()
46
47 def isuidvalidityok(self, remotefolder):
48 myval = self.getuidvalidity()
49 if myval != None:
50 return myval == remotefolder.getuidvalidity()
51 else:
52 self.saveuidvalidity(remotefolder.getuidvalidity())
53 return 1
54
55 def cachemessagelist(self):
56 """Cache the message list. Maildir flags are:
57 R (replied)
58 S (seen)
59 T (trashed)
60 D (draft)
61 F (flagged)
62 and must occur in ASCII order."""
63 self.messagelist = {}
64 files = []
65 nouidcounter = -1 # Messages without UIDs get
66 # negative UID numbers.
67 for dirannex in ['new', 'cur']:
68 fulldirname = os.path.join(self.getfullname(), dirannex)
69 files.extend([os.path.join(fulldirname, filename) for
70 filename in os.listdir(fulldirname)])
71 for file in files:
72 messagename = os.path.basename(file)
73 uidmatch = re.search(',U=(\d+)', messagename)
74 uid = None
75 if not uidmatch:
76 uid = nouidcounter
77 nouidcounter -= 1
78 else:
79 uid = long(uidmatch.group(1))
80 flagmatch = re.search(':.*2,([A-Z]+)', messagename)
81 flags = []
82 if flagmatch:
83 flags = [x for x in flagmatch.group(1)]
84 flags.sort()
85 self.messagelist[uid] = {'uid': uid,
86 'flags': flags,
87 'filename': file}
88
89 def getmessagelist(self):
90 return self.messagelist
91
92 def getmessage(self, uid):
93 filename = self.getmessagelist()[uid]['filename']
94 file = open(filename, 'rt')
95 retval = file.read()
96 file.close()
97 return retval
98
99 def savemessage(self, uid, content, flags):
100 if uid < 0:
101 # We cannot assign a new uid.
102 return uid
103 if uid in self.getmessagelist():
104 # We already have it.
105 self.savemessageflags(uid, flags)
106 return uid
107 newdir = os.path.join(self.getfullname(), 'new')
108 tmpdir = os.path.join(self.getfullname(), 'tmp')
109 messagename = None
110 attempts = 0
111 while 1:
112 if attempts > 15:
113 raise IOError, "Couldn't write to file %s" % messagename
114 messagename = '%d.%d.%s,U=%d' % \
115 (long(time.time()),
116 os.getpid(),
117 socket.gethostname(),
118 uid)
119 if os.path.exists(os.path.join(tmpdir, messagename)):
120 time.sleep(2)
121 attempts += 1
122 else:
123 break
124 file = open(os.path.join(tmpdir, messagename), "wt")
125 file.write(content)
126 file.close()
127 os.link(os.path.join(tmpdir, messagename),
128 os.path.join(newdir, messagename))
129 os.unlink(os.path.join(tmpdir, messagename))
130 self.messagelist[uid] = {'uid': uid, 'flags': [],
131 'filename': os.path.join(newdir, messagename)}
132 self.savemessageflags(uid, flags)
133 return uid
134
135 def getmessageflags(self, uid):
136 return self.getmessagelist()[uid]['flags']
137
138 def savemessageflags(self, uid, flags):
139 oldfilename = self.getmessagelist()[uid]['filename']
140 newpath, newname = os.path.split(oldfilename)
141 infostr = ':'
142 infomatch = re.search('(:.*)$', newname)
143 if infomatch: # If the info string is present..
144 infostr = infomatch.group(1)
145 newname = newname.split(':')[0] # Strip off the info string.
146 infostr = re.sub('2,[A-Z]*', '', infostr)
147 flags.sort()
148 infostr += '2,' + ''.join(flags)
149 newname += infostr
150
151 newfilename = os.path.join(newpath, newname)
152 if (newfilename != oldfilename):
153 os.rename(oldfilename, newfilename)
154 self.getmessagelist()[uid]['flags'] = flags
155 self.getmessagelist()[uid]['filename'] = newfilename
156
157 def getmessageflags(self, uid):
158 return self.getmessagelist()[uid]['flags']
159
160 def deletemessage(self, uid):
161 if not uid in self.messagelist:
162 return
163 filename = self.getmessagelist()[uid]['filename']
164 os.unlink(filename)
165 del(self.messagelist[uid])
166