/*
 * Copyright (c) 2014 Wael Chatila / Icegreen Technologies. All Rights Reserved.
 * This software is released under the Apache license 2.0
 * This file has been used and modified.
 * Original file can be found on http://foedus.sourceforge.net
 */
package com.icegreen.greenmail.smtp;

import com.icegreen.greenmail.foedus.util.Workspace;
import com.icegreen.greenmail.server.BuildInfo;
import com.icegreen.greenmail.server.ProtocolHandler;
import com.icegreen.greenmail.smtp.commands.SmtpCommand;
import com.icegreen.greenmail.smtp.commands.SmtpCommandRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;

class SmtpHandler implements ProtocolHandler {
    private static final Logger log = LoggerFactory.getLogger(SmtpHandler.class);

    // protocol and configuration global stuff
    SmtpCommandRegistry registry;
    SmtpManager manager;
    Workspace workspace;

    // session stuff
    SmtpConnection conn;
    SmtpState state;

    // command parsing stuff
    boolean quitting;
    String currentLine;
    private Socket socket;

    public SmtpHandler(SmtpCommandRegistry registry,
                       SmtpManager manager, Workspace workspace, Socket socket) {
        this.registry = registry;
        this.manager = manager;
        this.workspace = workspace;
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            conn = new SmtpConnection(this, socket);
            state = new SmtpState(workspace);
            quitting = false;

            sendGreetings();

            while (!quitting) {
                handleCommand();
            }

        } catch (SocketTimeoutException ste) {
            conn.send("421 Service shutting down and closing transmission channel");

        } catch (Exception e) {
            // Closing socket on blocked read
            if(!quitting) {
                log.error("Unexpected error handling connection, quitting=", e);
                throw new IllegalStateException(e);
            }
        } finally {
            if (null != state) {
                state.clearMessage();
            }
        }
    }

    protected void sendGreetings() {
        conn.send("220 " + conn.getServerGreetingsName() +
                " GreenMail SMTP Service v" + BuildInfo.INSTANCE.getProjectVersion() + " ready");
    }

    protected void handleCommand()
            throws IOException {
        currentLine = conn.receiveLine();

        if (currentLine == null) {
            close();

            return;
        }

        // eliminate invalid line lengths before parsing
        if (!commandLegalSize()) {

            return;
        }

        String commandName = currentLine.substring(0, 4).toUpperCase();

        SmtpCommand command = registry.getCommand(commandName);

        if (command == null) {
            conn.send("500 Command not recognized");

            return;
        }

        command.execute(conn, state, manager, currentLine);
    }

    private boolean commandLegalSize() {
        if (currentLine.length() < 4) {
            conn.send("500 Invalid command. Must be 4 characters");

            return false;
        }

        if (currentLine.length() > 4 &&
                currentLine.charAt(4) != ' ') {
            conn.send("500 Invalid command. Must be 4 characters");

            return false;
        }

        if (currentLine.length() > 1000) {
            conn.send("500 Command too long.  1000 character maximum.");

            return false;
        }

        return true;
    }

    @Override
    public void close() {
        if (log.isTraceEnabled()) {
            final StringBuilder msg = new StringBuilder("Closing SMTP(s) handler connection");
            if (null != socket) {
                msg.append(' ').append(socket.getInetAddress()).append(':')
                        .append(socket.getPort());
            }
            log.trace(msg.toString());
        }
        quitting = true;
        try {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        } catch(IOException ignored) {
            //empty
        }
    }
}
@@ -729,16 +729,23 @@
 ion;%0D%0A%0D%0A
+public 
 class Sm
@@ -790,21 +790,23 @@
 %0D%0A    pr
-ivate
+otected
  static 
@@ -919,24 +919,34 @@
  stuff%0D%0A    
+protected 
 SmtpCommandR
@@ -964,24 +964,34 @@
 istry;%0D%0A    
+protected 
 SmtpManager 
@@ -1004,16 +1004,26 @@
 r;%0D%0A    
+protected 
 Workspac
@@ -1060,24 +1060,34 @@
  stuff%0D%0A    
+protected 
 SmtpConnecti
@@ -1096,24 +1096,34 @@
  conn;%0D%0A    
+protected 
 SmtpState st
@@ -1164,16 +1164,26 @@
 ff%0D%0A    
+protected 
 boolean 
@@ -1193,24 +1193,34 @@
 tting;%0D%0A    
+protected 
 String curre
@@ -1234,21 +1234,23 @@
 %0D%0A    pr
-ivate
+otected
  Socket 
@@ -3260,13 +3260,15 @@
   pr
-ivate
+otected
  boo
@@ -4469,8 +4469,10 @@
     %7D%0D%0A%7D
+%0D%0A

Allow for SmtpHandler override