📄 smtphandler.java
字号:
/**************************************************************** * Licensed to the Apache Software Foundation (ASF) under one * * or more contributor license agreements. See the NOTICE file * * distributed with this work for additional information * * regarding copyright ownership. The ASF licenses this file * * to you under the Apache License, Version 2.0 (the * * "License"); you may not use this file except in compliance * * with the License. You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, * * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * ****************************************************************/package org.apache.james.smtpserver;import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;import org.apache.avalon.excalibur.pool.Poolable;import org.apache.avalon.framework.activity.Disposable;import org.apache.avalon.framework.container.ContainerUtil;import org.apache.avalon.framework.logger.AbstractLogEnabled;import org.apache.james.Constants;import org.apache.james.util.CRLFTerminatedReader;import org.apache.james.util.InternetPrintWriter;import org.apache.james.util.watchdog.Watchdog;import org.apache.james.util.watchdog.WatchdogTarget;import org.apache.mailet.Mail;import org.apache.mailet.dates.RFC822DateFormat;import java.io.BufferedInputStream;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStream;import java.io.InterruptedIOException;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;import java.net.SocketException;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Locale;import java.util.Random;/** * Provides SMTP functionality by carrying out the server side of the SMTP * interaction. * * @version CVS $Revision: 494012 $ $Date: 2007-01-08 11:23:58 +0100 (Mo, 08 Jan 2007) $ */public class SMTPHandler extends AbstractLogEnabled implements ConnectionHandler, Poolable, SMTPSession { /** * The constants to indicate the current processing mode of the session */ private final static byte COMMAND_MODE = 1; private final static byte RESPONSE_MODE = 2; private final static byte MESSAGE_RECEIVED_MODE = 3; private final static byte MESSAGE_ABORT_MODE = 4; /** * SMTP Server identification string used in SMTP headers */ private final static String SOFTWARE_TYPE = "JAMES SMTP Server " + Constants.SOFTWARE_VERSION; /** * Static Random instance used to generate SMTP ids */ private final static Random random = new Random(); /** * Static RFC822DateFormat used to generate date headers */ private final static RFC822DateFormat rfc822DateFormat = new RFC822DateFormat(); /** * The name of the currently parsed command */ String curCommandName = null; /** * The value of the currently parsed command */ String curCommandArgument = null; /** * The SMTPHandlerChain object set by SMTPServer */ SMTPHandlerChain handlerChain = null; /** * The mode of the current session */ private byte mode; /** * The MailImpl object set by the DATA command */ private Mail mail = null; /** * The session termination status */ private boolean sessionEnded = false; /** * The thread executing this handler */ private Thread handlerThread; /** * The TCP/IP socket over which the SMTP * dialogue is occurring. */ private Socket socket; /** * The incoming stream of bytes coming from the socket. */ private InputStream in; /** * The writer to which outgoing messages are written. */ private PrintWriter out; /** * A Reader wrapper for the incoming stream of bytes coming from the socket. */ private CRLFTerminatedReader inReader; /** * The remote host name obtained by lookup on the socket. */ private String remoteHost; /** * The remote IP address of the socket. */ private String remoteIP; /** * The user name of the authenticated user associated with this SMTP transaction. */ private String authenticatedUser; /** * whether or not authorization is required for this connection */ private boolean authRequired; /** * whether or not this connection can relay without authentication */ private boolean relayingAllowed; /** * Whether the remote Server must send HELO/EHLO */ private boolean heloEhloEnforcement; /** * TEMPORARY: is the sending address blocklisted */ private boolean blocklisted; /** * The id associated with this particular SMTP interaction. */ private String smtpID; /** * The per-service configuration data that applies to all handlers */ private SMTPHandlerConfigurationData theConfigData; /** * The hash map that holds variables for the SMTP message transfer in progress. * * This hash map should only be used to store variable set in a particular * set of sequential MAIL-RCPT-DATA commands, as described in RFC 2821. Per * connection values should be stored as member variables in this class. */ private HashMap state = new HashMap(); /** * The watchdog being used by this handler to deal with idle timeouts. */ private Watchdog theWatchdog; /** * The watchdog target that idles out this handler. */ private WatchdogTarget theWatchdogTarget = new SMTPWatchdogTarget(); /** * The per-handler response buffer used to marshal responses. */ private StringBuffer responseBuffer = new StringBuffer(256); /** * Set the configuration data for the handler * * @param theData the per-service configuration data for this handler */ void setConfigurationData(SMTPHandlerConfigurationData theData) { theConfigData = theData; } /** * Set the Watchdog for use by this handler. * * @param theWatchdog the watchdog */ void setWatchdog(Watchdog theWatchdog) { this.theWatchdog = theWatchdog; } /** * Gets the Watchdog Target that should be used by Watchdogs managing * this connection. * * @return the WatchdogTarget */ WatchdogTarget getWatchdogTarget() { return theWatchdogTarget; } /** * Idle out this connection */ void idleClose() { if (getLogger() != null) { getLogger().error("SMTP Connection has idled out."); } try { if (socket != null) { socket.close(); } } catch (Exception e) { // ignored } synchronized (this) { // Interrupt the thread to recover from internal hangs if (handlerThread != null) { handlerThread.interrupt(); } } } /** * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandler#handleConnection(Socket) */ public void handleConnection(Socket connection) throws IOException { try { this.socket = connection; synchronized (this) { handlerThread = Thread.currentThread(); } in = new BufferedInputStream(socket.getInputStream(), 1024); // An ASCII encoding can be used because all transmissions other // that those in the DATA command are guaranteed // to be ASCII // inReader = new BufferedReader(new InputStreamReader(in, "ASCII"), 512); inReader = new CRLFTerminatedReader(in, "ASCII"); remoteIP = socket.getInetAddress().getHostAddress(); remoteHost = socket.getInetAddress().getHostName(); smtpID = random.nextInt(1024) + ""; relayingAllowed = theConfigData.isRelayingAllowed(remoteIP); authRequired = theConfigData.isAuthRequired(remoteIP); heloEhloEnforcement = theConfigData.useHeloEhloEnforcement(); sessionEnded = false; resetState(); } catch (Exception e) { StringBuffer exceptionBuffer = new StringBuffer(256) .append("Cannot open connection from ") .append(remoteHost) .append(" (") .append(remoteIP) .append("): ") .append(e.getMessage()); String exceptionString = exceptionBuffer.toString(); getLogger().error(exceptionString, e ); throw new RuntimeException(exceptionString); } if (getLogger().isInfoEnabled()) { StringBuffer infoBuffer = new StringBuffer(128) .append("Connection from ") .append(remoteHost) .append(" (") .append(remoteIP) .append(")"); getLogger().info(infoBuffer.toString()); } try { out = new InternetPrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()), 1024), false); // Initially greet the connector // Format is: Sat, 24 Jan 1998 13:16:09 -0500 responseBuffer.append("220 ") .append(theConfigData.getHelloName()) .append(" SMTP Server (") .append(SOFTWARE_TYPE) .append(") ready ") .append(rfc822DateFormat.format(new Date())); String responseString = clearResponseBuffer(); writeLoggedFlushedResponse(responseString); //the core in-protocol handling logic //run all the connection handlers, if it fast fails, end the session //parse the command command, look up for the list of command handlers //Execute each of the command handlers. If any command handlers writes //response then, End the subsequent command handler processing and //start parsing new command. Once the message is received, run all //the message handlers. The message handlers can either terminate //message or terminate session //At the beginning //mode = command_mode //once the commandHandler writes response, the mode is changed to RESPONSE_MODE. //This will cause to skip the subsequent command handlers configured for that command. //For instance: //There are 2 commandhandlers MailAddressChecker and MailCmdHandler for //MAIL command. If MailAddressChecker validation of the MAIL FROM //address is successful, the MailCmdHandlers will be executed. //Incase it fails, it has to write response. Once we write response //there is no need to execute the MailCmdHandler. //Next, Once MAIL message is received the DataCmdHandler and any other //equivalent commandHandler will call setMail method. this will change //he mode to MAIL_RECEIVED_MODE. This mode will trigger the message //handlers to be execute. Message handlers can abort message. In that case, //message will not spooled. //Session started - RUN all connect handlers List connectHandlers = handlerChain.getConnectHandlers(); if(connectHandlers != null) { int count = connectHandlers.size(); for(int i = 0; i < count; i++) { ((ConnectHandler)connectHandlers.get(i)).onConnect(this); if(sessionEnded) { break; } } } theWatchdog.start(); while(!sessionEnded) { //Reset the current command values curCommandName = null; curCommandArgument = null; mode = COMMAND_MODE; //parse the command String cmdString = readCommandLine(); if (cmdString == null) { break; } int spaceIndex = cmdString.indexOf(" "); if (spaceIndex > 0) { curCommandName = cmdString.substring(0, spaceIndex); curCommandArgument = cmdString.substring(spaceIndex + 1); } else { curCommandName = cmdString; } curCommandName = curCommandName.toUpperCase(Locale.US); //fetch the command handlers registered to the command List commandHandlers = handlerChain.getCommandHandlers(curCommandName); if(commandHandlers == null) { //end the session break; } else { int count = commandHandlers.size(); for(int i = 0; i < count; i++) { ((CommandHandler)commandHandlers.get(i)).onCommand(this); theWatchdog.reset(); //if the response is received, stop processing of command handlers if(mode != COMMAND_MODE) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -