Author: norman Date: Sat Dec 31 19:14:11 2011 New Revision: 1226180 URL: http://svn.apache.org/viewvc?rev=1226180&view=rev Log: Add abstract base classes for header and body filtering / modification. See PROTOCOLS-77
Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/AbstractAddHeadersFilter.java (with props) james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/SeparatingDataLineFilter.java (with props) Modified: james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/ReceivedDataLineFilter.java Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/AbstractAddHeadersFilter.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/AbstractAddHeadersFilter.java?rev=1226180&view=auto ============================================================================== --- james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/AbstractAddHeadersFilter.java (added) +++ james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/AbstractAddHeadersFilter.java Sat Dec 31 19:14:11 2011 @@ -0,0 +1,126 @@ +/**************************************************************** + * 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.protocols.smtp.core; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.james.protocols.api.ProtocolSession.State; +import org.apache.james.protocols.api.Response; +import org.apache.james.protocols.api.handler.LineHandler; +import org.apache.james.protocols.smtp.SMTPSession; + +public abstract class AbstractAddHeadersFilter extends SeparatingDataLineFilter{ + + private static final AtomicInteger COUNTER = new AtomicInteger(0); + + private final String headersPrefixAdded = "HEADERS_PREFIX_ADDED" + COUNTER.incrementAndGet(); + + enum Location{ + Prefix, + Suffix + } + + /** + * Return the {@link Location} to add the headers in + * + * @return location + */ + protected abstract Location getLocation(); + + + @Override + protected Response onSeparatorLine(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { + if (getLocation() == Location.Suffix && session.getAttachment(headersPrefixAdded, State.Transaction) == null) { + session.setAttachment(headersPrefixAdded, Boolean.TRUE, State.Transaction); + return addHeaders(session, line, next); + } + return super.onSeparatorLine(session, line, next); + } + + @Override + protected Response onHeadersLine(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { + if (getLocation() == Location.Prefix) { + return addHeaders(session, line, next); + } + return super.onHeadersLine(session, line, next); + } + + private Response addHeaders(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { + Response response; + for (Header header: headers(session)) { + response = header.transferTo(session, next); + if (response != null) { + return response; + } + } + return next.onLine(session, line); + } + + /** + * Return the {@link Header}'s to operate on + * + * @return headers + */ + protected abstract Collection<Header> headers(SMTPSession session); + + public final static class Header { + public final String name; + public final String value; + + public Header(String name, String value) { + this.name = name; + this.value = value; + } + + public String toString() { + return name + ": " + value + "\r\n"; + } + + /** + * Transfer the content of the {@link Header} to the given {@link LineHandler}. + * + * This is done for each line of the {@link Header} until the end is reached or the {@link LineHandler#onLine(org.apache.james.protocols.api.ProtocolSession, ByteBuffer)} + * return <code>non-null</code> + * + * @param session + * @param handler + * @return response + */ + public Response transferTo(SMTPSession session, LineHandler<SMTPSession> handler) { + try { + String[] lines = toString().split("\r\n"); + Response response = null; + for (int i = 0; i < lines.length; i++) { + response = handler.onLine(session, ByteBuffer.wrap((lines[i] + "\r\n").getBytes("US-ASCII"))); + if (response != null) { + break; + } + } + return response; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("NO US-ASCII ?", e); + } + } + } + + +} Propchange: james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/AbstractAddHeadersFilter.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/ReceivedDataLineFilter.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/ReceivedDataLineFilter.java?rev=1226180&r1=1226179&r2=1226180&view=diff ============================================================================== --- james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/ReceivedDataLineFilter.java (original) +++ james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/ReceivedDataLineFilter.java Sat Dec 31 19:14:11 2011 @@ -18,24 +18,19 @@ ****************************************************************/ package org.apache.james.protocols.smtp.core; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Locale; import org.apache.james.protocols.api.ProtocolSession.State; -import org.apache.james.protocols.api.Response; -import org.apache.james.protocols.api.handler.LineHandler; import org.apache.james.protocols.smtp.MailAddress; import org.apache.james.protocols.smtp.SMTPSession; -public class ReceivedDataLineFilter implements DataLineFilter { - - private final static String CHARSET = "US-ASCII"; +public class ReceivedDataLineFilter extends AbstractAddHeadersFilter { private static final ThreadLocal<DateFormat> DATEFORMAT = new ThreadLocal<DateFormat>() { @@ -47,91 +42,6 @@ public class ReceivedDataLineFilter impl }; - private final static String HEADERS_WRITTEN = "HEADERS_WRITTEN"; - - - - /* - * (non-Javadoc) - * @see org.apache.james.protocols.smtp.core.DataLineFilter#onLine(org.apache.james.protocols.smtp.SMTPSession, java.nio.ByteBuffer, org.apache.james.protocols.api.handler.LineHandler) - */ - public Response onLine(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { - if (session.getAttachment(HEADERS_WRITTEN, State.Transaction) == null) { - Response response = addNewReceivedMailHeaders(session, next); - - session.setAttachment(HEADERS_WRITTEN, true, State.Transaction); - - if (response != null) { - return response; - } - } - Response resp = next.onLine(session, line); - return resp; - } - - @SuppressWarnings("unchecked") - private Response addNewReceivedMailHeaders(SMTPSession session, LineHandler<SMTPSession> next) { - try { - StringBuilder headerLineBuffer = new StringBuilder(); - - String heloMode = (String) session.getAttachment(SMTPSession.CURRENT_HELO_MODE, State.Connection); - String heloName = (String) session.getAttachment(SMTPSession.CURRENT_HELO_NAME, State.Connection); - - // Put our Received header first - headerLineBuffer.append("Received: from ").append(session.getRemoteAddress().getHostName()); - - if (heloName != null) { - headerLineBuffer.append(" (").append(heloMode).append(" ").append(heloName).append(") "); - } - - headerLineBuffer.append(" ([").append(session.getRemoteAddress().getAddress().getHostAddress()).append("])").append("\r\n"); - - Response response = next.onLine(session, ByteBuffer.wrap(headerLineBuffer.toString().getBytes(CHARSET))); - if (response != null) { - return response; - } - headerLineBuffer.delete(0, headerLineBuffer.length()); - - headerLineBuffer.append(" by ").append(session.getConfiguration().getHelloName()).append(" (").append(session.getConfiguration().getSoftwareName()).append(") with ").append(getServiceType(session, heloMode)); - - - headerLineBuffer.append(" ID ").append(session.getSessionID()); - - if (((Collection<?>) session.getAttachment(SMTPSession.RCPT_LIST, State.Transaction)).size() == 1) { - // Only indicate a recipient if they're the only recipient - // (prevents email address harvesting and large headers in - // bulk email) - headerLineBuffer.append("\r\n"); - next.onLine(session, ByteBuffer.wrap(headerLineBuffer.toString().getBytes(CHARSET))); - headerLineBuffer.delete(0, headerLineBuffer.length()); - - headerLineBuffer.delete(0, headerLineBuffer.length()); - headerLineBuffer.append(" for <").append(((List<MailAddress>) session.getAttachment(SMTPSession.RCPT_LIST, State.Transaction)).get(0).toString()).append(">;").append("\r\n"); - response = next.onLine(session, ByteBuffer.wrap(headerLineBuffer.toString().getBytes(CHARSET))); - - if (response != null) { - return response; - } - headerLineBuffer.delete(0, headerLineBuffer.length()); - headerLineBuffer.delete(0, headerLineBuffer.length()); - - } else { - // Put the ; on the end of the 'by' line - headerLineBuffer.append(";"); - headerLineBuffer.append("\r\n"); - - response = next.onLine(session, ByteBuffer.wrap(headerLineBuffer.toString().getBytes(CHARSET))); - if (response != null) { - return response; - } - headerLineBuffer.delete(0, headerLineBuffer.length()); - } - headerLineBuffer = null; - return next.onLine(session, ByteBuffer.wrap((" " + DATEFORMAT.get().format(new Date()) + "\r\n").getBytes(CHARSET))); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("No US-ASCII support ?"); - } - } /** @@ -161,4 +71,44 @@ public class ReceivedDataLineFilter impl return "SMTP"; } } + + @Override + protected Location getLocation() { + return Location.Prefix; + } + + @SuppressWarnings("unchecked") + @Override + protected Collection<Header> headers(SMTPSession session) { + StringBuilder headerLineBuffer = new StringBuilder(); + + String heloMode = (String) session.getAttachment(SMTPSession.CURRENT_HELO_MODE, State.Connection); + String heloName = (String) session.getAttachment(SMTPSession.CURRENT_HELO_NAME, State.Connection); + + // Put our Received header first + headerLineBuffer.append("from ").append(session.getRemoteAddress().getHostName()); + + if (heloName != null) { + headerLineBuffer.append(" (").append(heloMode).append(" ").append(heloName).append(") "); + } + headerLineBuffer.append(" ([").append(session.getRemoteAddress().getAddress().getHostAddress()).append("])").append("\r\n"); + headerLineBuffer.delete(0, headerLineBuffer.length()); + + headerLineBuffer.append(" by ").append(session.getConfiguration().getHelloName()).append(" (").append(session.getConfiguration().getSoftwareName()).append(") with ").append(getServiceType(session, heloMode)); + headerLineBuffer.append(" ID ").append(session.getSessionID()); + + if (((Collection<?>) session.getAttachment(SMTPSession.RCPT_LIST, State.Transaction)).size() == 1) { + // Only indicate a recipient if they're the only recipient + // (prevents email address harvesting and large headers in + // bulk email) + headerLineBuffer.append("\r\n"); + headerLineBuffer.append(" for <").append(((List<MailAddress>) session.getAttachment(SMTPSession.RCPT_LIST, State.Transaction)).get(0).toString()).append(">;"); + } else { + // Put the ; on the end of the 'by' line + headerLineBuffer.append(";"); + } + headerLineBuffer.append(" " + DATEFORMAT.get().format(new Date())); + + return Arrays.asList(new Header("Received", headerLineBuffer.toString())); + } } Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/SeparatingDataLineFilter.java URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/SeparatingDataLineFilter.java?rev=1226180&view=auto ============================================================================== --- james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/SeparatingDataLineFilter.java (added) +++ james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/SeparatingDataLineFilter.java Sat Dec 31 19:14:11 2011 @@ -0,0 +1,113 @@ +/**************************************************************** + * 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.protocols.smtp.core; + +import java.nio.ByteBuffer; + +import org.apache.james.protocols.api.Response; +import org.apache.james.protocols.api.ProtocolSession.State; +import org.apache.james.protocols.api.handler.LineHandler; +import org.apache.james.protocols.smtp.SMTPSession; + +/** + * Abstract base class which makes it easier to handles lines be providing one method per message part. + * </br> + * </br> + * This is: + * </br> + * <strong>headers</strong></br> + * <strong>separator</strong></br> + * <strong>body</strong></br> + * </br> + * + * Subclasses should override at least one of these methods: + * </br> + * {@link #onHeadersLine(SMTPSession, ByteBuffer, LineHandler)}</br> + * {@link #onSeparatorLine(SMTPSession, ByteBuffer, LineHandler)}</br> + * {@link #onBodyLine(SMTPSession, ByteBuffer, LineHandler)}</br> + * + * + */ +public abstract class SeparatingDataLineFilter implements DataLineFilter{ + + private static final String HEADERS_COMPLETE = "HEADERS_COMPLETE"; + + /* + * (non-Javadoc) + * @see org.apache.james.protocols.smtp.core.DataLineFilter#onLine(org.apache.james.protocols.smtp.SMTPSession, java.nio.ByteBuffer, org.apache.james.protocols.api.handler.LineHandler) + */ + public final Response onLine(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { + if (session.getAttachment(HEADERS_COMPLETE, State.Transaction) == null) { + if (line.remaining() == 2 ) { + if (line.get() == '\r' && line.get() == '\n') { + line.rewind(); + Response response = onSeparatorLine(session, line, next); + session.setAttachment(HEADERS_COMPLETE, Boolean.TRUE, State.Transaction); + return response; + } + line.rewind(); + } + return onHeadersLine(session, line, next); + } + + return onBodyLine(session, line, next); + } + + /** + * Gets called when the separating line is received. This is the CLRF sequence. + * + * This implementation just calls {@link LineHandler#onLine(org.apache.james.protocols.api.ProtocolSession, ByteBuffer)} but subclasses should override it if needed. + * + * @param session + * @param line + * @param next + * @return response + */ + protected Response onSeparatorLine(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { + return next.onLine(session, line); + } + + /** + * Gets called for each received line until the CRLF sequence was received. + * + * This implementation just calls {@link LineHandler#onLine(org.apache.james.protocols.api.ProtocolSession, ByteBuffer)} but subclasses should override it if needed. + * + * @param session + * @param line + * @param next + * @return response + */ + protected Response onHeadersLine(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { + return next.onLine(session, line); + } + + /** + * Gets called for each received line after the CRLF sequence was received. + * + * This implementation just calls {@link LineHandler#onLine(org.apache.james.protocols.api.ProtocolSession, ByteBuffer)} but subclasses should override it if needed. + * + * @param session + * @param line + * @param next + * @return response + */ + protected Response onBodyLine(SMTPSession session, ByteBuffer line, LineHandler<SMTPSession> next) { + return next.onLine(session, line); + } +} Propchange: james/protocols/trunk/smtp/src/main/java/org/apache/james/protocols/smtp/core/SeparatingDataLineFilter.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