Author: bago
Date: Tue Oct  6 17:56:14 2009
New Revision: 822379

URL: http://svn.apache.org/viewvc?rev=822379&view=rev
Log:
First draft of jDKIM mailets: DKIMSign and ConvertTo7Bit.

Added:
    james/jdkim/trunk/mailets/src/
    james/jdkim/trunk/mailets/src/main/
    james/jdkim/trunk/mailets/src/main/java/
    james/jdkim/trunk/mailets/src/main/java/org/
    james/jdkim/trunk/mailets/src/main/java/org/apache/
    james/jdkim/trunk/mailets/src/main/java/org/apache/james/
    james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/
    james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/
    
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/ConvertTo7Bit.java
   (with props)
    
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
   (with props)
    
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/HeaderSkippingOutputStream.java
   (with props)

Added: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/ConvertTo7Bit.java
URL: 
http://svn.apache.org/viewvc/james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/ConvertTo7Bit.java?rev=822379&view=auto
==============================================================================
--- 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/ConvertTo7Bit.java
 (added)
+++ 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/ConvertTo7Bit.java
 Tue Oct  6 17:56:14 2009
@@ -0,0 +1,75 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jdkim.mailets;
+
+import java.io.IOException;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimePart;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMailet;
+
+/**
+ * Make sure the message stream is 7bit.
+ * Every 8bit part is encoded to quoted-printable or base64 and the message is 
saved.
+ */
+public class ConvertTo7Bit extends GenericMailet {
+
+       public void service(Mail mail) throws MessagingException {
+               MimeMessage message = mail.getMessage();
+               try {
+                       convertTo7Bit(message);
+                       message.saveChanges();
+               } catch (IOException e) {
+                       throw new MessagingException("IOException converting 
message to 7bit: "+e.getMessage(), e);
+               }
+       }
+
+    /**
+     * Converts a message to 7 bit.
+     * 
+     * @param part
+     */
+    private void convertTo7Bit(MimePart part) throws MessagingException, 
IOException {
+        if (part.isMimeType("multipart/*")) {
+            MimeMultipart parts = (MimeMultipart) part.getContent();
+            int count = parts.getCount();
+            for (int i = 0; i < count; i++) {
+                convertTo7Bit((MimePart)parts.getBodyPart(i));
+            }
+        } else if ("8bit".equals(part.getEncoding())) {
+            // The content may already be in encoded the form (likely with 
mail created from a
+            // stream).  In that case, just changing the encoding to 
quoted-printable will mangle 
+            // the result when this is transmitted.  We must first convert the 
content into its 
+            // native format, set it back, and only THEN set the transfer 
encoding to force the 
+            // content to be encoded appropriately.
+            
+            // if the part doesn't contain text it will be base64 encoded.
+            String contentTransferEncoding = part.isMimeType("text/*") ? 
"quoted-printable" : "base64";
+            part.setContent(part.getContent(), part.getContentType()); 
+            part.setHeader("Content-Transfer-Encoding", 
contentTransferEncoding);
+            part.addHeader("X-MIME-Autoconverted", "from 8bit to 
"+contentTransferEncoding+" by "+getMailetContext().getServerInfo());
+        }
+    }
+
+}

Propchange: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/ConvertTo7Bit.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/ConvertTo7Bit.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
URL: 
http://svn.apache.org/viewvc/james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java?rev=822379&view=auto
==============================================================================
--- 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
 (added)
+++ 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
 Tue Oct  6 17:56:14 2009
@@ -0,0 +1,160 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jdkim.mailets;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.jdkim.BodyHashJob;
+import org.apache.james.jdkim.DKIMCommon;
+import org.apache.james.jdkim.DKIMSigner;
+import org.apache.james.jdkim.Headers;
+import org.apache.james.jdkim.PermFailException;
+import org.apache.james.jdkim.SignatureRecord;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMailet;
+
+/**
+ * This mailet sign a message using the DKIM protocol
+ * 
+ * Sample configuration:
+ * <pre><code>
+ * &lt;mailet match="All" class="DKIMSign"&gt;
+ *   &lt;signatureTemplate&gt;v=1; s=selector; d=example.com; 
h=from:to:received:received; a=rsa-sha256; bh=; b=;&lt;/signatureTemplate&gt;
+ *   
&lt;privateKey&gt;MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANgNpgpfPBVjCpZsuGa4nrppMA3zCYNH6t8cTwd+eRI5rHSgihMznOq5mtMujfTzvRgx9jPHB8HqP83PdB3CtQP+3RgxgmJQrJYmcIp9lcckEn7J9Eevuhb5RbdxWj0IbZsF8jGwifBh7XvmD1SPKe0mla56p0QijVzZuG/0ynrpAgMBAAECgYEAjxdzCdmLRKrk3z3AX6AU2GdEQWjeuwkNoJjyKod0DkMOWevdptv/KGKnDQj/UeWALp8gbah7Fc5cVaX5RKCpG3WRO32NeFUUTGDyY2SjZR6UDAW2yXwJGNVxhA5x514f9Yz+ZeODbBSqpl6cGaUqUPq81vvSMUl5VoMn/ufuPwECQQD02QfYPhmCP8g4BVhxxlgfvj5WA7R7tWRSNCT3C0naPpwaono9+PSuhUgxRbOgFvxh8StHyXomdVBt/LzeAl6JAkEA4eTejDsmMCfxe47JnHbgpxNphYpSQBB9FZgMUU5hAXgpX3EtIS3JxjSSOx3EYoO51ZywBOWUXNcMJAXoNM0hYQJAQDnZ4/BOMqtWctN8IsQbg6Acq+Vm53hqa2HAPIlagwQfYKE0HaN7U3gkusAE4T6GT466gqcoAoSNZ3x/cmD+uQJAePyZCaiAephaKSA/8VJmXnXyNXjxNqjeJduq9T0yjZPrLNg0IKoigMsVax41WcJNnRBv4h+IR/VR5lVXmjgn4QJANq02dLdX2phQqOP+Ss1EP9TT7t6HxLbKUuoPdGVKf0q1gZEyAC1Re2I4SLMEfpt3+ivMj1X2zDzIHP5mogfblA==&lt;/privateKey&gt;
+ * &lt;/mailet&gt;
+ * </code></pre>
+ *
+ * @version CVS $Revision: 713949 $ $Date: 2008-11-14 08:40:21 +0100 (ven, 14 
nov 2008) $
+ * @since 2.2.0
+ */
+public class DKIMSign extends GenericMailet {
+
+       /**
+        * An adapter to let DKIMSigner read headers from MimeMessage
+        */
+       private final class MimeMessageHeaders implements Headers {
+
+               private Map/* String, String */ headers;
+               private List/* String */ fields;
+
+               public MimeMessageHeaders(MimeMessage message) throws 
MessagingException {
+                       headers = new HashMap();
+                       fields = new LinkedList();
+                       for (Enumeration e = message.getAllHeaderLines(); 
e.hasMoreElements(); ) {
+                               String head = (String) e.nextElement();
+                               int p = head.indexOf(':');
+                               if (p <= 0) throw new MessagingException("Bad 
header line: "+head);
+                               String headerName = head.substring(0, p).trim();
+                               String headerNameLC = headerName.toLowerCase();
+                               fields.add(headerName);
+                               List/* String */ strings = (List) 
headers.get(headerNameLC);
+                               if (strings == null) {
+                                       strings = new LinkedList();
+                                       headers.put(headerNameLC, strings);
+                               }
+                               strings.add(head);
+                       }
+               }
+
+               public List getFields() {
+                       return fields;
+               }
+
+               public List getFields(String name) {
+                       return (List) headers.get(name);
+               }
+       }
+
+       private String signatureTemplate;
+       private PrivateKey privateKey;
+
+       public void init() throws MessagingException {
+               signatureTemplate = getInitParameter("signatureTemplate");
+               String privateKeyString = getInitParameter("privateKey");
+           try {
+                       privateKey = DKIMSigner.getPrivateKey(privateKeyString);
+               } catch (NoSuchAlgorithmException e) {
+                       throw new MessagingException("Unknown private key 
algorythm: "+e.getMessage(), e);
+               } catch (InvalidKeySpecException e) {
+                       throw new MessagingException("PrivateKey should be in 
base64 encoded PKCS8 (der) format: "+e.getMessage(), e);
+               }
+       }
+
+       public void service(Mail mail) throws MessagingException {
+               DKIMSigner signer = new DKIMSigner(signatureTemplate, 
privateKey);
+               SignatureRecord signRecord = 
signer.newSignatureRecord(signatureTemplate);
+               try {
+                       BodyHashJob bhj = 
DKIMCommon.prepareBodyHashJob(signRecord, signatureTemplate);
+                       MimeMessage message = mail.getMessage();
+                       Headers headers = new MimeMessageHeaders(message);
+                       try {
+                               message.writeTo(new 
HeaderSkippingOutputStream(bhj.getOutputStream()));
+                               bhj.getOutputStream().close();
+                       } catch (IOException e) {
+                               throw new MessagingException("Exception 
calculating bodyhash: "+e.getMessage(), e);
+                       }
+                       String signatureHeader = signer.sign(headers, bhj);
+                       // Unfortunately JavaMail does not give us a method to 
add headers on top.
+                       // message.addHeaderLine(signatureHeader);
+                       prependHeader(message, signatureHeader);
+               } catch (NoSuchAlgorithmException e) {
+                       throw new MessagingException("Unknown algorythm: 
"+e.getMessage(), e);
+               } catch (PermFailException e) {
+                       throw new MessagingException("PermFail while signing: 
"+e.getMessage(), e);
+               }
+
+       }
+
+       private void prependHeader(MimeMessage message, String signatureHeader)
+                       throws MessagingException {
+               List prevHeader = new LinkedList();
+               // read all the headers
+               for (Enumeration e = message.getAllHeaderLines(); 
e.hasMoreElements(); ) { 
+                       String headerLine = (String) e.nextElement();
+                       prevHeader.add(headerLine);
+               }
+               // remove all the headers
+               for (Enumeration e = message.getAllHeaders(); 
e.hasMoreElements(); ) {
+                       Header header = (Header) e.nextElement();
+                       message.removeHeader(header.getName());
+               }
+               // add our header
+               message.addHeaderLine(signatureHeader);
+               // add the remaining headers using "addHeaderLine" that won't 
alter the insertion order.
+               for (Iterator i = prevHeader.iterator(); i.hasNext(); ) {
+                       String header = (String) i.next();
+                       message.addHeaderLine(header);
+               }
+       }
+
+}

Propchange: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/DKIMSign.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/HeaderSkippingOutputStream.java
URL: 
http://svn.apache.org/viewvc/james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/HeaderSkippingOutputStream.java?rev=822379&view=auto
==============================================================================
--- 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/HeaderSkippingOutputStream.java
 (added)
+++ 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/HeaderSkippingOutputStream.java
 Tue Oct  6 17:56:14 2009
@@ -0,0 +1,69 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jdkim.mailets;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Ignore writes until the given sequence is found.
+ */
+public class HeaderSkippingOutputStream extends FilterOutputStream {
+       
+       boolean inHeaders = true;
+       byte[] skipTo = "\r\n\r\n".getBytes();
+       int pos = 0;
+
+       public HeaderSkippingOutputStream(OutputStream out) {
+               super(out);
+       }
+
+       public void write(byte[] b, int off, int len) throws IOException {
+               if (inHeaders) {
+                       for (int i = off; i < off+len; i++) {
+                               if (b[i] == skipTo[pos]) {
+                                       pos++;
+                                       if (pos == skipTo.length) {
+                                               inHeaders = false;
+                                               if (len-i-1 > 0) out.write(b, 
i+1, len-i-1);
+                                               break;
+                                       }
+                               } else pos = 0;
+                       }
+               } else {
+                       out.write(b, off, len);
+               }
+       }
+
+       public void write(int b) throws IOException {
+               if (inHeaders) {
+                       if (skipTo[pos] == b) {
+                               pos++;
+                               if (pos == skipTo.length) inHeaders = false;
+                       } else pos = 0;
+               } else {
+                       out.write(b);
+               }
+       }
+       
+       
+
+}

Propchange: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/HeaderSkippingOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
james/jdkim/trunk/mailets/src/main/java/org/apache/james/jdkim/mailets/HeaderSkippingOutputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org
For additional commands, e-mail: server-dev-h...@james.apache.org

Reply via email to