Construcción de un cliente para correo electrónico

En esta práctica usted debe completar el código, en lenguaje Java, para construir un cliente para correo electrónico que puede envíar mensajes a hosts remotos. La meta es elaborar el código para programar la interacción entre el agente de usuario y el servidor SMTP remoto. El cliente provee una interface gráfica que contiene los campos para colocar las direcciones del remitente, el destinatario, el asunto y el mensaje. La siguiente gráfica muestra el aspecto de la interface.

[Interface]

Con esta interface, cuando se desee enviar un correo, primero hay que colocar la dirección completa, tanto del remitente como del destinatario, es decir, jose@algunsitio.edu, y no sólo jose. Sólo se puede enviar correo a un destinatario. Además, la parte de la dirección que identifica el dominio del receptor debe escribirse con el nombre canónico del servidor SMTP que acepta el correo en el sistio del destinatario. Por ejemplo, si se desea enviar correo a usuario@algunsitio.edu y el servidor SMTP de algunsitio.edu es correo.algunsitio.edu, se debe utilizar la dirección usuario@correo.algunsitio.edu.

Cuando se termine de construir un mensaje de correo, se debe presionar Enviar para enviarlo.

El código

El programa consta de 4 clases:
 
ClienteDeCorreo La interface de usuario
Mensaje El mensaje de correo
Envoltura Envoltura SMTP alrededor del Mensaje
ConexionSmtp Conexión al servidor SMTP server

Usted debe completar el código de la clase ConexionSmtp para lograr tener un programa que pueda enviar un mensaje de correo a cualquier destinatario.

Los lugares donde usted debe completar código han sido marcados con el comentario /* Para completar */. Cada parte a completar requiere una o más líneas de código.

La clase ClienteDeCorreo implementa una interface de usuario e invoca las otras clases a medida que sea necesario. Cuando se presiones el botón Enviar, la clase ClienteDeCorreo construira un objeto de la clase Mensaje para colocar el mensaje de correo. El objeto Mensaje mantendrá los headers y el cuerpo del mensaje real. Luego el objeto ClienteDeCorreo construira una envoltura SMTP utilizando la clase Envoltura. Esta clase guardará la información del remitente y el destinatario SMTP, el servidor SMTP del dominio del destinatario, y el objeto Mensaje. Entonces el objeto ClienteDeCorreo creará el objeto ConexionSmtp que abre una conexión al servidor SMTP y el objeto ClienteDeCorreo enviará el mensaje sobre la conexión. El envío del correo sucede en tres etapas:

  1. El objeto ClienteDeCorreo crea el objeto ConexionSmtp y abre la conexión al servidor SMTP.
  2. El objeto ClienteDeCorreo envía el mensaje utilizando la función ConexionSmtp.enviar().
  3. El objeto ClienteDeCorreo cierra la conexión SMTP.
La clase Mensaje contiene la función esValido() que es utilizada para revisar la dirección del remitente y la del destinatario para asegurarse que sólo hay una dirección y que la dirección contiene el signo @. El código presentado aquí no hace ninguna evaluación de errores.

Códigos de respuesta

Para envíar un mensaje se debe implementar sólo parte del protocolo SMTP. Para esto sólo se deben implementar los siguientes comandos del protocolo
 
Comando  Código de respuesta
DATA 354
HELO 250
MAIL FROM 250
QUIT 221
RCPT TO 250

La tabla anterior también lista los códigos de respuesta aceptados para cada uno de los comandos SMTP que se deben implementar. Por simplicidad, se puede asumir que cualquier otra respuesta desde el servidor indica un error fatal y se terminará inmediatamente el envío del mensaje. En realidad, SMTP distingue entre errores transientes (códigos de respuesta 4xx) y permanentes (códigos de respuesta 5xx), y al emisor se le permite repetir comandos que hayan generado un error transiente. Revise el apéndice E del RFC 821 para más información.

Además, cuando se abra la conexión con el servidor, él responderá con el código 220.

Nota: El RFC 821 permite el código 251 como respuesta al comando RCPT TO-comando que indica que el destinatario no es un usuario local. Si lo desea, puede verificar manualmente con el comando telnet qué responde su servidor SMTP local.

Pistas

La mayoria del código que usted debe completar es similar al código que usted escribió el la páctica construcción de un servidor web multi-hilos. Usted podría utilizar dicho código para ayudarse.

Para facilitar la depuración del programa, al comienzo, no incluya el código que abre el socket. Se recomienda que utilice las sisguientes definiciones para desdeElServidor y haciaElServidor. Con estas definiciones su programa enviará los comandos a la pantalla. Actuando como servidor SMTP, usted debe proporcionar los códigos de respuesta correctos. Cuando su programa funcione, agregue el código para abrir el socket hacia el servidor.

        desdeElServidor =  new BufferedReader(new InputStreamReader(System.in));
        haciaElServidor =  new DataOutputStream(System.out);
Las líneas para abrir y cerrar el socket, es decir las líneas socketParaConexion = ... en el método constructor y la línea socketParaConexion.close() en la función close(), han sido comentadas de antemano.

Comience completando la función analiceRespuesta(). Usted necesitará esta función en varias partes del código. En la función analiceRespuesta(), se debe utilizar la clase StringTokenizer para analizar sintácticamente las líneas de respuesta. Se puede convertir una cadena a un entero de la siguiente manera:

           int i = Integer.parseInt(argv[0]);
En la función envieComandoSmtp(), se debe utilizar la función writeBytes() para escribir los comandos al servidor. La ventaja de utilizar writeBytes() en lugar de write() es que el primero automáticamente convierte la cadena a bytes, que es lo que el servidor espera. No olvide terminar cada comando con la cadena CRLF.

Usted puede lanzar excepciones como esta:

           throw new IOException();
Usted no debe preocuparse por los detalles, ya que las excepciones en esta práctica son utilizadas sólo para señalar un error, sin dar información detallada sobre qué resultó mal.

Ejercicios opcionales

El programa puede ser más sofisticado si se le agregan las siguientes características. Para implementarlas, deberá modificar también las otras clases (ClienteDeCorreo, Mensaje, and Envoltura).