KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > mail > LocalMailbox


1 /*
2  * LocalMailbox.java
3  *
4  * Copyright (C) 2000-2003 Peter Graves
5  * $Id: LocalMailbox.java,v 1.4 2003/06/29 00:19:34 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j.mail;
23
24 import java.io.BufferedReader JavaDoc;
25 import java.io.BufferedWriter JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStreamReader JavaDoc;
28 import java.io.OutputStreamWriter JavaDoc;
29 import java.io.RandomAccessFile JavaDoc;
30 import java.io.UnsupportedEncodingException JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Vector JavaDoc;
34 import javax.swing.SwingUtilities JavaDoc;
35 import org.armedbear.j.Buffer;
36 import org.armedbear.j.BufferIterator;
37 import org.armedbear.j.Debug;
38 import org.armedbear.j.Directories;
39 import org.armedbear.j.Editor;
40 import org.armedbear.j.EditorIterator;
41 import org.armedbear.j.File;
42 import org.armedbear.j.FastStringBuffer;
43 import org.armedbear.j.Headers;
44 import org.armedbear.j.Line;
45 import org.armedbear.j.LocalFile;
46 import org.armedbear.j.Log;
47 import org.armedbear.j.ProgressNotifier;
48 import org.armedbear.j.Property;
49 import org.armedbear.j.Sidebar;
50 import org.armedbear.j.Utilities;
51 import org.armedbear.j.View;
52
53 public class LocalMailbox extends Mailbox
54 {
55     private File JavaDoc mailboxFile;
56
57     public LocalMailbox(MailboxURL url)
58     {
59         super(url);
60         if (url instanceof LocalMailboxURL)
61             mailboxFile = ((LocalMailboxURL)url).getFile();
62         init();
63     }
64
65     private void init()
66     {
67         supportsUndo = false;
68         type = TYPE_MAILBOX;
69         mode = MailboxMode.getMode();
70         formatter = mode.getFormatter(this);
71         readOnly = true;
72         if (mailboxFile != null)
73             title = mailboxFile.canonicalPath();
74         setInitialized(true);
75     }
76
77     public String JavaDoc getName()
78     {
79         Debug.assertTrue(mailboxFile != null);
80         return mailboxFile.canonicalPath();
81     }
82
83     public final File JavaDoc getMailboxFile()
84     {
85         return mailboxFile;
86     }
87
88     public final void setMailboxFile(File JavaDoc mailboxFile)
89     {
90         this.mailboxFile = mailboxFile;
91     }
92
93     public int getMessageCount()
94     {
95         if (entries == null)
96             return 0;
97         return entries.size();
98     }
99
100     public Message getMessage(MailboxEntry entry, ProgressNotifier progressNotifier)
101     {
102         try {
103             RandomAccessFile JavaDoc raf = mailboxFile.getRandomAccessFile("r");
104             String JavaDoc header = getMessageHeader(entry, raf);
105             Headers headers = Headers.parse(header);
106             String JavaDoc charset = null;
107             String JavaDoc contentType = headers.getValue(Headers.CONTENT_TYPE);
108             if (contentType != null)
109                 charset = Utilities.getCharsetFromContentType(contentType);
110             String JavaDoc body = getMessageBody(entry, raf, charset);
111             return new Message(header + "\r\n" + body, headers);
112         }
113         catch (IOException JavaDoc e) {
114             Log.error(e);
115             return null;
116         }
117     }
118
119     private String JavaDoc getMessageHeader(MailboxEntry entry, RandomAccessFile JavaDoc raf)
120     {
121         FastStringBuffer sb = new FastStringBuffer(8192);
122         try {
123             long offset = ((LocalMailboxEntry) entry).getMessageStart();
124             raf.seek(offset);
125             String JavaDoc text = raf.readLine();
126             if (!text.startsWith("From ")) {
127                 Log.debug("LocalMailbox.getMessage expected \"From \"");
128                 Log.debug("text = |" + text + "|");
129                 Log.debug("offset = " + offset);
130                 Debug.assertTrue(false);
131                 return ""; // BUG!
132
}
133             while (true) {
134                 text = raf.readLine();
135                 if (text == null)
136                     break; // End of file (shouldn't happen).
137
if (text.length() == 0)
138                     break; // Reached end of header.
139
sb.append(text);
140                 sb.append("\r\n");
141             }
142         }
143         catch (IOException JavaDoc e) {
144             Log.error(e);
145         }
146         return sb.toString();
147     }
148
149     // File pointer must be at the start of the message body.
150
private String JavaDoc getMessageBody(MailboxEntry entry, RandomAccessFile JavaDoc raf, String JavaDoc charset)
151     {
152         byte[] bytes = null;
153         try {
154             long bodyStart = raf.getFilePointer();
155             long size = ((LocalMailboxEntry) entry).getNextMessageStart() - bodyStart;
156             Debug.assertTrue(size >= 0);
157             Debug.assertTrue(size < Integer.MAX_VALUE);
158             bytes = new byte[(int) size];
159             raf.readFully(bytes);
160         }
161         catch (IOException JavaDoc e) {
162             Log.error(e);
163             return "";
164         }
165         try {
166             return new String JavaDoc(bytes, Utilities.getEncodingFromCharset(charset));
167         }
168         catch (UnsupportedEncodingException JavaDoc e) {
169             Log.error(e);
170         }
171         // Use platform's default character encoding.
172
return new String JavaDoc(bytes);
173     }
174
175     public void getNewMessages()
176     {
177         Log.error("LocalMailbox.getNewMessages is not implemented");
178     }
179
180     public void createFolder()
181     {
182         Log.error("LocalMailbox.createFolder is not implemented");
183     }
184
185     public void deleteFolder()
186     {
187         Log.error("LocalMailbox.deleteFolder is not implemented");
188     }
189
190     public void saveToFolder()
191     {
192         Log.error("LocalMailbox.saveToFolder is not implemented");
193     }
194
195     public void moveToFolder()
196     {
197         Log.error("LocalMailbox.moveToFolder is not implemented");
198     }
199
200     public void delete()
201     {
202         Editor editor = Editor.currentEditor();
203         if (lock()) {
204             try {
205                 editor.setWaitCursor();
206                 boolean advanceDot = false;
207                 List JavaDoc toBeDeleted = getTaggedEntries();
208                 if (toBeDeleted == null) {
209                     Line line = editor.getDotLine();
210                     if (!(line instanceof MailboxLine))
211                         return;
212                     toBeDeleted = new Vector JavaDoc();
213                     toBeDeleted.add(((MailboxLine) line).getMailboxEntry());
214                     advanceDot = true;
215                 }
216                 int size = toBeDeleted.size();
217                 for (int i = 0; i < size; i++) {
218                     MailboxEntry entry = (MailboxEntry) toBeDeleted.get(i);
219                     if (!entry.isDeleted()) {
220                         entry.setFlags(entry.getFlags() | MailboxEntry.DELETED);
221                         dirty = true;
222                         updateEntry(entry);
223                     }
224                 }
225                 countMessages();
226                 // Update message count in sidebar buffer list.
227
Sidebar.repaintBufferListInAllFrames();
228                 if (advanceDot)
229                     advanceDot(editor.getDotLine());
230             }
231             finally {
232                 unlock();
233             }
234         } else
235             editor.status("Mailbox is locked");
236     }
237
238     public void undelete()
239     {
240         Editor editor = Editor.currentEditor();
241         if (lock()) {
242             try {
243                 editor.setWaitCursor();
244                 boolean advanceDot = false;
245                 List JavaDoc toBeUndeleted = getTaggedEntries();
246                 if (toBeUndeleted == null) {
247                     Line line = editor.getDotLine();
248                     if (!(line instanceof MailboxLine))
249                         return;
250                     toBeUndeleted = new Vector JavaDoc();
251                     toBeUndeleted.add(((MailboxLine) line).getMailboxEntry());
252                     if (getBooleanProperty(Property.UNDELETE_ADVANCE_DOT))
253                         advanceDot = true;
254                 }
255                 int size = toBeUndeleted.size();
256                 for (int i = 0; i < size; i++) {
257                     MailboxEntry entry = (MailboxEntry) toBeUndeleted.get(i);
258                     if (entry.isDeleted()) {
259                         entry.setFlags(entry.getFlags() & ~MailboxEntry.DELETED);
260                         dirty = true;
261                         updateEntry(entry);
262                     }
263                 }
264                 countMessages();
265                 // Update message count in sidebar buffer list.
266
Sidebar.repaintBufferListInAllFrames();
267                 if (advanceDot)
268                     advanceDot(editor.getDotLine());
269             }
270             finally {
271                 unlock();
272             }
273         } else
274             editor.status("Mailbox is locked");
275     }
276
277     public void markRead()
278     {
279         Editor editor = Editor.currentEditor();
280         if (lock()) {
281             try {
282                 boolean advanceDot = false;
283                 List JavaDoc list = getTaggedEntries();
284                 if (list == null) {
285                     Line line = editor.getDotLine();
286                     if (!(line instanceof MailboxLine))
287                         return;
288                     list = new Vector JavaDoc();
289                     list.add(((MailboxLine) line).getMailboxEntry());
290                     advanceDot = true;
291                 }
292                 for (int i = 0; i < list.size(); i++) {
293                     MailboxEntry entry = (MailboxEntry) list.get(i);
294                     if ((entry.getFlags() & MailboxEntry.SEEN) == 0) {
295                         entry.setFlags(entry.getFlags() | MailboxEntry.SEEN);
296                         dirty = true;
297                         updateEntry(entry);
298                     }
299                 }
300                 countMessages();
301                 // Update message count in sidebar buffer list.
302
Sidebar.repaintBufferListInAllFrames();
303                 if (advanceDot)
304                     advanceDot(editor.getDotLine());
305             }
306             finally {
307                 unlock();
308             }
309         } else
310             editor.status("Mailbox is locked");
311     }
312
313     public void markUnread()
314     {
315         Editor editor = Editor.currentEditor();
316         if (lock()) {
317             try {
318                 boolean advanceDot = false;
319                 List JavaDoc list = getTaggedEntries();
320                 if (list == null) {
321                     Line line = editor.getDotLine();
322                     if (!(line instanceof MailboxLine))
323                         return;
324                     list = new Vector JavaDoc();
325                     list.add(((MailboxLine) line).getMailboxEntry());
326                     advanceDot = true;
327                 }
328                 for (int i = 0; i < list.size(); i++) {
329                     MailboxEntry entry = (MailboxEntry) list.get(i);
330                     if ((entry.getFlags() & MailboxEntry.SEEN) == MailboxEntry.SEEN) {
331                         entry.setFlags(entry.getFlags() & ~MailboxEntry.SEEN);
332                         dirty = true;
333                         updateEntry(entry);
334                     }
335                 }
336                 countMessages();
337                 // Update message count in sidebar buffer list.
338
Sidebar.repaintBufferListInAllFrames();
339                 if (advanceDot)
340                     advanceDot(editor.getDotLine());
341             }
342             finally {
343                 unlock();
344             }
345         } else
346             editor.status("Mailbox is locked");
347     }
348
349     public void flag()
350     {
351         final Editor editor = Editor.currentEditor();
352         if (lock()) {
353             try {
354                 boolean advanceDot = false;
355                 List JavaDoc list = getTaggedEntries();
356                 if (list == null) {
357                     Line line = editor.getDotLine();
358                     if (!(line instanceof MailboxLine))
359                         return;
360                     list = new ArrayList JavaDoc();
361                     list.add(((MailboxLine)line).getMailboxEntry());
362                     advanceDot = true;
363                 }
364                 for (int i = 0; i < list.size(); i++) {
365                     MailboxEntry entry = (MailboxEntry) list.get(i);
366                     entry.toggleFlag();
367                     updateEntry(entry);
368                     dirty = true;
369                 }
370                 if (advanceDot)
371                     advanceDot(editor.getDotLine());
372             }
373             finally {
374                 unlock();
375             }
376         } else
377             editor.status("Mailbox is locked");
378     }
379
380     public void setAnsweredFlag(MailboxEntry entry)
381     {
382         if ((entry.getFlags() & MailboxEntry.ANSWERED) == 0) {
383             entry.setFlags(entry.getFlags() | MailboxEntry.ANSWERED);
384             setDirty(true);
385             updateEntry(entry);
386         }
387     }
388
389     public void expunge()
390     {
391         Log.error("LocalMailbox.expunge is not implemented");
392     }
393
394     public int load()
395     {
396         if (lock()) {
397             setBusy(true);
398             new Thread JavaDoc(loadRunnable).start();
399             setLoaded(true);
400             return LOAD_PENDING;
401         } else
402             return LOAD_FAILED;
403     }
404
405     private Runnable JavaDoc loadRunnable = new Runnable JavaDoc() {
406         public void run()
407         {
408             try {
409                 readMailboxFile(null);
410                 refreshBuffer();
411             }
412             finally {
413                 unlock();
414                 setBusy(false);
415                 Runnable JavaDoc completionRunnable = new Runnable JavaDoc() {
416                     public void run()
417                     {
418                         for (EditorIterator it = new EditorIterator(); it.hasNext();) {
419                             Editor ed = it.nextEditor();
420                             View view = new View();
421                             view.setDotEntry(getInitialEntry());
422                             ed.setView(LocalMailbox.this, view);
423                             if (ed.getBuffer() == LocalMailbox.this) {
424                                 ed.bufferActivated(true);
425                                 ed.updateDisplay();
426                             }
427                         }
428                     }
429                 };
430                 SwingUtilities.invokeLater(completionRunnable);
431             }
432         }
433     };
434
435     protected void readMailboxFile(ProgressNotifier progressNotifier)
436     {
437         Log.debug("LocalMailbox.readMailboxFile");
438         long start = System.currentTimeMillis();
439         Mbox mbox = Mbox.getInstance(mailboxFile);
440         if (mbox == null)
441             return;
442         if (mbox.lock()) {
443             entries = mbox.getEntries(progressNotifier);
444             mbox.unlock();
445         }
446         Log.debug("readMailboxFile " + (System.currentTimeMillis() - start) + " ms");
447     }
448
449     public void readMessage(Line line)
450     {
451         readMessage(line, false);
452     }
453
454     public void readMessageOtherWindow(Line line)
455     {
456         readMessage(line, true);
457     }
458
459     private void readMessage(Line line, boolean useOtherWindow)
460     {
461         Editor editor = Editor.currentEditor();
462         MailboxEntry entry = ((MailboxLine)line).getMailboxEntry();
463         Buffer buf = null;
464         for (BufferIterator it = new BufferIterator(); it.hasNext();) {
465             Buffer b = it.nextBuffer();
466             if (b instanceof MessageBuffer) {
467                 if (((MessageBuffer)b).getMailboxEntry() == entry) {
468                     buf = b;
469                     break;
470                 }
471             }
472         }
473         if (buf == null)
474             buf = new LocalMessageBuffer(this, entry);
475         activateMessageBuffer(editor, (MessageBuffer)buf, useOtherWindow);
476     }
477
478     protected boolean rewriteMailbox(boolean purge)
479     {
480         Log.debug("rewriteMailbox");
481         long start = System.currentTimeMillis();
482         boolean succeeded = false;
483         try {
484             BufferedReader JavaDoc reader =
485                 new BufferedReader JavaDoc(new InputStreamReader JavaDoc(mailboxFile.getInputStream(),
486                     "ISO8859_1"));
487             File JavaDoc tempFile =
488                 Utilities.getTempFile(mailboxFile.getParentFile());
489             BufferedWriter JavaDoc writer =
490                 new BufferedWriter JavaDoc(new OutputStreamWriter JavaDoc(tempFile.getOutputStream(),
491                     "ISO8859_1"));
492             long newOffsets[] = new long[entries.size()];
493             long offset = 0;
494             boolean skip = false;
495             int i = 0;
496             while (true) {
497                 String JavaDoc text = reader.readLine();
498                 if (text == null)
499                     break;
500                 if (text.startsWith("From ")) {
501                     if (purge)
502                         skip = ((MailboxEntry) entries.get(i)).isDeleted();
503                     else
504                         skip = false;
505                     if (skip)
506                         newOffsets[i] = -1;
507                     else {
508                         newOffsets[i] = offset;
509                         writer.write(text);
510                         writer.write('\n');
511                         offset += text.length() + 1;
512                     }
513                     // Headers.
514
boolean seenXJStatus = false;
515                     while (true) {
516                         text = reader.readLine();
517                         if (text == null)
518                             break;
519                         if (text.length() == 0)
520                             break; // End of headers.
521
if (!skip) {
522                             if (text.startsWith("X-J-Status: ")) {
523                                 text = "X-J-Status: " + getMessageStatus(i);
524                                 seenXJStatus = true;
525                             }
526                             writer.write(text);
527                             writer.write('\n');
528                             offset += text.length() + 1;
529                         }
530                     }
531                     if (!skip) {
532                         if (!seenXJStatus) {
533                             writer.write("X-J-Status: " + getMessageStatus(i));
534                             writer.write('\n');
535                         }
536                         writer.write('\n');
537                         offset += 1;
538                     }
539                     ++i;
540                 } else {
541                     // Body of message.
542
if (!skip) {
543                         writer.write(text);
544                         writer.write('\n');
545                         offset += text.length() + 1;
546                     }
547                 }
548             }
549             writer.flush();
550             writer.close();
551             reader.close();
552             if (Utilities.deleteRename(tempFile, mailboxFile)) {
553                 // Update offsets.
554
for (i = 0; i < entries.size(); i++) {
555                     LocalMailboxEntry entry = (LocalMailboxEntry) entries.get(i);
556                     long newOffset = newOffsets[i];
557                     if (newOffset == -1) {
558                         if (!entry.isDeleted())
559                             Debug.bug("expected entry " + i + " to be deleted");
560                     }
561                     entry.setMessageStart(newOffset);
562                 }
563                 if (purge) {
564                     // Copy entries to new list, skipping deleted entries.
565
Vector JavaDoc v = new Vector JavaDoc(entries.size(), 10);
566                     for (i = 0; i < entries.size(); i++) {
567                         MailboxEntry entry = (MailboxEntry) entries.get(i);
568                         if (!entry.isDeleted())
569                             v.add(entry);
570                     }
571                     v.trimToSize();
572                     entries = v;
573                 }
574                 // Update nextMessageStart for every entry.
575
LocalMailboxEntry thisEntry = null;
576                 LocalMailboxEntry lastEntry = null;
577                 for (i = 0; i < entries.size(); i++) {
578                     thisEntry = (LocalMailboxEntry) entries.get(i);
579                     if (lastEntry != null)
580                         lastEntry.setNextMessageStart(thisEntry.getMessageStart());
581                     lastEntry = thisEntry;
582                 }
583                 if (thisEntry != null)
584                     thisEntry.setNextMessageStart(offset);
585                 // Success!
586
dirty = false;
587                 succeeded = true;
588             }
589         }
590         catch (IOException JavaDoc e) {
591             Log.error(e);
592         }
593         finally {
594             long elapsed = System.currentTimeMillis() - start;
595             Log.debug("rewriteMailbox " + elapsed + " ms");
596         }
597         return succeeded;
598     }
599
600     private final int getMessageStatus(int i)
601     {
602         return ((MailboxEntry) entries.get(i)).getFlags();
603     }
604
605     private static String JavaDoc localPrefix;
606
607     private boolean isOwned()
608     {
609         if (localPrefix == null)
610             localPrefix = Directories.getMailDirectory().canonicalPath().concat(LocalFile.getSeparator());
611         return mailboxFile.canonicalPath().startsWith(localPrefix);
612     }
613
614     public void dispose()
615     {
616         Log.debug("LocalMailbox.dispose");
617         Mbox.cleanup();
618         MailboxProperties.saveProperties(this);
619         if (isOwned()) {
620             Log.debug("mailbox is owned");
621         } else {
622             Log.debug("mailbox is foreign");
623             return;
624         }
625         Runnable JavaDoc disposeRunnable = new Runnable JavaDoc() {
626             public void run()
627             {
628                 try {
629                     Log.debug("disposeRunnable.run() calling acquire()...");
630                     acquire(); // Blocks, may throw InterruptedException.
631
Log.debug("disposeRunnable.run() back from acquire()");
632                     clearRecent();
633                     if (dirty) {
634                         final Object JavaDoc pending = new Object JavaDoc();
635                         Editor.getPendingOperations().add(pending);
636                         Log.debug("disposeRunnable.run() calling rewriteMailbox()...");
637                         rewriteMailbox(false);
638                         Log.debug("disposeRunnable.run() back from rewriteMailbox()");
639                         Editor.getPendingOperations().remove(pending);
640                     }
641                     release();
642                     Log.debug("disposeRunnable.run() back from release()");
643                 }
644                 catch (InterruptedException JavaDoc e) {
645                     Log.error(e);
646                 }
647             }
648         };
649         new Thread JavaDoc(disposeRunnable).start();
650     }
651
652     protected void finalize() throws Throwable JavaDoc
653     {
654         Log.debug("LocalMailbox.finalize");
655         super.finalize();
656     }
657
658     public String JavaDoc toString()
659     {
660         final String JavaDoc name;
661         if (isOwned())
662             name = mailboxFile.getParentFile().getName();
663         else if (mailboxFile.isLocal())
664             name = mailboxFile.canonicalPath();
665         else
666             name = mailboxFile.netPath();
667         final int newMessageCount = getNewMessageCount();
668         if (newMessageCount > 0) {
669             FastStringBuffer sb = new FastStringBuffer(name);
670             sb.append(" (");
671             sb.append(newMessageCount);
672             sb.append(" new)");
673             return sb.toString();
674         } else
675             return name;
676     }
677 }
678
Popular Tags