KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * PopMailbox.java
3  *
4  * Copyright (C) 2000-2003 Peter Graves
5  * $Id: PopMailbox.java,v 1.3 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.FileNotFoundException JavaDoc;
27 import java.io.FileReader JavaDoc;
28 import java.io.FileWriter JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.InputStream JavaDoc;
31 import java.io.OutputStream JavaDoc;
32 import java.io.RandomAccessFile JavaDoc;
33 import java.text.SimpleDateFormat JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Calendar JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.List JavaDoc;
39 import java.util.Locale JavaDoc;
40 import java.util.Properties JavaDoc;
41 import javax.swing.SwingUtilities JavaDoc;
42 import org.armedbear.j.BackgroundProcess;
43 import org.armedbear.j.Debug;
44 import org.armedbear.j.Editor;
45 import org.armedbear.j.Directories;
46 import org.armedbear.j.EditorIterator;
47 import org.armedbear.j.File;
48 import org.armedbear.j.FastStringBuffer;
49 import org.armedbear.j.Log;
50 import org.armedbear.j.PasswordDialog;
51 import org.armedbear.j.Property;
52 import org.armedbear.j.StatusBarProgressNotifier;
53 import org.armedbear.j.Utilities;
54 import org.armedbear.j.View;
55
56 public final class PopMailbox extends LocalMailbox
57 {
58     private final PopSession session;
59
60     private File JavaDoc localStore;
61     private StatusBarProgressNotifier progressNotifier;
62     private boolean cancelled;
63     private Thread JavaDoc backgroundThread;
64
65     public PopMailbox(PopURL url, PopSession session)
66     {
67         super(url);
68         this.session = session;
69         if (url.getUser() == null)
70             url.setUser(session.getUser());
71         setMailboxFile(getLocalStore());
72         setInitialized(true);
73     }
74
75     public String JavaDoc getFileNameForDisplay()
76     {
77         FastStringBuffer sb = new FastStringBuffer(64);
78         sb.append(url.toString());
79         String JavaDoc limitPattern = getLimitPattern();
80         if (limitPattern != null) {
81             sb.append(' ');
82             sb.append(limitPattern);
83         }
84         return sb.toString();
85     }
86
87     public final String JavaDoc getName()
88     {
89         return url.toString();
90     }
91
92     public synchronized int load()
93     {
94         if (isLoaded())
95             return LOAD_COMPLETED;
96         if (lock()) {
97             Debug.assertTrue(backgroundThread == null);
98             backgroundThread = new Thread JavaDoc(loadProcess);
99             backgroundThread.start();
100             setLoaded(true);
101             return LOAD_PENDING;
102         }
103         // Not loaded, lock() failed. Shouldn't happen.
104
Debug.bug("PopMailbox.load can't lock mailbox");
105         return LOAD_FAILED;
106     }
107
108     private BackgroundProcess loadProcess = new BackgroundProcess() {
109         public void run()
110         {
111             // Mailbox is already locked at this point.
112
boolean abort = false;
113             try {
114                 setBusy(true);
115                 cancelled = false;
116                 progressNotifier = new StatusBarProgressNotifier(PopMailbox.this);
117                 progressNotifier.progressStart();
118                 setBackgroundProcess(this);
119                 readMailboxFile(progressNotifier); // Error handling?
120
if (cancelled) {
121                     abort = true;
122                     return;
123                 }
124                 readExpungedUidlsList();
125                 clearRecent();
126                 if (cancelled || getBooleanProperty(Property.OFFLINE))
127                     return;
128                 if (retrieveNewMessages()) {
129                     addEntriesToAddressBook(entries);
130                     setLastCheckMillis(System.currentTimeMillis());
131                 } else if (!cancelled) {
132                     // Error!
133
error(session.getErrorText(), url.toString());
134                 }
135             }
136             finally {
137                 if (abort) {
138                     Runnable JavaDoc r = new Runnable JavaDoc() {
139                         public void run()
140                         {
141                             kill();
142                             for (EditorIterator it = new EditorIterator(); it.hasNext();)
143                                 it.nextEditor().updateDisplay();
144                         }
145                     };
146                     SwingUtilities.invokeLater(r);
147                 } else {
148                     refreshBuffer();
149                     Runnable JavaDoc r = new Runnable JavaDoc() {
150                         public void run()
151                         {
152                             setBusy(false);
153                             for (EditorIterator it = new EditorIterator(); it.hasNext();) {
154                                 Editor ed = it.nextEditor();
155                                 View view = new View();
156                                 view.setDotEntry(getInitialEntry());
157                                 ed.setView(PopMailbox.this, view);
158                                 if (ed.getBuffer() == PopMailbox.this) {
159                                     ed.bufferActivated(true);
160                                     ed.updateDisplay();
161                                 }
162                             }
163                         }
164                     };
165                     SwingUtilities.invokeLater(r);
166                 }
167                 setBackgroundProcess(null);
168                 backgroundThread = null;
169                 if (progressNotifier != null) {
170                     progressNotifier.setText(cancelled ? "Cancelled" : "");
171                     progressNotifier.progressStop();
172                     progressNotifier = null;
173                 }
174                 unlock();
175             }
176         }
177
178         public void cancel()
179         {
180             Log.debug("loadProcess.cancel");
181             cancelled = true;
182             progressNotifier.cancel();
183             progressNotifier.setText("Cancelled, cleaning up...");
184             if (backgroundThread != null && backgroundThread.isAlive())
185                 backgroundThread.interrupt();
186             session.disconnect();
187         }
188     };
189
190     public void getNewMessages()
191     {
192         if (session.getPassword() == null) {
193             String JavaDoc password =
194                 PasswordDialog.showPasswordDialog(Editor.currentEditor(),
195                     "Password:", "Password");
196             if (password == null || password.length() == 0)
197                 return;
198             session.setPassword(password);
199         }
200         if (lock())
201             getNewMessages(true);
202         else
203             Editor.currentEditor().status("Mailbox is locked");
204     }
205
206     public void getNewMessages(boolean userInitiated)
207     {
208         Debug.assertTrue(isLocked());
209         // This method can get called in the background so we can't put up a
210
// dialog.
211
if (session.getPassword() == null)
212             return;
213         Log.debug("PopMailbox.getNewMessages " + userInitiated);
214         setBusy(true);
215         Log.debug("PopMailbox.getNewMessages back from setBusy(true)");
216         if (userInitiated)
217             saveDisplayState();
218         Debug.assertTrue(backgroundThread == null);
219         backgroundThread = new Thread JavaDoc(new GetNewMessagesProcess(userInitiated));
220         backgroundThread.start();
221     }
222
223     private class GetNewMessagesProcess implements BackgroundProcess
224     {
225         private boolean userInitiated;
226
227         // If this constructor is private, we run into jikes 1.15 bug #2256.
228
/*private*/ GetNewMessagesProcess(boolean userInitiated)
229         {
230             this.userInitiated = userInitiated;
231         }
232
233         public void run()
234         {
235             try {
236                 boolean changed = false;
237                 if (userInitiated)
238                     changed = clearRecent();
239                 cancelled = false;
240                 progressNotifier = new StatusBarProgressNotifier(PopMailbox.this);
241                 progressNotifier.progressStart();
242                 setBackgroundProcess(this);
243                 int oldSize = entries.size();
244                 boolean ok = retrieveNewMessages();
245                 if (changed || entries.size() != oldSize) {
246                     refreshBuffer();
247                     addEntriesToAddressBook(entries);
248                     updateDisplay();
249                 }
250                 newMessagesStatus();
251                 if (!ok) {
252                     if (userInitiated && !cancelled)
253                         error(session.getErrorText(), url.toString());
254                 }
255             }
256             finally {
257                 setBusy(false);
258                 if (progressNotifier != null) {
259                     progressNotifier.setText(cancelled ? "Cancelled" : "");
260                     progressNotifier.progressStop();
261                     progressNotifier = null;
262                 }
263                 setBackgroundProcess(null);
264                 backgroundThread = null;
265                 setLastCheckMillis(System.currentTimeMillis());
266                 unlock();
267                 Editor.updateDisplayLater(PopMailbox.this);
268             }
269         }
270
271         public void cancel()
272         {
273             Log.debug("GetNewMessagesProcess.cancel");
274             cancelled = true;
275             progressNotifier.cancel();
276             progressNotifier.setText("Cancelled, cleaning up...");
277             if (backgroundThread != null && backgroundThread.isAlive()) {
278                 Log.debug("interrupting background thread...");
279                 backgroundThread.interrupt();
280             }
281             session.disconnect();
282         }
283     }
284
285     private boolean retrieveNewMessages()
286     {
287         Log.debug("PopMailbox.retrieveNewMessages");
288         if (!connect())
289             return false;
290         File JavaDoc outputFile = null;
291         long start = System.currentTimeMillis();
292         try {
293             if (Thread.currentThread().isInterrupted() || cancelled)
294                 return true;
295             int count = stat();
296             if (count < 0)
297                 return false; // Error.
298
if (count == 0)
299                 return true; // No messages on the server.
300
if (Thread.currentThread().isInterrupted() || cancelled)
301                 return true;
302             List JavaDoc serverMessageList = getServerMessageList(count);
303             if (serverMessageList == null)
304                 return false; // Error.
305
if (Thread.currentThread().isInterrupted() || cancelled)
306                 return true;
307             // Removed uidls from the expunged uidls list if the corresponding
308
// message no longer exists on the server.
309
pruneExpungedUidlsList(serverMessageList);
310             if (Thread.currentThread().isInterrupted() || cancelled)
311                 return true;
312             // Remove messages from the server message list if we already have
313
// them or if they've been expunged locally.
314
List JavaDoc messagesToBeRetrieved =
315                 getMessagesToBeRetrieved(serverMessageList);
316             if (Thread.currentThread().isInterrupted() || cancelled)
317                 return true;
318             // Is there anything left?
319
if (messagesToBeRetrieved.size() == 0) {
320                 // If we're not keeping messages on the server, delete the old
321
// messages.
322
if (!getBooleanProperty(Property.POP_KEEP_MESSAGES_ON_SERVER))
323                     deleteMessagesOnServer(serverMessageList);
324                 Log.debug("no new messages");
325                 return true;
326             }
327             if (getLocalStore() == null)
328                 return false; // Error.
329
if (localStore.isFile() && localStore.length() > 0) {
330                 outputFile = Utilities.getTempFile(localStore.getParentFile());
331                 Log.debug("calling copyFile");
332                 long copyStart = System.currentTimeMillis();
333                 if (!Utilities.copyFile(localStore, outputFile))
334                     return false;
335                 Log.debug("back from copyFile " + (System.currentTimeMillis() - copyStart) + " ms");
336             } else
337                 outputFile = localStore;
338             MailboxFileWriter writer =
339                 MailboxFileWriter.getInstance(outputFile, true); // Append.
340
if (writer == null)
341                 return false; // Error.
342
boolean ok = retrieveMessages(messagesToBeRetrieved, writer);
343             try {
344                 writer.flush();
345                 writer.close();
346             }
347             catch (IOException JavaDoc e) {
348                 Log.error(e);
349                 return false;
350             }
351             if (!ok) {
352                 Log.debug("not ok...");
353                 // retrieveMessages() was interrupted. Truncate output file to
354
// proper length.
355
if (entries.size() == 0)
356                     return false;
357                 LocalMailboxEntry entry =
358                     (LocalMailboxEntry) entries.get(entries.size() - 1);
359                 long offset = entry.getNextMessageStart(); // Truncate to here.
360
try {
361                     RandomAccessFile JavaDoc raf =
362                         outputFile.getRandomAccessFile("rw");
363                     Log.debug("before raf.length() = " + raf.length());
364                     Log.debug("truncating to " + offset);
365                     raf.setLength(offset);
366                     Log.debug("after raf.length() = " + raf.length());
367                     raf.close();
368                 }
369                 catch (IOException JavaDoc e) {
370                     Log.error(e);
371                     return false;
372                 }
373             }
374             if (outputFile != localStore)
375                 if (!Utilities.deleteRename(outputFile, localStore))
376                     return false;
377             if (getBooleanProperty(Property.POP_KEEP_MESSAGES_ON_SERVER) == false)
378                 deleteMessagesOnServer(serverMessageList);
379             outputFile = null;
380         }
381         finally {
382             Log.debug("retrieveNewMessages calling logout() ...");
383             session.logout();
384             if (outputFile != null && outputFile.isFile())
385                 outputFile.delete();
386         }
387         Log.debug("retrieveNewMessages " + (System.currentTimeMillis() - start) + " ms");
388         return true; // Success!
389
}
390
391     private boolean connect()
392     {
393         if (progressNotifier != null)
394             progressNotifier.setText("Connecting to " + session.getHost());
395         if (session.connect()) {
396             if (progressNotifier != null)
397                 progressNotifier.setText("Connected to " + session.getHost());
398             return true;
399         }
400         return false;
401     }
402
403     // Returns number of messages on server or -1 if there is an error.
404
private int stat()
405     {
406         int count = -1;
407         session.write("stat");
408         String JavaDoc response = session.readLine();
409         if (response.startsWith("+OK")) {
410             try {
411                 count = Utilities.parseInt(response.substring(3).trim());
412             }
413             catch (NumberFormatException JavaDoc e) {
414                 Log.error(e);
415             }
416         } else {
417             Log.error("stat failed");
418             Log.error(response);
419         }
420         return count;
421     }
422
423     private List JavaDoc getServerMessageList(int count)
424     {
425         long start = System.currentTimeMillis();
426         session.write("uidl");
427         String JavaDoc response = session.readLine();
428         if (!response.startsWith("+OK")) {
429             Log.error("getServerMessageList uidl failed");
430             Log.error(response);
431             return null;
432         }
433         List JavaDoc list = new ArrayList JavaDoc(count);
434         while (true) {
435             String JavaDoc s = session.readLine();
436             if (s == null)
437                 return null; // Shouldn't happen.
438
if (s.equals("."))
439                 break;
440             int index = s.indexOf(' ');
441             if (index >= 0) {
442                 int messageNumber = Integer.parseInt(s.substring(0, index)); // BUG! Error handling!
443
String JavaDoc uidl = s.substring(index + 1);
444                 if (messageNumber >= 1)
445                     list.add(new MessageListEntry(messageNumber, uidl));
446             }
447         }
448         Log.debug("getServerMessageList " + (System.currentTimeMillis() - start) + " ms");
449         Log.debug("getServerMessageList count = " + count + " list size = " + list.size());
450         return list;
451     }
452
453     private List JavaDoc getMessagesToBeRetrieved(List JavaDoc serverMessageList)
454     {
455         long start = System.currentTimeMillis();
456         HashSet JavaDoc hashSet = null;
457         if (entries != null) {
458             int size = entries.size();
459             hashSet = new HashSet JavaDoc(size);
460             for (int i = 0; i < size; i++) {
461                 LocalMailboxEntry mailboxEntry =
462                     (LocalMailboxEntry) entries.get(i);
463                 hashSet.add(mailboxEntry.getUidl());
464             }
465         }
466         List JavaDoc toBeReturned = new ArrayList JavaDoc();
467         int size = serverMessageList.size();
468         for (int i = 0; i < size; i++) {
469             MessageListEntry messageListEntry =
470                 (MessageListEntry) serverMessageList.get(i);
471             String JavaDoc uidl = messageListEntry.uidl;
472             if (isExpunged(uidl))
473                 continue;
474             if (hashSet != null && hashSet.contains(uidl))
475                 continue;
476             toBeReturned.add(messageListEntry);
477         }
478         Log.debug("getMessagesToBeRetrieved " + (System.currentTimeMillis() - start) + " ms");
479         return toBeReturned;
480     }
481
482     private boolean retrieveMessages(List JavaDoc messageList, MailboxFileWriter writer)
483     {
484         Log.debug("entering retrieveMessages");
485         long start = System.currentTimeMillis();
486         for (int i = 0; i < messageList.size(); i++) {
487             String JavaDoc text = "Retrieving message " + (i + 1) + " of " + messageList.size();
488             if (i == 0)
489                 progressNotifier.setText(text); // Make sure the user sees this.
490
else
491                 progressNotifier.progress(text);
492             MessageListEntry entry = (MessageListEntry) messageList.get(i);
493             if (!retrieveMessage(entry.messageNumber, entry.uidl, writer)) {
494                 Log.error("retrieveMessages error retrieving message " + entry.messageNumber);
495                 return false;
496             }
497         }
498         progressNotifier.setText(cancelled ? "Cancelled, cleaning up" : "");
499         Log.debug("leaving retrieveMessages " +(System.currentTimeMillis() - start) + " ms");
500         return true;
501     }
502
503     // Returns true if no error.
504
private boolean retrieveMessage(int i, String JavaDoc uidl, MailboxFileWriter writer)
505     {
506         String JavaDoc command = "list " + i;
507         session.write(command);
508         int size = 0;
509         String JavaDoc response = session.readLine();
510         String JavaDoc expected = "+OK " + i + " ";
511         if (response != null && response.startsWith(expected)) {
512             try {
513                 size = Integer.parseInt(response.substring(expected.length()));
514             }
515             catch (NumberFormatException JavaDoc e) {
516                 Log.error(e);
517                 return false; // Error!
518
}
519         } else {
520             Log.error("retrieveMessage command failed: " + command);
521             Log.error("response = " + response);
522         }
523         command = "retr " + i;
524         session.write(command);
525         response = session.readLine();
526         if (response == null || !response.startsWith("+OK")) {
527             Log.error("retrieveMessage command failed: " + command);
528             Log.error(response);
529             return false; // Error!
530
}
531         try {
532             final long messageStart = writer.getOffset();
533             writer.write("From - ");
534             writer.write(getDateTimeStamp());
535             writer.write('\n');
536             // Headers.
537
FastStringBuffer sb = new FastStringBuffer(2048);
538             while (true) {
539                 String JavaDoc s = session.readLine();
540                 if (s == null)
541                     return false; // Error! (Reached end of stream before reaching end of headers.)
542
if (s.length() == 0) {
543                     // Reached end of headers.
544
// Add X-J-Status.
545
String JavaDoc status = "X-J-Status: 0\n";
546                     writer.write(status);
547                     sb.append(status);
548                     // Add X-UIDL.
549
if (uidl != null) {
550                         writer.write("X-UIDL: ");
551                         writer.write(uidl);
552                         writer.write('\n');
553                         sb.append("X-UIDL: ");
554                         sb.append(uidl);
555                         sb.append('\n');
556                     }
557                     writer.write('\n');
558                     break;
559                 }
560                 if (s.toUpperCase().startsWith("X-UIDL"))
561                     continue;
562                 writer.write(s);
563                 writer.write('\n');
564                 sb.append(s);
565                 sb.append('\n');
566             }
567             // Body.
568
boolean echo = session.getEcho();
569             session.setEcho(false);
570             while (true) {
571                 String JavaDoc s = session.readLine();
572                 if (s == null)
573                     return false; // Error! (Reached end of stream before reaching end of message.)
574
if (s.equals("."))
575                     break; // End of message.
576
if (s.length() > 1 && s.charAt(0) == '.' && s.charAt(1) == '.') {
577                     // Remove dot-stuffing.
578
s = s.substring(1);
579                 } else if (s.startsWith("From ")) {
580                     // Mangle lines starting with "From " in body of message.
581
writer.write('>');
582                 }
583                 writer.write(s);
584                 writer.write('\n');
585             }
586             session.setEcho(echo);
587             // Add a newline after the end of the message.
588
writer.write('\n');
589             LocalMailboxEntry entry = new LocalMailboxEntry(entries.size()+1,
590                 messageStart, sb.toString());
591             entry.setNextMessageStart(writer.getOffset());
592             entry.setSize(size);
593             entry.setFlags(MailboxEntry.RECENT);
594             entries.add(entry);
595             setDirty(true);
596             return true; // No error.
597
}
598         catch (IOException JavaDoc e) {
599             Log.error(e);
600             return false; // Error!
601
}
602     }
603
604     private boolean deleteMessagesOnServer(List JavaDoc serverMessageList)
605     {
606         Log.debug("deleteMessagesOnServer need to delete " + serverMessageList.size() + " messages");
607         for (int i = 0; i < serverMessageList.size(); i++) {
608             MessageListEntry messageListEntry =
609                 (MessageListEntry) serverMessageList.get(i);
610             session.write("dele " + messageListEntry.messageNumber);
611             String JavaDoc response = session.readLine();
612             if (response == null || !response.startsWith("+OK")) {
613                 Log.error("deleteMessagesOnServer dele failed response = " + response);
614                 session.write("rset");
615                 session.readLine();
616                 return false; // Error!
617
}
618         }
619         Log.debug("deleteMessagesOnServer success!");
620         return true;
621     }
622
623     public void expunge()
624     {
625         if (lock()) {
626             setBusy(true);
627             saveDisplayState();
628             Debug.assertTrue(backgroundThread == null);
629             backgroundThread = new Thread JavaDoc(expungeProcess);
630             backgroundThread.start();
631         }
632     }
633
634     private Runnable JavaDoc expungeProcess = new BackgroundProcess() {
635         public void run()
636         {
637             try {
638                 setBackgroundProcess(this);
639                 progressNotifier = new StatusBarProgressNotifier(PopMailbox.this);
640                 progressNotifier.progressStart();
641                 cancelled = false;
642                 expungeInternal();
643             }
644             finally {
645                 setBackgroundProcess(null);
646                 backgroundThread = null;
647                 setBusy(false);
648                 if (progressNotifier != null) {
649                     if (cancelled)
650                         progressNotifier.setText("Cancelled");
651                     progressNotifier.progressStop();
652                     progressNotifier = null;
653                 }
654                 unlock();
655                 updateDisplay();
656             }
657         }
658
659         public void cancel()
660         {
661             Log.debug("expungeProcess.cancel");
662             cancelled = true;
663             if (backgroundThread != null && backgroundThread.isAlive())
664                 backgroundThread.interrupt();
665             session.disconnect();
666         }
667     };
668
669     private void expungeInternal()
670     {
671         if (entries == null)
672             return; // No error.
673
if (getBooleanProperty(Property.POP_EXPUNGE_DELETED_MESSAGES_ON_SERVER) == false ||
674             getBooleanProperty(Property.POP_KEEP_MESSAGES_ON_SERVER) == false) {
675             // This is the "local expunge only" case.
676
Log.debug("expungeInternal \"local expunge only\" case");
677             // First add all deleted entries to expunged list.
678
for (int i = entries.size() - 1; i >= 0; i--) {
679                 MailboxEntry entry = (MailboxEntry) entries.get(i);
680                 if (entry.isDeleted())
681                     addToExpungedUidlsList(entry.getUidl());
682             }
683             writeExpungedUidlsList(); // BUG! Error handling?
684
rewriteMailbox(true); // BUG! Error handling!
685
refreshBuffer();
686             return;
687         }
688         // Reaching here, we want to expunge the deleted messages on the server too.
689
Log.debug("expungeInternal \"expunge through\" case");
690         if (!connect()) {
691             Log.error("expungeInternal can't connect");
692             return;
693         }
694         try {
695             int count = stat();
696             if (count < 0)
697                 return; // Error.
698
if (count > 0) {
699                 List JavaDoc serverMessageList = getServerMessageList(count);
700                 for (int i = 0; i < entries.size(); i++) {
701                     MailboxEntry entry = (MailboxEntry) entries.get(i);
702                     if (entry.isDeleted()) {
703                         String JavaDoc uidl = entry.getUidl();
704                         if (!expungeUidl(uidl, serverMessageList))
705                             return; // Error!
706
if (cancelled) {
707                             progressNotifier.setText("Cancelled");
708                             return;
709                         }
710                     }
711                 }
712                 if (expungedUidlsList != null) {
713                     // Expunge on the server messages that were previously expunged locally.
714
Iterator JavaDoc it = expungedUidlsList.iterator();
715                     while (it.hasNext()) {
716                         String JavaDoc uidl = (String JavaDoc) it.next();
717                         if (!expungeUidl(uidl, serverMessageList))
718                             return; // Error!
719
if (cancelled) {
720                             progressNotifier.setText("Cancelled");
721                             return;
722                         }
723                     }
724                 }
725             }
726             progressNotifier.progress("Logging out...");
727             if (!session.logout())
728                 return; // Error!
729
// All deletions have been completed on the server.
730
progressNotifier.progress("Saving mailbox...");
731             rewriteMailbox(true); // BUG! Error handling!
732
progressNotifier.setText("Saving mailbox...done");
733             refreshBuffer();
734         }
735         finally {
736             session.disconnect();
737         }
738     }
739
740     private boolean expungeUidl(String JavaDoc uidl, List JavaDoc serverMessageList)
741     {
742         if (uidl != null) {
743             for (int j = serverMessageList.size() - 1; j >= 0; j--) {
744                 MessageListEntry messageListEntry = (MessageListEntry) serverMessageList.get(j);
745                 if (uidl.equals(messageListEntry.uidl)) {
746                     if (progressNotifier != null) {
747                         FastStringBuffer sb = new FastStringBuffer("Deleting message ");
748                         sb.append(messageListEntry.messageNumber);
749                         sb.append(" on server");
750                         progressNotifier.progress(sb.toString());
751                     }
752                     session.write("dele " + messageListEntry.messageNumber);
753                     String JavaDoc response = session.readLine();
754                     if (response != null && response.startsWith("+OK"))
755                         return true;
756                     // Error!
757
Log.error("expungeUidl dele failed response = " + response);
758                     session.write("rset");
759                     session.readLine();
760                     return false;
761                 }
762             }
763         }
764         // Didn't find uidl.
765
return true;
766     }
767
768     private HashSet JavaDoc expungedUidlsList;
769
770     private final boolean isExpunged(String JavaDoc uidl)
771     {
772         if (expungedUidlsList == null)
773             return false;
774         return expungedUidlsList.contains(uidl);
775     }
776
777     private final void addToExpungedUidlsList(String JavaDoc uidl)
778     {
779         if (expungedUidlsList == null)
780             expungedUidlsList = new HashSet JavaDoc();
781         expungedUidlsList.add(uidl);
782     }
783
784     // Prune our list of expunged uidls, removing entries that no longer exist
785
// on the server.
786
private void pruneExpungedUidlsList(List JavaDoc serverMessageList)
787     {
788         Log.debug("pruneExpungedUidlsList");
789         if (expungedUidlsList == null)
790             return; // Nothing to do.
791
boolean changed = false;
792         long start = System.currentTimeMillis();
793         int size = serverMessageList.size();
794         HashSet JavaDoc serverUidls = new HashSet JavaDoc(size);
795         for (int i = 0; i < size; i++) {
796             MessageListEntry entry = (MessageListEntry) serverMessageList.get(i);
797             serverUidls.add(entry.uidl);
798         }
799         Iterator JavaDoc it = expungedUidlsList.iterator();
800         while (it.hasNext()) {
801             String JavaDoc uidl = (String JavaDoc) it.next();
802             if (!serverUidls.contains(uidl)) {
803                 Log.warn("removing uidl " + uidl + " (no longer exists on server)");
804                 it.remove();
805                 changed = true;
806             }
807         }
808         if (changed)
809             writeExpungedUidlsList();
810         Log.debug("pruneExpungedUidlsList " + (System.currentTimeMillis() - start) + " ms");
811     }
812
813     private void readExpungedUidlsList()
814     {
815         File JavaDoc mailboxFile = getMailboxFile();
816         if (mailboxFile == null) {
817             Debug.bug("readExpungedUidlsList mailboxFile is null");
818             return;
819         }
820         String JavaDoc filename = mailboxFile.canonicalPath() + ".expunged";
821         try {
822             BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new FileReader JavaDoc(filename));
823             String JavaDoc s;
824             while ((s = reader.readLine()) != null) {
825                 if (expungedUidlsList == null)
826                     expungedUidlsList = new HashSet JavaDoc();
827                 expungedUidlsList.add(s);
828             }
829             reader.close();
830         }
831         catch (FileNotFoundException JavaDoc e) {
832             // Might happen.
833
}
834         catch (IOException JavaDoc e) {
835             Log.error(e);
836         }
837     }
838
839     private void writeExpungedUidlsList()
840     {
841         File JavaDoc mailboxFile = getMailboxFile();
842         if (mailboxFile == null) {
843             Debug.bug("writeExpungedUidls mailboxFile is null");
844             return;
845         }
846         String JavaDoc filename = mailboxFile.canonicalPath() + ".expunged";
847         if (expungedUidlsList == null || expungedUidlsList.size() == 0) {
848             File JavaDoc file = File.getInstance(filename);
849             if (file.isFile())
850                 file.delete();
851             return;
852         }
853         try {
854             BufferedWriter JavaDoc writer = new BufferedWriter JavaDoc(new FileWriter JavaDoc(filename));
855             Iterator JavaDoc it = expungedUidlsList.iterator();
856             while (it.hasNext()) {
857                 writer.write((String JavaDoc) it.next());
858                 writer.newLine();
859             }
860             writer.flush();
861             writer.close();
862         }
863         catch (IOException JavaDoc e) {
864             Log.error(e);
865         }
866     }
867
868     private static final SimpleDateFormat JavaDoc df =
869         new SimpleDateFormat JavaDoc("EEE MMM dd HH:mm:ss yyyy", Locale.US);
870
871     private final String JavaDoc getDateTimeStamp()
872     {
873         return df.format(Calendar.getInstance().getTime());
874     }
875
876     private File JavaDoc getLocalStore()
877     {
878         if (localStore != null)
879             return localStore;
880         File JavaDoc popDir = File.getInstance(Directories.getMailDirectory(), "pop");
881         if (!popDir.isDirectory()) {
882             popDir.mkdirs();
883             if (!popDir.isDirectory()) {
884                 Log.error("can't make directory " + popDir.canonicalPath());
885                 return null;
886             }
887         }
888         File JavaDoc catalogFile = File.getInstance(popDir, "catalog");
889         Properties JavaDoc catalog = new Properties JavaDoc();
890         // Load the catalog.
891
try {
892             if (catalogFile.isFile()) {
893                 InputStream JavaDoc in = catalogFile.getInputStream();
894                 catalog.load(in);
895                 in.close();
896             }
897         }
898         catch (IOException JavaDoc e) {
899             Log.error(e);
900         }
901         String JavaDoc mailboxName = getUrl().toString();
902         if (mailboxName.startsWith("pop://"))
903             mailboxName = mailboxName.substring(6);
904         String JavaDoc filename = catalog.getProperty(mailboxName);
905         if (filename != null) // Found existing store.
906
return localStore = File.getInstance(popDir, filename);
907         File JavaDoc file = Utilities.getTempFile(popDir);
908         catalog.put(mailboxName, file.getName());
909         try {
910             OutputStream JavaDoc out = catalogFile.getOutputStream();
911             catalog.save(out, null);
912             out.flush();
913             out.close();
914             return localStore = file;
915         }
916         catch (IOException JavaDoc e) {
917             Log.error(e);
918             return null;
919         }
920     }
921
922     public void dispose()
923     {
924         Log.debug("PopMailbox.dispose");
925         Runnable JavaDoc disposeRunnable = new Runnable JavaDoc() {
926             public void run()
927             {
928                 try {
929                     Log.debug("disposeRunnable.run() calling acquire()...");
930                     acquire(); // Blocks, may throw InterruptedException.
931
Log.debug("disposeRunnable.run() back from acquire()");
932                     if (dirty) {
933                         final Object JavaDoc pending = new Object JavaDoc();
934                         Editor.getPendingOperations().add(pending);
935                         Log.debug("disposeRunnable.run() calling rewriteMailbox()...");
936                         rewriteMailbox(false);
937                         Log.debug("disposeRunnable.run() back from rewriteMailbox()");
938                         Editor.getPendingOperations().remove(pending);
939                     }
940                     Debug.assertTrue(session != null);
941                     Log.debug("disposeRunnable.run() calling session.logout()...");
942                     session.logout();
943                     release();
944                     Log.debug("disposeRunnable.run() back from release()");
945                 }
946                 catch (InterruptedException JavaDoc e) {
947                     Log.error(e);
948                 }
949             }
950         };
951         new Thread JavaDoc(disposeRunnable).start();
952         MailboxProperties.saveProperties(this);
953     }
954
955     protected void finalize() throws Throwable JavaDoc
956     {
957         Log.debug("PopMailbox.finalize");
958         super.finalize();
959     }
960
961     public String JavaDoc toString()
962     {
963         int newMessageCount = getNewMessageCount();
964         if (newMessageCount > 0) {
965             FastStringBuffer sb = new FastStringBuffer(url.toString());
966             sb.append(" (");
967             sb.append(newMessageCount);
968             sb.append(" new)");
969             return sb.toString();
970         } else
971             return url.toString();
972     }
973
974     public String JavaDoc getTitle()
975     {
976         return toString();
977     }
978 }
979
980 class MessageListEntry
981 {
982     int messageNumber;
983     String JavaDoc uidl;
984
985     MessageListEntry(int messageNumber, String JavaDoc uidl)
986     {
987         this.messageNumber = messageNumber;
988         this.uidl = uidl;
989     }
990 }
991
Popular Tags