001: //The contents of this file are subject to the Mozilla Public License Version 1.1
002: //(the "License"); you may not use this file except in compliance with the
003: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
004: //
005: //Software distributed under the License is distributed on an "AS IS" basis,
006: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
007: //for the specific language governing rights and
008: //limitations under the License.
009: //
010: //The Original Code is "The Columba Project"
011: //
012: //The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
013: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
014: //
015: //All Rights Reserved.
016:
017: package org.columba.mail.gui.composer.command;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.nio.charset.Charset;
022: import java.nio.charset.UnsupportedCharsetException;
023: import java.text.DateFormat;
024: import java.util.logging.Logger;
025:
026: import org.columba.api.command.ICommandReference;
027: import org.columba.api.command.IWorkerStatusController;
028: import org.columba.core.command.Command;
029: import org.columba.core.gui.frame.DefaultContainer;
030: import org.columba.core.io.StreamUtils;
031: import org.columba.core.xml.XmlElement;
032: import org.columba.mail.command.MailFolderCommandReference;
033: import org.columba.mail.composer.MessageBuilderHelper;
034: import org.columba.mail.config.AccountItem;
035: import org.columba.mail.config.MailConfig;
036: import org.columba.mail.folder.IMailbox;
037: import org.columba.mail.gui.composer.ComposerController;
038: import org.columba.mail.gui.composer.ComposerModel;
039: import org.columba.mail.gui.composer.util.QuoteFilterInputStream;
040: import org.columba.mail.gui.util.AddressListRenderer;
041: import org.columba.mail.parser.text.HtmlParser;
042: import org.columba.mail.util.MailResourceLoader;
043: import org.columba.ristretto.coder.Base64DecoderInputStream;
044: import org.columba.ristretto.coder.CharsetDecoderInputStream;
045: import org.columba.ristretto.coder.QuotedPrintableDecoderInputStream;
046: import org.columba.ristretto.message.Address;
047: import org.columba.ristretto.message.BasicHeader;
048: import org.columba.ristretto.message.Header;
049: import org.columba.ristretto.message.MimeHeader;
050: import org.columba.ristretto.message.MimePart;
051: import org.columba.ristretto.message.MimeTree;
052:
053: /**
054: * Reply to message.
055: * <p>
056: * Bodytext is quoted.
057: *
058: * @author fdietz
059: */
060: public class ReplyCommand extends Command {
061:
062: /** JDK 1.4+ logging framework logger, used for logging. */
063: private static final Logger LOG = Logger
064: .getLogger("org.columba.mail.gui.composer.command");
065:
066: protected final String[] headerfields = new String[] { "Subject",
067: "Date", "From", "To", "Reply-To", "Message-ID",
068: "In-Reply-To", "References" };
069:
070: protected ComposerController controller;
071:
072: protected ComposerModel model;
073:
074: /**
075: * Constructor for ReplyCommand.
076: *
077: * @param frameMediator
078: * @param references
079: */
080: public ReplyCommand(ICommandReference reference) {
081: super (reference);
082: }
083:
084: public void updateGUI() throws Exception {
085: // open composer frame
086: controller = new ComposerController();
087: new DefaultContainer(controller);
088:
089: // apply model
090: controller.setComposerModel(model);
091:
092: // model->view update
093: controller.updateComponents(true);
094:
095: // Set the focus to the editor pane and set cursor to the top
096: controller.getCurrentEditor().getViewUIComponent()
097: .requestFocus();
098: controller.getCurrentEditor().getViewUIComponent()
099: .moveCaretPosition(0);
100: controller.getCurrentEditor().getViewUIComponent().select(0, 0);
101: }
102:
103: public void execute(IWorkerStatusController worker)
104: throws Exception {
105: // create composer model
106: model = new ComposerModel();
107:
108: // get selected folder
109: IMailbox folder = (IMailbox) ((MailFolderCommandReference) getReference())
110: .getSourceFolder();
111:
112: // get first selected message
113: Object[] uids = ((MailFolderCommandReference) getReference())
114: .getUids();
115:
116: // ->set source reference in composermodel
117: // when replying this is the original sender's message
118: // you selected and replied to
119: MailFolderCommandReference ref = new MailFolderCommandReference(
120: folder, uids);
121: model.setSourceReference(ref);
122:
123: // setup to, references and account
124: initHeader(folder, uids);
125:
126: // get mimeparts
127: MimeTree mimePartTree = folder.getMimePartTree(uids[0]);
128:
129: XmlElement html = MailConfig.getInstance()
130: .getMainFrameOptionsConfig().getRoot().getElement(
131: "/options/html");
132:
133: // Which Bodypart shall be shown? (html/plain)
134: MimePart bodyPart = null;
135:
136: if (Boolean.valueOf(html.getAttribute("prefer")).booleanValue()) {
137: bodyPart = mimePartTree.getFirstTextPart("html");
138: } else {
139: bodyPart = mimePartTree.getFirstTextPart("plain");
140: }
141:
142: if (bodyPart != null) {
143: // setup charset and html
144: initMimeHeader(bodyPart);
145:
146: Integer[] address = bodyPart.getAddress();
147:
148: String quotedBodyText = createQuotedBody(bodyPart
149: .getHeader(), folder, uids, address);
150:
151: // debug output
152: LOG.fine("Quoted body text:\n" + quotedBodyText);
153:
154: model.setBodyText(quotedBodyText);
155: }
156: }
157:
158: protected void initMimeHeader(MimePart bodyPart) {
159: MimeHeader bodyHeader = bodyPart.getHeader();
160:
161: if (bodyHeader.getMimeType().getSubtype().equals("html")) {
162: model.setHtml(true);
163: } else {
164: model.setHtml(false);
165: }
166:
167: // Select the charset of the original message
168: String charset = bodyHeader.getContentParameter("charset");
169:
170: if (charset != null) {
171: try {
172: model.setCharset(Charset.forName(charset));
173: } catch (UnsupportedCharsetException e) {
174: // Stick with the default charset
175: }
176: }
177: }
178:
179: protected void initHeader(IMailbox folder, Object[] uids)
180: throws Exception {
181: // get headerfields
182: Header header = folder.getHeaderFields(uids[0], headerfields);
183:
184: BasicHeader rfcHeader = new BasicHeader(header);
185:
186: // set subject
187: model.setSubject(MessageBuilderHelper
188: .createReplySubject(rfcHeader.getSubject()));
189:
190: // Use reply-to field if given, else use from
191: Address[] to = rfcHeader.getReplyTo();
192:
193: if (to.length == 0) {
194: to = new Address[] { rfcHeader.getFrom() };
195: }
196:
197: // Add addresses to the addressbook
198: MessageBuilderHelper.addAddressesToAddressbook(to);
199: model.setTo(to);
200:
201: // create In-Reply-To:, References: headerfields
202: MessageBuilderHelper
203: .createMailingListHeaderItems(header, model);
204:
205: // select the account this mail was received from
206: Integer accountUid = (Integer) folder.getAttribute(uids[0],
207: "columba.accountuid");
208: AccountItem accountItem = MessageBuilderHelper
209: .getAccountItem(accountUid);
210: model.setAccountItem(accountItem);
211: }
212:
213: protected String createQuotedBody(MimeHeader header,
214: IMailbox folder, Object[] uids, Integer[] address)
215: throws IOException, Exception {
216: InputStream bodyStream = folder.getMimePartBodyStream(uids[0],
217: address);
218:
219: // Do decoding stuff
220: switch (header.getContentTransferEncoding()) {
221: case MimeHeader.QUOTED_PRINTABLE: {
222: bodyStream = new QuotedPrintableDecoderInputStream(
223: bodyStream);
224: break;
225: }
226:
227: case MimeHeader.BASE64: {
228: bodyStream = new Base64DecoderInputStream(bodyStream);
229: }
230: }
231: String charset = header.getContentParameter("charset");
232: if (charset != null) {
233: try {
234: bodyStream = new CharsetDecoderInputStream(bodyStream,
235: Charset.forName(charset));
236: } catch (UnsupportedCharsetException e) {
237: // Stick with the default charset
238: }
239: }
240:
241: String quotedBody;
242: // Quote original message - different methods for text and html
243: if (model.isHtml()) {
244: // Html: Insertion of text before and after original message
245: // get necessary headerfields
246: BasicHeader rfcHeader = new BasicHeader(folder
247: .getHeaderFields(uids[0], headerfields));
248: String subject = rfcHeader.getSubject();
249: String date = DateFormat.getDateTimeInstance(
250: DateFormat.LONG, DateFormat.MEDIUM).format(
251: rfcHeader.getDate());
252: String from = AddressListRenderer.renderToHTMLWithLinks(
253: new Address[] { rfcHeader.getFrom() }).toString();
254: String to = AddressListRenderer.renderToHTMLWithLinks(
255: rfcHeader.getTo()).toString();
256:
257: // build "quoted" message
258: StringBuffer buf = new StringBuffer();
259: buf.append("<html><body><p>");
260: buf.append(MailResourceLoader.getString("dialog",
261: "composer", "original_message_start"));
262: buf.append("<br>"
263: + MailResourceLoader.getString("header", "header",
264: "subject") + ": " + subject);
265: buf.append("<br>"
266: + MailResourceLoader.getString("header", "header",
267: "date") + ": " + date);
268: buf.append("<br>"
269: + MailResourceLoader.getString("header", "header",
270: "from") + ": " + from);
271: buf.append("<br>"
272: + MailResourceLoader.getString("header", "header",
273: "to") + ": " + to);
274: buf.append("</p>");
275: buf.append(HtmlParser.removeComments(// comments are not displayed
276: // correctly in composer
277: HtmlParser
278: .getHtmlBody(StreamUtils
279: .readCharacterStream(bodyStream)
280: .toString())));
281: buf.append("<p>");
282: buf.append(MailResourceLoader.getString("dialog",
283: "composer", "original_message_end"));
284: buf.append("</p></body></html>");
285:
286: quotedBody = buf.toString();
287: } else {
288: // Text: Addition of > before each line
289: quotedBody = StreamUtils.readCharacterStream(
290: new QuoteFilterInputStream(bodyStream)).toString();
291: }
292:
293: bodyStream.close();
294: return quotedBody;
295: }
296:
297: /**
298: * Get composer model.
299: * <p>
300: * Needed for testcases.
301: *
302: * @return Returns the model.
303: */
304: public ComposerModel getModel() {
305: return model;
306: }
307: }
|