001: /*
002: * Drafts.java
003: *
004: * Copyright (C) 2002 Peter Graves
005: * $Id: Drafts.java,v 1.2 2002/10/11 01:42:37 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j.mail;
023:
024: import java.io.BufferedReader;
025: import java.io.IOException;
026: import java.io.InputStreamReader;
027: import java.util.ArrayList;
028: import java.util.Date;
029: import java.util.Iterator;
030: import java.util.List;
031: import javax.swing.SwingUtilities;
032: import org.armedbear.j.Buffer;
033: import org.armedbear.j.BufferIterator;
034: import org.armedbear.j.Debug;
035: import org.armedbear.j.Editor;
036: import org.armedbear.j.EditorIterator;
037: import org.armedbear.j.File;
038: import org.armedbear.j.FastStringBuffer;
039: import org.armedbear.j.Headers;
040: import org.armedbear.j.Line;
041: import org.armedbear.j.Log;
042: import org.armedbear.j.View;
043:
044: public final class Drafts extends Mailbox {
045: private final File directory;
046:
047: public Drafts(LocalMailboxURL url) {
048: super ();
049: this .url = url;
050: this .directory = url.getFile();
051: supportsUndo = false;
052: type = TYPE_MAILBOX;
053: mode = MailboxMode.getMode();
054: formatter = mode.getFormatter(this );
055: readOnly = true;
056: title = "drafts";
057: setInitialized(true);
058: }
059:
060: public File getDirectory() {
061: return directory;
062: }
063:
064: public String getName() {
065: return directory.canonicalPath();
066: }
067:
068: public int getMessageCount() {
069: return 0;
070: }
071:
072: public void getNewMessages() {
073: reload();
074: }
075:
076: public void createFolder() {
077: notImplemented("Drafts.createFolder");
078: }
079:
080: public void deleteFolder() {
081: notImplemented("Drafts.deleteFolder");
082: }
083:
084: public void saveToFolder() {
085: notImplemented("Drafts.saveToFolder");
086: }
087:
088: public void moveToFolder() {
089: notImplemented("Drafts.moveToFolder");
090: }
091:
092: public void delete() {
093: final Editor editor = Editor.currentEditor();
094: boolean advanceDot = false;
095: List list = getTaggedEntries();
096: if (list == null) {
097: Line line = editor.getDotLine();
098: if (!(line instanceof MailboxLine))
099: return;
100: advanceDot = true;
101: list = new ArrayList();
102: list.add(((MailboxLine) line).getMailboxEntry());
103: }
104: Iterator iter = list.iterator();
105: while (iter.hasNext()) {
106: DraftsEntry entry = (DraftsEntry) iter.next();
107: if (entry == null)
108: continue;
109: File file = entry.getFile();
110: if (file == null)
111: continue;
112: if (!file.isFile())
113: continue;
114: String name = file.getName();
115: if (name.endsWith(".deleted"))
116: continue; // Already deleted.
117: File deleted = File.getInstance(directory, name
118: .concat(".deleted"));
119: if (deleted.isFile()) {
120: Debug.bug();
121: return;
122: }
123: Log.debug("delete renaming " + file.getName() + " to "
124: + deleted.getName());
125: if (file.renameTo(deleted)) {
126: entry.setFile(deleted);
127: entry.setFlags(entry.getFlags() | MailboxEntry.DELETED);
128: updateEntry(entry);
129: }
130: }
131: if (advanceDot)
132: advanceDot(editor.getDotLine());
133: }
134:
135: public void undelete() {
136: final Editor editor = Editor.currentEditor();
137: boolean advanceDot = false;
138: List list = getTaggedEntries();
139: if (list == null) {
140: Line line = editor.getDotLine();
141: if (!(line instanceof MailboxLine))
142: return;
143: advanceDot = true;
144: list = new ArrayList();
145: list.add(((MailboxLine) line).getMailboxEntry());
146: }
147: Iterator iter = list.iterator();
148: while (iter.hasNext()) {
149: DraftsEntry entry = (DraftsEntry) iter.next();
150: if (entry == null)
151: continue;
152: File file = entry.getFile();
153: if (file == null)
154: continue;
155: if (!file.isFile())
156: continue;
157: String name = file.getName();
158: if (!name.endsWith(".deleted"))
159: continue; // Not deleted.
160: File undeleted = File.getInstance(directory, name
161: .substring(0, name.length() - 8));
162: if (undeleted.isFile()) {
163: Debug.bug();
164: return;
165: }
166: Log.debug("undelete renaming " + file.getName() + " to "
167: + undeleted.getName());
168: if (file.renameTo(undeleted)) {
169: entry.setFile(undeleted);
170: entry
171: .setFlags(entry.getFlags()
172: & ~MailboxEntry.DELETED);
173: updateEntry(entry);
174: }
175: }
176: if (advanceDot)
177: advanceDot(editor.getDotLine());
178: }
179:
180: public void markRead() {
181: notImplemented("Drafts.markRead");
182: }
183:
184: public void markUnread() {
185: notImplemented("Drafts.markUnread");
186: }
187:
188: public void flag() {
189: notImplemented("Drafts.flag");
190: }
191:
192: public void setAnsweredFlag(MailboxEntry entry) {
193: notImplemented("Drafts.setAnsweredFlag");
194: }
195:
196: public void expunge() {
197: if (lock()) {
198: setBusy(true);
199: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
200: Editor ed = it.nextEditor();
201: if (ed.getBuffer() == this )
202: ed.saveView();
203: }
204: new Thread(expungeRunnable).start();
205: }
206: }
207:
208: private Runnable expungeRunnable = new Runnable() {
209: public void run() {
210: try {
211: if (expungeInternal())
212: loadInternal();
213: } finally {
214: setBusy(false);
215: unlock();
216: updateDisplay();
217: }
218: }
219: };
220:
221: // Returns true if at least one message is expunged.
222: private boolean expungeInternal() {
223: boolean result = false;
224: entries = new ArrayList();
225: String[] names = directory.list();
226: if (names != null) {
227: for (int i = 0; i < names.length; i++) {
228: final String name = names[i];
229: if (name.indexOf(".deleted") >= 0) {
230: File file = File.getInstance(directory, name);
231: Log.debug("deleting " + file);
232: file.delete();
233: result = true;
234: }
235: }
236: }
237: return result;
238: }
239:
240: public int load() {
241: if (lock()) {
242: setBusy(true);
243: setLoaded(true);
244: new Thread(loadRunnable).start();
245: return LOAD_PENDING;
246: } else
247: return LOAD_FAILED;
248: }
249:
250: public void reload() {
251: if (lock()) {
252: setBusy(true);
253: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
254: Editor ed = it.nextEditor();
255: if (ed.getBuffer() == this )
256: ed.saveView();
257: }
258: new Thread(reloadRunnable).start();
259: }
260: }
261:
262: private Runnable loadRunnable = new Runnable() {
263: public void run() {
264: try {
265: loadInternal();
266: } finally {
267: unlock();
268: setBusy(false);
269: Runnable completionRunnable = new Runnable() {
270: public void run() {
271: for (EditorIterator it = new EditorIterator(); it
272: .hasNext();) {
273: Editor ed = it.nextEditor();
274: View view = new View();
275: view.setDotEntry(getInitialEntry());
276: ed.setView(Drafts.this , view);
277: if (ed.getBuffer() == Drafts.this ) {
278: ed.bufferActivated(true);
279: ed.updateDisplay();
280: }
281: }
282: }
283: };
284: SwingUtilities.invokeLater(completionRunnable);
285: }
286: }
287: };
288:
289: private Runnable reloadRunnable = new Runnable() {
290: public void run() {
291: try {
292: loadInternal();
293: updateDisplay();
294: } finally {
295: unlock();
296: setBusy(false);
297: }
298: }
299: };
300:
301: private void loadInternal() {
302: entries = new ArrayList();
303: String[] names = directory.list();
304: if (names != null) {
305: for (int i = 0; i < names.length; i++) {
306: final String name = names[i];
307: DraftsEntry entry = DraftsEntry.parseEntry(directory,
308: name);
309: if (entry != null) {
310: if (name.endsWith(".deleted"))
311: entry.setFlags(entry.getFlags()
312: | MailboxEntry.DELETED);
313: entries.add(entry);
314: }
315: }
316: }
317: refreshBuffer();
318: }
319:
320: public MailboxEntry getInitialEntry() {
321: Line line = getFirstLine();
322: if (line == null)
323: return null;
324: while (true) {
325: if (line.next() == null)
326: break; // Reached last line.
327: line = line.next();
328: }
329: return ((MailboxLine) line).getMailboxEntry();
330: }
331:
332: public void readMessage(Line line) {
333: readMessage(line, false);
334: }
335:
336: public void readMessageOtherWindow(Line line) {
337: readMessage(line, true);
338: }
339:
340: // useOtherWindow is ignored. Since it's a mail composition buffer, it's
341: // opened full-height.
342: private void readMessage(Line line, boolean useOtherWindow) {
343: DraftsEntry entry = (DraftsEntry) ((MailboxLine) line)
344: .getMailboxEntry();
345: if (entry == null)
346: return;
347: File file = entry.getFile();
348: if (file == null)
349: return;
350: Buffer buf = null;
351: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
352: Buffer b = it.nextBuffer();
353: if (b instanceof SendMail) {
354: if (file.equals(b.getFile())) {
355: buf = b;
356: break;
357: }
358: }
359: }
360: if (buf == null)
361: buf = new SendMail(file);
362: Editor editor = Editor.currentEditor();
363: editor.makeNext(buf);
364: editor.switchToBuffer(buf);
365: }
366:
367: public File getCurrentDirectory() {
368: return directory;
369: }
370:
371: public String toString() {
372: return "drafts";
373: }
374:
375: private static class DraftsEntry extends MailboxEntry {
376: private File file;
377:
378: DraftsEntry(File file) {
379: this .file = file;
380: }
381:
382: public final File getFile() {
383: return file;
384: }
385:
386: public final void setFile(File file) {
387: this .file = file;
388: }
389:
390: public static DraftsEntry parseEntry(File directory,
391: String fileName) {
392: File file = File.getInstance(directory, fileName);
393: if (file != null && file.isFile()) {
394: FastStringBuffer sb = new FastStringBuffer();
395: try {
396: BufferedReader reader = new BufferedReader(
397: new InputStreamReader(file.getInputStream()));
398: while (true) {
399: String s = reader.readLine();
400: if (s == null)
401: break;
402: if (s.length() == 0)
403: break;
404: char c = s.charAt(0);
405: if (c == '-')
406: break; // Header separator line.
407: if (c != ' ' && c != '\t' && s.indexOf(':') < 0)
408: break; // Not a header line.
409: sb.append(s);
410: sb.append("\r\n");
411: }
412: reader.close();
413: } catch (IOException e) {
414: Log.error(e);
415: }
416: Headers headers = Headers.parse(sb.toString());
417: if (headers == null)
418: return null;
419: DraftsEntry entry = new DraftsEntry(file);
420: entry.subject = RFC2047.decode(headers
421: .getValue(Headers.SUBJECT));
422: String dateString = headers.getValue(Headers.DATE);
423: if (dateString != null)
424: entry.date = RFC822Date.parseDate(dateString);
425: else
426: entry.date = new RFC822Date(new Date(file
427: .lastModified()));
428: entry.size = (int) file.length();
429: String fromString = RFC2047.decode(headers
430: .getValue(Headers.FROM));
431: if (fromString != null)
432: entry.from = MailAddress.parseAddresses(fromString);
433: else {
434: MailAddress ma = Mail.getUserMailAddress();
435: if (ma != null)
436: entry.from = new MailAddress[] { ma };
437: }
438: entry.replyTo = MailAddress.parseAddresses(RFC2047
439: .decode(headers.getValue(Headers.REPLY_TO)));
440: entry.to = MailAddress.parseAddresses(RFC2047
441: .decode(headers.getValue(Headers.TO)));
442: entry.cc = MailAddress.parseAddresses(RFC2047
443: .decode(headers.getValue(Headers.CC)));
444: entry.messageId = headers.getValue(Headers.MESSAGE_ID);
445: entry.inReplyTo = parseInReplyTo(headers
446: .getValue(Headers.IN_REPLY_TO));
447: String refs = headers.getValue(Headers.REFERENCES);
448: if (refs != null)
449: entry.references = parseReferences(refs);
450: entry.flags = SEEN;
451: return entry;
452: }
453: return null;
454: }
455: }
456: }
|