1 /*****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
19
20 package org.apache.james.smtpserver;
21
22 import java.util.ArrayList;
23 import java.util.Enumeration;
24 import java.util.List;
25 import java.util.HashMap;
26 import java.util.Locale;
27 import java.util.Properties;
28
29 import org.apache.avalon.framework.configuration.Configurable;
30 import org.apache.avalon.framework.configuration.Configuration;
31 import org.apache.avalon.framework.configuration.ConfigurationException;
32 import org.apache.avalon.framework.configuration.DefaultConfiguration;
33 import org.apache.avalon.framework.container.ContainerUtil;
34 import org.apache.avalon.framework.logger.AbstractLogEnabled;
35 import org.apache.avalon.framework.logger.LogEnabled;
36 import org.apache.avalon.framework.service.ServiceException;
37 import org.apache.avalon.framework.service.ServiceManager;
38 import org.apache.avalon.framework.service.Serviceable;
39
40 /***
41 * The SMTPHandlerChain is per service object providing access
42 * ConnectHandlers, Commandhandlers and message handlers
43 */
44 public class SMTPHandlerChain extends AbstractLogEnabled implements Configurable, Serviceable {
45
46 private HashMap commandHandlerMap = new HashMap();
47 private ArrayList messageHandlers = new ArrayList();
48 private ArrayList connectHandlers = new ArrayList();
49
50 private final CommandHandler unknownHandler = new UnknownCmdHandler();
51 private ServiceManager serviceManager;
52
53 private final static String[] mandatoryCommands = { "MAIL" , "RCPT", "DATA"};
54
55 public void service(ServiceManager arg0) throws ServiceException {
56 serviceManager = arg0;
57 }
58
59
60 /***
61 * loads the various handlers from the configuration
62 * @param configuration configuration under handlerchain node
63 */
64 public void configure(Configuration configuration) throws ConfigurationException {
65 addToMap(UnknownCmdHandler.UNKNOWN_COMMAND, unknownHandler);
66 if(configuration == null || configuration.getChildren("handler") == null || configuration.getChildren("handler").length == 0) {
67 configuration = new DefaultConfiguration("handlerchain");
68 Properties cmds = new Properties();
69 cmds.setProperty("AUTH",AuthCmdHandler.class.getName());
70 cmds.setProperty("DATA",DataCmdHandler.class.getName());
71 cmds.setProperty("EHLO",EhloCmdHandler.class.getName());
72 cmds.setProperty("EXPN",ExpnCmdHandler.class.getName());
73 cmds.setProperty("HELO",HeloCmdHandler.class.getName());
74 cmds.setProperty("HELP",HelpCmdHandler.class.getName());
75 cmds.setProperty("MAIL",MailCmdHandler.class.getName());
76 cmds.setProperty("NOOP",NoopCmdHandler.class.getName());
77 cmds.setProperty("QUIT",QuitCmdHandler.class.getName());
78 cmds.setProperty("RCPT" ,RcptCmdHandler.class.getName());
79 cmds.setProperty("RSET",RsetCmdHandler.class.getName());
80 cmds.setProperty("VRFY",VrfyCmdHandler.class.getName());
81 cmds.setProperty("Default SendMailHandler",SendMailHandler.class.getName());
82 Enumeration e = cmds.keys();
83 while (e.hasMoreElements()) {
84 String cmdName = (String) e.nextElement();
85 String className = cmds.getProperty(cmdName);
86 DefaultConfiguration cmdConf = new DefaultConfiguration("handler");
87 cmdConf.setAttribute("command",cmdName);
88 cmdConf.setAttribute("class",className);
89 ((DefaultConfiguration) configuration).addChild(cmdConf);
90 }
91 }
92 if(configuration != null) {
93 Configuration[] children = configuration.getChildren("handler");
94 if ( children != null ) {
95 ClassLoader classLoader = getClass().getClassLoader();
96 for ( int i = 0 ; i < children.length ; i++ ) {
97 String className = children[i].getAttribute("class");
98 if(className != null) {
99
100 try {
101 Object handler = classLoader.loadClass(className).newInstance();
102
103
104 if (handler instanceof LogEnabled) {
105 ((LogEnabled)handler).enableLogging(getLogger());
106 }
107
108
109 ContainerUtil.service(handler,serviceManager);
110
111
112 ContainerUtil.configure(handler,children[i]);
113
114
115 if(handler instanceof ConnectHandler) {
116 connectHandlers.add((ConnectHandler)handler);
117 if (getLogger().isInfoEnabled()) {
118 getLogger().info("Added ConnectHandler: " + className);
119 }
120 }
121
122
123 if(handler instanceof CommandHandler) {
124 String commandName = children[i].getAttribute("command");
125 commandName = commandName.toUpperCase(Locale.US);
126 addToMap(commandName, (CommandHandler)handler);
127 if (getLogger().isInfoEnabled()) {
128 getLogger().info("Added Commandhandler: " + className);
129 }
130
131 }
132
133
134 if(handler instanceof MessageHandler) {
135 messageHandlers.add((MessageHandler)handler);
136 if (getLogger().isInfoEnabled()) {
137 getLogger().info("Added MessageHandler: " + className);
138 }
139 }
140
141 } catch (ClassNotFoundException ex) {
142 if (getLogger().isErrorEnabled()) {
143 getLogger().error("Failed to add Commandhandler: " + className,ex);
144 }
145 throw new ConfigurationException("Failed to add Commandhandler: " + className,ex);
146 } catch (IllegalAccessException ex) {
147 if (getLogger().isErrorEnabled()) {
148 getLogger().error("Failed to add Commandhandler: " + className,ex);
149 }
150 throw new ConfigurationException("Failed to add Commandhandler: " + className,ex);
151 } catch (InstantiationException ex) {
152 if (getLogger().isErrorEnabled()) {
153 getLogger().error("Failed to add Commandhandler: " + className,ex);
154 }
155 throw new ConfigurationException("Failed to add Commandhandler: " + className,ex);
156 } catch (ServiceException e) {
157 if (getLogger().isErrorEnabled()) {
158 getLogger().error("Failed to service Commandhandler: " + className,e);
159 }
160 throw new ConfigurationException("Failed to add Commandhandler: " + className,e);
161 }
162 }
163 }
164 }
165 }
166
167
168 if(commandHandlerMap.size() < 2) {
169 if (getLogger().isErrorEnabled()) {
170 getLogger().error("No commandhandlers configured");
171 }
172 throw new ConfigurationException("No commandhandlers configured");
173 } else {
174 boolean found = true;
175 for (int i = 0; i < mandatoryCommands.length; i++) {
176 if(!commandHandlerMap.containsKey(mandatoryCommands[i])) {
177 if (getLogger().isErrorEnabled()) {
178 getLogger().error("No commandhandlers configured for the command:" + mandatoryCommands[i]);
179 }
180 found = false;
181 break;
182 }
183 }
184
185 if(!found) {
186 throw new ConfigurationException("No commandhandlers configured for mandatory commands");
187 }
188
189 if (messageHandlers.size() == 0) {
190 if (getLogger().isErrorEnabled()) {
191 getLogger().error("No messageHandler configured. Check that SendMailHandler is configured in the SMTPHandlerChain");
192 }
193 throw new ConfigurationException("No messageHandler configured");
194 }
195
196 }
197 }
198
199 /***
200 * Add it to map (key as command name, value is an array list of commandhandlers)
201 *
202 * @param commandName the command name which will be key
203 * @param cmdHandler The commandhandler object
204 */
205 private void addToMap(String commandName, CommandHandler cmdHandler) {
206 ArrayList handlers = (ArrayList)commandHandlerMap.get(commandName);
207 if(handlers == null) {
208 handlers = new ArrayList();
209 commandHandlerMap.put(commandName, handlers);
210 }
211 handlers.add(cmdHandler);
212 }
213
214 /***
215 * Returns all the configured commandhandlers for the specified command
216 *
217 * @param commandName the command name which will be key
218 * @return List of commandhandlers
219 */
220 List getCommandHandlers(String command) {
221 if (command == null) {
222 return null;
223 }
224 if (getLogger().isDebugEnabled()) {
225 getLogger().debug("Lookup command handler for command: " + command);
226 }
227 List handlers = (List)commandHandlerMap.get(command);
228 if(handlers == null) {
229 handlers = (List)commandHandlerMap.get(UnknownCmdHandler.UNKNOWN_COMMAND);
230 }
231
232 return handlers;
233 }
234
235 /***
236 * Returns all the configured message handlers
237 *
238 * @return List of message handlers
239 */
240 List getMessageHandlers() {
241 return messageHandlers;
242 }
243
244 /***
245 * Returns all the configured connect handlers
246 *
247 * @return List of connect handlers
248 */
249 List getConnectHandlers() {
250 return connectHandlers;
251 }
252
253 }