package org.subethamail.smtp.server; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.Collection; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.subethamail.smtp.DropConnectionException; /** * This class manages execution of a SMTP command. * * @author Jon Stevens * @author Scott Hernandez */ public class CommandHandler { private final static Logger log = LoggerFactory.getLogger(CommandHandler.class); /** * The map of known SMTP commands. Keys are upper case names of the * commands. */ private Map<String, Command> commandMap = new HashMap<String, Command>(); /** */ public CommandHandler() { // This solution should be more robust than the earlier "manual" configuration. for (CommandRegistry registry: CommandRegistry.values()) { this.addCommand(registry.getCommand()); } } /** * Create a command handler with a specific set of commands. * * @param availableCommands the available commands (not null) * TLS note: wrap commands with {@link RequireTLSCommandWrapper} when appropriate. */ public CommandHandler(Collection<Command> availableCommands) { for (Command command: availableCommands) { this.addCommand(command); } } /** * Adds or replaces the specified command. */ public void addCommand(Command command) { if (log.isDebugEnabled()) log.debug("Added command: " + command.getName()); this.commandMap.put(command.getName(), command); } /** * Returns the command object corresponding to the specified command name. * * @param commandName * case insensitive name of the command. * @return the command object, or null, if the command is unknown. */ public Command getCommand(String commandName) { String upperCaseCommandName = commandName.toUpperCase(Locale.ENGLISH); return this.commandMap.get(upperCaseCommandName); } /** */ public boolean containsCommand(String command) { return this.commandMap.containsKey(command); } /** */ public Set<String> getVerbs() { return this.commandMap.keySet(); } /** */ public void handleCommand(Session context, String commandString) throws SocketTimeoutException, IOException, DropConnectionException { try { Command command = this.getCommandFromString(commandString); command.execute(commandString, context); } catch (CommandException e) { context.sendResponse("500 " + e.getMessage()); } } /** * @return the HelpMessage object for the given command name (verb) * @throws CommandException */ public HelpMessage getHelp(String command) throws CommandException { return this.getCommandFromString(command).getHelp(); } /** */ private Command getCommandFromString(String commandString) throws UnknownCommandException, InvalidCommandNameException { Command command = null; String key = this.toKey(commandString); if (key != null) { command = this.commandMap.get(key); } if (command == null) { // some commands have a verb longer than 4 letters String verb = this.toVerb(commandString); if (verb != null) { command = this.commandMap.get(verb); } } if (command == null) { throw new UnknownCommandException("Error: command not implemented"); } return command; } /** */ private String toKey(String string) throws InvalidCommandNameException { if (string == null || string.length() < 4) throw new InvalidCommandNameException("Error: bad syntax"); return string.substring(0, 4).toUpperCase(Locale.ENGLISH); } /** */ private String toVerb(String string) throws InvalidCommandNameException { StringTokenizer stringTokenizer = new StringTokenizer(string); if (!stringTokenizer.hasMoreTokens()) throw new InvalidCommandNameException("Error: bad syntax"); return stringTokenizer.nextToken().toUpperCase(Locale.ENGLISH); } }