1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.mercury.maildir;
14
15 import java.io.File;
16 import java.io.FileOutputStream;
17 import java.io.FilenameFilter;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.net.InetAddress;
21 import java.net.UnknownHostException;
22 import java.util.Date;
23 import java.util.Random;
24
25 import javax.mail.Flags;
26 import javax.mail.MessagingException;
27 import javax.mail.internet.MimeMessage;
28
29 import org.abstracthorizon.mercury.maildir.file.FileProvider;
30 import org.abstracthorizon.mercury.maildir.file.SharedInputStreamPool;
31 import org.abstracthorizon.mercury.maildir.util.LazyParsingMessage;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public class MaildirMessage extends LazyParsingMessage implements FilenameFilter, FileProvider, Comparable<MaildirMessage> {
54
55
56 public static final int CREATE_FILE_RETRIES = 6;
57
58
59 protected MaildirFolderData maildirFolder;
60
61
62 protected File file;
63
64
65 protected String baseName;
66
67
68 protected char infoSeparator;
69
70
71 protected long fileSize = -1;
72
73
74 protected boolean isNew;
75
76
77 public static final String FLAGS_SEPERATOR = "2,";
78
79
80 protected static final Random randomGenerator = new Random();
81
82
83 protected static String host;
84
85 static {
86 try {
87 host = InetAddress.getLocalHost().getHostName();
88 } catch (UnknownHostException e) {
89 host = "localhost";
90 }
91 }
92
93
94
95
96
97
98
99
100
101 protected MaildirMessage(MaildirFolderData folder, MimeMessage message, int msgnum) throws MessagingException, IOException {
102 super(message);
103 this.maildirFolder = folder;
104 this.folder = folder;
105 this.msgnum = msgnum;
106 infoSeparator = folder.getMaildirStore().getInfoSeparator();
107 isNew = true;
108
109 createFile(null);
110 storeMessage(message);
111 setFlags(message.getFlags(), true);
112 initialise();
113 }
114
115
116
117
118
119
120
121
122
123 public MaildirMessage(MaildirFolderData folder, File file, int msgnum, boolean initialise) throws MessagingException {
124 super(folder, msgnum);
125 this.maildirFolder = folder;
126 infoSeparator = maildirFolder.getMaildirStore().getInfoSeparator();
127
128 if (!file.exists()) {
129 throw new MessagingException("File not found; "+file.getAbsolutePath());
130 }
131
132 setFile(file);
133
134 if (initialise) {
135 initialise();
136 }
137 }
138
139
140
141
142
143
144
145
146 public MaildirMessage(MaildirFolderData folder, File file, int msgnum) throws MessagingException {
147 this(folder, file, msgnum, true);
148 }
149
150
151
152
153
154
155
156 protected void initialise() throws MessagingException {
157 parse(SharedInputStreamPool.getDefaultInstance().newStream(this, 0, fileSize));
158 }
159
160
161
162
163
164
165 public static String baseNameFromFile(File file) {
166 String name = file.getName();
167 int i = name.lastIndexOf(MaildirMessage.FLAGS_SEPERATOR);
168 if (i > 0) {
169 name = name.substring(0, i-1);
170 }
171 return name;
172 }
173
174
175
176
177
178
179
180 public File getFile() throws IOException {
181 if (!file.exists()) {
182 try {
183 synchronise();
184 } catch (MessagingException e) {
185 throw new IOException(e.getMessage());
186 }
187 }
188 return file;
189 }
190
191
192
193
194
195
196 public void setFile(File file) throws MessagingException {
197 this.file = file;
198
199 File parentFile = file.getParentFile();
200 isNew = parentFile.getAbsolutePath().endsWith(File.pathSeparator + "new");
201
202 baseName = file.getName();
203 int i = baseName.lastIndexOf(FLAGS_SEPERATOR);
204
205 Flags flags = null;
206 if (i > 0) {
207 String flagsStr = baseName.substring(i+2);
208 baseName = baseName.substring(0, i-1);
209 flags = FlagUtilities.fromMaildirString(flagsStr);
210 } else {
211 flags = new Flags();
212 }
213 if (isNew) {
214 flags.add(Flags.Flag.RECENT);
215 }
216
217 super.setFlags(flags, true);
218
219 fileSize = file.length();
220 }
221
222
223
224
225
226 public String getBaseName() {
227 return baseName;
228 }
229
230
231
232
233 protected void closeFile() {
234 SharedInputStreamPool.getDefaultInstance().closeWithProvider(this);
235 }
236
237
238
239
240
241
242
243
244 protected String createFileName(String flags) throws MessagingException {
245 String time = Long.toString(System.currentTimeMillis());
246 String milis = time.substring(time.length()-3);
247 time = time.substring(0, time.length()-3);
248 String pid = Integer.toString(Thread.currentThread().hashCode());
249 String random = Integer.toString(randomGenerator.nextInt(131072));
250 if ((flags == null) || (flags.length() == 0)) {
251 return time+".M" + milis + "P" + pid + "R" + random + "." + host;
252 } else {
253 return time+".M" + milis + "P" + pid + "R" + random + "." + host + infoSeparator + FLAGS_SEPERATOR + flags;
254 }
255 }
256
257
258
259
260
261
262 protected void createFile(String flags) throws MessagingException {
263 for (int i = 0; i < CREATE_FILE_RETRIES; i++) {
264 String fileName = createFileName(flags);
265 file = new File(maildirFolder.getTmpDir(), fileName);
266 try {
267 if (file.createNewFile()) {
268 baseName = file.getName();
269 return;
270 }
271 } catch (IOException e) {
272 throw new MessagingException("Cannot create new file " + file.getAbsolutePath(), e);
273 }
274 }
275 throw new MessagingException("Cannot create new file after " + CREATE_FILE_RETRIES + " retries.");
276 }
277
278
279
280
281
282
283 protected void storeMessage(MimeMessage message) throws MessagingException {
284 try {
285 FileOutputStream fos = new FileOutputStream(file);
286 try {
287 message.writeTo(fos);
288 } finally {
289 fos.close();
290 }
291 File newDir = maildirFolder.getNewDir();
292 String fn = file.getName();
293 File newFile = new File(newDir, fn);
294
295 if (!file.renameTo(newFile)) {
296 throw new MessagingException("Cannot move file "+file.getAbsolutePath()+" to "+newFile.getAbsolutePath());
297 }
298 file = newFile;
299
300
301 } catch (IOException e) {
302 throw new MessagingException("Cannot write file", e);
303 }
304 }
305
306
307
308
309
310 public long getFileSize() {
311 return fileSize;
312 }
313
314
315
316
317
318
319 public boolean synchronise() throws MessagingException {
320
321 File newFile = new File(maildirFolder.getNewDir(), baseName);
322 if (newFile.exists()) {
323 file = newFile;
324 return true;
325 }
326 File curDir = maildirFolder.getCurDir();
327 File[] files = curDir.listFiles(this);
328 if ((files != null) && (files.length == 1)) {
329 setFile(files[0]);
330 } else {
331 expunged = true;
332 }
333 return false;
334 }
335
336
337
338
339
340
341
342 public boolean accept(File file, String name) {
343 return name.startsWith(baseName);
344 }
345
346
347
348
349
350
351 protected void parse(InputStream is) throws MessagingException {
352 super.parse(is);
353 }
354
355
356
357
358
359
360 protected synchronized void parseImpl() throws MessagingException {
361 if (!parsed) {
362 super.parseImpl();
363 closeFile();
364 }
365 }
366
367
368
369
370
371 protected boolean expunge() {
372 synchronized (this) {
373 if (file.exists()) {
374 closeFile();
375 boolean res = file.delete();
376 setExpunged(res);
377 return res;
378 } else {
379 setExpunged(true);
380 return false;
381 }
382 }
383 }
384
385
386
387
388
389
390 public Date getReceivedDate() throws MessagingException {
391 if (!file.exists()) {
392 synchronise();
393 }
394 if (file.exists()) {
395 long l = file.lastModified();
396 if (l != -1L) {
397 return new Date(l);
398 }
399 }
400 return null;
401 }
402
403
404
405
406
407
408
409 public void setFlags(Flags flags, boolean set) throws MessagingException {
410 super.setFlags(flags, set);
411 if (!file.exists()) {
412 synchronise();
413 }
414 if (file.exists()) {
415 synchronized (this) {
416 closeFile();
417 File newFile = null;
418 String oldFlgs = file.getName();
419 int i = oldFlgs.lastIndexOf(FLAGS_SEPERATOR);
420 if (i > 0) {
421 oldFlgs = oldFlgs.substring(i+2);
422 } else {
423 oldFlgs = "";
424 }
425
426 Flags currentFlags = getFlags();
427
428 String flgs = FlagUtilities.toMaildirString(currentFlags);
429 if ((!flgs.equals(oldFlgs) || (isNew != currentFlags.contains(Flags.Flag.RECENT)))) {
430 if (flags.contains(Flags.Flag.RECENT) ) {
431 super.setFlags(getFlags(), false);
432 super.setFlags(new Flags(Flags.Flag.RECENT), true);
433 newFile = new File(maildirFolder.getNewDir(), baseName);
434 isNew = true;
435 } else if (flgs.length() > 0) {
436 newFile = new File(maildirFolder.getCurDir(), baseName+infoSeparator+FLAGS_SEPERATOR+flgs);
437 isNew = false;
438 } else {
439 newFile = new File(maildirFolder.getCurDir(), baseName);
440 isNew = false;
441 }
442 }
443
444 if (newFile != null) {
445 if (!file.renameTo(newFile)) {
446 throw new MessagingException("Cannot set flags; oldFile="+file.getAbsolutePath()+", newFile="+newFile);
447 }
448 file = newFile;
449 }
450 }
451 }
452 }
453
454
455
456
457
458
459
460
461 public int compareTo(MaildirMessage o) {
462 String s1 = baseName;
463 String s2 = ((MaildirMessage)o).getBaseName();
464 return s1.compareTo(s2);
465 }
466 }