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 }