001    /*
002     * Copyright 2010 the original author or authors.
003     * 
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package com.hs.mail.smtp.processor;
017    
018    import java.io.IOException;
019    import java.io.InputStream;
020    import java.io.PushbackInputStream;
021    import java.util.StringTokenizer;
022    
023    import com.hs.mail.container.config.Config;
024    import com.hs.mail.container.server.socket.TcpTransport;
025    import com.hs.mail.io.CharTerminatedInputStream;
026    import com.hs.mail.io.MessageSizeException;
027    import com.hs.mail.io.SizeLimitedInputStream;
028    import com.hs.mail.smtp.SmtpException;
029    import com.hs.mail.smtp.SmtpSession;
030    import com.hs.mail.smtp.message.SmtpMessage;
031    
032    /**
033     * Handler for DATA command. Reads in message data, creates header, and store to
034     * message spool for delivery.
035     * 
036     * @author Won Chul Doh
037     * @since May 29, 2010
038     * 
039     */
040    public class DataProcessor extends AbstractSmtpProcessor {
041            
042            /**
043             * The character array that indicates termination of an SMTP connection
044             */
045            private final static char[] DATA_TERMINATOR = { '\r', '\n', '.', '\r', '\n' };
046    
047            @Override
048            protected void doProcess(SmtpSession session, TcpTransport trans,
049                            StringTokenizer st) throws SmtpException {
050                    SmtpMessage message = session.getMessage(); 
051                    if (message == null || message.getFrom() == null) {
052                            throw new SmtpException(SmtpException.COMMAND_OUT_OF_SEQUENCE);
053                    }
054                    if (message.getRecipientsSize() == 0) {
055                            throw new SmtpException(SmtpException.NO_VALID_RECIPIENTS);
056                    }
057                    if (st.hasMoreTokens()) {
058                            throw new SmtpException(SmtpException.INVALID_COMMAND_PARAM);
059                    }
060                    
061                    session.writeResponse("354 Start mail input; end with <CRLF>.<CRLF>");
062                    
063                    long maxMessageSize = Config.getMaxMessageSize();
064                    try {
065                            String received = new StringBuilder().append("Received: from ").append(session.getClientDomain()).append(" (").append(session.getRemoteHost()).append(" [").append(session.getRemoteIP()).append("])\r\n")
066                                            .append("\tby ").append(Config.getHelloName()).append(" with ").append(session.getProtocol()).append(" id ").append(session.getSessionID()).append(";\r\n")
067                                            .append("\t").append(message.getDate()).append("\r\n").toString();
068                            InputStream msgIn = new PushbackInputStream(trans.getInputStream(),
069                                            received.length());
070                            ((PushbackInputStream) msgIn).unread(received.getBytes("ASCII"));
071                            if (maxMessageSize > 0) {
072                                    // If message size limit has been set, wrap msgIn with a
073                                    // SizeLimitedInputStream
074                                    msgIn = new SizeLimitedInputStream(msgIn, maxMessageSize);
075                            }
076                            message.setContent(new CharTerminatedInputStream(msgIn,
077                                            DATA_TERMINATOR));
078                            message.store();
079                            // Place the mail on the spool for processing
080                            message.createTrigger();
081                            // Clear reverse-path buffer, forward-path buffer, and mail data
082                            // buffer
083                            session.setMessage(null);
084                            session.writeResponse("250 2.6.0 OK");
085                    } catch (IOException e) {
086                            // If exception caught, remove temporary files
087                            message.dispose();
088                            if (e instanceof MessageSizeException) {
089                                    StringBuilder errorBuffer = new StringBuilder(256)
090                                                    .append("Rejected message from ")
091                                                    .append(session.getClientDomain())
092                                                    .append(" from host ")
093                                                    .append(session.getRemoteHost())
094                                                    .append(" (")
095                                                    .append(session.getRemoteIP())
096                                                    .append(") exceeding system maximum message size of ")
097                                                    .append(maxMessageSize);
098                                    logger.error(errorBuffer.toString());
099                                    throw new SmtpException("552 5.3.4 Error processing message: "
100                                                    + e.getMessage());
101                            } else {
102                                    StringBuilder errorBuffer = new StringBuilder(256)
103                                                    .append("Unknown error occurred while processing DATA.");
104                                    logger.error(errorBuffer.toString(), e);
105                                    throw new SmtpException("451 4.0.0 Error processing message: "
106                                                    + e.getMessage());
107                            }
108                    }
109            }
110            
111    }