]>
code.delx.au - offlineimap/blob - head/offlineimap/folder/Maildir.py
1 # Maildir folder support
2 # Copyright (C) 2002 John Goerzen
3 # <jgoerzen@complete.org>
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.
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.
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
19 from Base
import BaseFolder
20 from offlineimap
import imaputil
21 import os
.path
, os
, re
, time
, socket
, md5
27 thistime
= long(time
.time())
28 if thistime
== lasttime
:
32 lasttime
= long(time
.time())
36 class MaildirFolder(BaseFolder
):
37 def __init__(self
, root
, name
):
41 self
.uidfilename
= os
.path
.join(self
.getfullname(), "offlineimap.uidvalidity")
42 self
.messagelist
= None
44 def getfullname(self
):
45 return os
.path
.join(self
.getroot(), self
.getname())
47 def getuidvalidity(self
):
48 if not os
.path
.exists(self
.uidfilename
):
50 file = open(self
.uidfilename
, "rt")
51 retval
= long(file.readline().strip())
55 def saveuidvalidity(self
, newval
):
56 file = open(self
.uidfilename
, "wt")
57 file.write("%d\n" % newval
)
60 def isuidvalidityok(self
, remotefolder
):
61 myval
= self
.getuidvalidity()
63 return myval
== remotefolder
.getuidvalidity()
65 self
.saveuidvalidity(remotefolder
.getuidvalidity())
68 def cachemessagelist(self
):
69 """Cache the message list. Maildir flags are:
75 and must occur in ASCII order."""
78 nouidcounter
= -1 # Messages without UIDs get
79 # negative UID numbers.
80 for dirannex
in ['new', 'cur']:
81 fulldirname
= os
.path
.join(self
.getfullname(), dirannex
)
82 files
.extend([os
.path
.join(fulldirname
, filename
) for
83 filename
in os
.listdir(fulldirname
)])
85 messagename
= os
.path
.basename(file)
86 foldermatch
= re
.search(',FMD5=([0-9a-f]{32})', messagename
)
87 if (not foldermatch
) or \
88 md5
.new(self
.getvisiblename()).hexdigest() \
89 != foldermatch
.group(1):
90 # If there is no folder MD5 specified, or if it mismatches,
91 # assume it is a foreign (new) message and generate a
95 else: # It comes from our folder.
96 uidmatch
= re
.search(',U=(\d+)', messagename
)
102 uid
= long(uidmatch
.group(1))
103 flagmatch
= re
.search(':.*2,([A-Z]+)', messagename
)
106 flags
= [x
for x
in flagmatch
.group(1)]
108 self
.messagelist
[uid
] = {'uid': uid
,
112 def getmessagelist(self
):
113 return self
.messagelist
115 def getmessage(self
, uid
):
116 filename
= self
.getmessagelist()[uid
]['filename']
117 file = open(filename
, 'rt')
122 def savemessage(self
, uid
, content
, flags
):
124 # We cannot assign a new uid.
126 if uid
in self
.getmessagelist():
127 # We already have it.
128 self
.savemessageflags(uid
, flags
)
130 newdir
= os
.path
.join(self
.getfullname(), 'new')
131 tmpdir
= os
.path
.join(self
.getfullname(), 'tmp')
136 raise IOError, "Couldn't write to file %s" % messagename
137 messagename
= '%d_%d.%d.%s,U=%d,FMD5=%s' % \
141 socket
.gethostname(),
143 md5
.new(self
.getvisiblename()).hexdigest())
144 if os
.path
.exists(os
.path
.join(tmpdir
, messagename
)):
149 file = open(os
.path
.join(tmpdir
, messagename
), "wt")
152 os
.link(os
.path
.join(tmpdir
, messagename
),
153 os
.path
.join(newdir
, messagename
))
154 os
.unlink(os
.path
.join(tmpdir
, messagename
))
155 self
.messagelist
[uid
] = {'uid': uid
, 'flags': [],
156 'filename': os
.path
.join(newdir
, messagename
)}
157 self
.savemessageflags(uid
, flags
)
160 def getmessageflags(self
, uid
):
161 return self
.getmessagelist()[uid
]['flags']
163 def savemessageflags(self
, uid
, flags
):
164 oldfilename
= self
.getmessagelist()[uid
]['filename']
165 newpath
, newname
= os
.path
.split(oldfilename
)
167 infomatch
= re
.search('(:.*)$', newname
)
168 if infomatch
: # If the info string is present..
169 infostr
= infomatch
.group(1)
170 newname
= newname
.split(':')[0] # Strip off the info string.
171 infostr
= re
.sub('2,[A-Z]*', '', infostr
)
173 infostr
+= '2,' + ''.join(flags
)
176 newfilename
= os
.path
.join(newpath
, newname
)
177 if (newfilename
!= oldfilename
):
178 os
.rename(oldfilename
, newfilename
)
179 self
.getmessagelist()[uid
]['flags'] = flags
180 self
.getmessagelist()[uid
]['filename'] = newfilename
182 def getmessageflags(self
, uid
):
183 return self
.getmessagelist()[uid
]['flags']
185 def deletemessage(self
, uid
):
186 if not uid
in self
.messagelist
:
188 filename
= self
.getmessagelist()[uid
]['filename']
190 del(self
.messagelist
[uid
])