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.util.ArrayList;
022    import java.util.Collection;
023    import java.util.LinkedList;
024    import java.util.List;
025    
026    import org.apache.james.protocols.api.Request;
027    import org.apache.james.protocols.api.Response;
028    import org.apache.james.protocols.api.handler.CommandHandler;
029    import org.apache.james.protocols.api.handler.ExtensibleHandler;
030    import org.apache.james.protocols.api.handler.LineHandler;
031    import org.apache.james.protocols.api.handler.WiringException;
032    import org.apache.james.protocols.smtp.MailEnvelope;
033    import org.apache.james.protocols.smtp.MailEnvelopeImpl;
034    import org.apache.james.protocols.smtp.SMTPResponse;
035    import org.apache.james.protocols.smtp.SMTPRetCode;
036    import org.apache.james.protocols.smtp.SMTPSession;
037    import org.apache.james.protocols.smtp.dsn.DSNStatus;
038    import org.apache.mailet.MailAddress;
039    
040    
041    /**
042      * handles DATA command
043     */
044    public class DataCmdHandler implements CommandHandler<SMTPSession>, ExtensibleHandler {
045    
046        public static final class DataConsumerLineHandler implements LineHandler<SMTPSession> {
047    
048            /**
049             * @see org.apache.james.protocols.api.handler.LineHandler
050             * #onLine(org.apache.james.protocols.api.ProtocolSession, byte[])
051             */
052            public SMTPResponse onLine(SMTPSession session, byte[] line) {
053                
054                // Discard everything until the end of DATA session
055                if (line.length == 3 && line[0] == 46) {
056                    session.popLineHandler();
057                }
058                return null;
059            }
060        }
061    
062        public static final class DataLineFilterWrapper implements LineHandler<SMTPSession> {
063    
064            private DataLineFilter filter;
065            private LineHandler<SMTPSession> next;
066            
067            public DataLineFilterWrapper(DataLineFilter filter, LineHandler<SMTPSession> next) {
068                this.filter = filter;
069                this.next = next;
070            }
071            
072            /**
073             * @see org.apache.james.protocols.api.handler.LineHandler
074             * #onLine(org.apache.james.protocols.api.ProtocolSession, byte[])
075             */
076            public Response onLine(SMTPSession session, byte[] line) {
077                return filter.onLine(session, line, next);
078            }
079                    
080        }
081       
082        public final static String MAILENV = "MAILENV";
083        
084        private LineHandler<SMTPSession> lineHandler;
085        
086        /**
087         * process DATA command
088         *
089         */
090        public Response onCommand(SMTPSession session, Request request) {
091            String parameters = request.getArgument();
092            SMTPResponse response = doDATAFilter(session,parameters);
093            
094            if (response == null) {
095                return doDATA(session, parameters);
096            } else {
097                return response;
098            }
099        }
100    
101    
102        /**
103         * Handler method called upon receipt of a DATA command.
104         * Reads in message data, creates header, and delivers to
105         * mail server service for delivery.
106         *
107         * @param session SMTP session object
108         * @param argument the argument passed in with the command by the SMTP client
109         */
110        @SuppressWarnings("unchecked")
111        protected SMTPResponse doDATA(SMTPSession session, String argument) {
112            MailEnvelope env = createEnvelope(session, (MailAddress) session.getState().get(SMTPSession.SENDER), new ArrayList<MailAddress>((Collection)session.getState().get(SMTPSession.RCPT_LIST)));
113            session.getState().put(MAILENV, env);
114            session.pushLineHandler(lineHandler);
115            
116            return new SMTPResponse(SMTPRetCode.DATA_READY, "Ok Send data ending with <CRLF>.<CRLF>");
117        }
118        
119        protected MailEnvelope createEnvelope(SMTPSession session, MailAddress sender, List<MailAddress> recipients) {
120            MailEnvelopeImpl env = new MailEnvelopeImpl();
121            env.setRecipients(recipients);
122            env.setSender(sender);
123            return env;
124        }
125        
126        
127        /**
128         * @see org.apache.james.protocols.api.handler.CommandHandler#getImplCommands()
129         */
130        public Collection<String> getImplCommands() {
131            Collection<String> implCommands = new ArrayList<String>();
132            implCommands.add("DATA");
133            
134            return implCommands;
135        }
136    
137    
138        /**
139         * @see org.apache.james.protocols.api.handler.ExtensibleHandler#getMarkerInterfaces()
140         */
141        @SuppressWarnings("unchecked")
142        public List getMarkerInterfaces() {
143            List classes = new LinkedList();
144            classes.add(DataLineFilter.class);
145            return classes;
146        }
147    
148    
149        /**
150         * @see org.apache.james.protocols.api.handler.ExtensibleHandler#wireExtensions(java.lang.Class, java.util.List)
151         */
152        @SuppressWarnings("unchecked")
153        public void wireExtensions(Class interfaceName, List extension) throws WiringException {
154            if (DataLineFilter.class.equals(interfaceName)) {
155    
156                LineHandler<SMTPSession> lineHandler = new DataConsumerLineHandler();
157                for (int i = extension.size() - 1; i >= 0; i--) {
158                    lineHandler = new DataLineFilterWrapper((DataLineFilter) extension.get(i), lineHandler);
159                }
160    
161                this.lineHandler = lineHandler;
162            }
163        }
164    
165        protected SMTPResponse doDATAFilter(SMTPSession session, String argument) {
166            if ((argument != null) && (argument.length() > 0)) {
167                return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_COMMAND_UNRECOGNIZED, DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_ARG)+" Unexpected argument provided with DATA command");
168            }
169            if (!session.getState().containsKey(SMTPSession.SENDER)) {
170                return new SMTPResponse(SMTPRetCode.BAD_SEQUENCE, DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" No sender specified");
171            } else if (!session.getState().containsKey(SMTPSession.RCPT_LIST)) {
172                return new SMTPResponse(SMTPRetCode.BAD_SEQUENCE, DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" No recipients specified");
173            }
174            return null;
175        }
176        
177        protected LineHandler<SMTPSession> getLineHandler() {
178            return lineHandler;
179        }
180    
181    }