KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > mail > gui > message > filter > PGPMessageFilter


1 // The contents of this file are subject to the Mozilla Public License Version
2
// 1.1
3
//(the "License"); you may not use this file except in compliance with the
4
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
5
//
6
//Software distributed under the License is distributed on an "AS IS" basis,
7
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
8
//for the specific language governing rights and
9
//limitations under the License.
10
//
11
//The Original Code is "The Columba Project"
12
//
13
//The Initial Developers of the Original Code are Frederik Dietz and Timo
14
// Stich.
15
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
16
//
17
//All Rights Reserved.
18
package org.columba.mail.gui.message.filter;
19
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25
26 import javax.swing.SwingUtilities JavaDoc;
27
28 import org.columba.core.io.StreamUtils;
29 import org.columba.core.logging.Logging;
30 import org.columba.mail.command.IMailFolderCommandReference;
31 import org.columba.mail.command.MailFolderCommandReference;
32 import org.columba.mail.config.AccountItem;
33 import org.columba.mail.config.MailConfig;
34 import org.columba.mail.config.SecurityItem;
35 import org.columba.mail.folder.IMailbox;
36 import org.columba.mail.gui.frame.MailFrameMediator;
37 import org.columba.mail.gui.message.IMessageController;
38 import org.columba.mail.gui.message.viewer.SecurityStatusViewer;
39 import org.columba.mail.message.ColumbaMessage;
40 import org.columba.mail.pgp.JSCFController;
41 import org.columba.mail.pgp.PGPPassChecker;
42 import org.columba.mail.util.MailResourceLoader;
43 import org.columba.ristretto.io.CharSequenceSource;
44 import org.columba.ristretto.io.Source;
45 import org.columba.ristretto.message.MimeHeader;
46 import org.columba.ristretto.message.MimePart;
47 import org.columba.ristretto.message.MimeTree;
48 import org.columba.ristretto.message.MimeType;
49 import org.columba.ristretto.parser.BodyParser;
50 import org.columba.ristretto.parser.HeaderParser;
51 import org.columba.ristretto.parser.ParserException;
52 import org.waffel.jscf.JSCFConnection;
53 import org.waffel.jscf.JSCFException;
54 import org.waffel.jscf.JSCFResultSet;
55 import org.waffel.jscf.JSCFStatement;
56
57 /**
58  * Filter decrypting and verifying messages.
59  * <p>
60  * A {@link SecurityStatusEvent}is used to notify all listeners.
61  * <p>
62  * {@link SecurityStatusViewer}is currently the only listener. In the future a
63  * status icon will be added to the message header, too.
64  * <p>
65  *
66  * @author fdietz
67  *
68  */

