001    /****************************************************************
002     * Licensed to the Apache Software Foundation (ASF) under one   *
003     * or more contributor license agreements.  See the NOTICE file *
004     * distributed with this work for additional information        *
005     * regarding copyright ownership.  The ASF licenses this file   *
006     * to you under the Apache License, Version 2.0 (the            *
007     * "License"); you may not use this file except in compliance   *
008     * with the License.  You may obtain a copy of the License at   *
009     *                                                              *
010     *   http://www.apache.org/licenses/LICENSE-2.0                 *
011     *                                                              *
012     * Unless required by applicable law or agreed to in writing,   *
013     * software distributed under the License is distributed on an  *
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015     * KIND, either express or implied.  See the License for the    *
016     * specific language governing permissions and limitations      *
017     * under the License.                                           *
018     ****************************************************************/
019    package org.apache.james.protocols.smtp.core;
020    
021    import java.nio.charset.Charset;
022    import java.util.Collection;
023    import java.util.Date;
024    import java.util.List;
025    
026    import org.apache.james.protocols.api.Response;
027    import org.apache.james.protocols.api.handler.LineHandler;
028    import org.apache.james.protocols.smtp.SMTPSession;
029    import org.apache.mailet.base.RFC2822Headers;
030    import org.apache.mailet.base.RFC822DateFormat;
031    
032    public class ReceivedDataLineFilter implements DataLineFilter {
033    
034        private final static Charset CHARSET = Charset.forName("US-ASCII");
035        
036        private final static String SOFTWARE_TYPE = "JAMES SMTP Server ";
037    
038        // Replace this with something usefull
039        // + Constants.SOFTWARE_VERSION;
040    
041        /**
042         * Static RFC822DateFormat used to generate date headers
043         */
044        private final static RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
045        private final static String HEADERS_WRITTEN = "HEADERS_WRITTEN";
046    
047    
048        /**
049         * @see org.apache.james.protocols.smtp.core.DataLineFilter#onLine(SMTPSession, byte[], LineHandler)
050         */
051        public Response onLine(SMTPSession session,  byte[] line, LineHandler<SMTPSession> next) {
052            if (session.getState().containsKey(HEADERS_WRITTEN) == false) {
053                Response response = addNewReceivedMailHeaders(session, next);
054    
055                session.getState().put(HEADERS_WRITTEN, true);
056                
057                if (response != null) {
058                    return response;
059                }
060            }
061            Response resp =  next.onLine(session, line);
062            return resp;
063        }
064    
065        private Response addNewReceivedMailHeaders(SMTPSession session, LineHandler<SMTPSession> next) {
066            StringBuilder headerLineBuffer = new StringBuilder();
067    
068            String heloMode = (String) session.getConnectionState().get(
069                    SMTPSession.CURRENT_HELO_MODE);
070            String heloName = (String) session.getConnectionState().get(
071                    SMTPSession.CURRENT_HELO_NAME);
072    
073            // Put our Received header first
074            headerLineBuffer.append(RFC2822Headers.RECEIVED + ": from ").append(
075                    session.getRemoteHost());
076    
077            if (heloName != null) {
078                headerLineBuffer.append(" (").append(heloMode).append(" ").append(
079                        heloName).append(") ");
080            }
081    
082            headerLineBuffer.append(" ([").append(session.getRemoteIPAddress())
083                    .append("])").append("\r\n");
084                
085            Response response = next.onLine(session, headerLineBuffer.toString().getBytes(CHARSET));
086            if (response != null) {
087                return response;
088            }
089            headerLineBuffer.delete(0, headerLineBuffer.length());
090    
091            headerLineBuffer.append("          by ").append(session.getHelloName())
092                    .append(" (").append(SOFTWARE_TYPE).append(") with ");
093    
094            // Check if EHLO was used
095            if ("EHLO".equals(heloMode)) {
096                // Not successful auth
097                if (session.getUser() == null) {
098                    headerLineBuffer.append("ESMTP");
099                } else {
100                    // See RFC3848
101                    // The new keyword "ESMTPA" indicates the use of ESMTP when the
102                    // SMTP
103                    // AUTH [3] extension is also used and authentication is
104                    // successfully
105                    // achieved.
106                    headerLineBuffer.append("ESMTPA");
107                }
108            } else {
109                headerLineBuffer.append("SMTP");
110            }
111    
112            headerLineBuffer.append(" ID ").append(session.getSessionID());
113    
114            if (((Collection) session.getState().get(SMTPSession.RCPT_LIST)).size() == 1) {
115                // Only indicate a recipient if they're the only recipient
116                // (prevents email address harvesting and large headers in
117                // bulk email)
118                headerLineBuffer.append("\r\n");  
119                next.onLine(session, headerLineBuffer.toString().getBytes(CHARSET));
120                headerLineBuffer.delete(0, headerLineBuffer.length());
121    
122                headerLineBuffer.delete(0, headerLineBuffer.length());
123                headerLineBuffer.append("          for <").append(((List) session.getState().get(SMTPSession.RCPT_LIST)).get(0).toString()).append(">;").append("\r\n");
124                response = next.onLine(session, headerLineBuffer.toString().getBytes(CHARSET));
125               
126                if (response != null) {
127                    return response; 
128                }
129                headerLineBuffer.delete(0, headerLineBuffer.length());
130                headerLineBuffer.delete(0, headerLineBuffer.length());
131                
132            } else {
133                // Put the ; on the end of the 'by' line
134                headerLineBuffer.append(";");
135                headerLineBuffer.append("\r\n");
136    
137                response = next.onLine(session, headerLineBuffer.toString().getBytes(CHARSET));
138                if (response != null) {
139                    return response;
140                }
141                headerLineBuffer.delete(0, headerLineBuffer.length());
142            }
143            headerLineBuffer = null;
144            return next.onLine(session, ("          " + rfc822DateFormat.format(new Date()) + "\r\n").getBytes(CHARSET));
145    
146        }
147    }