📄 datacmdhandler.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.framework.logger.AbstractLogEnabled;import org.apache.james.Constants;import org.apache.james.core.MailHeaders;import org.apache.james.core.MailImpl;import org.apache.james.fetchmail.ReaderInputStream;import org.apache.james.util.CharTerminatedInputStream;import org.apache.james.util.DotStuffingInputStream;import org.apache.james.util.mail.dsn.DSNStatus;import org.apache.james.util.watchdog.BytesReadResetInputStream;import org.apache.mailet.MailAddress;import org.apache.mailet.RFC2822Headers;import org.apache.mailet.dates.RFC822DateFormat;import javax.mail.MessagingException;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.io.SequenceInputStream;import java.io.StringReader;import java.util.Collection;import java.util.Date;import java.util.Enumeration;import java.util.List;/** * handles DATA command */public class DataCmdHandler extends AbstractLogEnabled implements CommandHandler { private final static String SOFTWARE_TYPE = "JAMES SMTP Server " + Constants.SOFTWARE_VERSION; /** * Static RFC822DateFormat used to generate date headers */ private final static RFC822DateFormat rfc822DateFormat = new RFC822DateFormat(); // Keys used to store/lookup data in the internal state hash map /** * The mail attribute holding the SMTP AUTH user name, if any. */ private final static String SMTP_AUTH_USER_ATTRIBUTE_NAME = "org.apache.james.SMTPAuthUser"; /** * The character array that indicates termination of an SMTP connection */ private final static char[] SMTPTerminator = { '\r', '\n', '.', '\r', '\n' }; /** * process DATA command * * @see org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession) */ public void onCommand(SMTPSession session) { doDATA(session, session.getCommandArgument()); } /** * Handler method called upon receipt of a DATA command. * Reads in message data, creates header, and delivers to * mail server service for delivery. * * @param session SMTP session object * @param argument the argument passed in with the command by the SMTP client */ private void doDATA(SMTPSession session, String argument) { String responseString = null; if ((argument != null) && (argument.length() > 0)) { responseString = "500 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_ARG)+" Unexpected argument provided with DATA command"; session.writeResponse(responseString); } if (!session.getState().containsKey(SMTPSession.SENDER)) { responseString = "503 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" No sender specified"; session.writeResponse(responseString); } else if (!session.getState().containsKey(SMTPSession.RCPT_LIST)) { responseString = "503 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" No recipients specified"; session.writeResponse(responseString); } else { responseString = "354 Ok Send data ending with <CRLF>.<CRLF>"; session.writeResponse(responseString); InputStream msgIn = new CharTerminatedInputStream(session.getInputStream(), SMTPTerminator); try { msgIn = new BytesReadResetInputStream(msgIn, session.getWatchdog(), session.getConfigurationData().getResetLength()); // if the message size limit has been set, we'll // wrap msgIn with a SizeLimitedInputStream long maxMessageSize = session.getConfigurationData().getMaxMessageSize(); if (maxMessageSize > 0) { if (getLogger().isDebugEnabled()) { StringBuffer logBuffer = new StringBuffer(128) .append("Using SizeLimitedInputStream ") .append(" with max message size: ") .append(maxMessageSize); getLogger().debug(logBuffer.toString()); } msgIn = new SizeLimitedInputStream(msgIn, maxMessageSize); } // Removes the dot stuffing msgIn = new DotStuffingInputStream(msgIn); // Parse out the message headers MailHeaders headers = new MailHeaders(msgIn); headers = processMailHeaders(session, headers); processMail(session, headers, msgIn); headers = null; } catch (MessagingException me) { // Grab any exception attached to this one. Exception e = me.getNextException(); // If there was an attached exception, and it's a // MessageSizeException if (e != null && e instanceof MessageSizeException) { // Add an item to the state to suppress // logging of extra lines of data // that are sent after the size limit has // been hit. session.getState().put(SMTPSession.MESG_FAILED, Boolean.TRUE); // then let the client know that the size // limit has been hit. responseString = "552 "+DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SYSTEM_MSG_TOO_BIG)+" Error processing message: " + e.getMessage(); StringBuffer errorBuffer = new StringBuffer(256) .append("Rejected message from ") .append(session.getState().get(SMTPSession.SENDER).toString()) .append(" from host ") .append(session.getRemoteHost()) .append(" (") .append(session.getRemoteIPAddress()) .append(") exceeding system maximum message size of ") .append(session.getConfigurationData().getMaxMessageSize()); getLogger().error(errorBuffer.toString()); } else { responseString = "451 "+DSNStatus.getStatus(DSNStatus.TRANSIENT,DSNStatus.UNDEFINED_STATUS)+" Error processing message: " + me.getMessage(); getLogger().error("Unknown error occurred while processing DATA.", me); } session.writeResponse(responseString); return; } finally { if (msgIn != null) { try { msgIn.close(); } catch (Exception e) { // Ignore close exception } msgIn = null; } } } } private MailHeaders processMailHeaders(SMTPSession session, MailHeaders headers) throws MessagingException { // If headers do not contains minimum REQUIRED headers fields, // add them if (!headers.isSet(RFC2822Headers.DATE)) { headers.setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new Date())); } if (!headers.isSet(RFC2822Headers.FROM) && session.getState().get(SMTPSession.SENDER) != null) { headers.setHeader(RFC2822Headers.FROM, session.getState().get(SMTPSession.SENDER).toString()); } // RFC 2821 says that we cannot examine the message to see if // Return-Path headers are present. If there is one, our // Received: header may precede it, but the Return-Path header // should be removed when making final delivery. // headers.removeHeader(RFC2822Headers.RETURN_PATH); StringBuffer headerLineBuffer = new StringBuffer(512); // We will rebuild the header object to put our Received header at the top Enumeration headerLines = headers.getAllHeaderLines(); MailHeaders newHeaders = new MailHeaders(); // Put our Received header first headerLineBuffer.append(RFC2822Headers.RECEIVED + ": from ") .append(session.getRemoteHost()) .append(" ([") .append(session.getRemoteIPAddress()) .append("])"); newHeaders.addHeaderLine(headerLineBuffer.toString()); headerLineBuffer.delete(0, headerLineBuffer.length()); headerLineBuffer.append(" by ") .append(session.getConfigurationData().getHelloName()) .append(" (") .append(SOFTWARE_TYPE) .append(") with SMTP ID ") .append(session.getSessionID()); if (((Collection) session.getState().get(SMTPSession.RCPT_LIST)).size() == 1) { // Only indicate a recipient if they're the only recipient // (prevents email address harvesting and large headers in // bulk email) newHeaders.addHeaderLine(headerLineBuffer.toString()); headerLineBuffer.delete(0, headerLineBuffer.length()); headerLineBuffer.append(" for <") .append(((List) session.getState().get(SMTPSession.RCPT_LIST)).get(0).toString()) .append(">;"); newHeaders.addHeaderLine(headerLineBuffer.toString()); headerLineBuffer.delete(0, headerLineBuffer.length()); } else { // Put the ; on the end of the 'by' line headerLineBuffer.append(";"); newHeaders.addHeaderLine(headerLineBuffer.toString()); headerLineBuffer.delete(0, headerLineBuffer.length()); } headerLineBuffer = null; newHeaders.addHeaderLine(" " + rfc822DateFormat.format(new Date())); // Add all the original message headers back in next while (headerLines.hasMoreElements()) { newHeaders.addHeaderLine((String) headerLines.nextElement()); } return newHeaders; } /** * Processes the mail message coming in off the wire. Reads the * content and delivers to the spool. * * @param session SMTP session object * @param headers the headers of the mail being read * @param msgIn the stream containing the message content */ private void processMail(SMTPSession session, MailHeaders headers, InputStream msgIn) throws MessagingException { ByteArrayInputStream headersIn = null; MailImpl mail = null; List recipientCollection = null; try { headersIn = new ByteArrayInputStream(headers.toByteArray()); recipientCollection = (List) session.getState().get(SMTPSession.RCPT_LIST); mail = new MailImpl(session.getConfigurationData().getMailServer().getId(), (MailAddress) session.getState().get(SMTPSession.SENDER), recipientCollection, new SequenceInputStream(new SequenceInputStream(headersIn, msgIn), new ReaderInputStream(new StringReader("\r\n")))); // Call mail.getSize() to force the message to be // loaded. Need to do this to enforce the size limit if (session.getConfigurationData().getMaxMessageSize() > 0) { mail.getMessageSize(); } mail.setRemoteHost(session.getRemoteHost()); mail.setRemoteAddr(session.getRemoteIPAddress()); if (session.getUser() != null) { mail.setAttribute(SMTP_AUTH_USER_ATTRIBUTE_NAME, session.getUser()); } session.setMail(mail); } catch (MessagingException me) { // if we get here, it means that we received a // MessagingException, which would happen BEFORE we call // session.setMail, so the mail object is still strictly // local to us, and we really should clean it up before // re-throwing the MessagingException for our call chain // to process. // // So why has this worked at all so far? Initial // conjecture is that it has depended upon finalize to // call dispose. Not in the MailImpl, which doesn't have // one, but even further down in the MimeMessageInputStreamSource. if (mail != null) { mail.dispose(); } throw me; } finally { if (recipientCollection != null) { recipientCollection.clear(); } recipientCollection = null; if (headersIn != null) { try { headersIn.close(); } catch (IOException ioe) { // Ignore exception on close. } } headersIn = null; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -