FileDocCategorySizeDatePackage
DataCmdHandler.javaAPI DocApache James 2.3.114648Fri Jan 12 12:56:26 GMT 2007org.apache.james.smtpserver

DataCmdHandler

public class DataCmdHandler extends org.apache.avalon.framework.logger.AbstractLogEnabled implements CommandHandler
handles DATA command

Fields Summary
private static final String
SOFTWARE_TYPE
private static final org.apache.mailet.dates.RFC822DateFormat
rfc822DateFormat
Static RFC822DateFormat used to generate date headers
private static final String
SMTP_AUTH_USER_ATTRIBUTE_NAME
The mail attribute holding the SMTP AUTH user name, if any.
private static final char[]
SMTPTerminator
The character array that indicates termination of an SMTP connection
Constructors Summary
Methods Summary
private voiddoDATA(SMTPSession session, java.lang.String argument)
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

        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;
                }
            }
        }
    
public voidonCommand(SMTPSession session)
process DATA command

see
org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession)


              
        
        doDATA(session, session.getCommandArgument());
    
private voidprocessMail(SMTPSession session, org.apache.james.core.MailHeaders headers, java.io.InputStream msgIn)
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

        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;
        }

    
private org.apache.james.core.MailHeadersprocessMailHeaders(SMTPSession session, org.apache.james.core.MailHeaders headers)

        // 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;