
/**
@version 1.01 2001-06-26
@author Cay Horstmann
 */
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.*;

import java.io.*;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;


public class JavaMail {

    public static void main(String[] args) {
        JFrame frame = new JavaTestFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
/**
The frame for the mail GUI.
 */
class JavaTestFrame extends JFrame {

    public JavaTestFrame() {

        setSize(width, height);
        setTitle("JavaMail");

        getContentPane().setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 0;
        gbc.weighty = 0;

        /* Dirección de correo fuente*/
        gbc.weightx = 0;
        add(new JLabel("From:"), gbc, 0, 0, 1, 1);
        gbc.weightx = 100;
        from = new JTextField(20);
        add(from, gbc, 1, 0, 1, 1);

        /* Contraseña de dirección de correo fuente*/
        gbc.weightx = 0;
        add(new JLabel("Password:"), gbc, 0, 1, 1, 1);
        gbc.weightx = 100;
        password = new JPasswordField(20);
        add(password, gbc, 1, 1, 1, 1);

        /* Dirección de correo destino*/
        gbc.weightx = 0;
        add(new JLabel("To:"), gbc, 0, 2, 1, 1);
        gbc.weightx = 100;
        to = new JTextField(20);
        add(to, gbc, 1, 2, 1, 1);

        /* Nombre o IP de servidor smtp*/
        gbc.weightx = 0;
        add(new JLabel("SMTP server:"), gbc, 0, 3, 1, 1);
        gbc.weightx = 100;
        smtpServer = new JTextField(20);
        add(smtpServer, gbc, 1, 3, 1, 1);

        /* Puerto que escucha el servidor SMTP*/
        gbc.weightx = 0;
        add(new JLabel("SMTP port:"), gbc, 0, 4, 1, 1);
        gbc.weightx = 100;
        smtpPort = new JTextField(20);
        add(smtpPort, gbc, 1, 4, 1, 1);

        /* Contenido del mensaje*/
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weighty = 100;
        message = new JTextArea();
        add(new JScrollPane(message), gbc, 0, 5, 2, 1);

        /* Contiene un log de la comunicación entre el servidor SMTP y el localhost*/
        communication = new JTextArea();
        add(new JScrollPane(communication), gbc, 0, 6, 2, 1);

        gbc.weighty = 0;
        JButton sendButton = new JButton("Send");
        sendButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent evt) {
                new Thread() {

                    @Override
                    public void run() {
                        if (!(from.getText().equals("") || password.getText().equals("") ||
                                to.getText().equals("") || smtpServer.getText().equals("") ||
                                smtpPort.getText().equals(""))) {
                            sendMail();
                        } else {
                            communication.append("Es necesario rellenar todos los campos.\n");
                        }
                    }
                }.start();
            }
        });
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(sendButton);
        add(buttonPanel, gbc, 0, 7, 2, 1);


    }

    /**
    Add a component to this frame.
    @param c the component to add
    @param gbc the grid bag constraints
    @param x the grid bax column
    @param y the grid bag row
    @param w the number of grid bag columns spanned
    @param h the number of grid bag rows spanned
     */
    private void add(Component c, GridBagConstraints gbc,
            int x, int y, int w, int h) {
        gbc.gridx = x;
        gbc.gridy = y;
        gbc.gridwidth = w;
        gbc.gridheight = h;
        getContentPane().add(c, gbc);
    }

    /**
    Sends the mail message that has been authored in the GUI.
     */
    public void sendMail() {

        /* Se formatea agregando los separadores al string a ser codificadoen base64*/
        String aux = new String("\000" + from.getText() + "\000" + password.getText());

        /* El codificador en Base64 fue buscado en internet, se verifico que funcionara
        de igual manera que la linea de comando perl utilizada en la Tarea 1*/
        Base64 base64 = new Base64();
        base64login = base64.encode(aux);


        Runtime runTime = Runtime.getRuntime();
        Process process = null;
        try {
            /* Ejecuta openssl y entrega a la variable "in" el flujo de output del programa
            y a out, el flujo de input del programa*/
            process = runTime.exec("/usr/bin/openssl s_client -starttls smtp -crlf -connect " + smtpServer.getText() + ":" + smtpPort.getText());

            in = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));
            out = new PrintWriter(process.getOutputStream());

            /* Envia mensajes al servidor smtp y recibe las respuestas de este*/
            send("ehlo");
            receive();
            send("AUTH PLAIN " + base64login);
            receive();
            send("mail from: <" + from.getText() + ">");
            receive();
            send("rcpt to: <" + to.getText() + ">");
            receive();
            send("data");
            receive();
            send("From: Remitente <" + from.getText() + ">");
            send("To: Destinatario <" + to.getText() + ">");
            send("Subject: none");
            send("");

            StringTokenizer tokenizer = new StringTokenizer(
                    message.getText(), "\n");
            while (tokenizer.hasMoreTokens()) {
                send(tokenizer.nextToken());
            }
            send(".");
            receive();
            send("quit");
            receive();

        } catch (IOException e) {

            System.out.println("Error: " + e);
            System.exit(-1);
        }
        if (process != null) {
            process.destroy();
        }
    }

    /**
    Sends a string to the socket and echoes it in the
    communication text area.
    @param s the string to send.
     */
    public void send(String s) {
        try {
            /* Se pausa el programa por precaución, dado que en la tarea 1 habia
            problemas al enviar todos los mensajes rápidamente*/
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            Logger.getLogger(JavaTestFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        /* Escribe en el log los mensajes del HACIA el servidor smtp y luego
        efectivamente los envía*/
        communication.append(s);
        communication.append("\n");
        out.write(s + "\n");
        out.flush();


    }

    /**
    Receives a string from the socket and displays it
    in the communication text area.
     */
    public void receive() throws IOException {
        String line = "";

        /* La condición del while contiene todos los números identificadores de recepción
         correcta de los mensajes hacia el servidor smtp, es decir, espera a que se recepcione
         correctamente cada mensaje*/
        while (!(line.contains("235") || line.contains("250") || line.contains("354") || line.contains("221"))) {
            line = in.readLine();
            /* Agrega al log el mensaje DESDE el servidor smtp */
            communication.append(line);
            communication.append("\n");
        }
        /* Se agrega una última vez al log, puesto que al llegar el último mensaje
         aceptando el último comando, rompe el while y no alcanza imprimir el ultimo mensaje*/
        communication.append(line);
        communication.append("\n");
    }
    private BufferedReader in;
    private PrintWriter out;
    private JTextField from;
    private JTextField password;
    private JTextField to;
    private JTextField smtpServer;
    private JTextField smtpPort;
    private JTextArea message;
    private JTextArea communication;
    private String base64login;
    public static final int width = 300;
    public static final int height = 300;
}