69 public class PGPMessageFilter extends AbstractFilter {
70
71     private static final java.util.logging.Logger JavaDoc LOG = java.util.logging.Logger
72             .getLogger("org.columba.mail.gui.message.filter");
73
74     private MimeTree mimePartTree;
75
76     private int pgpMode = SecurityStatusViewer.NOOP;
77
78     private String JavaDoc pgpMessage = "";
79
80     private ColumbaMessage message;
81
82     private List JavaDoc listeners;
83
84     public PGPMessageFilter(MailFrameMediator mediator,
85             IMessageController messageController) {
86         super(mediator, messageController);
87
88         listeners = new ArrayList JavaDoc();
89     }
90
91     public void addSecurityStatusListener(SecurityStatusListener l) {
92         listeners.add(l);
93     }
94
95     public void fireSecurityStatusEvent(SecurityStatusEvent ev) {
96         final SecurityStatusEvent event = ev;
97
98         Iterator JavaDoc it = listeners.iterator();
99         while (it.hasNext()) {
100             final SecurityStatusListener l = (SecurityStatusListener) it.next();
101             Runnable JavaDoc doWorkRunnable = new Runnable JavaDoc() {
102                 public void run() {
103                     l.statusUpdate(event);
104
105                 }
106             };
107             SwingUtilities.invokeLater(doWorkRunnable);
108
109         }
110     }
111
112     /**
113      * @see org.columba.mail.gui.message.filter.Filter#filter(org.columba.mail.folder.Folder,
114      * java.lang.Object)
115      */

116     public IMailFolderCommandReference filter(IMailbox folder, Object JavaDoc uid)
117             throws Exception JavaDoc {
118
119         mimePartTree = folder.getMimePartTree(uid);
120
121         // Check if the message still exists
122
// or has been moved by e.g. a filter
123
if (mimePartTree == null)
124             return null;
125
126         // TODO (@author waffel): encrypt AND sign dosN#t work. The message is
127
// always only
128
// encrypted. We need a function that knows, here
129
// is an encrypted AND signed Message. Thus first encyrpt and then
130
// verifySign the message
131
// if this message is signed/encrypted we have to use
132
// GnuPG to extract the decrypted bodypart
133
// - multipart/encrypted
134
// - multipart/signed
135
MimeType firstPartMimeType = mimePartTree.getRootMimeNode().getHeader()
136                 .getMimeType();
137
138         AccountItem defAccount = MailConfig.getInstance().getAccountList()
139                 .getDefaultAccount();
140
141         boolean pgpActive = false;
142
143         if (defAccount != null) {
144             SecurityItem pgpItem = defAccount.getPGPItem();
145             LOG.fine("pgp activated: " + pgpItem.get("enabled"));
146             pgpActive = new Boolean JavaDoc((pgpItem.get("enabled"))).booleanValue();
147         }
148
149         IMailFolderCommandReference result = null;
150         LOG.fine("pgp is true");
151         if (firstPartMimeType.getSubtype().equals("signed")) {
152             result = verify(folder, uid, pgpActive);
153
154         } else if (firstPartMimeType.getSubtype().equals("encrypted")) {
155             LOG.fine("Mimepart type encrypted found");
156             result = decrypt(folder, uid, pgpActive);
157
158         } else {
159             pgpMode = SecurityStatusViewer.NOOP;
160         }
161
162         // notify listeners
163
fireSecurityStatusEvent(new SecurityStatusEvent(this, pgpMessage,
164                 pgpMode));
165
166         return result;
167     }
168
169     /**
170      * Decrypt message.
171      *
172      * @param folder
173      * selected folder
174      * @param uid
175      * selected message UID
176      * @throws Exception
177      * @throws IOException
178      */

179     private IMailFolderCommandReference decrypt(IMailbox folder, Object JavaDoc uid,
180             boolean pgpActive) throws Exception JavaDoc, IOException JavaDoc {
181         InputStream JavaDoc decryptedStream = null;
182         LOG.fine("start decrypting");
183         if (!pgpActive) {
184             pgpMessage = "";
185             pgpMode = SecurityStatusViewer.NO_KEY;
186         } else {
187             
188
189             MimePart encryptedMultipart = mimePartTree.getRootMimeNode();
190
191             // the second child must be the encrypted message
192
InputStream JavaDoc encryptedPart = folder.getMimePartBodyStream(uid,
193                     encryptedMultipart.getChild(1).getAddress());
194
195             try {
196                 JSCFController controller = JSCFController.getInstance();
197                 JSCFConnection con = controller.getConnection();
198                 LOG.fine("new JSCConnection");
199                 JSCFStatement stmt = con.createStatement();
200                 LOG.fine("new Statement");
201                 PGPPassChecker passCheck = PGPPassChecker.getInstance();
202                 boolean check = passCheck.checkPassphrase(con);
203                 LOG.fine("after pass check, check is " + check);
204                 if (!check) {
205                     pgpMode = SecurityStatusViewer.DECRYPTION_FAILURE;
206                     // TODO (@author fdietz): make i18n!
207
pgpMessage = "wrong passphrase";
208                     return null;
209                 }
210                 LOG.fine("encrypted is != null?: " + (encryptedPart != null));
211                 JSCFResultSet res = stmt.executeDecrypt(encryptedPart);
212                 LOG.fine("after calling decrypting");
213                 if (res.isError()) {
214                     LOG.fine("the result set contains errors ");
215                     pgpMode = SecurityStatusViewer.DECRYPTION_FAILURE;
216                     pgpMessage = StreamUtils.readCharacterStream(
217                             res.getErrorStream()).toString();
218                     LOG.fine("error message: " + pgpMessage);
219                     decryptedStream = res.getResultStream();
220                     // return null;
221
} else {
222                     decryptedStream = res.getResultStream();
223                     pgpMode = SecurityStatusViewer.DECRYPTION_SUCCESS;
224                 }
225             } catch (JSCFException e) {
226                 e.printStackTrace();
227                 LOG.severe(e.getMessage());
228                 pgpMode = SecurityStatusViewer.DECRYPTION_FAILURE;
229                 pgpMessage = e.getMessage();
230
231                 // just show the encrypted raw message
232
decryptedStream = encryptedPart;
233             }
234         }
235         try {
236             LOG.fine("decrypted Stream is: " + decryptedStream);
237             CharSequence JavaDoc decryptedBodyPart = "";
238             // if the pgp mode is active we should get the decrypted part
239
if (pgpActive) {
240                 // TODO (@author fdietz): should be removed if we only use
241
// Streams!
242
decryptedBodyPart = StreamUtils
243                         .readCharacterStream(decryptedStream);
244                 // check if the returned String is has a length != 0
245
if (decryptedBodyPart.length() == 0) {
246                     LOG
247                             .fine("decrypted body part has a 0 length ... fixing it");
248                     decryptedBodyPart = new StringBuffer JavaDoc(
249                             "Content-Type: text/plain; charset=\"ISO-8859-15\"\n\n");
250                 }
251             }
252             // else we set the body to the i18n String
253
else {
254                 decryptedBodyPart = new StringBuffer JavaDoc(
255                         "Content-Type: text/plain; charset=\"ISO-8859-15\"\n\n"
256                                 + MailResourceLoader.getString("menu",
257                                         "mainframe",
258                                         "security_decrypt_encrypted") + "\n");
259             }
260             LOG.fine("the decrypted Body part: " + decryptedBodyPart);
261             // construct new Message from decrypted string
262
message = new ColumbaMessage(folder.getAllHeaderFields(uid));
263
264             Source decryptedSource = new CharSequenceSource(decryptedBodyPart);
265             MimeHeader mimeHeader = new MimeHeader(HeaderParser
266                     .parse(decryptedSource));
267             mimePartTree = new MimeTree(BodyParser.parseMimePart(mimeHeader,
268                     decryptedSource));
269             message.setMimePartTree(mimePartTree);
270
271             InputStream JavaDoc messageSourceStream = folder
272                     .getMessageSourceStream(uid);
273             message.setSource(new CharSequenceSource(StreamUtils
274                     .readCharacterStream(messageSourceStream)));
275             messageSourceStream.close();
276
277             // call AbstractFilter to do the tricky part
278
return filter(folder, uid, message);
279             // header = (ColumbaHeader) message.getHeaderInterface();
280
} catch (ParserException e) {
281             e.printStackTrace();
282
283         } catch (IOException JavaDoc e) {
284             e.printStackTrace();
285
286         }
287
288         /*
289          * controlPart.close(); encryptedPart.close(); if (decryptedStream !=
290          * null) { decryptedStream.close(); }
291          */

292         return null;
293     }
294
295     /**
296      * Verify message.
297      *
298      * @param folder
299      * selected folder
300      * @param uid
301      * selected message UID
302      * @throws Exception
303      * @throws IOException
304      */

305     private MailFolderCommandReference verify(IMailbox folder, Object JavaDoc uid,
306             boolean pgpActive) throws Exception JavaDoc, IOException JavaDoc {
307         if (!pgpActive) {
308             pgpMessage = "";
309             pgpMode = SecurityStatusViewer.NO_KEY;
310             return null;
311         }
312         MimePart signedMultipart = mimePartTree.getRootMimeNode();
313
314         // the first child must be the signed part
315
InputStream JavaDoc signedPart = folder.getMimePartSourceStream(uid,
316                 signedMultipart.getChild(0).getAddress());
317
318         // the second child must be the pgp-signature
319
InputStream JavaDoc signature = folder.getMimePartBodyStream(uid,
320                 signedMultipart.getChild(1).getAddress());
321
322         try {
323             JSCFController controller = JSCFController.getInstance();
324             JSCFConnection con = controller.getConnection();
325             JSCFStatement stmt = con.createStatement();
326             String JavaDoc micalg = signedMultipart.getHeader().getContentParameter(
327                     "micalg").substring(4);
328             JSCFResultSet res = stmt.executeVerify(signedPart, signature,
329                     micalg);
330             if (res.isError()) {
331                 pgpMode = SecurityStatusViewer.VERIFICATION_FAILURE;
332                 pgpMessage = StreamUtils.readCharacterStream(
333                         res.getErrorStream()).toString();
334             } else {
335                 pgpMode = SecurityStatusViewer.VERIFICATION_SUCCESS;
336                 pgpMessage = StreamUtils.readCharacterStream(
337                         res.getResultStream()).toString();
338             }
339
340         } catch (JSCFException e) {
341
342             if (Logging.DEBUG)
343                 e.printStackTrace();
344
345             pgpMode = SecurityStatusViewer.VERIFICATION_FAILURE;
346             pgpMessage = e.getMessage();
347             // something really got wrong here -> show error dialog
348
// JOptionPane.showMessageDialog(null, e.getMessage());
349

350             pgpMode = SecurityStatusViewer.VERIFICATION_FAILURE;
351         }
352
353         signedPart.close();
354         signature.close();
355
356         return null;
357     }
358
359 }
Popular Tags