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    
020    
021    
022    package org.apache.james.protocols.smtp.core;
023    
024    import java.io.IOException;
025    import java.io.OutputStream;
026    import java.util.LinkedList;
027    import java.util.List;
028    
029    import org.apache.james.protocols.api.Response;
030    import org.apache.james.protocols.api.handler.ExtensibleHandler;
031    import org.apache.james.protocols.api.handler.LineHandler;
032    import org.apache.james.protocols.api.handler.WiringException;
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.james.protocols.smtp.hook.HookResult;
039    import org.apache.james.protocols.smtp.hook.HookResultHook;
040    import org.apache.james.protocols.smtp.hook.HookReturnCode;
041    import org.apache.james.protocols.smtp.hook.MessageHook;
042    
043    /**
044     * This class handles the actual calling of the {@link MessageHook} implementations to queue the message. If no {@link MessageHook} return OK or DECLINED it will write back an
045     * error to the client to report the problem while trying to queue the message 
046     *
047     */
048    public class DataLineMessageHookHandler implements DataLineFilter, ExtensibleHandler {
049    
050        
051        private List messageHandlers;
052        
053        private List rHooks;
054        
055    
056        /**
057         * @see org.apache.james.protocols.smtp.core.DataLineFilter#onLine(SMTPSession, byte[], LineHandler)
058         */
059        public Response onLine(final SMTPSession session, byte[] line, LineHandler<SMTPSession> next) {
060            MailEnvelopeImpl env = (MailEnvelopeImpl) session.getState().get(DataCmdHandler.MAILENV);
061            OutputStream out = env.getMessageOutputStream();
062            try {
063                // 46 is "."
064                // Stream terminated
065                if (line.length == 3 && line[0] == 46) {
066                    out.flush();
067                    out.close();
068                    
069                    Response response = processExtensions(session, env);
070                    session.popLineHandler();
071                    session.resetState();
072                    return response;
073                    
074                // DotStuffing.
075                } else if (line[0] == 46 && line[1] == 46) {
076                    out.write(line,1,line.length-1);
077                // Standard write
078                } else {
079                    // TODO: maybe we should handle the Header/Body recognition here
080                    // and if needed let a filter to cache the headers to apply some
081                    // transformation before writing them to output.
082                    out.write(line);
083                }
084                out.flush();
085            } catch (IOException e) {
086                SMTPResponse response = new SMTPResponse(SMTPRetCode.LOCAL_ERROR,DSNStatus.getStatus(DSNStatus.TRANSIENT,
087                                DSNStatus.UNDEFINED_STATUS) + " Error processing message: " + e.getMessage());
088                
089                session.getLogger().error(
090                        "Unknown error occurred while processing DATA.", e);
091                
092                session.resetState();
093                return response;
094            } 
095            return null;
096        }
097    
098    
099        /**
100         * @param session
101         */
102        protected Response processExtensions(SMTPSession session, MailEnvelopeImpl mail) {
103           
104    
105            if (mail != null && messageHandlers != null) {
106                int count = messageHandlers.size();
107                for (int i = 0; i < count; i++) {
108                    MessageHook rawHandler = (MessageHook) messageHandlers.get(i);
109                    session.getLogger().debug("executing message handler " + rawHandler);
110    
111                    long start = System.currentTimeMillis();
112                    HookResult hRes = rawHandler.onMessage(session, mail);
113                    long executionTime = System.currentTimeMillis() - start;
114    
115                    if (rHooks != null) {
116                        for (int i2 = 0; i2 < rHooks.size(); i2++) {
117                            Object rHook = rHooks.get(i2);
118                            session.getLogger().debug("executing hook " + rHook);
119    
120                            hRes = ((HookResultHook) rHook).onHookResult(session, hRes, executionTime, rawHandler);
121                        }
122                    }
123    
124                    SMTPResponse response = AbstractHookableCmdHandler.calcDefaultSMTPResponse(hRes);
125    
126                    // if the response is received, stop processing of command
127                    // handlers
128                    if (response != null) {
129                        return response;
130                    }
131                }
132    
133                // Not queue the message!
134                SMTPResponse response = AbstractHookableCmdHandler.calcDefaultSMTPResponse(new HookResult(HookReturnCode.DENY));
135                return response;
136    
137              
138            }
139            
140            return null;
141        }
142    
143        /**
144         * @see org.apache.james.protocols.api.handler.ExtensibleHandler#wireExtensions(java.lang.Class, java.util.List)
145         */
146        @SuppressWarnings("unchecked")
147        public void wireExtensions(Class interfaceName, List extension) throws WiringException {
148            if (MessageHook.class.equals(interfaceName)) {
149                this.messageHandlers = extension;
150                checkMessageHookCount(messageHandlers);
151            } else if (HookResultHook.class.equals(interfaceName)) {
152                this.rHooks = extension;
153            }
154        }
155    
156        protected void checkMessageHookCount(List messageHandlers) throws WiringException {
157            if (messageHandlers.size() == 0) {
158                throw new WiringException("No messageHandler configured");
159            }
160        }
161        /**
162         * @see org.apache.james.protocols.api.handler.ExtensibleHandler#getMarkerInterfaces()
163         */
164        public List<Class<?>> getMarkerInterfaces() {
165            List<Class<?>> classes = new LinkedList<Class<?>>();
166            classes.add(MessageHook.class);
167            classes.add(HookResultHook.class);
168            return classes;
169        }
170    
171    }