1 21 22 package org.armedbear.j.mail; 23 24 import java.io.BufferedReader ; 25 import java.io.BufferedWriter ; 26 import java.io.IOException ; 27 import java.io.InputStreamReader ; 28 import java.io.OutputStreamWriter ; 29 import java.io.RandomAccessFile ; 30 import java.io.UnsupportedEncodingException ; 31 import java.util.ArrayList ; 32 import java.util.List ; 33 import java.util.Vector ; 34 import javax.swing.SwingUtilities ; 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 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 getName() 78 { 79 Debug.assertTrue(mailboxFile != null); 80 return mailboxFile.canonicalPath(); 81 } 82 83 public final File getMailboxFile() 84 { 85 return mailboxFile; 86 } 87 88 public final void setMailboxFile(File 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 raf = mailboxFile.getRandomAccessFile("r"); 104 String header = getMessageHeader(entry, raf); 105 Headers headers = Headers.parse(header); 106 String charset = null; 107 String contentType = headers.getValue(Headers.CONTENT_TYPE); 108 if (contentType != null) 109 charset = Utilities.getCharsetFromContentType(contentType); 110 String body = getMessageBody(entry, raf, charset); 111 return new Message(header + "\r\n" + body, headers); 112 } 113 catch (IOException e) { 114 Log.error(e); 115 return null; 116 } 117 } 118 119 private String getMessageHeader(MailboxEntry entry, RandomAccessFile raf) 120 { 121 FastStringBuffer sb = new FastStringBuffer(8192); 122 try { 123 long offset = ((LocalMailboxEntry) entry).getMessageStart(); 124 raf.seek(offset); 125 String 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 ""; } 133 while (true) { 134 text = raf.readLine(); 135 if (text == null) 136 break; if (text.length() == 0) 138 break; sb.append(text); 140 sb.append("\r\n"); 141 } 142 } 143 catch (IOException e) { 144 Log.error(e); 145 } 146 return sb.toString(); 147 } 148 149 private String getMessageBody(MailboxEntry entry, RandomAccessFile raf, String 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 e) { 162 Log.error(e); 163 return ""; 164 } 165 try { 166 return new String (bytes, Utilities.getEncodingFromCharset(charset)); 167 } 168 catch (UnsupportedEncodingException e) { 169 Log.error(e); 170 } 171 return new String (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 toBeDeleted = getTaggedEntries(); 208 if (toBeDeleted == null) { 209 Line line = editor.getDotLine(); 210 if (!(line instanceof MailboxLine)) 211 return; 212 toBeDeleted = new Vector (); 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 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 toBeUndeleted = getTaggedEntries(); 246 if (toBeUndeleted == null) { 247 Line line = editor.getDotLine(); 248 if (!(line instanceof MailboxLine)) 249 return; 250 toBeUndeleted = new Vector (); 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 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 list = getTaggedEntries(); 284 if (list == null) { 285 Line line = editor.getDotLine(); 286 if (!(line instanceof MailboxLine)) 287 return; 288 list = new Vector (); 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 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 list = getTaggedEntries(); 320 if (list == null) { 321 Line line = editor.getDotLine(); 322 if (!(line instanceof MailboxLine)) 323 return; 324 list = new Vector (); 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 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 list = getTaggedEntries(); 356 if (list == null) { 357 Line line = editor.getDotLine(); 358 if (!(line instanceof MailboxLine)) 359 return; 360 list = new ArrayList (); 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 (loadRunnable).start(); 399 setLoaded(true); 400 return LOAD_PENDING; 401 } else 402 return LOAD_FAILED; 403 } 404 405 private Runnable loadRunnable = new Runnable () { 406 public void run() 407 { 408 try { 409 readMailboxFile(null); 410 refreshBuffer(); 411 } 412 finally { 413 unlock(); 414 setBusy(false); 415 Runnable completionRunnable = new Runnable () { 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 reader = 485 new BufferedReader (new InputStreamReader (mailboxFile.getInputStream(), 486 "ISO8859_1")); 487 File tempFile = 488 Utilities.getTempFile(mailboxFile.getParentFile()); 489 BufferedWriter writer = 490 new BufferedWriter (new OutputStreamWriter (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 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 boolean seenXJStatus = false; 515 while (true) { 516 text = reader.readLine(); 517 if (text == null) 518 break; 519 if (text.length() == 0) 520 break; 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 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 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 Vector v = new Vector (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 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 dirty = false; 587 succeeded = true; 588 } 589 } 590 catch (IOException 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 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 disposeRunnable = new Runnable () { 626 public void run() 627 { 628 try { 629 Log.debug("disposeRunnable.run() calling acquire()..."); 630 acquire(); Log.debug("disposeRunnable.run() back from acquire()"); 632 clearRecent(); 633 if (dirty) { 634 final Object pending = new Object (); 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 e) { 645 Log.error(e); 646 } 647 } 648 }; 649 new Thread (disposeRunnable).start(); 650 } 651 652 protected void finalize() throws Throwable 653 { 654 Log.debug("LocalMailbox.finalize"); 655 super.finalize(); 656 } 657 658 public String toString() 659 { 660 final String 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 |