/* * Copyright 2008 The Apache Software Foundation or its licensors, as * applicable. * * Licensed 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. * * A licence was granted to the ASF by Florian Sager on 30 November 2008 */ package de.agitos.dkim; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import com.sun.mail.smtp.SMTPMessage; import com.sun.mail.util.LineOutputStream; /* * Extension of SMTPMessage for the inclusion of a DKIM signature. * * @author Florian Sager, http://www.agitos.de, 22.11.2008 */ public class SMTPDKIMMessage extends SMTPMessage { private DKIMSigner signer; private String encodedBody; public SMTPDKIMMessage(Session session, DKIMSigner signer) { super(session); this.signer = signer; } public SMTPDKIMMessage(MimeMessage message, DKIMSigner signer) throws MessagingException { super(message); this.signer = signer; } public SMTPDKIMMessage(Session session, InputStream is, DKIMSigner signer) throws MessagingException { super(session, is); this.signer = signer; } /** * Output the message as an RFC 822 format stream, without * specified headers. If the <code>saved</code> flag is not set, * the <code>saveChanges</code> method is called. * If the <code>modified</code> flag is not * set and the <code>content</code> array is not null, the * <code>content</code> array is written directly, after * writing the appropriate message headers. * * @exception javax.mail.MessagingException * @exception IOException if an error occurs writing to the stream * or if an error is generated by the * javax.activation layer. * @see javax.activation.DataHandler#writeTo * * This method enhances the JavaMail method MimeMessage.writeTo(OutputStream os String[] ignoreList); * See the according Sun Licence, this contribution is CDDL. */ public void writeTo(OutputStream os, String[] ignoreList) throws IOException, MessagingException { ByteArrayOutputStream osBody = new ByteArrayOutputStream(); // Inside saveChanges() it is assured that content encodings are set in all parts of the body if (!saved) { saveChanges(); } // First, write out the body to the body buffer if (modified) { // Finally, the content. Encode if required. // XXX: May need to account for ESMTP ? OutputStream osEncoding = MimeUtility.encode(osBody, this.getEncoding()); this.getDataHandler().writeTo(osEncoding); osEncoding.flush(); // Needed to complete encoding } else { // Else, the content is untouched, so we can just output it // Finally, the content. if (content == null) { // call getContentStream to give subclass a chance to // provide the data on demand InputStream is = getContentStream(); // now copy the data to the output stream byte[] buf = new byte[8192]; int len; while ((len = is.read(buf)) > 0) osBody.write(buf, 0, len); is.close(); buf = null; } else { osBody.write(content); } osBody.flush(); } encodedBody = osBody.toString(); // Second, sign the message String signatureHeaderLine; try { signatureHeaderLine = signer.sign(this); } catch (Exception e) { throw new MessagingException(e.getLocalizedMessage(), e); } // Third, write out the header to the header buffer LineOutputStream los = new LineOutputStream(os); // set generated signature to the top los.writeln(signatureHeaderLine); Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList); while (hdrLines.hasMoreElements()) { los.writeln((String) hdrLines.nextElement()); } // The CRLF separator between header and content los.writeln(); // Send signed mail to waiting DATA command os.write(osBody.toByteArray()); os.flush(); } public String getEncodedBody() { return encodedBody; } public void setEncodedBody(String encodedBody) { this.encodedBody = encodedBody; } // Don't allow to switch to 8-bit MIME, instead 7-bit ascii should be kept // 'cause in forwarding scenarios a change to Content-Transfer-Encoding // to 7-bit ascii breaks DKIM signatures public void setAllow8bitMIME(boolean allow) { // super.setAllow8bitMIME(false); } }