freemail.smtp.SMTPHandler.java Source code

Java tutorial

Introduction

Here is the source code for freemail.smtp.SMTPHandler.java

Source

/*
 * SMTPHandler.java
 * This file is part of Freemail
 * Copyright (C) 2006,2007,2008 Dave Baker
 * Copyright (C) 2007 Alexander Lehmann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package freemail.smtp;

import java.net.Socket;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.File;
import java.io.PrintWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Vector;

import freemail.Freemail;
import freemail.AccountManager;
import freemail.FreemailAccount;
import freemail.MessageSender;
import freemail.ServerHandler;
import freemail.utils.EmailAddress;
import freemail.utils.Logger;

import org.bouncycastle.util.encoders.Base64;

public class SMTPHandler extends ServerHandler implements Runnable {
    private final OutputStream os;
    private final PrintStream ps;
    private final BufferedReader bufrdr;
    private FreemailAccount account;
    private final MessageSender msgsender;
    public static final String MY_HOSTNAME = "localhost";

    private final AccountManager accountmanager;

    private Vector<EmailAddress> to;

    public SMTPHandler(AccountManager accMgr, Socket client, MessageSender sender) throws IOException {
        super(client);
        accountmanager = accMgr;
        this.msgsender = sender;
        this.account = null;
        this.os = client.getOutputStream();
        this.ps = new PrintStream(this.os);
        this.bufrdr = new BufferedReader(new InputStreamReader(client.getInputStream()));

        this.to = new Vector<EmailAddress>();
    }

    @Override
    public void run() {
        this.sendWelcome();

        String line;
        try {
            while (!this.client.isClosed() && (line = this.bufrdr.readLine()) != null) {
                SMTPCommand msg = null;
                try {
                    //Logger.normal(this,line);
                    msg = new SMTPCommand(line);
                } catch (SMTPBadCommandException bce) {
                    Logger.debug(this, "Parsing failed, line was: " + line);
                    continue;
                }

                Logger.debug(this, "Received: " + line);
                this.dispatch(msg);
            }

            this.client.close();
        } catch (IOException ioe) {

        }
    }

    private void dispatch(SMTPCommand cmd) {
        if (cmd.command.equals("helo")) {
            this.handle_helo(cmd);
        } else if (cmd.command.equals("ehlo")) {
            this.handle_ehlo(cmd);
        } else if (cmd.command.equals("quit")) {
            this.handle_quit(cmd);
        } else if (cmd.command.equals("turn")) {
            this.handle_turn(cmd);
        } else if (cmd.command.equals("auth")) {
            this.handle_auth(cmd);
        } else if (cmd.command.equals("mail")) {
            this.handle_mail(cmd);
        } else if (cmd.command.equals("rcpt")) {
            this.handle_rcpt(cmd);
        } else if (cmd.command.equals("data")) {
            this.handle_data(cmd);
        } else if (cmd.command.equals("rset")) {
            this.handle_rset(cmd);
        } else {
            Logger.error(this, "Unknown command: " + cmd.command);
            this.ps.print("502 Unimplemented\r\n");
        }
    }

    private void handle_helo(SMTPCommand cmd) {
        this.ps.print("250 " + MY_HOSTNAME + "\r\n");
    }

    private void handle_ehlo(SMTPCommand cmd) {
        this.ps.print("250-" + MY_HOSTNAME + "\r\n");
        this.ps.print("250 AUTH LOGIN PLAIN \r\n");
    }

    private void handle_quit(SMTPCommand cmd) {
        this.ps.print("221 " + MY_HOSTNAME + "\r\n");
        try {
            this.client.close();
        } catch (IOException ioe) {

        }
    }

    private void handle_turn(SMTPCommand cmd) {
        this.ps.print("502 No\r\n");
    }

    private void handle_auth(SMTPCommand cmd) {
        String uname;
        String password;

        if (cmd.args.length == 0) {
            this.ps.print("504 No auth type given\r\n");
            return;
        } else if (cmd.args[0].equalsIgnoreCase("login")) {
            this.ps.print("334 " + new String(Base64.encode("Username:".getBytes())) + "\r\n");

            String b64username;
            String b64password;
            try {
                b64username = this.bufrdr.readLine();
            } catch (IOException ioe) {
                return;
            }
            if (b64username == null)
                return;

            this.ps.print("334 " + new String(Base64.encode("Password:".getBytes())) + "\r\n");
            try {
                b64password = this.bufrdr.readLine();
            } catch (IOException ioe) {
                return;
            }
            if (b64password == null)
                return;

            uname = new String(Base64.decode(b64username.getBytes()));
            password = new String(Base64.decode(b64password.getBytes()));
        } else if (cmd.args[0].equalsIgnoreCase("plain")) {
            String b64creds;

            if (cmd.args.length > 1) {
                b64creds = cmd.args[1];
            } else {
                this.ps.print("334 \r\n");
                try {
                    b64creds = this.bufrdr.readLine();
                    if (b64creds == null)
                        return;
                } catch (IOException ioe) {
                    return;
                }
            }

            String creds_plain = new String(Base64.decode(b64creds.getBytes()));
            String[] creds = creds_plain.split("\0");

            if (creds.length < 2)
                return;

            // most documents seem to reckon you send the
            // username twice. Some think only once.
            // This will work either way.
            uname = creds[0];
            // there may be a null first (is this always the case?)
            if (uname.length() < 1) {
                uname = creds[1];
            }
            password = creds[creds.length - 1];
        } else {
            this.ps.print("504 Auth type unimplemented - weren't you listening?\r\n");
            return;
        }

        account = accountmanager.authenticate(uname, password);
        if (account != null) {
            this.ps.print("235 Authenticated\r\n");
        } else {
            this.ps.print("535 Authentication failed\r\n");
        }
    }

    private void handle_mail(SMTPCommand cmd) {
        if (this.account == null) {
            this.ps.print("530 Authentication required\r\n");
            return;
        }

        this.to.clear();

        // we don't really care.
        this.ps.print("250 OK\r\n");
    }

    private void handle_rcpt(SMTPCommand cmd) {
        if (cmd.args.length < 1) {
            this.ps.print("504 Insufficient arguments\r\n");
            return;
        }

        if (this.account == null) {
            this.ps.print("530 Authentication required\r\n");
            return;
        }

        String allargs = new String();
        for (int i = 0; i < cmd.args.length; i++) {
            allargs += cmd.args[i];
        }

        String[] parts = allargs.split(":", 2);
        if (parts.length < 2) {
            this.ps.print("504 Can't understand that syntax\r\n");
            return;
        }

        EmailAddress addr = new EmailAddress(parts[1]);
        if (addr.user == null || addr.domain == null) {
            this.ps.print("504 Bad address\r\n");
            return;
        }

        if (!addr.is_freemail_address()) {
            this.ps.print("553 Not a Freemail address\r\n");
            return;
        }

        this.to.add(addr);

        this.ps.print("250 OK\r\n");
    }

    private void handle_data(SMTPCommand cmd) {
        if (this.account == null) {
            this.ps.print("530 Authentication required\r\n");
            return;
        }

        if (this.to.size() == 0) {
            this.ps.print("503 RCPT first\r\n");
            return;
        }

        try {
            File tempfile = File.createTempFile("freemail-", ".message", Freemail.getTempDir());
            PrintWriter pw = new PrintWriter(new FileOutputStream(tempfile));

            this.ps.print("354 Go crazy\r\n");

            String line;
            boolean done = false;
            while ((line = this.bufrdr.readLine()) != null) {
                if (line.equals(".")) {
                    done = true;
                    break;
                }
                pw.print(line + "\r\n");
            }

            pw.close();
            if (!done) {
                // connection closed before the message was
                // finished. bail out.
                tempfile.delete();
                return;
            }

            this.msgsender.sendMessage(this.account, to, tempfile);

            tempfile.delete();

            this.ps.print("250 So be it\r\n");
        } catch (IOException ioe) {
            this.ps.print("452 Can't store message\r\n");
        }
    }

    private void handle_rset(SMTPCommand cmd) {
        this.to.clear();
        this.ps.print("250 Reset\r\n");
    }

    private void sendWelcome() {
        this.ps.print("220 " + MY_HOSTNAME + " ready\r\n");
    }
}